Files

3.1 KiB

VD-05: Semantic icon facade와 native-first interaction

  • 상태: Accepted
  • 결정일: 2026-07-26
  • 적용 브랜치: feature-frontend-design-system-platform
  • 재검토: native 계약으로 충족할 수 없는 widget 요구가 확인될 때

배경

앱 셸과 공통 UI는 문자 glyph, raw button/select와 페이지별 focus 처리를 사용했다. 아이콘 공급자와 복합 interaction을 제품 코드에 직접 노출하면 번들, 접근성, vendor type과 교체 비용이 모든 feature로 전파된다. 반대로 실제 요구가 없는 두 개의 headless vendor를 기본 설치하면 skeleton 소비자가 제거해야 할 의존성과 중복 interaction 모델이 생긴다.

결정

  1. 아이콘 공급자는 lockfile 최소 게시 유예를 통과한 lucide-react@1.25.0으로 고정한다.
  2. lucide-react의 static named import는 design-system/icons/vendors/lucide.tsx 한 파일에서만 허용한다.
  3. public API는 MenuIcon, CloseIcon, WarningIcon 같은 의미 이름만 노출한다. vendor component type, icon name, stroke API와 dynamic icon registry는 노출하지 않는다.
  4. 장식 아이콘은 accessibility tree에서 제외한다. 정보를 단독 전달하는 아이콘은 label, icon-only action은 필수 accessibleName을 사용한다.
  5. 현재 복합 control은 native dialog, form control, details와 local TypeScript state model로 구현한다. Menu는 roving focus/typeahead/Escape, Tabs는 manual/automatic activation, Drawer는 modal/background 비활성화/focus restore 계약을 가진다.
  6. React Aria와 Radix는 기본 dependency로 추가하지 않는다. native platform이 collision, nested overlay, virtualized collection 또는 복합 select 요구를 충족하지 못한다는 재현 가능한 요구가 생길 때 prototype과 ADR로 다시 평가한다.
  7. Storybook과 pinned visual baseline은 VD-08/RP-10에서 도입한다. RP-07의 runtime gallery와 browser interaction test는 해당 workshop을 대체한다고 주장하지 않는다.

경계와 검증

  • 제품 코드는 presentation/design-system/index만 import한다.
  • design-system 검사기는 direct icon/headless import, deep import, raw palette, undefined token과 tooltip-only required information fixture를 거절한다.
  • type negative fixture는 accessible name 없는 IconButton을 거절한다.
  • component test는 decorative icon, form control, Menu, Tabs, Drawer와 Toast를 검증한다.
  • Chromium/Firefox E2E는 compact Drawer의 native modal 상태, Escape, focus restore, gallery keyboard interaction과 axe를 검증한다.
  • 로컬 WebKit 실행은 host libevent-2.1.so.7 부재로 환경 검증이 남아 있으며 공급자 선택이나 product behavior의 PASS로 숨기지 않는다.

Rollback

기존 presentation/components/ui/* 경로는 canonical TypeScript primitive를 재수출하므로 소비 코드를 즉시 되돌릴 수 있다. Lucide 제거 시 vendor facade와 semantic icon 구현만 교체하고 제품 API는 유지한다. headless vendor를 나중에 도입해도 public props와 interaction test를 유지한다.