Getting started
Theming
제품별 강조색, 배경, elevation, 모서리 값을 재정의하고 컴포넌트 래퍼에서 관리하는 방법을 설명합니다.
Setup
앱 진입점에서 완성 CSS를 한 번 불러옵니다. 테마를 지정하지 않으면 :root의 light 값을 사용합니다. data-theme="dark" 또는 .dark는 dark 값을 적용합니다. 테마 상태는 앱에서 관리하며 시스템 설정을 자동으로 따르지 않습니다.
next-themes와 연결
@amineslab/ui/theme/next는 next-themes의 ThemeProvider와 useTheme를 다시 내보냅니다. attribute는 data-theme로 설정합니다. color-scheme은 패키지 CSS가 적용하므로 비활성화합니다. hydration 전에 attribute가 바뀌므로 html에 suppressHydrationWarning을 붙입니다.
일부 영역에 다른 테마 적용
ThemeScope는 영역과 그 안에서 여는 Select, Dialog, Tooltip 등의 포털 표면에 같은 light 또는 dark 테마를 전달합니다. theme을 생략하면 상위 scope를 따르고, scope 밖에서는 기존 문서 테마를 사용합니다. asChild는 별도 div 없이 자식 요소를 경계로 사용합니다. 임의의 CSS 변수나 브랜드 속성은 복사하지 않습니다.
Token 계층
색상 토큰은 원시 값, 서비스별 원시 alias, 의미별 palette, 배경에 얹는 elevation, 컴포넌트 역할별 토큰 순서로 참조합니다. 제품 테마에서는 필요한 alias, 역할별 토큰, radius만 재정의합니다.
원시 palette와 alias의 전체 목록, 의미별 palette와 역할별 토큰의 light·dark 값은 Color 페이지에 있습니다.
단계 숫자와 OKLCH
기본 palette는 Tailwind CSS v4와 동일한 11단계(50~950)이며 숫자는 색의 강도입니다. 50이 가장 옅고 950이 가장 짙습니다. 원시 값은 light/dark에서 변하지 않는 Tailwind oklch(...) 값입니다. 중성·colored elevation은 합성식을 사용할 수 있고, 역할별 토큰은 이미 정의된 palette나 elevation을 참조해야 합니다. 예를 들어 success-300은 light에서 green-300, dark에서 green-700입니다. 전체 원시 값은 Color 페이지와 Tailwind 색상 문서에 있습니다.
제품 색상 override
제품 식별자는 테마 상태를 나타내는 data-theme와 분리해 data-brand="product"처럼 지정합니다. 원시 값은 유지하고, gray/accent alias는 50~950 전체 ramp를 선택합니다. 화면에서는 의미별 palette, elevation, 역할별 토큰을 사용합니다. 역할별 토큰에도 OKLCH 값을 직접 넣지 말고 기존 palette나 elevation을 참조합니다. 컴포넌트 CSS는 주로 역할별 토큰을 참조합니다. scrim과 tooltip의 내부 대비 색은 이름이 있는 원시 값을 직접 사용하므로 내부 클래스를 재정의하지 않습니다.
Gray family 선택
기본 gray는 Neutral입니다. 서비스 색조에 맞춰 Neutral, Slate, Stone, Zinc 중 하나를 고른 뒤 50부터 950까지 해당 계열로 연결합니다. 네 계열 모두 Tailwind의 OKLCH 값을 사용합니다. alias는 원시 값이므로 dark selector에서 다시 정의하지 않습니다.
Accent palette 지정
기본 accent는 Amineslab Coral입니다. 아래 예시는 서비스 accent를 Violet 원시 ramp에 연결합니다. 자체 ramp가 있다면 --color-violet-* 위치에 서비스 토큰을 넣고 data-brand 범위에서 전체 alias를 연결합니다. brand palette의 light/dark 단계와 단일 brand 역할은 패키지 매핑을 따릅니다. Button은 이 brand palette와 colored·neutral elevation을 내부에서 조합합니다. 제품은 전체 accent ramp만 바꾸고 Button을 그대로 사용합니다.
테마별 브랜드 역할
--color-brand와 채움 위의 글자·아이콘 색인 --color-brand-foreground를 테마별로 지정할 수 있습니다. 기본값은 500과 흰색입니다. 브랜드 채움의 hover·active는 지정한 brand에서 파생되며, palette와 elevation은 기존 accent ramp를 따릅니다.
함께 확인할 role
- background primary·secondary·tertiary와 그 위의 text 위계
- section, form field, interactive item 각각의 기본·상호작용·disabled 상태
- success, warning, danger, info의 palette와 colored elevation ramp
- border에 사용하는 neutral elevation 강도
- 링크와 포커스 표시
Button과 IconButton의 solid·subtle·hover·disabled 상태는 brand palette와 colored·neutral elevation으로 컴포넌트 내부에서 처리합니다. 화면에서 원시 색상 utility를 조합하지 말고 컴포넌트를 사용합니다.
Light와 dark를 함께 정의하기
gray/accent 원시 alias와 palette·elevation에는 패키지의 dark 매핑이 자동으로 적용됩니다. background, section, form field, interactive item 등 역할별 값을 바꿀 때만 dark 범위에 해당 값을 추가합니다. alias나 scale은 dark에서 다시 선언하지 않습니다.
Elevation
Elevation은 중성 색의 opacity를 50~950의 11단계 배경 tint로 구성합니다. 자식 콘텐츠의 opacity는 바뀌지 않으며 숫자는 배경의 강도를 나타냅니다. light와 dark 값은 각 단계가 비슷한 시각적 무게를 갖도록 조정되어 있습니다.
밝은 색은 어두운 배경에서 동일한 opacity여도 약하게 보입니다. 이를 보정하기 위해 dark 값은 낮은 단계에서 더 크게 높이고, 단계가 올라갈수록 보정 폭을 줄입니다.
Colored elevation
accent, success, warning, danger, info에는 각각 50~950 colored elevation ramp가 있습니다. light 50은 원색 4% tint, dark 50은 8% tint에서 시작하고 950은 불투명합니다. 선택한 항목의 배경, 상태 강조, 아이콘처럼 기존 배경 위에 의미색을 더할 때 사용합니다. 모드별 색은 Color에서 비교할 수 있습니다.
Border
별도 border alias는 없습니다. 주변 배경과 구분할 강도에 맞춰 border-elevation-50부터 border-elevation-950까지 neutral elevation을 직접 선택합니다. 기본 구분선은 700, 강조된 컨트롤은 800, 강한 상호작용 상태의 테두리는 950부터 조정합니다.
Radius와 geometry override
Radius는 컴포넌트 이름이 아니라 역할별 토큰으로 조정합니다. action과 form field는 기본값이 모두 md여도 별도 토큰이므로 각각 재정의할 수 있습니다. 컨트롤 높이와 터치 영역 등 공통 치수는 component alias를 사용합니다.
원·pill의 full, 직각의 none, 자식 요소의 inherit, 연결된 컨트롤의 안쪽 모서리 0처럼 구조가 모양을 정하면 원시 값을 사용합니다. 역할별 선택과 시각 샘플은 Radius에 있습니다. size prop은 한 컨트롤의 크기, data-density는 화면의 정보 밀도를 정합니다. 모바일 전용 컴포넌트나 토큰 이름은 만들지 않습니다.
Design tokens
색상을 제외한 토큰 228개의 light 기본값입니다. 이 값은 @amineslab/ui/tokens의 generatedTokens에서 가져옵니다.
Component alias
컨트롤 높이와 터치 영역 등 컴포넌트 공통 치수. radius는 역할별 토큰을 사용
Radius
원시 단계와 action, form field, section, overlay 역할별 토큰
Shadow
11단계 원시 값과 raised, floating, modal 역할별 토큰. dark에서 원시 값이 바뀜
Spacing
2px 단위. 레이아웃 간격의 기준
Typography recipes
용도별 글꼴, 크기, 줄 높이, 두께, 자간을 한 번에 적용
Font family
Font size
Font weight
Line height
Letter spacing
Motion
reduced motion에서 재생 시간이 0ms
Density
data-density="compact"에서 바뀜
Layer
z-index 순서
Safe area
env() 값을 그대로 전달
Tailwind CSS v4 bridge
패키지가 제공하는 완성 CSS만 사용하는 앱에는 이 bridge가 필요 없습니다. 역할별 토큰을 Tailwind utility에서 사용할 때 추가합니다. 색상은 Tailwind의 표준 --color-* namespace를 공유하므로 bg-info-300처럼 쓰고, radius는 rounded-action, rounded-form-field, rounded-section, rounded-overlay처럼 역할로 고릅니다. Typography는 text-body-2와 text-code-2처럼 글꼴 계열을 포함한 recipe로 선택합니다.
프로젝트에서 쓰는 구조
제품의 UI 폴더에는 재내보내기 진입점, 테마 파일, 필요한 컴포넌트 래퍼만 둡니다. 화면 코드에서는 패키지 이름 대신 제품의 UI 진입점에서 컴포넌트를 가져옵니다.
얇은 래퍼
제품 기본값을 바꿀 때만 래퍼를 만듭니다. 래퍼는 props와 ref를 전달하며 패키지의 클래스나 DOM 구조에 의존하지 않습니다. 새 variant가 필요하면 패키지 API로 제안합니다.
- 기본 size, 기본 variant, 제품 아이콘 등 제품 공통값만 고정합니다.
- prop 이름은 패키지와 동일하게 유지합니다.
- 내부 클래스를 selector로 선택해 덮어쓰지 않습니다.
- 컴포넌트 소스를 복사하거나 패키지에 없는 prop을 추가하지 않습니다.
유지보수
패키지 버전을 정확히 고정하고 CHANGELOG를 읽은 뒤 typecheck, lint, build를 실행합니다. 업그레이드할 때는 UI 진입점, 테마 파일, 컴포넌트 래퍼의 변경을 먼저 검사합니다.
- 역할별 토큰 이름이 바뀌면
theme.css의 참조를 고치고 Color 페이지의 목록과 비교합니다. - 컴포넌트 props가 바뀌면 래퍼를 먼저 수정합니다. 화면 코드는
@/ui에서 컴포넌트를 가져옵니다. - 동일한 props로 이 문서의 Playground와 제품 화면을 나란히 비교합니다.
- tarball은 Versioning 절차에 따라 독립 fixture에 설치합니다.
대비 확인
기본 토큰은 핵심 색상 조합의 대비 테스트를 통과합니다. 역할별 색상을 바꿨다면 브라우저에 계산된 색상값으로 다음 대비를 다시 검사합니다.
- background와 section 위의 일반 텍스트 4.5:1, 큰 텍스트 3:1
- Button variant와 colored elevation 위의 텍스트·아이콘 대비
- 포커스 표시와 인접 배경의 대비 3:1
- 색상 외에 텍스트나 아이콘으로도 상태 전달
사용 기준
- 원시 색을 바로 쓰지 말고 background, section, form field, interactive item 토큰을 palette와 elevation에 연결합니다.
- Button의 상태 색은 brand palette와 elevation으로 처리합니다.
- 고정된 coral이나 orange를 작은 글자에 쓰지 마세요.
- 대비를 검사한 역할별 색을 사용합니다.
확인 기준
- 공개 import 경로와 타입 선언에 맞춰 사용했는지 확인합니다.
- 라이트·다크 모드에서 정보와 포커스를 구분할 수 있어야 합니다.
- 390px 화면, 200% 확대, 키보드 탐색에서도 작업을 마칠 수 있어야 합니다.