콘텐츠로 이동

@gj-kit/expo-ui 핵심 개념

이름 없는 IconButton·Tabs·Slider가 스크린 리더 버그가 아니라 컴파일 에러가 되는 React Native·웹 UI primitive 모음입니다.

React Native 디자인 시스템의 사고는 조용히 일어납니다. accessibility label 없는 IconButton이 그대로 배포되고, Tabs의 value 오타 하나로 panel이 빈 화면이 되고, EmptyState의 action이 onPress 없이 눌러도 아무 일 없는 버튼으로 렌더되고, 손으로 조립한 theme 객체가 style에 undefined를 흘립니다. 어느 것도 빌드를 깨뜨리지 않으니 스크린 리더에서, 프로덕션에서, 남의 기기에서야 드러납니다.

접근성은 컴포넌트 계약의 일부입니다

섹션 제목: “접근성은 컴포넌트 계약의 일부입니다”

접근성 이름, press handler, range label은 컴포넌트 타입이 요구하며 레이아웃 뒤에 확인할 리뷰 체크리스트가 아닙니다.

제어 상태는 완전한 형태를 가집니다

섹션 제목: “제어 상태는 완전한 형태를 가집니다”

Tabs는 literal item value를 panel까지 전달하므로 누락한 panel은 빈 화면이 아니라 타입 오류가 됩니다.

  • Android 팝업 좌표를 실제로 측정합니다 — Android anchored Menu·Select는 root 기준 트리거 page 좌표를 측정해 full-window Modal과 맞춥니다. 상태 표시줄 높이를 고정값으로 더하지 않으며 iOS·웹의 기존 배치 경로는 유지합니다.
  • 접근성 이름을 타입이 요구합니다 — accessibilityLabel 없는 IconButton, 이름을 유추할 수 없는 rich children Button, thumb이 둘인데 label은 하나인 range Slider가 모두 타입 검사에서 거부됩니다.
  • 죽은 버튼은 컴파일되지 않습니다 — Button·IconButton의 interaction prop이 union이라 disabled도 loading도 아니면 onPress가 필수이고, EmptyState의 action은 label과 onPress를 함께 요구합니다. 눌러도 아무 일 없는 버튼은 애초에 컴파일되지 않습니다.
  • Tabs는 panel을 잃지 않습니다 — panels 타입이 Readonly<Record<NoInfer<ItemValue>, NonNullable<ReactNode>>>이고 value에도 NoInfer가 걸려 있어, items에 없는 value 오타는 물론 panel 하나 누락이나 null panel까지 타입 검사에서 걸립니다.
  • theme은 createTheme만 만듭니다 — UiProvider의 theme prop은 createTheme·createThemes가 찍어낸 branded Theme·ThemePair만 받습니다. 손으로 조립한 token 객체는 컴파일 에러라, 키가 빠진 반쪽 theme이 런타임에 undefined 스타일로 새지 않습니다.
  • 소스에 디자인 리터럴이 없습니다 — guard 테스트가 src/components 아래 .tsx 파일을 재귀로 훑어, 따옴표 안 hex 색·숫자 fontSize·따옴표 안 숫자 fontWeight가 하나라도 남아 있으면 실패합니다. 토큰만 쓴다는 규칙을 리뷰가 아니라 테스트가 지킵니다.

optional safe-area와 React Native Web peer는 문서화된 subpath에서만 가져오고, 제품 문구는 프리미티브에 넣지 말고 strings로 주입하세요.

다음은 검증된 레시피 두 개입니다.