0.1.18
TextArea Root/Input compound 분해와 Select.ItemIndicator 제거 두 갈래 breaking과 함께, focusableWhenDisabled 모드 / Field.required ctx 전파 / Item ctx propagation / leaf own override 일괄을 도입합니다.
이번 릴리즈는 두 갈래의 breaking — TextArea compound 분해와 Select.ItemIndicator 제거 — 를 포함합니다. 동시에 disabled 상태의 컴포넌트가 Tooltip.Trigger로 감싸일 때 hover/focus가 정상 동작하도록 focusableWhenDisabled 모드를 다수 컴포넌트에 도입하고, leaf component의 own override 패턴(disabled? / readOnly? / size? / loading?)을 일괄 정리합니다.
Breaking Changes — TextArea Root/Input compound 분해
TextArea가 callable 단일 컴포넌트에서 TextArea.Root + TextArea.Input compound로 재구성됩니다. 다른 form primitive(TextInput, Field, Select 등)와 같은 레이아웃·상태 전파 패턴을 따릅니다.
/* Before */
<TextArea status="error" disabled rows={4} placeholder="..." />
/* After */
<TextArea.Root status="error" disabled>
<TextArea.Input rows={4} placeholder="..." />
</TextArea.Root>| Before | After |
|---|---|
TextArea (callable) | TextArea.Root + TextArea.Input (compound) |
TextAreaProps | TextAreaRootProps + TextAreaInputProps |
(단일 <textarea>) | Root는 <div> (border/radius/bg/padding), Input은 <textarea> (typography/scroll) |
Breaking — <TextArea ... /> 사용처는 모두 <TextArea.Root> + <TextArea.Input> 패턴으로 교체해 주세요. TextAreaProps를 import한 코드는 TextAreaRootProps 또는 TextAreaInputProps로 분리됩니다. Root에 size / status / disabled / readOnly를, Input에 rows / placeholder 등을 두는 것이 권장 분리입니다. 시각은 동일합니다.
TextAreaContext는 size / disabled / readOnly를 자식 Input에 전파하므로, 같은 값을 두 번 적을 필요가 없습니다.
Skeleton.TextArea에도 size? prop이 추가되어 line-height × rows 기반으로 자리 크기가 계산됩니다. 로딩이 끝나고 실제 TextArea로 치환될 때 layout shifting이 발생하지 않습니다.
Breaking Changes — Select.ItemIndicator 제거
Select.ItemIndicator 컴포넌트가 제거됩니다. 동일 역할은 다중 선택일 때 Select.ItemCheckbox, 단일 선택일 때 Select.ItemRadio가 담당합니다.
Breaking — Select.ItemIndicator / SelectItemIndicator / SelectItemIndicatorProps 사용처는 Select.ItemCheckbox 또는 Select.ItemRadio로 교체해 주세요. itemIndicatorClass 스타일도 함께 제거됩니다.
focusableWhenDisabled 모드
disabled 상태에서도 포커스를 유지해 hover/focus 이벤트가 발화되도록 하는 focusableWhenDisabled? prop이 다수 컴포넌트에 추가됩니다. Tooltip.Trigger로 감싸 disabled 컨트롤에 도움말을 다는 패턴이 정상 동작합니다.
대상: Tag.Root, Link.Root, Dropdown.Trigger, Select.Trigger. (이미 지원하던 Pagination triggers는 동작 일관성 정리.)
<Tooltip.Root>
<Tooltip.Trigger
render={
<Button.Root disabled focusableWhenDisabled>
<Button.Text>저장</Button.Text>
</Button.Root>
}
/>
<Tooltip.Popup>변경 사항이 없습니다</Tooltip.Popup>
</Tooltip.Root>native disabled는 keyboard / touch / screen reader 사용자가 도달하지 못하므로, 완전한 a11y를 위해 focusableWhenDisabled를 명시하는 패턴을 권장합니다. Tooltip.Trigger는 dev 모드에서 disabled child가 focusableWhenDisabled 없이 들어오면 console warn을 출력합니다.
이 모드의 동작에는 @featuring-corp/design-tokens@0.1.1의 normalize 변경이 함께 반영됩니다. components/preset/* 또는 동등 버전의 design-tokens normalize를 사용하는 환경에서 정상 동작합니다.
Field.required 컨텍스트 전파
Field.Root에 required? prop이 추가되고, FieldContext로 자식에 전파됩니다.
<Field.Root required>
<Field.Label>이메일</Field.Label>
<TextInput.Root>
<TextInput.Input />
</TextInput.Root>
</Field.Root>Field.Label이*인디케이터를 자동 표시 (ownrequiredprop으로 override 가능)TextInput.Input/TextArea.Input/Select.Root가aria-required="true"를 자동 부여
Field.Message는 status="error"일 때 role="alert", 그 외에는 aria-live="polite"로 분기되어 검증 메시지 가시성이 개선됩니다.
Item ctx propagation (Dropdown / Menu / Select)
Dropdown.Item / Dropdown.Action이 자식 ItemCheckbox / ItemRadio에 disabled / size를 컨텍스트로 자동 전파합니다. Menu / Select도 동일하게 동작합니다.
{/* Item에 disabled를 한 번만 박으면 자식 indicator도 자동으로 disabled */}
<Menu.Item disabled>
<Menu.ItemCheckbox checked={value} />
내보내기
</Menu.Item>base-ui parent(BaseMenu.Item / BaseSelect.Item)가 cloneElement로 박는 data-disabled도 own > inherited 우선순위로 흡수합니다. own이 명시되면 inherited를 무시하므로 disabled={false}로 부모의 disabled를 override 하는 것도 허용합니다.
Menu.ItemCheckbox / Menu.ItemRadio / Select.ItemCheckbox / Select.ItemRadio에서 disabled prop은 더 이상 직접 받지 않습니다 — 부모 Item에 박아 주세요. 시각 contract와 props 타입은 Dropdown* 단일 소스를 그대로 extends 합니다.
Leaf own override 일괄 적용
compound leaf가 own > ctx > fallback 패턴으로 prop을 받을 수 있도록 다음 컴포넌트에 own prop이 확장됩니다.
| 컴포넌트 | 추가 own prop |
|---|---|
Button.Icon | disabled?, loading? |
Tag.Icon, Tag.RemoveButton | disabled? |
Tabs.Icon | disabled?, active? |
SegmentedControl.Icon | disabled?, selected? |
TextInput.Input, TextInput.Icon | disabled?, readOnly? |
TextArea.Input | disabled?, readOnly?, size? |
Label.Text | disabled? |
Select.Action, Select.Button | size? |
함께 size prop의 own > ctx > fallback 우선순위가 모든 leaf에서 일관되게 적용됩니다 — 이전 0.1.17까지는 일부 leaf에서만 동작했습니다.
prop 결합 연산자도 || → ?? 로 통일됩니다. disabled={false} / readOnly={false}로 컨텍스트의 값을 끄는 것이 허용됩니다.
Tabs / SegmentedControl 시각 정합
Tabs는 Base UI Tabs.Indicator를 자동 렌더해 List/Panels에서 활성 탭의 underline이 따라 움직입니다. 동시에 hover / press preview transition이 추가되어 비활성 탭의 호버·프레스 상태에서 미세한 underline preview가 표시됩니다.
SegmentedControl은 내부적으로 Base UI Toggle.Group / Toggle로 위임되도록 리팩터되어, single-select / multi-select 동작이 표준 Toggle 의미론에 정렬됩니다. 시각·동작은 동일합니다.
내부 정리
useRenderComponent에stateAttributesMapping옵션이 도입되고,state.disabled/state.readOnly가 truthy일 때data-*+aria-*가 자동으로 부여됩니다. 각 컴포넌트가 수동으로 박던extraProps: { 'data-disabled': ..., 'aria-readonly': ... }가 일괄 제거되었습니다 (소비자 영향 없음, 내부 일관성).Menu/Select의Item/Action/Button/ItemCheckbox/ItemRadioprops 타입이Dropdown*타입을 직접 extends 하도록 통합됩니다. 시각 contract가 단일 소스로 모입니다.Select.Root의 base-ui 패스스루 14개 prop이...basePropsspread로 단순화됩니다.select-prim/multi-select-prim(CoreSelectPrim/CoreMultiSelectPrim) 내부 디렉터리가 다른Core*와 동일한legacy/경로로 이동합니다. 소비자가 named import로 사용하므로 외부 영향은 없습니다.- CSS 가드 셀렉터가
:not(:disabled, [data-disabled])형태로 통일되어 nativedisabled와 base-uidata-disabled를 동시에 가드합니다. Calendar.Close의children=function분기에서 hook 순서가 깨지던 문제를 별도 컴포넌트로 분리해 해소합니다.Tag/LinkCSS의pointer-events: none을 제거(cursor: not-allowed유지)해 disabled+Tooltip 결합이 가능해집니다. click 차단은 핸들러에서 처리됩니다.Tooltip스타일이 레거시typoVariant/flex의존을 끊고typoPropertyMap['$body-1']을 인라인으로 사용합니다.