goon/mobile/src/theme.ts
goon-foss 7deda639bf fix(theme): rozdziel role akcentu i dodaj ramke, ktora cokolwiek oddziela
Pomiar WCAG na realnych parach z apki (2026-08-13):

    accent #B23A48 jako tekst:  3,21 na bg | 2,96 na bgElevated | 2,75 na card
    border #3A3128 jako granica: 1,48 na bg | 1,27 na card

Czyli kolor MARKI byl ponizej nawet progu 3:1 dla elementow interfejsu,
a nie tylko ponizej 4,5 dla tekstu. Dotyczylo to aktywnych zakladek,
gwiazdek ulubionych, linkow i chipow. Ramki przy 1,27 nie byly granica,
tylko cieniem.

NIE zmieniam koloru marki. Podniesienie samego `accent` do progu tekstu
wymagaloby zejscia w roz (#CE6975), co likwiduje tozsamosc, ktorej broni
naglowek theme.ts. Zamiast tego rozdzielam ROLE tego samego koloru:

  accent      zostaje do WYPELNIEN — jasny tekst na nim ma 5,03, przechodzi
  accentText  #C75260 dla tekstu i obwodek — 4,30 / 3,96 / 3,68

62 podmiany w 24 plikach (34 color, 26 borderColor, 2 thumbColor).
38 wypelnien nietknietych. Sprawdzone skryptem, ze zaden zmieniony tekst
nie stoi na tle akcentu.

borderStrong #7B7065 dla granic NIOSACYCH ZNACZENIE (pola tekstowe, 12
miejsc); dekoracyjne wloski zostaja na `border`. Wartosc dobrana pod obie
powierzchnie, bo pola leza i na bg, i na card: #6B6157 dawal na karcie
2,66, a #73695E dokladnie 2,999, czyli wciaz ponizej progu.
2026-08-14 09:50:08 +02:00

113 lines
4.4 KiB
TypeScript

/**
* Goon theme, warm dark + oxblood, NOT generic navy/purple AI-default.
*
* Audit 2026-05-29 (impeccable.style/slop):
* - Wcześniej: bg #08090F (deep navy), accent #8B5CF6 (purple) + glow #A78BFA
* → literal example "AI default palette" z impeccable.
* - Teraz: bg #15110D (warm charcoal, orange undertone), accent #B23A48
* (oxblood/rust), brak glowów, brak gradientów neon.
*
* Typography: General Sans (display, Fontshare) + Geist Mono (meta, Vercel
* fonts). Font files w `mobile/assets/fonts/`, `useFonts()` w App.tsx
* graceful-fallback do system gdy brak (development).
*/
export const theme = {
// Warm dark, charcoal z orange undertone (NIE navy).
// Filmowy, premium feel, jak Letterboxd/Mubi/A24.
bg: '#15110D',
bgElevated: '#1E1A14',
card: '#26201A',
border: '#3A3128',
// Granica, która faktycznie oddziela. `border` ma 1,48:1 na `bg` i **1,27:1 na `card`**,
// czyli przy wymogu 3:1 nie jest granicą, tylko cieniem — zostaje do dekoracyjnych
// włosków. Wszystko, co niesie znaczenie (pola, fokus, podział sekcji), bierze to.
//
// Wartość dobrana pod OBIE powierzchnie: pola tekstowe leżą i na `bg` (ekrany), i na
// `card` (arkusze filtrów), a #6B6157 dawał na karcie 2,66, zaś #73695E dokładnie 2,999, czyli wciąż poniżej.
borderStrong: '#7B7065', // 3,89 na bg · 3,33 na card (obie powierzchnie ≥3:1)
borderFocus: '#B23A48',
// Foreground, warm off-white (nie pure white żeby nie biło na warm dark).
fg: '#F5EDE0',
muted: '#A89B85',
mutedDim: '#6F6555',
// Accent, oxblood/rust. Distinctive, niekonwencjonalne dla "media app".
// Brak glow/neon. Deep wariant dla pressed states.
accent: '#B23A48',
// WYPEŁNIENIE ma zostać oxbloodem: jasny tekst na nim daje 5,03:1, czyli przechodzi.
// Ale TEN SAM kolor jako tekst albo cienka obwódka jest nieczytelny — pomiar WCAG
// 2026-08-13: 3,21 na `bg`, 2,96 na `bgElevated`, **2,75 na `card`**, czyli poniżej
// nawet progu 3:1 dla elementów interfejsu, nie mówiąc o 4,5 dla tekstu.
//
// Podniesienie samego `accent` do progu tekstu wymagałoby zejścia w róż (#CE6975),
// co likwiduje tożsamość, której broni nagłówek tego pliku. Zamiast tego rozdzielamy
// ROLE: `accent` wypełnia, `accentText` pisze i obrysowuje.
accentText: '#C75260', // 4,30 na bg · 3,96 na bgElevated · 3,68 na card
accentGlow: '#B23A48', // alias, back-compat, ale bez prawdziwego glow
accentDeep: '#7A1F2A',
accentSecondary: '#D89B4A', // muted amber dla secondary CTAs
// Status, tonowane do palety (nie generic green/red).
good: '#5E8C5A', // muted olive-green
bad: '#C44545',
warn: '#D89B4A', // amber
} as const;
/**
* Font family, General Sans (display + body, Fontshare) + Geist Mono (meta,
* Vercel OFL). Pliki .ttf w `mobile/assets/fonts/`, ładowane przez useFonts()
* w App.tsx (runtime load, działa przez OTA bo expo-font native jest w APK).
*
* WAŻNE (custom-font gotcha): RN NIE syntezuje weightów dla custom fontów,
* fontWeight:'700' na fontFamily:'GeneralSans-Regular' NIE pogrubi. Trzeba
* jawnie wskazać rodzinę per-weight. Stąd 4 osobne sloty:
* - body → Regular (400), domyślny tekst
* - medium → Medium (500) , labels, tab inactive
* - display → Semibold (600)headingi, tytuły, bold
* - mono → Geist Mono meta, duration, liczby, kategorie
*
* Gdy useFonts jeszcze nie ready, App.tsx blokuje render (fonty z bundla
* ładują się <100ms), więc te stałe są zawsze valid przy pierwszym paint.
*/
export const fonts = {
body: 'GeneralSans-Regular',
medium: 'GeneralSans-Medium',
display: 'GeneralSans-Semibold',
mono: 'GeistMono-Regular',
} as const;
export function scoreColor(score: number): string {
if (score >= 0.92) return theme.good;
if (score >= 0.75) return theme.warn;
return theme.bad;
}
/**
* Type-scale (impeccable: "at least 1.25 ratio between steps").
* Base 14 → 17 → 22 → 28 → 36. Użyj zamiast hardcodowanego fontSize.
*/
export const type = {
micro: 11,
meta: 13,
body: 14,
bodyLarge: 17,
title: 22,
display: 28,
hero: 36,
} as const;
/**
* Spacing scale, "tight groupings, generous between sections" (impeccable).
* Pomijamy 12/20, preferuj 8/16/24/32 dla widocznej hierarchii sekcji.
*/
export const space = {
xs: 4,
sm: 8,
md: 16,
lg: 24,
xl: 32,
xxl: 48,
} as const;