Przejdź do głównej zawartości

Kolor

Draft

Komponenty i strony nigdy nie odwołują się do koloru wprost (#1E5A8C, hsl(...)) ani do warstwy core/brand tokenów (--color-gray-500). Jedyny dozwolony interfejs to rola semantyczna: color.background.page, color.text.primary, color.brand.primary… Rola mówi, DO CZEGO kolor służy — sama wartość zmienia się pod spodem zależnie od marki i trybu, a kod komponentu nie wie (i nie musi wiedzieć), jaka to dziś wartość.

Ta sama rola, trzy miejsca użycia:

CSS własny
/* zmienna prosto z tokens.css */
.card {
background: var(--color-background-raised);
color: var(--color-text-primary);
}
Komponent React
// klasa narzędziowa Tailwind — ta sama wartość co var() wyżej
<div className="bg-background-raised text-text-primary"></div>
Dla agentów
// ścieżka tokenu w tokens/dist/tokens.json (nazwa = klucz)
tokens["color.background.raised"]
// → cssVar: "--color-background-raised"
// → value: "#FFFFFF" (insurance.light)
// → values["insurance.dark"]: "#4A5361"

Reguła: używamy WYŁĄCZNIE ról semantycznych (color.<kategoria>.<rola>). Ramp bazowa (navy-500, blue-600…) nie istnieje jako publiczny kontrakt — nie pojawia się w kodzie ui//docs/ pod żadną postacią.

Próbka pokazuje wartość roli w marce insurance (lewy kwadrat = light, prawy = dark) — pełną macierz wszystkich pięciu marek pokazuje sekcja niżej.

PodglądRola (token)Zmienna CSSKlasa TailwindHex — lightHex — dark
color.background.page--color-background-pagebg-background-page#F8F9FB#0B1326
color.background.surface--color-background-surfacebg-background-surface#FFFFFF#1C2430
color.background.raised--color-background-raisedbg-background-raised#FFFFFF#4A5361
color.background.inverse--color-background-inversebg-background-inverse#0B1326#FFFFFF
color.text.primary--color-text-primarytext-text-primary#1C2430#F8F9FB
color.text.secondary--color-text-secondarytext-text-secondary#4A5361#D2D8E2
color.text.muted--color-text-mutedtext-text-muted#667080#AEB6C2
color.text.inverse--color-text-inversetext-text-inverse#FFFFFF#0B1326
color.text.on-brand--color-text-on-brandtext-text-on-brand#FFFFFF#FFFFFF
color.text.link--color-text-linktext-text-link#274A74#9FB8D6
color.brand.primary--color-brand-primarybg-brand-primary#355F92#355F92
color.brand.hover--color-brand-hoverbg-brand-hover#274A74#274A74
color.brand.active--color-brand-activebg-brand-active#1A3556#1A3556
color.brand.subtle--color-brand-subtlebg-brand-subtle#E3ECF5#1A3556
color.brand.on-subtle--color-brand-on-subtletext-brand-on-subtle#1A3556#C5D6E8
color.accent.default--color-accent-defaultbg-accent-default#3183A4#3183A4
color.accent.subtle--color-accent-subtlebg-accent-subtle#DCF4F2#184D68
color.border.default--color-border-defaultborder-border-default#D2D8E2#667080
color.border.strong--color-border-strongborder-border-strong#667080#AEB6C2
color.border.focus--color-border-focusborder-border-focus#355F92#9FB8D6
color.status.success.bg--color-status-success-bgbg-status-success-bg#F5F9F7#F5F9F7
color.status.success.fg--color-status-success-fgtext-status-success-fg#0E7D36#0E7D36
color.status.warning.bg--color-status-warning-bgbg-status-warning-bg#FAF8F5#FAF8F5
color.status.warning.fg--color-status-warning-fgtext-status-warning-fg#966106#966106
color.status.danger.bg--color-status-danger-bgbg-status-danger-bg#FAF5F5#FAF5F5
color.status.danger.fg--color-status-danger-fgtext-status-danger-fg#D81212#D81212
color.status.danger.surface--color-status-danger-surfacebg-status-danger-surface#D81212#D81212
color.status.danger.on-surface--color-status-danger-on-surfacetext-status-danger-on-surface#FFFFFF#FFFFFF
color.status.danger.hover--color-status-danger-hoverbg-status-danger-hover#AD0E0E#AD0E0E
color.status.danger.text--color-status-danger-texttext-status-danger-text#D81212#F15656
color.status.info.bg--color-status-info-bgbg-status-info-bg#F2F5F8#F2F5F8
color.status.info.fg--color-status-info-fgtext-status-info-fg#1E62AE#1E62AE

data-brand (insurance/home/travel/health/moto) × data-theme (light/dark) na <html> — 10 kombinacji. Tryb ciemny nie jest osobną paletą — to przemapowanie tych samych ról na inne wartości (np. background.page z jasnoszarego na granatowy), więc komponent napisany raz działa identycznie w obu trybach. Zobacz komponent na żywo w dowolnej marce/trybie: dowolna strona /preview/* (np. przy komponencie Button) ma własny przełącznik marki/trybu w pasku narzędzi.

insurance.lightinsurance.darkhome.lighthome.darkhealth.lighthealth.darkmoto.lightmoto.darktravel.lighttravel.dark
color.background.page
color.background.surface
color.background.raised
color.background.inverse
color.text.primary
color.text.secondary
color.text.muted
color.text.on-brand
color.brand.primary
color.brand.hover
color.brand.active
color.brand.subtle
color.accent.default
color.accent.subtle
color.border.default
color.border.strong
color.status.success.fg
color.status.warning.fg
color.status.danger.fg
color.status.info.fg

Każda para tekst/tło zdefiniowana w warstwie semantycznej przechodzi WCAG 2.2 AA (4.5:1 dla tekstu, 3:1 dla obrysu/focus) w każdej z 10 kombinacji — sprawdza to tokens/check.mjs przy każdym buildzie tokenów, nie ręczna kontrola na oko. Gdy odcień marki nie przechodzi progu, poprawka wchodzi w tokens/src/brands/*.tokens.json — nigdy w próg wewnątrz check.mjs.

Konsekwencja dla komponentów: nie zestawiaj ról z różnych „rodzin” dowolnie (np. color.text.on-brand na color.background.page) — role są projektowane w zamierzonych parach (np. text.on-brandbrand.primary jako tło). Nowa para to decyzja do tokenów, nie do lokalnego CSS.

Nazwy ramp 1:1 z Figmą:

Marka Ramp primary Ramp accent
insurance blue turquoise (jedyny nazwany akcent — pozostałe pochodne)
home red pochodny z primary
health teal pochodny z primary
moto violet pochodny z primary
travel sky pochodny z primary

@todo(S-GRAFIK): nazwany akcent dla home/health/moto/travel — dziś wyprowadzony automatycznie z ramp primary (@todo(accent-grafik) w tokens/src/brands/*.tokens.json).

Neutralna rampa (color.background.*, color.text.*, color.border.*) to navy: navy-50 = tło jasne, navy-800 = tekst, navy-900 = tło ciemne. Kolor marki (color.brand.primary, tło CTA) wskazuje stop rampy primary oznaczony jako „button-primary” — nie jest to ten sam numer stopnia w każdej marce (600 dla insurance/home, 700 dla health/moto/travel); mapowanie żyje w brands/*.tokens.json (sekcja role), żeby nazwy i wartości stopni zostały 1:1 z Figmą mimo tej niejednorodności.