# TeeCup — designsystem (referanse) > Dette dokumentet beskriver det VISUELLE grunnlaget for alt frontend- > arbeid i TeeCup — fargetoken, typografi, avstand, komponentmønstre og > tilgjengelighetsregler. Det er en beskrivelse av hva som ER etablert i > koden (`frontend/app/globals.css`, `frontend/components/ui/*`, og > mønstrene brukt på tvers av `components/*.tsx`), ikke et nytt forslag — > hensikten er at både V0-prompter og håndkodet arbeid skal kunne > forankres i akkurat dette, i stedet for å gjettes på nytt hver gang. > > Ufravikelig grunnregel (fra CLAUDE.md, gjelder ALT, eksisterende og > fremtidig): appen skal være lesbar, forståelig og betjenbar for noen med > noe redusert syn UTEN briller. God kontrast, stor nok skrift, store nok > trykkflater, ikke ikon-only uten tekstlabel for viktige handlinger, ikke > avhengig av finmotorikk/skarpt syn. Enhver visuell beslutning under > veies mot dette først. ## Merkevare-opprinnelse Fargeformen/-tonen er hentet fra Teeoff-logoen (samme merkevarefamilie, egne farger — se ADR-009/ADR-016), IKKE navn eller logo. Basisfargene ble regnet ut som presise OKLCH-verdier fra de opprinnelige hex-fargene: - Grønn (primær): `#8bc24a` - Oransje (sekundær/"brand-orange"): `#ff5722` Disse to er de ENESTE to kjernefargene i merkevaren. Et forslag om en tredje ("informasjons"-)farge ble vurdert 2026-07-25 og IKKE besluttet — se FEATURE_BACKLOG.md. Anbefalingen der var å gjenbruke `--chart-3` (allerede en blåtone i statistikk-skalaen) fremfor å finne på noe nytt. ## Fargetoken (`frontend/app/globals.css`) Alle farger er definert som OKLCH-variabler, med egne verdier for lys og mørk modus (`:root` / `.dark` / `@media (prefers-color-scheme: dark)`, alle tre holdt i synk). Bruk ALLTID token-navnene under (Tailwind-klasser som `bg-primary`, `text-muted-foreground` osv.) — aldri rå hex/oklch i ny kode. | Token | Lys modus | Rolle | |---|---|---| | `background` / `foreground` | nesten hvit / mørk grønn-grå | sideflate / brødtekst | | `card` / `card-foreground` | hvit / mørk grønn-grå | kort, paneler, seksjoner | | `primary` / `primary-foreground` | `oklch(0.7512 0.1613 130.33)` (grønn) / mørk | hovedhandling, valgt tilstand, "under par"/positiv | | `secondary` / `secondary-foreground` | lys grønn-tint | sekundær vekt, sjeldent brukt alene | | `muted` / `muted-foreground` | lys grå-grønn / dempet | bakgrunnstoner, sekundær tekst/bildetekst | | `accent` / `accent-foreground` | lys grønn-tint | hover-tilstand på nøytrale flater | | `destructive` | rød-oransje | slett/fare-handlinger | | `border` / `input` | lys grå-grønn | kant på kort, felt, delelinjer | | `ring` | samme som `primary` | fokus-ring (tastatur-tilgjengelighet) | | `brand-orange` / `brand-orange-foreground` | `oklch(0.6792 0.2128 36.53)` | sekundærfarge — "over par"/advarsel, IKKE feil (det er `destructive`) | | `chart-1` … `chart-6` | grønn→blå→gul→oransje→rød-skala | statistikk-/fordelingsvisualiseringer (ADR-033), ALDRI brukt for kategoriske UI-tilstander | **Farge-semantikk, konsekvent på tvers av hele appen:** - `primary` (grønn) = valgt/aktiv, positivt resultat (under par, ledende, bekreftet), primærhandling. - `brand-orange` = nest viktigst/kontrast-pol i et par (over par, "Poeng" i noen sammenhenger), IKKE en feilfarge. - `destructive` = KUN faktiske slette-/fare-handlinger (Slett runde, fjern spiller), aldri "over par" eller lignende domenetilstand. - `muted-foreground` = all sekundær/bildetekst-tekst, aldri brødtekstens hovedfarge. **Aldri farge alene.** Hver gang farge bærer betydning (over/under par, leder, valgt/uvalgt), følges den av FORM (sirkel vs. firkant, fylt vs. bordered), tekst (+N/−N/E, "Deg"/"Eier"), eller et ikon — se "Golfscore-språket" under. Dette er ikke en stilistisk preferanse, det er et direkte utslag av tilgjengelighetsregelen øverst. ## Typografi - **Skrifttype:** Nunito (`next/font/google`, lastet i `app/layout.tsx`, eksponert som `--font-sans`) — rund, vennlig, god lesbarhet på små trykkflater. Ingen sekundær/monospace-font i UI-et (tabular-nums brukes for tallinnretting i stedet for en egen tallfont — se under). - **Skala** (Tailwind-klasser, brukt konsekvent): - `text-sm` — bildetekst, sekundær informasjon, knapp-tekst i tette kontekster - `text-base` — brødtekst, standard knapp-/felt-tekst, listeelementer - `text-lg` / `text-xl` — kortoverskrifter, seksjonstitler - `text-2xl` / `text-3xl` — sideoverskrifter, hull-header, store resultat-tall - **Vekt:** `font-semibold`/`font-bold` for all vektlagt tekst, `font-extrabold` for tall og hovedoverskrifter. Vanlig (regular) vekt brukes nesten aldri i UI-kontroller — selv sekundærtekst er ofte `font-semibold` for lesbarhet på avstand/i sollys. - **`tabular-nums` på ALL numerisk visning** (skår, HCP, datoer, rangeringer) — hindrer tall fra å "hoppe" i bredde når de endres. - **Golfvis fortegn:** til-par-tall formateres ALLTID som "E" (jevnt med par), "+N" (over), "−N" — ekte minustegn (U+2212), ALDRI en vanlig bindestrek eller et bart negativt tall. Samme regel for HCP/ differensial-tall der fortegn er meningsbærende. ## Avstand, hjørner og lag - **Hjørneradius** (fra `--radius: 0.625rem`, skalert opp via `--radius-sm/md/lg/xl/2xl/3xl/4xl`): `rounded-xl` for felt/knapper i tette rader, `rounded-2xl` for kort/paneler/knapper med normal vekt, `rounded-3xl` for store side-nivå-containere (hull-panel, rundekort). `rounded-full` reservert for sirkulære tilstander (se golfscore-språket) og pille-formede brytere/merker. - **Kort/panel-basismønster:** `rounded-2xl` eller `rounded-3xl`, `border border-border`, `bg-card`, `p-4` (tett) til `p-5 sm:p-6` (romslig), ofte `shadow-sm shadow-black/5` for lett løft fra bakgrunnen. - **Sammenhengende lister** (leaderboard, spillerliste): ÉN container (`rounded-2xl border border-border overflow-hidden`) med `divide-y divide-border` mellom radene — IKKE separate kort med mellomrom, det gir en "kort-følelse" som virker mer oppstykket enn nødvendig når radene i praksis hører sammen (bevisst rettet opp 2026-07-26 på leaderboardet, se CLAUDE.md). - **Stiplet kant** (`border border-dashed border-border`) markerer en "legg til"/tom-tilstand-affordance (f.eks. "+ Medspiller"), ALDRI brukt på innhold som allerede finnes. ## Trykkflater og avstand mellom elementer - **44px er det ufravikelige gulvet** for enhver trykkbar flate (`min-h-11` i Tailwind-tall siden 1 enhet = 4px). Primærhandlinger er ofte enda større: `h-12`–`h-16` for tallvelgere/hovedknapper. - Runde ikon-only-knapper (lukk, fjern) er minimum `size-11`. - Gap mellom relaterte kontroller: `gap-2`/`gap-2.5`/`gap-3` normalt, `gap-4`/`gap-6` mellom distinkte seksjoner. ## Layout-containere - Innloggede/app-sider: `mx-auto w-full max-w-3xl` for innholdsrike sider (runde-detalj), `max-w-xl` for enklere/lesevisninger (leaderboard, scorekort, statistikk). - Sticky header: `sticky top-0 z-10` (eller `z-20` når flere lag stables), `border-b border-border`, `bg-background/90` til `/95` + `backdrop-blur` — innhold forblir lesbart under skrolling uten å bli en solid, brå kant. - Fullskjerm-overlegg (scoringsveiviser): `fixed inset-0 z-50 flex flex-col bg-background`, med `header`/`main` (scroller, `flex-1 overflow-y-auto`) /`footer` (handling-rad, `shrink-0`) — samme tre-delt struktur uansett hvor i appen et slikt overlegg brukes. - Fast-per-rad-høyde når kort skal se enhetlige ut i en liste (`min-h-[Npx]` + `truncate` på tekstlinjer i stedet for fri wrapping) — brukt bevisst på spillerkort 2026-07-26 for at rader med ulikt antall merker/lengde på navn likevel blir like høye. ## Komponentmønstre (gjenbrukes, ikke gjenoppfinnes) Disse er alle allerede bygget som lokale funksjoner i de respektive `components/*.tsx`-filene (ikke delte imports på tvers av filer — hver fil har sin egen lokale kopi tilpasset sin kontekst, et bevisst valg gjentatt flere ganger denne økten fremfor tidlig abstraksjon). - **`NumberPicker`** (`round-detail.tsx`) — 3-kolonners rutenett av store (`h-16`) tallknapper, valgt tilstand = fylt `bg-primary`, med en valgfri liten bildetekst under tallet (par-merke, eller — Slag- varianten — kontekstuell golf-term: Albatross/Eagle/Birdie/Par/Bogey/ Dobbel bogey relativt til hullets par). Utvidbar "10+"-knapp for sjeldne høye verdier, holder standardvisningen kort. - **`Stepper`** (+/−) — pille-formet rad med to `size-11`-knapper som flankerer et sentrert tall. Brukt for løpende antall (putter, chip, bunker, straffeslag) der en full talltavle ville vært overkill. - **`ChoiceRow`** — horisontal rad av segmenterte knapper (`flex-1`, valgt = `bg-primary`), for korte, faste alternativlister (avstand første putt, kjønn, statistikknivå). - **`DirectionCross`** — 3×3-rutenett med retningsikoner (`ArrowUp/Left/ Right/Down` + `Target` i sentrum) for utslags-/innspillretning. Tekst- label på hver knapp (ALDRI kun ikon), matcher tilgjengelighetsregelen. - **`ModeToggle`/segmentert bryter** — `inline-flex gap-1 rounded-full bg-muted p-1`, hver fane `rounded-full`, valgt = `bg-primary text- primary-foreground`. Brukt for Brutto/Netto/Poeng, Score/Spillere-faner osv. - **`Badge`** (delt shadcn-komponent, `components/ui/badge.tsx`) — `variant="default"` (fylt grønn) for "Deg", `variant="outline"` (bordered) for "Eier" — de to skal ALLTID være visuelt distinkte varianter av hverandre, ikke samme stil i to farger. ## Golfscore-språket (form + farge, aldri farge alene) Det viktigste, mest gjenbrukte visuelle mønsteret i appen — brukt i `round-scorecard.tsx` (`ScoreMark`), `round-leaderboard.tsx` (`ToParMark`/`PointsMark`/`HoleMark`), og flere steder til: - **Sirkel (`rounded-full`)** = under par / positivt resultat. - **Firkant med liten radius (`rounded-[4px]`–`rounded-[6px]`)** = over par / negativt resultat. - **Ren tekst, ingen ramme** = nøyaktig par ("E"). - **Fylt bakgrunn** (`bg-primary`/`bg-brand-orange` + kontrastfarget tekst) = ekstra vekt (eagle+/dobbel bogey+, eller lederen i en rangering) — fortsatt SAMME form som den vanlige varianten, kun fyllingen endres. Vanlig variant = `border-2` + `/10`-tint bakgrunn + farget tekst. - **Poeng (stableford) har ingen retning** (høyere er alltid bedre) — får derfor kun ÉN fast form (sirkel), samme fylt/bordered-emphasis- logikk som resten. - Rangeringstall bruker et eget, nøytralt merke (`bg-muted`/`bg-primary` ved leder) — ikke samme form-språk som selve resultatet, for å unngå at rangering og resultat blandes visuelt sammen. ## Ikonografi - **`lucide-react`**, konsekvent gjennom hele appen — ingen annen ikonpakke blandet inn. - Ethvert ikon som er rent dekorativt får `aria-hidden="true"`. - **Ikon-only er ALDRI lov for en viktig handling** — en tekstlabel følger alltid med (unntak eksplisitt vurdert og akseptert: bjelleikonet for varsler, fordi det er universelt gjenkjent OG har en beskrivende `aria-label` med antall uleste). ## Tilbakemelding og tilstander - **Lasting:** sentrert spinner, `size-8`–`size-10 animate-spin rounded-full border-4 border-primary/20 border-t-primary`. - **Feil:** `role="alert"`, `text-destructive`, alltid som ren tekst (aldri kun et ikon eller en farget kant). - **Tomme lister:** en kort forklarende tekst + evt. en stiplet "legg til"-affordance, aldri en stille blank flate. - **Sanntid (WebSocket):** ingen egen visuell "oppdatert!"-indikator — data hentes på nytt og erstatter stille, siden radene allerede har stabile nøkler/layout (unngår blunking). ## Navneformat (innhold, ikke et rent visuelt punkt — men styrer hva som vises hvor) Direkte adressering av brukeren (dashbord-hilsen, e-post/varsel rettet TIL mottakeren) bruker KUN fornavn. Alt annet (lister, roster, chat, administrasjonsvisninger) bruker fullt navn/initialer — se CLAUDE.md for den fulle, ufravikelige regelen. Nevnt her fordi det er en like fast STANDING forventning til fremtidig UI som resten av dette dokumentet. ## Hva dette dokumentet bevisst IKKE er - Ikke en erstatning for CLAUDE.md sin tilgjengelighetsregel (den er autoritativ og gjengitt her kun i sammendrag). - Ikke en frys av dagens design — nye mønstre legges til etter hvert som de bygges (både via V0 og håndkodet), men skal alltid først sjekkes mot det som ALLEREDE er etablert her fremfor å finne opp noe nytt. - Ikke en komponent-API-referanse — se selve kildefilene (`components/ui/*.tsx`, og de lokale komponentene i hver `components/*.tsx`) for eksakte prop-signaturer.