OKLCH 팔레트 공식
제품의 중립 색상(페이지 배경, 카드 표면, 웰스, 헤어라인, 네 단계의 텍스트)은 일반적으로 테마당 15~30개의 16진수 값으로 구성되며 수동으로 선택됩니다. 이 값들은 시간이 지나면서 일관성이 없어지고, 다크 모드는 또 다른 수동 선택 세트가 필요하며, 어떤 쌍이 여전히 대비를 통과하는지 아무도 모릅니다. 이제 공식으로 대체하세요:
표면류 토큰: L = paper + depth × n(token) chroma = tint hue = hue
잉크 토큰: L = ink + step × n(token) chroma = ink-tint hue = hue
n(token)은 토큰이 래더 상에 있는 위치입니다. 각 테마마다 하나의 래더가 있습니다. 하나의 입력값을 변경하면 전체 램프가 함께 움직이므로, 대비가 결정되는 단계 간의 간격이 유지됩니다. 제품의 색조를 변경하는 것은 한 줄(--hue)만 수정하면 됩니다.
세 개의 노브(--hue, --lightness, --chroma 또는 --contrast)가 모든 색상을 구동합니다.
파일
references/palette.css: 시작 입력값과 래더가 포함된 즉시 사용 가능한 규칙.scripts/fit_palette.py: 기존 16진수 팔레트에 입력값을 피팅하고 토큰별 ΔE를 출력합니다.scripts/ladders.json: 기본 래더(밝음, 어두움).scripts/palette-audit.mjs: 실제 브라우저에서 두 테마의 모든 토큰을 분석하고 대비를 검증합니다.scripts/oklch.py: 변환 및 ΔE / WCAG 헬퍼.
절차
-
토큰을 값이 아닌 역할로 명명합니다. 기본값:
- 배경류:
ground(페이지),panel(레일, 바),surface(카드, 메뉴),surface-2(웰스),surface-3(칩, 호버된 행),field. - 선:
line,line-2,line-3(헤어라인, 가장 밝은 것부터 가장 어두운 것까지), 그리고 컨트롤의 가장자리인field-line은 3:1을 충족해야 합니다. - 잉크:
ink,ink-2,ink-3,ink-4(텍스트, 가장 어두운 것부터 가장 흐린 것까지). 모든 잉크 단계는 텍스트이므로 각각 4.5:1을 충족해야 합니다.
- 배경류:
-
다시 고르지 말고 피팅합니다. 기존 제품의 경우
python3 scripts/fit_palette.py theme.css를 실행합니다. 이 스크립트는--color-*16진수 선언(밝음 테마, 그다음[data-theme="dark"]규칙)을 읽고, 기본 래더에 대해 입력값을 피팅한 후 토큰별 ΔE를 출력합니다. 2 미만이면 시각적으로 동일합니다. -
디자인이 단계 간 간격을 다르게 배치하는 경우 래더 단계를 재정의합니다. 색상을 재정의하지 마세요.
fit_palette.py theme.css light '<inputs>' '<overrides>'로 후보를 평가하여 최악의 ΔE가 약 2 미만이 될 때까지 조정합니다. 일반적인 재정의:field: 0(필드는 카드 면), 더 넓은line-3,ink와ink-2사이의 더 큰 간격. -
규칙을 작성합니다(
references/palette.css참조):- 입력값과
--color-*공식을 테마 속성을 가진 요소에 배치합니다. - 다크 규칙에서 변경하는 모든 입력값을 다시 명시합니다. 키트 기본값에 대해 작성된 다크 규칙은 그렇지 않으면 자체 값으로 우선합니다.
- Tailwind v4에서는 유틸리티가 존재하도록
@theme에 토큰 이름을 유지합니다. 런타임 선언이 이를 재정의합니다.
- 입력값과
-
실제 브라우저에서 감사합니다:
node scripts/palette-audit.mjs <url>. 각 토큰을 렌더링하고 sRGB를 읽어온 후 다음을 확인합니다:- 모든 잉크 단계가 모든 배경에 대해 4.5:1을 만족하는지;
field-line과ring이 3:1을 만족하는지.
실패한 경우 해당 토큰의 래더 단계를 이동하여 수정하고, 색상을 특별 처리하지 마세요. 기존 팔레트에서 실패가 예상됩니다. 17개 앱 마이그레이션에서 감사 결과 절반 이상에서
ink-4또는field-line값이 실패했으며, 이 모든 값은 이전에 출시된 것입니다.
함정
var()내부의 사용자 지정 속성은 선언된 위치에서 해석됩니다.:root에 공식을 선언하고 자식에 입력값을 설정하면 자식은 다시 색칠되지 않습니다.- 별칭 토큰(
--color-heading: var(--color-ink))도 동일한 테마 요소에 선언되어야 합니다.
- Tailwind v4
@theme값은:root에 있습니다. 앱 루트([data-app])로 범위가 지정된 테마는@theme이 아닌 해당 위치에 공식을 선언해야 합니다. - 캔버스나 맵 라이브러리에 색상을 전달하는 모든 것(
getComputedStyle(...).getPropertyValue('--color-x'))은 이제 해석되지 않은oklch(calc(...))문자열을 받습니다. 먼저 해석하세요: 감사가 하는 것처럼 프로브 요소를 그리고 1px 캔버스를 읽습니다. - 배경과 잉크의 단일 색조(hue). 디자인상 잉크가 배경과 다른 색조를 가진다면
--ink-tint를 낮게 유지하세요. L 0.98에서 모든 크로마는 색조가 있는 것으로 읽힐 수 있습니다. - 브랜드, 상태 및 차트 색상은 공식 밖에 남겨둡니다. 별도로 고르는 대신 동일한 색조에서 고정 명도의 텍스트-온-틴트 변형(예:
brand-text)을 파생합니다. - 혼합 색조, 밝음 전용 팔레트가 컴프에서 샘플링된 경우 약 2 ΔE 이내로 맞지 않을 수 있습니다. 디자인을 왜곡하지 말고 건너뛰세요. 감사는 여전히 적용됩니다.