여러 앱, 하나의 시스템
일반적인 실패 사례: 앱 A를 위해 키트를 만들었는데, 앱 B의 버튼이 4px 더 높고 2px 더 둥글어서 B가 "이번 한 번만" 포크합니다. 앱이 5개가 되면 6개의 버튼, 8개의 셀렉트, 3개의 모달이 생기고, 모두 동일한 작업을 수행합니다.
공유 컴포넌트를 토큰 크기로 만드세요. 동작, 마크업 및 상태를 전달하고 각 테마가 측정값을 제공합니다. 그런 다음 앱별로 통합하세요.
토큰 크기 컴포넌트
앱마다 다른 모든 측정값을 테마 토큰에서 읽어옵니다.
@theme {
--spacing-btn-sm: 1.75rem; --spacing-btn-px-sm: 0.625rem;
--spacing-btn-md: 2.25rem; --spacing-btn-px-md: 0.875rem;
--spacing-field: 2.125rem; /* input / select height */
--text-label: 0.8125rem;
}
md: "h-btn-md px-btn-px-md text-sm",
icon: "size-btn-md",
그 다음:
- 제품군은 한 번의 테마에서 토큰을 재정의하고, 그 안의 모든 앱은 고유한 측정 컨트롤을 얻습니다.
- 반경, 채우기 및 그림자는 이미 토큰입니다 (
rounded-field,bg-accent,--shadow-card). - 채우기는 색상과 장식 클래스를 조합합니다. 예를 들어
bg-accent accent-fill입니다. 장식(광택)만 정의하는 테마는 투명 버튼을 절대 렌더링하지 않습니다.
references/button.tsx를 참조하세요.
앱별 통합 과정
-
로컬 복사본 목록을 작성합니다. 각 앱에서
export function (Btn|Button|IconBtn|Select|Field|Input|Modal|Dialog|Sheet|Badge|Empty|Pager|Toast|Stat|Status|Spinner)를 찾습니다. 호출 지점 수를 셉니다. -
테마 적용 후 공유 컴포넌트와 일치하는 항목을 교체하고 삭제합니다. 변형 이름을 명시적으로 매핑하고 매핑을 기록합니다.
로컬 이름 공유 변형 primary,navyaccentsecondaryoutlinequiet,softsubtledanger(solid)danger-solid -
앱별 특화 항목은 앱별로 유지합니다:
- 도메인 위젯, 차트 및 앱 크롬;
- 모양이 브랜드 자체인 컨트롤 (iOS 슬래브, Carbon 평면 컨트롤, 48px 알약 CTA);
- 공유 컴포넌트에 없는 동작이 있는 컨트롤 (삼중 상태 체크박스, 제거 가능한 칩, "임의" 재설정 항목, 다중 선택 필터).
얇은 래퍼는 한 가지를 추가할 때 괜찮습니다. 그 이유를 주석에 적으세요.
-
네이티브
<select>및 메뉴 기반 셀렉트를 하나의 Select로 교체합니다.- Radix Select는
""를 "값 없음"으로 예약합니다."all"과 같은 센티넬을 사용하고 다시 매핑하세요. aria-labelledby및aria-describedby를 전달하세요.selectOption()을 호출하는 워크스루 스크립트는 콤보박스를 클릭한 다음 옵션을 클릭해야 합니다.
- Radix Select는
-
각 앱을 자체 스크린샷과 대조하여 확인합니다(
visual-regression-pass참조). 실제 차이는 일반적으로 의도된 것입니다: 크기, 광택, 누름 스케일.
tailwind-merge 함정 (cn())
tailwind-merge는 Tailwind의 내장 스케일만 알고 있습니다. 사용자 정의 토큰은 세 가지 조용한 방식으로 깨집니다.
- 알 수 없는 간격 토큰.
cn("h-btn-md", "h-10")은 둘 다 유지합니다.h-btn-md가 높이인지 모르기 때문에 스타일시트 순서가 우선순위를 결정합니다. 호출자의h-10이 토큰에 밀려 40px 버튼이 36px로 렌더링되었습니다. 토큰을 등록하세요:extendTailwindMerge({ extend: { theme: { spacing: ["btn-sm","btn-md","field", …] } } }). - 알 수 없는 텍스트 크기.
cn("text-meta", "text-ink")는text-meta를 색상으로 처리하고 드롭합니다. 크기를 등록하세요:theme: { text: ["meta","micro","label"] }. - 사용자 정의 그림자 및 색상.
text-14가text-t3옆에 있거나, 임의의shadow-[var(--x)]가 명명된 그림자 옆에 있을 때: 동일한 문제입니다. 코드베이스가 모든 곳에 폐쇄된 사용자 정의 스케일을 가지고 있다면, 거기서는 일반clsx를 사용하세요.
references/cn.ts를 참조하세요.
초점과 유효성 검사
aria-invalid:ring-*스타일은focus-visible:ring-*보다 우선하여 초점이 맞춰진 유효하지 않은 필드에서 초점 링을 숨깁니다.aria-invalid:not-focus-visible:ring-…을 작성하세요.- 폼 내부의
<button>은 폼을 제출합니다. 공유 Button이type="button"을 기본값으로 하지 않으면 변환된 모든 제출이 아닌 호출 지점에서 설정해야 합니다.