# Indeks Designsystem > Felles retningslinjer og komponenter for SpareBank 1. Inneholder tokens, utility-klasser, React- og web components samt mønstre og maler. ## docs ### adr - [ADR-DS-001: Monorepo og byggverktøy](/docs/adr/ADR-DS-001-monorepo-og-byggverktoy.md): Alle pakker i én monorepo med pnpm (sikkerhetsisolasjon) og Turborepo (intelligent caching). Workspace-protokoll for interne avhengigheter. - [ADR-DS-002: Versjonering og publisering](/docs/adr/ADR-DS-002-versjonering-og-publisering.md): CSS, Web Components og React har alltid identisk versjonsnummer. Changesets for versjonering. Dual publishing til CDN (anbefalt) og npm. - [ADR-DS-003: CI/CD med GitHub Actions](/docs/adr/ADR-DS-003-ci-cd.md): GitHub Actions for all CI/CD med spesialiserte workflows gruppert etter prefiks — pr-, release-, deploy- og security-. - [ADR-DS-004: Web Components](/docs/adr/ADR-DS-004-web-components.md): Web Components brukes der ren HTML + CSS ikke er nok. React-pakken eksponerer samme funksjonalitet med idiomatisk React API. - [ADR-DS-005: React-bibliotek (Vite, ESM, React 18/19)](/docs/adr/ADR-DS-005-react-bibliotek.md): React-biblioteket bygges med Vite i library mode, distribueres som ESM-only, og støtter React 18 og 19 som peer dependency. - [ADR-DS-006: Design tokens](/docs/adr/ADR-DS-006-tokens-og-farger.md): Design tokens som JSON er single source of truth for web, iOS og Android. Toveis Figma-synkronisering med klare ansvarsgrenser. - [ADR-DS-007: Fargesystem](/docs/adr/ADR-DS-007-fargesystem.md): Farger genereres med OKLCH for perseptuelt uniforme skalaer. HSL forkastet fordi opplevd lysstyrke varierer mellom fargetoner. - [ADR-DS-008: Spacing-system (faste breakpoints + density)](/docs/adr/ADR-DS-008-spacing-system.md): Spacing bruker faste px-verdier på tre mobile-first breakpoints kombinert med et density-system (Compact/Default/Comfortable). Typografi bruker en rem-basert geometrisk skala uten density. - [ADR-DS-009: CSS-arkitektur](/docs/adr/ADR-DS-009-css-only-styling.md): All styling bruker ren HTML og CSS. Ingen CSS-in-JS. ix-prefix på alle klasser og variabler. - [ADR-DS-010: Komponentutvikling og testing](/docs/adr/ADR-DS-010-komponentutvikling-og-testing.md): Storybook for komponentutvikling. Docker-basert Playwright for reproduserbare visuelle tester og accessibility. - [ADR-DS-011: Dokumentasjon (Docusaurus, midlertidig)](/docs/adr/ADR-DS-011-dokumentasjon.md): Docusaurus 3 som midlertidig dokumentasjonsplattform. Migreres når CMS-avklaringer med merkevare er på plass. - [Kort fortalt](/docs/adr/oversikt.md): En rask, folkelig oversikt over hvorfor vi bygger Indeks selv og hvilke valg vi har tatt. For deg som vil ha helheten uten å lese alle ADR-ene. - [Retningslinjer for Indeks designsystem](/docs/adr/retningslinjer.md): Disse retningslinjene er ikke arkitekturbeslutninger, men konvensjoner teamet har blitt enige om. - [ADR-DS-XXX: [Beskrivende tittel]](/docs/adr/template.md): Mal for nye arkitekturbeslutninger i Indeks designsystem. ### grunnleggende - [Native](/docs/grunnleggende/native.md): I hybridapper vises Indeks-komponenter i WebView-en til en native app. Brukeren skiller ikke mellom de to, og forventer derfor at web-delen oppfører seg som resten av appen. - [Border](/docs/grunnleggende/tokens/border.md): Komponentene vi tilbyr har riktig border-radius og border-width som standard. Hvis du skal lage noe eget, kan du bruke border-variablene vi tilbyr. - [Farger for Native](/docs/grunnleggende/tokens/farger-native.md): Fargene i Indeks kan bygges ut til native-kode. Pakken @sb1/indeks-tokens har en build-colors-kommando som genererer Kotlin-farger til Android og Color Assets til iOS, fra de samme tokenene som ligger bak web. - [Farger for Web](/docs/grunnleggende/tokens/farger-web.md): Pakken @sb1/indeks-tokens inneholder fargetokens som brukes i hele designsystemet. På web trenger de aller fleste ikke å generere farger selv, for den ferdig bygde CSS-en inneholder alt du trenger. Har du et eget theme, kan du likevel generere din egen web-CSS med den samme build-colors-kommandoen som brukes for native (iOS/Android). - [Fargeskalaer](/docs/grunnleggende/tokens/fargeskalaer.md): Et theme defineres med sju basisfarger: brand, success, info, danger, warning, gray og neutral. Ved build utvides hver av disse til en 20-trinns fargeskala (0–950), som blir til primitivene (--ii-primitive-*) de semantiske fargene peker på. - [Introduksjon](/docs/grunnleggende/tokens/introduksjon.md): Design tokens er de minste byggesteinene i designsystemet. Det er navngitte variabler for visuelle verdier: farger, typografi, spacing, radius, skygger. I stedet for 16px eller #002776 skriver du --ix-spacing-sm eller --ix-color-foreground-main-default. - [Spacing](/docs/grunnleggende/tokens/spacing.md): Avstandene i Indeks er faste px-verdier med navn, ikke tall du velger selv. De justerer seg på tre breakpoints, og hele skalaen kan skrus tettere eller romsligere med en density-modus. - [Tailwind](/docs/grunnleggende/tokens/tailwind.md): Indeks kommer med egne utility-klasser med ix--prefiks - [Z Index](/docs/grunnleggende/tokens/z-index.md): z-index bestemmer hvilket element som ligger øverst når to elementer overlapper. Problemet oppstår når hvert team velger tall selv 100 havner under en header med 999, og så begynner budkrigen. - [Typografi](/docs/grunnleggende/typografi.md): Våre fonter ### hjem Indeks er designsystemet til SpareBank 1. Her ligger komponentene, tokenene og retningslinjene teamene bygger på, slik at en løsning ser ut som SpareBank 1 og fungerer for alle som skal bruke den. - [Indeks](/docs/hjem.md): Indeks er designsystemet til SpareBank 1. Her ligger komponentene, tokenene og retningslinjene teamene bygger på, slik at en løsning ser ut som SpareBank 1 og fungerer for alle som skal bruke den. ### kom-i-gang - [Bidra](/docs/kom-i-gang/bidra.md): Enten du mangler en komponent, har forslag til forbedringer, eller har funnet noe som ikke fungerer som det skal, vil vi gjerne høre fra deg. - [Designer](/docs/kom-i-gang/designer.md): Før du begynner - [Kontakt](/docs/kom-i-gang/kontakt.md): Indeks blir best når vi snakker sammen. Vi setter stor pris på alle innspill, spørsmål og bidrag fra dere som bruker systemet. - [Migrering fra FFE til Indeks](/docs/kom-i-gang/migrering.md): Indeks er ikke FFE med ny styling. Det er bygget opp på nytt, med en annen arkitektur, og på fem punkter må du derfor jobbe annerledes enn du er vant til. Denne siden går gjennom dem. - [Testing](/docs/kom-i-gang/testing.md): Skriver du tester mot skjemaelementene i Indeks, trenger du én linje i testoppsettet først. Uten den finner ikke testen checkboxene dine, og feilmeldingen sier ingenting om hvorfor. - [Utvikler](/docs/kom-i-gang/utvikler.md): Kom i gang med Indeks i prosjektet ditt. Velg teknologi under, så tilpasser guiden seg. ### komponenter - [Accordion](/docs/komponenter/accordion.md): Accordion viser og skjuler innhold i seksjoner. Den er nyttig når mye innhold skal gjøres oversiktlig: brukeren ser overskriftene og åpner kun det som er relevant. Komponenten bygger på native `/`, så tastatur og skjermleser fungerer uten ekstra oppsett. - [Button](/docs/komponenter/button.md): Button lar brukeren utføre en handling, som å sende inn et skjema, lagre endringer eller navigere videre i en prosess. - [Card](/docs/komponenter/card.md): Card er én ting: en avgrenset, selvstendig innholdsenhet med fast ramme og bakgrunn. Et kort kan - [Chip](/docs/komponenter/chip.md): Chip er små, interaktive komponenter som lar brukeren gjøre raske valg, filtrere innhold eller trigge handlinger. Chips brukes ofte i grupper og gir en kompakt og oversiktlig måte å presentere valg på. - [Button chip](/docs/komponenter/chip/button.md): En button chip fungerer som en knapp og trigger en handling — for eksempel et hurtigvalg, et forslag eller et filter brukeren aktiverer. Den har ingen vedvarende valgt tilstand. - [Checkbox chip](/docs/komponenter/chip/checkbox.md): En checkbox chip er en gruppe chips der brukeren kan velge ett eller flere alternativer samtidig — som kompakte filter- eller preferansevalg. Hvert valg er uavhengig av de andre. - [Radio chip](/docs/komponenter/chip/radio.md): En radio chip er en gruppe chips der brukeren velger nøyaktig ett alternativ — som en kompakt segment-velger eller filtervalg. Å velge én chip fjerner automatisk valget på den forrige. - [Removable chip](/docs/komponenter/chip/removable.md): En removable chip representerer et aktivt valg eller en verdi som brukeren kan fjerne — for eksempel et valgt filter. Den finnes kun i valgt tilstand (den eksisterer ikke før den er valgt) og vises med lyst aksent-fyll, aksent-kant og et kryss til høyre — samme valgt-uttrykk som radio chip og checkbox chip. - [DropdownMenu](/docs/komponenter/dropdown-menu.md): DropdownMenu er en nedtrekksmeny for handlinger eller navigasjon. Menyen vises når brukeren interagerer med en trigger-knapp og lukkes automatisk ved valg, klikk utenfor eller Escape. - [Icon](/docs/komponenter/icon.md): Ikon-komponenten viser SVG-ikoner fra SpareBank 1s CDN ved hjelp av CSS mask-image. Ikoner arver farge fra omgivende tekst. - [Ikonbruk](/docs/komponenter/icon-analyse.md): Oversikt over alle Material Design-ikoner brukt i SB1-apper, med hvor mange apper som bruker hvert ikon. Basert på bruksdata fra SpareBank 1-apper. - [InteractiveIcon](/docs/komponenter/interactive-icon.md): InteractiveIcon er en klikkbar ikon-flate — en kompakt knapp som bare viser et ikon. Den gir tydelig visuell tilbakemelding ved pekehvil, trykk og fokus, med en fargetone som kan styres av en status. - [Message](/docs/komponenter/message.md): Message formidler status eller resultatet av en handling uten å avbryte flyten. Den finnes i fire varianter: informasjon, suksess, advarsel og feil. Komponenten brukes inline, tett på innholdet den forklarer, og kan strekkes til full bredde av forelderen. - [Modal](/docs/komponenter/modal.md): Modal brukes til å vise innhold oppå eksisterende side og krever at brukeren forholder seg til innholdet før de kan fortsette. Komponenten fungerer som en tom container som fylles med innhold basert på behov. Den bygger på det native ``-elementet, så fokushåndtering, tastatur og skjermleser fungerer uten ekstra oppsett. - [Pagination](/docs/komponenter/pagination.md): Pagination brukes for å dele inn større mengder innhold i flere sider og gjøre det enklere å navigere mellom dem. Komponenten brukes ofte i tabeller, søkeresultater, lister og andre visninger der alt innhold ikke vises samtidig. - [Popover](/docs/komponenter/popover.md): En Popover brukes til å vise kontekstuell informasjon eller handlinger knyttet til et spesifikt element på siden. Popoveren vises når brukeren aktiverer et tilknyttet element, og plasseres i nærheten av dette elementet slik at sammenhengen er tydelig. - [Grid](/docs/komponenter/primitives/grid.md): Grid er en layout-primitiv for å stable innhold i to dimensjoner — vertikalt og horisontalt. Den fungerer både som custom element (`) og som CSS-klasse (.ix-grid`). - [Stack](/docs/komponenter/primitives/stack.md): Stack er en layout-primitiv for å stable innhold i én dimensjon — vertikalt eller horisontalt. Den fungerer både som custom element (`) og som CSS-klasse (.ix-stack`). - [ProgressBar](/docs/komponenter/progress-bar.md): ProgressBar viser fremdriften i én sammenhengende prosess — enten som pågående progresjon (for eksempel opplasting) eller som status for at prosessen er fullført eller feilet. Komponenten er rent informativ og kan ikke motta brukerinput. - [ReadMore](/docs/komponenter/read-more.md): ReadMore viser og skjuler én frittstående tekstblokk bak en klikkbar label. Den er nyttig for utdypende, sekundær informasjon som ikke alle trenger med en gang: brukeren ser labelen og åpner forklaringen kun ved behov. Komponenten bygger på native `/`, så tastatur og skjermleser fungerer uten ekstra oppsett. - [Checkbox](/docs/komponenter/skjema/checkbox.md): Checkbox lar brukeren velge én eller flere alternativer fra en liste. Hvert valg er uavhengig av de andre, og flere kan være valgt samtidig. - [CheckboxGroup](/docs/komponenter/skjema/checkbox-group.md): CheckboxGroup samler flere relaterte checkboxer i én gruppe der brukeren kan velge ett eller flere alternativer samtidig. Hvert valg er uavhengig av de andre. - [Designvalg for CheckboxGroup](/docs/komponenter/skjema/checkbox-group-designvalg.md): Denne siden forklarer hvorfor CheckboxGroup er bygget som den er. Den er ment for de som er nysgjerrige på avveiningene bak komponenten. Du trenger den ikke for å bruke den. For bruk og API, se CheckboxGroup. - [Combobox](/docs/komponenter/skjema/combobox.md): Combobox lar brukeren søke etter og velge ett eller flere alternativer fra en liste. Brukeren skriver for å filtrere, og valgte alternativer vises enten som verdi i feltet (single) eller som chips over feltet (flervalg). - [Combobox i ren HTML](/docs/komponenter/skjema/combobox-html.md): Denne siden forklarer i detalj hvordan ` fungerer når du skriver ren HTML, Astro eller et annet ikke-React-miljø. Bruker du React, holder det å lese Combobox, der hele strukturen er skjult bak Combobox`-komponenten. For bruk, tilstander og API, se hovedsiden. - [DateField](/docs/komponenter/skjema/date-field.md): DateField lar brukeren taste en dato i norsk format (dd.mm.åååå) eller åpne enhetens innebygde kalender via en knapp i feltet. Verdien som sendes videre er alltid ISO (åååå-mm-dd), uavhengig av hva som vises i feltet. - [Label](/docs/komponenter/skjema/label.md): Label er ledeteksten til et skjemafelt. - [PhoneNumberField](/docs/komponenter/skjema/phone-number-field.md): PhoneNumberField lar brukeren oppgi et telefonnummer med landkode. Komponenten setter sammen en søkbar landvelger (der brukeren velger land, ikke skriver tallkode) og et nummerfelt som formateres mens brukeren skriver, under én felles label. De to er uavhengige felt: hver kan ha sin egen feilmelding, og i React bindes de som to separate React Hook Form-felt. - [RadioGroup](/docs/komponenter/skjema/radio-group.md): RadioGroup lar brukeren velge ett alternativ fra en liste av gjensidig utelukkende valg. Når ett valg gjøres, blir eventuelle tidligere valg automatisk fjernet. - [Designvalg for RadioGroup](/docs/komponenter/skjema/radio-group-designvalg.md): Denne siden forklarer hvorfor RadioGroup er bygget som den er. Den er ment for de som er nysgjerrige på avveiningene bak komponenten. Du trenger den ikke for å bruke den. For bruk og API, se RadioGroup. - [Select](/docs/komponenter/skjema/select.md): Select lar brukeren velge ett alternativ fra en nedtrekksliste. Komponenten skjuler alternativene til brukeren åpner listen, og brukes når det ikke er nødvendig å vise alle valg samtidig. - [TextArea](/docs/komponenter/skjema/textarea.md): TextArea lar brukeren skrive inn lengre fritekst over flere linjer, som beskrivelser, meldinger eller begrunnelser. - [TextField](/docs/komponenter/skjema/textfield.md): TextField er et inputfelt for kortere tekst eller tall, som kontonummer, e-postadresse eller beløp. - [Formatering](/docs/komponenter/skjema/textfield-formatering.md): TextField kan formatere innholdet automatisk mens brukeren fyller ut: beløp med tusenskille, telefonnummer i grupper, kontonummer, fødselsnummer. Denne siden forklarer hvordan formatering virker, de to modusene, de tre måtene å definere en formatter på, og tilgjengelighetshensynene bak. For vanlig bruk og API, se TextField. - [Tooltip](/docs/komponenter/skjema/tooltip.md): En Tooltip er en liten informasjonsboks som vises når brukeren holder musepekeren over, fokuserer på eller berører et element. Den brukes til å gi ekstra kontekst, forklaringer eller detaljer uten å forstyrre hovedinnholdet. - [ValidationMessage](/docs/komponenter/skjema/validation-message.md): ValidationMessage viser en feilmelding knyttet til et skjemafelt eller en feltgruppe. - [Spinner](/docs/komponenter/spinner.md): Spinner viser at noe lastes inn eller at en operasjon pågår. Den er ikke-interaktiv og kommuniserer utelukkende tilstand. - [Surface](/docs/komponenter/surface.md): Surface er flaten flere ting ligger på: en region som samler flere beslektede elementer og - [Tabs](/docs/komponenter/tabs.md): Faner lar brukeren veksle mellom likeverdige innholdsseksjoner på samme side — uten å navigere til en ny side. Bare én seksjon vises om gangen, og en horisontal fane-liste styrer hvilken. Fanene supplerer SegmentedControl, som brukes til gjensidig utelukkende tilstander og filtre. - [Tag](/docs/komponenter/tag.md): Tag er en liten, ikke-interaktiv etikett som merker eller kategoriserer innhold — for eksempel en status, en egenskap eller et nøkkelord. Taggen bærer betydningen i teksten sin, og fargen støtter opp om hvilken type informasjon det er. - [Typografi](/docs/komponenter/typografi.md): Typografikomponenter i Indeks. Størrelse settes med data-size-attributtet. Oversikten på siden viser hvilken størrelse som hører til hvilket nivå. ### monstre-og-maler - [Deaktiverte tilstander](/docs/monstre-og-maler/deaktiverte-tilstander.md): Hovedregelen i Indeks er at du ikke bruker disabled. Det ser ut som en enkel måte å hindre feil, men det brukeren møter er en kontroll hen ikke får tak i, uten å få vite hvorfor eller hva som skal til. - [Form-validering](/docs/monstre-og-maler/form-validering.md): Indeks sine form-komponenter er bring-your-own-validation: hver komponent tar en ferdig errorMessage-streng og viser den (og setter aria-invalid selv). Reglene og feilmeldingene overlater vi til et form-rammeverk og et skjema. Denne siden viser det anbefalte oppsettet med React Hook Form (RHF) og Valibot. - [Layout](/docs/monstre-og-maler/layout.md): Indeks har utility-klasser for Flex og CSS Grid. Du skriver dem rett i markupen, så du slipper en egen CSS-fil for hver plassering. - [Spacing](/docs/monstre-og-maler/spacing.md): Luft mellom elementer er noe containeren bestemmer, ikke elementet selv. Det er hele ideen bak spacing i Indeks, og den koker ned til fire regler: ### ordbok Forklaringer på fagbegreper som brukes i Indeks-dokumentasjonen. - [Ordbok](/docs/ordbok.md): Forklaringer på fagbegreper som brukes i Indeks-dokumentasjonen. ### retningslinjer - [Farger](/docs/retningslinjer/farger.md): I Indeks bruker vi semantiske farger. En farge navngis etter hva den brukes til, ikke etter hvordan den ser ut: surface-main og fill-success, ikke «fjell» eller #E4002B. - [Fargemodus (light og dark)](/docs/retningslinjer/farger/fargemodus.md): De semantiske fargevariablene i Indeks (--ix-color-*) har egne verdier for light og dark mode. Alle komponenter leser disse variablene, så de tilpasser seg automatisk. ### utility-klasser - [Hitbox](/docs/utility-klasser/hitbox.md): Hitbox utvider det klikkbare arealet til et element uten å påvirke layout. Nyttig for å oppfylle - [Native](/docs/utility-klasser/native.md): Utility-klasser for å kontrollere tekstmarkering, berøring og dra-og-slipp-oppførsel i hybridapper der webinnhold vises i en native app sin WebView. - [Utility-klasser](/docs/utility-klasser/oversikt.md): En utility-klasse setter én CSS-property, eller et lite sett som hører sammen. Du kombinerer dem i markupen i stedet for å skrive egen CSS for hvert tilfelle, og komponentene i Indeks er i stor grad bygget på dem. --- # Full Documentation Content # ADR-DS-001: Monorepo og byggverktøy StatusBesluttet ## Beslutning[​](#beslutning "Direct link to Beslutning") Alle pakker samles i én [Monorepo](/docs/ordbok.md#Monorepo) med **[pnpm](/docs/ordbok.md#pnpm)** som pakkebehandler og **[Turborepo](/docs/ordbok.md#Turborepo)** for build-orkestrering. ### Monorepo[​](#monorepo "Direct link to Monorepo") Alle pakker i samme repo med workspace-protokoll (`workspace:*`) for interne avhengigheter. Pakker publisert til npm: `indeks-tokens`, `indeks-utils`, `indeks-css`, `indeks-web`, `indeks-react`. Private pakker: `indeks-docs`, `indeks-eksempel` og `indeks-storybook`. ### pnpm[​](#pnpm "Direct link to pnpm") Erstattet npm. To sentrale sikkerhetsmekanismer: `shamefully-hoist=false` sikrer streng isolasjon — pakker kan bare bruke avhengigheter de selv har deklarert i `package.json`. Det forhindrer at kode ved et uhell bruker transitive avhengigheter, noe som reduserer risikoen for at en ondsinnet pakke kan nå kode den ikke skal ha tilgang til. `strict-peer-dependencies=true` tvinger frem eksplisitte [peer dependency]()-erklæringer, slik at versjonskonflikter oppdages tidlig i utviklingen fremfor hos konsumenter i produksjon. Content-addressable storage betyr at hver pakkeversjon lagres én gang på disken og verifiseres med en hash ved installasjon — manipulasjon av nedlastede pakker oppdages umiddelbart. ### Turborepo[​](#turborepo "Direct link to Turborepo") Erstattet Lerna for bygging. `^build` sikrer riktig byggerekkefølge basert på dependency-grafen — `indeks-css` bygges aldri før `indeks-tokens` er ferdig. Caching baseres på innholdshasher av kildefiler og avhengigheter: hvis ingenting har endret seg siden forrige bygg, hoppes pakken over. Parallell bygging der det er mulig. ### Drivere for beslutningen[​](#drivere-for-beslutningen "Direct link to Drivere for beslutningen") * Tett kobling mellom pakker krever koordinert utvikling i samme repo * Kjent monorepo-mønster fra eksisterende SpareBank 1 designsystem (`@sb1/ffe-*`) * Bedre sikkerhet enn npm for pakkebehandling * Intelligent caching for raskere CI og lokal utvikling ## Bakgrunn[​](#bakgrunn "Direct link to Bakgrunn") Designsystemet har tette avhengigheter mellom pakkene: ``` indeks-tokens → indeks-utils → indeks-css → indeks-web → indeks-react ``` En endring i tokens påvirker hele kjeden. Pakkene må kunne utvikles og testes i sammenheng, ikke isolert. Vi startet med npm workspaces og Lerna, men npm manglet sikkerhetsfunksjoner og Lerna var utdatert og treg. ## Problemstilling[​](#problemstilling "Direct link to Problemstilling") Hvilken monorepo-strategi og verktøykjede støtter koordinert utvikling og testing på tvers av tett koblede pakker, med god sikkerhet og intelligent caching? ## Konsekvenser[​](#konsekvenser "Direct link to Konsekvenser") ### Hvem påvirkes?[​](#hvem-påvirkes "Direct link to Hvem påvirkes?") Alle utviklere som jobber med designsystemet må bruke pnpm og forstå workspace-konsepter. ### Ulemper[​](#ulemper "Direct link to Ulemper") * Hele repoet må klones (ikke bare én pakke) * Utviklere må installere pnpm — ikke alle har det fra før * CI-miljøer må konfigureres med pnpm * Noen eldre pakker kan ha problemer med pnpms strenge node\_modules-struktur ### Tiltak mot ulemper[​](#tiltak-mot-ulemper "Direct link to Tiltak mot ulemper") * Turborepo-caching og parallellisering gjør CI raskt til tross for én stor repo * Dokumentasjon veileder nye utviklere gjennom oppsett ## Forkastede alternativer[​](#forkastede-alternativer "Direct link to Forkastede alternativer") ### Separate repositories per pakke[​](#separate-repositories-per-pakke "Direct link to Separate repositories per pakke") Hver pakke i sitt eget git-repository, publisert uavhengig. **Forkastet fordi:** Vanskelig å koordinere endringer på tvers av pakker. En token-endring krever PR i fire separate repos med nøye rekkefølge — i praksis upraktisk. ### Én stor pakke[​](#én-stor-pakke "Direct link to Én stor pakke") Slå sammen alle pakker til ett npm-pakke. **Forkastet fordi:** Konsumenter som bare trenger tokens eller CSS må ta inn React-kode. Umuliggjør uavhengig versjonering. ### npm (i stedet for pnpm)[​](#npm-i-stedet-for-pnpm "Direct link to npm (i stedet for pnpm)") npm er standard og forhåndsinstallert hos alle Node.js-brukere. **Forkastet fordi:** Mangler pnpms sikkerhetsisolasjon og er tregere, særlig ved store dependency-grafer. Gammel npm-basert løsning var allerede en kilde til problemer. ### Yarn Berry (Plug'n'Play)[​](#yarn-berry-plugnplay "Direct link to Yarn Berry (Plug'n'Play)") Yarns nye arkitektur med zero-installs og PnP-resolver i stedet for node\_modules. **Forkastet fordi:** Plug'n'Play bryter mange verktøy som forventer en tradisjonell node\_modules-mappe (Jest, Storybook m.fl.) og krever stor migrasjonsoverhead for å gjøre eksisterende tooling kompatibelt. ### Nx[​](#nx "Direct link to Nx") Et build-system med monorepo-støtte og avansert dependency-graf. **Forkastet fordi:** Mer komplekst enn nødvendig for dette prosjektet. Turborepo dekker behovene med vesentlig enklere konfigurasjon. ### Lerna[​](#lerna "Direct link to Lerna") Det opprinnelige verktøyet vi brukte for monorepo-håndtering. **Forkastet fordi:** Utdatert, treg, og Lerna-teamet anbefaler selv Nx eller Turborepo for nye prosjekter. Vi opplevde konkrete ytelses- og vedlikeholdsproblemer. Deltakere Utarbeidet avTeam Designsystem --- # ADR-DS-002: Versjonering og publisering StatusBesluttet ## Beslutning[​](#beslutning "Direct link to Beslutning") `indeks-css`, `indeks-web` og `indeks-react` har alltid identisk versjonsnummer og bumpes alltid sammen. Endringer dokumenteres med **[Changesets](/docs/ordbok.md#Changesets)**. Pakker publiseres til både **[CDN](/docs/ordbok.md#CDN)** (anbefalt) og **npm**. ### Versjonering[​](#versjonering "Direct link to Versjonering") `indeks-css`, `indeks-web` og `indeks-react` har alltid identisk versjonsnummer — en endring i én bumper alle tre. `indeks-tokens` og `indeks-utils` versjoneres uavhengig. Bumping på tvers følger de faktiske avhengighetene. `indeks-utils` bruker `indeks-tokens` som en vanlig `dependency`, så en tokens-endring patch-bumper utils. Videre går det ikke: `indeks-css` har tokens og utils som `devDependencies` — PostCSS inliner dem ved build, så de er ikke avhengigheter for konsumenten — og changesets bumper ikke gjennom devDependencies. Skal en tokens- eller utils-endring ut til dem som konsumerer `indeks-css`, må changesetet nevne `indeks-css` eksplisitt. `indeks-web` er pakken med Web Components (rammeverk-uavhengig komponentlogikk) — se [ADR-DS-004](/docs/adr/ADR-DS-004-web-components.md) for mer om arkitekturen. ### Changesets[​](#changesets "Direct link to Changesets") Erstattet Lerna + Conventional Commits. `pnpm changeset` dokumenterer endringer i en fil uavhengig av commit-meldinger. Ingen krav til commit-format. ### Distribusjon[​](#distribusjon "Direct link to Distribusjon") **CDN (anbefalt):** `cdn.sparebank1.no/indeks/css/{versjon}/index.css` — versjonerte URLer kan caches "for alltid" i nettleseren. CDN-bygget publiserer `indeks-css`, `indeks-tokens`, `indeks-utils` og `indeks-web`. `indeks-css` henter automatisk inn tokens og utils, slik at konsumenter bare trenger én ``-tag. CDN-bygget er et eget build-target der PostCSS-plugin erstatter interne npm-imports med absolutte CDN-URLer, slik at CSS-filen er selvforsynt uten bundler. **npm (alternativ):** `@sb1/indeks-css`, `@sb1/indeks-web` og `@sb1/indeks-react` for bundler-baserte apps. Avhengigheter håndteres automatisk via `workspace:*`. ### Drivere for beslutningen[​](#drivere-for-beslutningen "Direct link to Drivere for beslutningen") * Garantert kompatibilitet mellom CSS og React — konsumenter slipper å teste kombinasjoner * Optimal CDN-caching med versjonerte URLer * Lavere terskel for bidragsytere uten krav om commit-format * Fleksibilitet for team med ulik teknisk infrastruktur ## Bakgrunn[​](#bakgrunn "Direct link to Bakgrunn") `indeks-web` og `indeks-react` avhenger av CSS-klasser fra `indeks-css` — versjoner ute av sync gir feil styling. Konsumenter trenger garantert kompatibilitet uten å måtte teste kombinasjoner. I tillegg må CSS kunne konsumeres på to måter: via bundler (npm) og direkte via ``-tag (CDN). CDN-distribusjon krever versjonerte URLer for å muliggjøre evig caching. Vi brukte tidligere Lerna med Conventional Commits, men brukte mye tid på å rette commit-meldinger og terskelen for å bidra var høy. Verktøyet var i tillegg utdatert. ## Problemstilling[​](#problemstilling "Direct link to Problemstilling") Hvordan sikrer vi garantert kompatibilitet mellom CSS og React-pakker, støtter både CDN og npm-distribusjon, og holder terskelen for bidrag lav? ## Konsekvenser[​](#konsekvenser "Direct link to Konsekvenser") ### Hvem påvirkes?[​](#hvem-påvirkes "Direct link to Hvem påvirkes?") Alle konsumenter velger mellom CDN og npm. Alle bidragsytere bruker `pnpm changeset` i stedet for Conventional Commits. ### Ulemper[​](#ulemper "Direct link to Ulemper") * Små CSS-endringer bumper React-versjon og omvendt, selv uten funksjonell endring * Ekstra steg (`pnpm changeset`) i utviklingsflyten * To build-outputs å vedlikeholde (npm + CDN) * CDN-brukere må manuelt oppdatere versjonsnummer i ``-tag ### Tiltak mot ulemper[​](#tiltak-mot-ulemper "Direct link to Tiltak mot ulemper") * Det finnes til enhver tid bare én åpen versjons-PR. CI oppdaterer den samme PR-en når nye changesets kommer inn, så versjonsoppdateringer kan ikke havne i kø bak hverandre. * npm, CDN og dokumentasjonsnettstedet oppdateres automatisk, i den rekkefølgen, etter at versjons-PR-en er merget. Hvert steg sjekker at det forrige er ute før det gjør noe — dokumentasjonen deployes for eksempel ikke før CDN-en faktisk svarer på de nye URL-ene. Se [ADR-DS-003](/docs/adr/ADR-DS-003-ci-cd.md) for mekanikken. * Merge av versjons-PR-en er det eneste manuelle steget. Det er også den eneste gaten: alt annet ruller av seg selv. ## Forkastede alternativer[​](#forkastede-alternativer "Direct link to Forkastede alternativer") ### Alle pakker samme versjon (som @sb1/ffe)[​](#alle-pakker-samme-versjon-som-sb1ffe "Direct link to Alle pakker samme versjon (som @sb1/ffe)") Tokens, CSS og React bumpes alltid til samme versjon, som i SpareBank 1s eksisterende designsystem FFE. **Forkastet fordi:** `indeks-tokens` og `indeks-utils` endres sjelden, og skal beholde et stabilt versjonsnummer slik at deres egne CDN-artefakter kan caches lengst mulig. Å låse alle fire til samme versjon (FFE-stil) ville bumpet tokens/utils ved hver minste css- eller react-endring og ugyldiggjort cachen deres helt unødvendig. ### Helt uavhengige versjoner[​](#helt-uavhengige-versjoner "Direct link to Helt uavhengige versjoner") Alle pakker versjoneres uavhengig uten noen form for synkronisering. **Forkastet fordi:** Vanskelig for konsumenter å vite hvilke kombinasjoner av CSS og React som fungerer sammen. Øker supportbyrden. ### Lerna + Conventional Commits[​](#lerna--conventional-commits "Direct link to Lerna + Conventional Commits") Det opprinnelige verktøyet vi brukte. **Forkastet fordi:** Utdatert verktøy, høy terskel grunnet krav om spesifikk commit-format, og vi brukte faktisk tid på å fikse commit-meldinger i stedet for å lage produktet. ### semantic-release[​](#semantic-release "Direct link to semantic-release") Automatisk versjonering basert på commit-meldinger. **Forkastet fordi:** Krever fortsatt Conventional Commits — løser ikke terskelen for bidrag. ### Kun npm eller kun CDN[​](#kun-npm-eller-kun-cdn "Direct link to Kun npm eller kun CDN") Publisere bare til én distribusjonskjøre i stedet for begge. **Forkastet fordi:** Team med ulik infrastruktur trenger begge. Kun CDN fungerer ikke for bundler-baserte apper som trenger [tree-shaking](/docs/ordbok.md#tree-shaking). Kun npm mister CDN-caching-fordelene. Deltakere Utarbeidet avTeam Designsystem --- # ADR-DS-003: CI/CD med GitHub Actions StatusBesluttet ## Beslutning[​](#beslutning "Direct link to Beslutning") All CI/CD kjøres med **GitHub Actions**, organisert i spesialiserte workflows gruppert etter prefiks: `pr-`, `release-`, `deploy-` og `security-`. ### PR-workflows[​](#pr-workflows "Direct link to PR-workflows") Kjøres på pull requests mot main. * **`pr-build-and-preview.yml`** — Bygger alle pakker, kjører lint og tester, bygger [Storybook](/docs/ordbok.md#Storybook), og deployer forhåndsvisning til Azure Static Web Apps. Inkluderer lisenssjekk og sikkerhetsaudit. * **`pr-playwright.yml`** — Kjører [Playwright](/docs/ordbok.md#Playwright)-tester i [Docker](/docs/ordbok.md#Docker) mot Storybook: screenshot-sammenligning og tilgjengelighetstesting. * **`pr-eksempel-e2e.yml`** — Kjører funksjonell e2e mot eksempelappen i Docker. Trigges bare ved endringer i eksempelappen eller pakkene den bruker. * **`pr-cleanup.yml`** — Sletter Azure-forhåndsvisningen når en PR lukkes. ### Release-workflows[​](#release-workflows "Direct link to Release-workflows") Versjonering og publisering er to workflows, ikke én. `changesets/action` velger alltid versjonering når det finnes uforbrukte changesets, så den kan ikke også brukes til å publisere på et senere tidspunkt. * **`release-versjons-pr.yml`** — Trigger: push til main. Kjører `changeset version` via `changesets/action` og oppretter eller oppdaterer PR-en «chore: version packages». Publiserer ingenting. * **`release-publiser.yml`** — Trigger: schedule, hver time i kjernetid på hverdager. Publiserer alt i den nyeste versjons-commiten som ikke allerede ligger på npm, pusher git-tags og oppretter en GitHub-release per pakke. Er alt publisert, gjør kjøringen ingenting. Pakkene publiseres med [npm provenance]() — en kryptografisk kobling mellom npm-pakken og GitHub Actions-kjøringen som produserte den, slik at konsumenter kan verifisere at pakken faktisk ble bygget fra kildekoden i repoet. Autentiseringen mot npm skjer med Trusted Publishing (OIDC), uten tilgangstoken. Publiseringen skjer fra versjons-commiten, ikke fra det main peker på når cron-jobben kjører. Ellers ville feature-PR-er som har landet i mellomtiden bli publisert under et versjonsnummer changeloggen deres ikke står i. ### Deploy-workflow[​](#deploy-workflow "Direct link to Deploy-workflow") * **`deploy-docs.yml`** — Trigger: schedule, hver time i kjernetid på hverdager. Bygger docs, Storybook og eksempel-app fra den nyeste release-taggen og deployer til Azure Static Web Apps. En egen, billig sjekk-jobb avgjør først om det er noe å deploye: den krever at CDN-en faktisk svarer på URL-ene for de nyeste versjonene, og at den versjonen ikke alt er deployet. Kan kjøres manuelt med valgfri commit når en tekstfiks må ut før neste release. Docs bygges fra release-taggen fordi `package.json` i det treet *er* den publiserte sannheten. Bygde vi fra main, ville installasjonseksemplene på nettstedet vist versjonsnumre som ennå ikke finnes på npm eller CDN. ### Publisering til CDN[​](#publisering-til-cdn "Direct link to Publisering til CDN") CDN-artefaktene bygges av et eget, internt repo (`sparebank1utvikling/sb1-indeks`) som poller Releases-API-et her, kloner de nye release-taggene, kjører CDN-bygget og laster opp til S3. Det skjer også på schedule, mellom npm-publiseringen og docs-deployen. Kjeden er polling-basert og ikke event-basert fordi de to repoene ligger i ulike GitHub-organisasjoner — en webhook ville krevd at en credential med skrivetilgang til det interne repoet lå lagret i dette, som er offentlig. Fordi hvert steg selv sjekker om det forrige er ute, er en forsinket eller droppet cron-kjøring ufarlig: steget gjør ingenting og prøver igjen neste time. ### Security-workflows[​](#security-workflows "Direct link to Security-workflows") * **`security-codeql.yml`** — CodeQL-skanning av JavaScript på PR, push til main og ukentlig schedule. Funn av alvorlighet `error` eller `high` blokkerer merge via rulesettet for main. * **`security-zizmor.yml`** — Skanner workflow-filer med [Zizmor](https://github.com/zizmorcore/zizmor), et statisk analyseverktøy som finner sikkerhetsproblemer i GitHub Actions-konfigurasjoner (f.eks. script injection, overprivilegerte tokens). Kjøres ved endringer i `.github/workflows/`. Kjøringen feiler ved funn, men er ikke en påkrevd statussjekk — den ville blokkert alle PR-er som ikke rører workflow-filer. * **`security-npm-deprecate.yml`** — Manuell workflow for å deprecate eller unpublish npm-pakker ved sikkerhetshendelser. ### Drivere for beslutningen[​](#drivere-for-beslutningen "Direct link to Drivere for beslutningen") * Tett integrasjon med GitHub — ingen separate tokens, bruker GITHUB\_TOKEN * God marketplace med ferdige actions for pnpm, Azure og GitHub Pages * Innebygd secrets management via GitHub Secrets * Alle på teamet kjenner GitHub Actions fra andre prosjekter ## Bakgrunn[​](#bakgrunn "Direct link to Bakgrunn") Prosjektet trenger automatisert bygging, testing, publisering og deployment. Kilden er GitHub, og vi ønsker sterk integrasjon mellom kode og CI/CD — ikke et separat verktøy som må autentiseres og vedlikeholdes ved siden av. ## Problemstilling[​](#problemstilling "Direct link to Problemstilling") Hvilken CI/CD-løsning gir best integrasjon med GitHub-basert kildekode, uten å innføre ekstra verktøy og autentiseringskompleksitet? ## Konsekvenser[​](#konsekvenser "Direct link to Konsekvenser") ### Hvem påvirkes?[​](#hvem-påvirkes "Direct link to Hvem påvirkes?") Alle utviklere som pusher kode trigger workflows. Teamet vedlikeholder workflow-filer. ### Ulemper[​](#ulemper "Direct link to Ulemper") * Vendor lock-in til GitHub — vanskelig å flytte til annen platform uten å skrive om workflows * YAML-konfigurasjon kan bli kompleks og er vanskelig å teste lokalt ### Tiltak mot ulemper[​](#tiltak-mot-ulemper "Direct link to Tiltak mot ulemper") * Workflows holdes enkle og modulære med klare ansvarsgrenser per prefiks * Kompleks logikk flyttes til scripts som kan kjøres lokalt ## Forkastede alternativer[​](#forkastede-alternativer "Direct link to Forkastede alternativer") ### CircleCI[​](#circleci "Direct link to CircleCI") En populær CI/CD-plattform med god ytelse og parallelliseringsevne. **Forkastet fordi:** Ekstra integrasjon og kostnad. GitHub Actions gir tilsvarende funksjonalitet uten ekstra verktøy og med bedre GitHub-integrasjon. ### GitLab CI[​](#gitlab-ci "Direct link to GitLab CI") CI/CD integrert i GitLab, med sterk integrasjon mellom kode og pipelines. **Forkastet fordi:** Kildekoden er på GitHub, og å flytte til GitLab bare for CI gir ekstra kompleksitet. Fordelene GitLab CI gir er de samme som GitHub Actions allerede tilbyr på GitHub-siden. ### Jenkins[​](#jenkins "Direct link to Jenkins") Et selvhostet, svært fleksibelt CI/CD-system med lang historikk. **Forkastet fordi:** Krever egen infrastruktur å vedlikeholde. For et lite team er vedlikeholdsoverheaden ikke verdt fleksibiliteten. ### Azure DevOps[​](#azure-devops "Direct link to Azure DevOps") Microsofts ALM-platform med Pipelines for CI/CD. **Forkastet fordi:** Ekstra verktøy utenfor GitHub. SpareBank 1 bruker Azure for hosting, men kodeplatformen er GitHub — å splitte CI/CD til Azure DevOps gir unødvendig kompleksitet. Deltakere Utarbeidet avTeam Designsystem --- # ADR-DS-004: Web Components StatusBesluttet | Statuslogg | | | ---------- | --------- | | 2026-07-29 | Besluttet | | 2026-07-24 | Besluttet | ## Beslutning[​](#beslutning "Direct link to Beslutning") Komponentlogikk implementeres som **[Web Components]()** i `indeks-web` og eksponeres til React-konsumenter via `indeks-react`. [Shadow DOM]() unngås som hovedregel. Eksempel: `ix-field` er ikke et input-felt — det er et omgivende lag som knytter label, input og feilmelding sammen med korrekte ARIA-relasjoner. Alt dette kan også gjøres med ren HTML; Web Component-en gjør det bare enklere og feilsikkert. **Shadow DOM:** Vi unngår Shadow DOM som hovedregel. Shadow DOM isolerer markup fra resten av dokumentet, noe som gjør tilgjengelighetsarbeid vanskeligere — skjermlesere og ARIA-relasjoner må fungere på tvers av shadow boundaries, og konsumenter mister muligheten til å style med CSS-variabler. Unntaket er komponenter som viser visuelt innhold basert på universelt utformet data i markup — for eksempel et chart-element som rendres visuelt i Shadow DOM mens tabelldata forblir tilgjengelig i vanlig DOM. **Arkitektur:** `indeks-web` (Web Components) → `indeks-react` (wrapper). React-pakken eksponerer komponentene med idiomatisk React API. Det meste av funksjonaliteten ligger i Web Component-en — React-laget håndterer React-spesifikke ting som event-mapping og prop-konvertering, slik at en ren-HTML-konsument får nøyaktig samme fullverdige komponent. Hvordan `indeks-react` bygges og distribueres er beskrevet i [ADR-DS-005](/docs/adr/ADR-DS-005-react-bibliotek.md). **Bevisste rammeverk-tilpasninger:** React-laget kan ha målrettede tilpasninger for å fungere godt med utbredte React-verktøy — for eksempel props som gjør en komponent kompatibel med React Hook Form (`register()`), slik at skjemakobling blir idiomatisk. Slike tilpasninger handler utelukkende om *integrasjon* — å få React til å fungere — aldri om å gi React-konsumenter mer funksjonalitet eller en annen brukeropplevelse. **UI og UX skal være identisk uansett konsument.** En bruker skal ikke kunne se eller kjenne — verken visuelt eller via tastatur og skjermleser — om et felt er bygget med ren HTML eller React. HTML-versjonen av hver komponent er fullverdig i seg selv; React-wrapperen legger ikke til noe ekstra, den kabler bare det som allerede finnes lenger ned i stacken. Det som *skal* skille seg, er API-flaten: React får idiomatiske props, typer og JSX, mens HTML får attributter og web component. Ulikt grensesnitt, lik komponent. Tilpasninger er tillatt så lenge to ting holder: (1) Web Component-en forblir kilden til sannhet — logikken legges i WC-en når den også gagner ikke-React-konsumenter (f.eks. native `change`-events og fokushåndtering på riktig element), og React-laget begrenser seg til tynn kabling; og (2) HTML-pariteten bevares — en ren-HTML- eller Vue/Angular-konsument får samme fullverdige komponent, ikke en redusert variant. En React-only-egenskap som ikke lar seg uttrykke i WC-en, skal være liten, veldokumentert og ikke bryte de andre konsumentene. ### Drivere for beslutningen[​](#drivere-for-beslutningen "Direct link to Drivere for beslutningen") * Rammeverk-uavhengighet — webstandard fungerer i React, Vue, Angular, Astro og ren HTML * Én kilde til sannhet for komponentlogikk * Fremtidssikring uavhengig av rammeverk-trender * Nettleser-native uten ekstra runtime ## Bakgrunn[​](#bakgrunn "Direct link to Bakgrunn") De fleste komponentene i Indeks er ren HTML-markup med CSS-styling — React wrapper bare dette, og man kan like gjerne bruke ren HTML. Men noen komponenter trenger mer: logikk, tilstandshåndtering eller sammensatt atferd som knytter flere elementer sammen. SpareBank 1 har team som bruker ulike teknologier — ikke bare React. En løsning som låser kompleks komponentlogikk til React er ikke fremtidssikret. ## Problemstilling[​](#problemstilling "Direct link to Problemstilling") Hvordan implementerer vi komponentlogikk slik at den er tilgjengelig for alle team, uavhengig av hvilke JavaScript-rammeverk de bruker? ## Konsekvenser[​](#konsekvenser "Direct link to Konsekvenser") ### Hvem påvirkes?[​](#hvem-påvirkes "Direct link to Hvem påvirkes?") * **Team Designsystem** — implementerer Web Components * **React-konsumenter** — bruker React-wrapperen og slipper å forholde seg til maskineriet under; for team som vil bygge frontend uten å tenke på hvordan det fungerer, er dette et godt valg * **Andre team** — får tilgang til komponentlogikk uten React-avhengighet; team som bruker andre rammeverk eller vil tilpasse mer, ser naturlig mer av kompleksiteten bak — men får samme komponent * **Designere** — ingen endring Abstraksjonsnivået skalerer altså med hvor mye konsumenten vil involvere seg: React skjuler mest, ren HTML og andre rammeverk eksponerer mer av maskineriet. Uansett nivå er komponenten — og brukeropplevelsen — den samme. ### Ulemper[​](#ulemper "Direct link to Ulemper") * [SSR](/docs/ordbok.md#SSR): `customElements.define()` er en nettleser-API som ikke finnes i Node.js. Vi har testet SSR i de tilfellene vi har prøvd, og det har fungert godt — men vi har ikke verifisert alle rammeverk (Next.js o.l.) uttømmende * Team Designsystem må beherske Web Components-standarden ([Custom Elements API]()) — i praksis en liten kostnad, siden komponentene vi skriver er forholdsvis enkle og konsumenter merker lite (de bruker React-wrapperen eller ren HTML) * Konsumenter er selv ansvarlige for å laste `indeks-web` én gang — dette forhindrer kollisjoner ved registrering av custom elements ### Tiltak mot ulemper[​](#tiltak-mot-ulemper "Direct link to Tiltak mot ulemper") * Etablere klare mønstre for Web Components-utvikling tidlig * SSR-støtte må kartlegges per komponent — dette er et åpent spørsmål som vil bli adressert når aktuelle komponenter nærmer seg produksjon ## Forkastede alternativer[​](#forkastede-alternativer "Direct link to Forkastede alternativer") ### Kun React-komponenter[​](#kun-react-komponenter "Direct link to Kun React-komponenter") Fortsette med React-only komponenter for all logikk. **Forkastet fordi:** Begrenser gjenbruk til React-prosjekter. Vi skal støtte team som bruker andre teknologier enn React, og disse teamene ville vært utestengt fra de mest komplekse komponentene. ### Lit[​](#lit "Direct link to Lit") Et lett bibliotek fra Google for å skrive Web Components med deklarativ template-syntaks og reaktivitetssystem. Brukes av blant andre Adobe (Spectrum), SAP og ING. **Forkastet fordi:** Vi ønsker å holde `indeks-web` fri for eksterne bibliotek- og rammeverk-avhengigheter. Lits verdi ligger i deklarativ templating og reaktiv rendering *inne i* komponenten — noe vi ikke trenger, fordi vi ikke bruker Shadow DOM. Våre Web Components er tynne wrappere som manipulerer lys-DOM direkte, så native Custom Elements er håndterbart for komponentene vi faktisk skriver. Vi ser også til [designsystemet.no](https://designsystemet.no) som gjør det samme med native Web Components — det gir oss mulighet til å lære av og bygge videre på deres erfaringer. ### Stencil.js (Ionic)[​](#stenciljs-ionic "Direct link to Stencil.js (Ionic)") Et kompilatorbasert rammeverk for Web Components med TypeScript-first API, JSX-syntaks og innebygd støtte for å generere React- og Vue-wrappers automatisk. **Forkastet fordi:** Introduserer et kompilatorlag og en egen komponentmodell som gjør koden avhengig av Stencil — native Custom Elements er håndterbart uten dette. Automatisk wrapper-generering er en fordel vi ikke trenger siden vi uansett skriver React-wrapperen manuelt. ### FAST (Microsoft)[​](#fast-microsoft "Direct link to FAST (Microsoft)") Et bibliotek fra Microsoft for å bygge Web Components, brukt som fundament for Fluent UI Web Components. **Forkastet fordi:** Samme begrunnelse som Lit — vi ønsker å holde `indeks-web` fri for eksterne avhengigheter. FAST introduserer i tillegg en egen komponentmodell og attributt-system som binder komponentene tettere til biblioteket enn native Custom Elements. ### Multi-framework med delt TypeScript-kjerne[​](#multi-framework-med-delt-typescript-kjerne "Direct link to Multi-framework med delt TypeScript-kjerne") Dele forretningslogikk i et rammeverk-agnostisk TypeScript-bibliotek, og skrive rammeverk-spesifikke wrappers for React, Vue og Angular. **Forkastet fordi:** Kompleks build-pipeline med mange wrappers å vedlikeholde. Fortsatt rammeverk-spesifikk kode i alle wrappers. Web Components løser det samme med én implementasjon. ### Mitosis (Builder.io)[​](#mitosis-builderio "Direct link to Mitosis (Builder.io)") Et verktøy som kompilerer én komponentdefinisjon til React, Vue, Angular og Web Components. **Forkastet fordi:** Umodent verktøy med begrenset adopsjon. Abstraksjonslaget over komponentdefinisjonene gjør feilsøking vanskeligere, og output-koden er ikke idiomatisk i noe av målrammeverket. Deltakere Utarbeidet avTeam Designsystem --- # ADR-DS-005: React-bibliotek (Vite, ESM, React 18/19) StatusBesluttet ## Beslutning[​](#beslutning "Direct link to Beslutning") React-biblioteket bygges med **[Vite](/docs/ordbok.md#Vite)** i library mode, distribueres som **[ESM](/docs/ordbok.md#ESM)-only**, og støtter **React 18 og 19** som [peer dependency](). ### Vite[​](#vite "Direct link to Vite") Library mode med `vite-plugin-dts` for TypeScript declarations. React defineres som external (peer dependency) — ikke bundlet inn i biblioteket. God integrasjon med [Storybook](/docs/ordbok.md#Storybook). ### ESM-only[​](#esm-only "Direct link to ESM-only") `"type": "module"` i package.json. Ingen CJS-build. Build-target holdes synkronisert manuelt med `.browserslistrc` (via `vite.shared.ts` — se [ADR-DS-001](/docs/adr/ADR-DS-001-monorepo-og-byggverktoy.md) og nettleserstøtte-baselinen), for å støtte nettlesere tilbake til Safari/iOS 15.4. Optimal [tree-shaking](/docs/ordbok.md#tree-shaking) slik at konsumenter bare betaler for det de bruker. ### React 18 og 19[​](#react-18-og-19 "Direct link to React 18 og 19") Peer dependency `"react": "^18.0.0 || ^19.0.0"` (samme for `react-dom`) — støtter både React 18 og 19. Det senker terskelen for konsumenter som fortsatt er på React 18, samtidig som React 19 anbefales. Vi holder oss til API-er som finnes i begge for å unngå å utestenge React 18-konsumenter. ### Drivere for beslutningen[​](#drivere-for-beslutningen "Direct link to Drivere for beslutningen") * Rask build og hot-reload for god utvikleropplevelse * ESM er moderne JavaScript-standard og støtter optimal tree-shaking * Bred React-støtte (18 og 19) senker adopsjonsterskelen for early adopters ## Bakgrunn[​](#bakgrunn "Direct link to Bakgrunn") Komponentbiblioteket trenger en bundler, et distribusjonsformat og en strategi for React-versjonsstøtte. ESM (ES Modules) er nå native i alle moderne nettlesere og Node.js-versjoner. React 19 ble sluppet i desember 2024. ## Problemstilling[​](#problemstilling "Direct link to Problemstilling") Hvilken bundler, distribusjonsformat og React-versjonsstrategi gir best utvikleropplevelse, optimal tree-shaking og langsiktig holdbarhet for komponentbiblioteket? ## Konsekvenser[​](#konsekvenser "Direct link to Konsekvenser") ### Hvem påvirkes?[​](#hvem-påvirkes "Direct link to Hvem påvirkes?") Konsumenter med eldre CJS-prosjekter må ta i bruk bundler. Konsumenter på React 18 og 19 kan bruke biblioteket direkte. ### Ulemper[​](#ulemper "Direct link to Ulemper") * Inkompatibel med eldre CJS-prosjekter som ikke bruker bundler * Å støtte både React 18 og 19 begrenser oss til API-er som finnes i begge versjonene * Vite er en abstraksjon over Rollup — feilsøking av edge cases i build kan kreve kunnskap om begge (ingen konkret mitigering, akseptert risiko) ### Tiltak mot ulemper[​](#tiltak-mot-ulemper "Direct link to Tiltak mot ulemper") * De fleste moderne bundlere (webpack, Vite, Parcel) håndterer ESM uten konfigurasjon * React 19-spesifikke API-er brukes bare der de har en trygg fallback på React 18 ## Forkastede alternativer[​](#forkastede-alternativer "Direct link to Forkastede alternativer") ### Rollup direkte[​](#rollup-direkte "Direct link to Rollup direkte") Vite bruker Rollup under panseret — bruke Rollup direkte gir full kontroll uten et abstraksjonslag. **Forkastet fordi:** Krever mer manuell konfigurasjon enn Vite, og vi mister Vites dev server og Storybook-integrasjon som brukes aktivt i utviklingsflyten. ### tsup / esbuild[​](#tsup--esbuild "Direct link to tsup / esbuild") Raskere bundlere enn Rollup/Vite, særlig for TypeScript-prosjekter. **Forkastet fordi:** Dårligere Storybook-integrasjon. Storybook er en sentral del av arbeidsflyten, og god integrasjon veier tyngre enn marginalt raskere build. ### Webpack[​](#webpack "Direct link to Webpack") Det mest utbredte build-verktøyet historisk sett. **Forkastet fordi:** Kompleks konfigurasjon og tregere enn Vite. Library mode er ikke Webpacks primære styrke. ### Dual CJS/ESM (begge formater)[​](#dual-cjsesm-begge-formater "Direct link to Dual CJS/ESM (begge formater)") Publisere både CommonJS og ES Module-versjoner for å støtte alle konsumenter. **Forkastet fordi:** Dobbel vedlikeholdsbyrde og økt pakke-størrelse. CJS er på vei ut av Node.js-økosystemet, og å vedlikeholde det forsinker avviklingen. ### Kun React 19 (`"react": "^19.0.0"`)[​](#kun-react-19-react-1900 "Direct link to kun-react-19-react-1900") Kun støtte React 19 og tvinge konsumenter på React 18 til å oppgradere. **Forkastet fordi:** Utestenger early adopters som fortsatt er på React 18 og gjør adopsjonsterskelen unødvendig høy. Kostnaden ved å holde seg til API-er som finnes i begge versjonene er lav sammenlignet med å låse ute konsumenter. React 19 anbefales fortsatt, men kreves ikke. Deltakere Utarbeidet avTeam Designsystem --- # ADR-DS-006: Design tokens StatusBesluttet ## Beslutning[​](#beslutning "Direct link to Beslutning") De fleste design tokens defineres som JSON i `indeks-tokens/tokens/` og er single source of truth for disse. Synkroniseres toveis med Figma med klare ansvarsgrenser. ### Token-struktur[​](#token-struktur "Direct link to Token-struktur") JSON-filer i `indeks-tokens/tokens/` (colors/, typography.json, border.json, breakpoints.json, z-index.json, transition.json, opacity.json, outline.json). Genererer [CSS custom properties]() (`--ix-*`), iOS Swift-verdier og Android XML. Fargesystemet er beskrevet i [ADR-DS-007](/docs/adr/ADR-DS-007-fargesystem.md). Spacing bor i CSS, ikke JSON Spacing er et bevisst unntak: `spacing.json` er en tom stubb, og spacing-tokenene defineres direkte som håndskrevet CSS i `indeks-utils/css/spacing.css` (faste px per breakpoint + density, se [ADR-DS-008](/docs/adr/ADR-DS-008-spacing-system.md)). Spacing-modellen (breakpoint-varianter × density-nivåer) er mer lesbar som CSS enn som generert JSON, og blir værende der. ### Figma-sync[​](#figma-sync "Direct link to Figma-sync") Synkroniseringen er toveis men med klare ansvarsgrenser: primitive verdier eies i kode og synkroniseres én vei til Figma. Semantisk mapping (f.eks. blue-600 → color-action) eies av designere i Figma og synkroniseres tilbake til kode via GitHub Actions workflow (`sync-figma-tokens.yml`). De to retningene er separate — det oppstår ikke konflikter mellom dem. ### Drivere for beslutningen[​](#drivere-for-beslutningen "Direct link to Drivere for beslutningen") * Konsistens på tvers av plattformer (web, iOS, Android) * Versjonskontroll av design-verdier — endringer er sporbare i git * Koden er fasit for primitive verdier — designere styrer [semantisk mapping]() * Theming — konsumenter som Spleis kan bytte ut primitive farger og få komplette fargeskalaer og semantiske tokens generert for sitt tema. Se [ADR-DS-007](/docs/adr/ADR-DS-007-fargesystem.md) for detaljer om fargesystemet ## Bakgrunn[​](#bakgrunn "Direct link to Bakgrunn") Et designsystem trenger konsistente verdier for farger, spacing, typografi og andre primitiver. Disse verdiene må brukes på flere plattformer (web, iOS, Android) og holdes synkronisert med Figma. Vi hadde ingen etablert kilde til sannhet for disse verdiene. Designere jobbet med farger i Figma, utviklere hardkodet verdier i CSS, og iOS- og Android-team hadde egne filer. Resultatet var drift mellom plattformene og manuelt arbeid hver gang noe skulle oppdateres. ## Problemstilling[​](#problemstilling "Direct link to Problemstilling") Hvordan definerer og distribuerer vi [design tokens]() på en måte som sikrer konsistens på tvers av web, iOS og Android — og holder Figma og kode i sync — uten at det krever manuelt arbeid ved hver endring? ## Konsekvenser[​](#konsekvenser "Direct link to Konsekvenser") ### Hvem påvirkes?[​](#hvem-påvirkes "Direct link to Hvem påvirkes?") Designere bruker Figma-synkroniseringen og definerer semantisk mapping. Frontend-utviklere bruker CSS tokens direkte. Mobilutviklere får genererte outputs for iOS og Android. ### Ulemper[​](#ulemper "Direct link to Ulemper") Ingen identifiserte ulemper. Figma-sync kjøres manuelt og ad hoc — det er en bevisst prosess der endringer i primitiver vurderes før de synkroniseres til Figma. ## Forkastede alternativer[​](#forkastede-alternativer "Direct link to Forkastede alternativer") ### Style Dictionary / Theo[​](#style-dictionary--theo "Direct link to Style Dictionary / Theo") Industristandard-verktøy (Adobe/Amazon) for å transformere design tokens til plattform-spesifikke outputs. Brukes av mange store designsystemer. **Forkastet fordi:** Gir mindre kontroll over output-formatet enn egne scripts, og løser ikke behovet for OKLCH-generering uten egne plugins uansett. ### CSS-variabler direkte (uten token-pipeline)[​](#css-variabler-direkte-uten-token-pipeline "Direct link to CSS-variabler direkte (uten token-pipeline)") Definere CSS custom properties manuelt i stilark, uten JSON-basert token-pipeline. **Forkastet fordi:** * Kan ikke generere outputs for iOS og Android * Ingen Figma-sync uten separat pipeline * Ingen versjonskontroll av selve verdiene som data ### Tokens Studio plugin[​](#tokens-studio-plugin "Direct link to Tokens Studio plugin") Figma-plugin som håndterer design tokens direkte i Figma og synkroniserer til kode. **Forkastet fordi:** Vanskelig å implementere OKLCH-generering, og gir designerne kontroll over primitive verdier — vi ønsker at primitiver eies i kode og at kun semantisk mapping styres fra Figma. ### Manuell eksport fra Figma[​](#manuell-eksport-fra-figma "Direct link to Manuell eksport fra Figma") Eksportere verdier manuelt fra Figma til CSS-filer ved behov. **Forkastet fordi:** Feilutsatt prosess uten versjonskontroll, og løser ikke behovet for plattform-spesifikke outputs. Deltakere Utarbeidet avTeam Designsystem --- # ADR-DS-007: Fargesystem StatusBesluttet ## Beslutning[​](#beslutning "Direct link to Beslutning") Fargeskalaer **genereres i [OKLCH](/docs/ordbok.md#OKLCH)** — et perseptuelt uniformt fargerom der lik lightness-verdi gir lik opplevd lysstyrke på tvers av fargetoner. Dette gjør det mulig å generere hele fargeskalaer fra én nøkkelfarge og få forutsigbare resultater. OKLCH er altså **genererings-/designrommet**. De **publiserte tokenene er hex-verdier** (`indeks-tokens/.build/css/themes/sb1.css`), ikke `oklch()`-funksjoner i runtime — den perseptuelle uniformiteten er en egenskap ved *hvordan* skalaene beregnes, og «fryses» ned til konkrete hex-verdier ved build. Konsumenter forholder seg til vanlige hex-farger. ### Skala-struktur[​](#skala-struktur "Direct link to Skala-struktur") Fargeskalaer er nummerert fra 0 til 950 (f.eks. blue-0 til blue-950). Fordi OKLCH er perseptuelt uniform gir dette designerne et forutsigbart system å jobbe med — man kan utlede hvilke kombinasjoner som fungerer visuelt og kontrastmessig uten å verifisere hvert par manuelt. Fargegenerering skjer ved build via scripts i `indeks-tokens/scripts/build-colors/`. Primitive fargeverdier defineres manuelt i JSON og er input til genereringen. ### Semantisk mapping og dark mode[​](#semantisk-mapping-og-dark-mode "Direct link to Semantisk mapping og dark mode") Primitive fargeverdier mappes til semantiske tokens som brukes i kode. De semantiske tokenene er organisert etter bruksområde — `foreground` (tekst), `background`, `fill` (flater og knapper), `surface`, `border` — med varianter som `main`, `subtle` og `inverse`. Eksempel på mapping: | Semantisk token | Light mode | Dark mode | | ------------------------------- | ----------- | ----------- | | `color-fill-main-default` | `brand-550` | `brand-450` | | `color-foreground-main-default` | `gray-900` | `gray-0` | Dark mode fungerer ved at de semantiske tokenene peker på andre trinn i samme skala — ingen nye farger defineres, bare andre trinn. Fordi OKLCH-skalaen er perseptuelt uniform fungerer de samme trinnvalgene for alle fargetoner, og [theming](/docs/ordbok.md#theming) for nye konsumenter krever kun at primitive farger byttes ut. ### Merkevarefarger[​](#merkevarefarger "Direct link to Merkevarefarger") Eksisterende merkevarefarger er ikke eksakt representert i OKLCH-skalaene — fargene justeres noe for å passe inn i den perseptuelt uniforme strukturen. Tilpasning mot merkevare er et kontinuerlig arbeid som pågår i samarbeid med merkevareteamet. ### Drivere for beslutningen[​](#drivere-for-beslutningen "Direct link to Drivere for beslutningen") * Theming — konsumenter som Spleis kan definere egne nøkkelfarger og få komplette skalaer generert automatisk * Forutsigbare fargeskalaer gjør fargearbeid enklere for designere — man slipper å kalibrere hver fargetone manuelt * Kontrastgarantier for universell utforming kan utledes fra skala-trinnet alene * Konsistente skalaer på tvers av alle fargetoner ## Bakgrunn[​](#bakgrunn "Direct link to Bakgrunn") Indeks skal støtte theming — konsumenter som Spleis har behov for å kunne tilpasse farger til sin merkevare. For at theming skal være praktisk, må det holde å definere et fåtall nøkkelfarger som systemet så bruker til å generere fullstendige fargeskalaer automatisk. Tilnærmingen er inspirert av ok-css (Kredittbankens CSS-rammeverk) som tilrettelegger for nettopp dette, og ligner på måten [designsystemet.no](https://designsystemet.no) håndterer farger. Dette stiller krav til fargerommet: genererte skalaer må fungere på tvers av ulike fargetoner uten manuell justering for hver kombinasjon. Med [HSL](/docs/ordbok.md#HSL) er dette ikke tilfellet — blå og gul med identiske S/L-verdier oppleves som ulike lysheter, fordi HSL ikke er perseptuelt uniform. Det betyr at en skala generert fra én farge ikke nødvendigvis fungerer like godt for en annen. ## Problemstilling[​](#problemstilling "Direct link to Problemstilling") Hvilket fargerom gir oss fargeskalaer som kan genereres fra en nøkkelfarge og fungere konsistent på tvers av fargetoner — uten manuell kalibrering per farge? ## Konsekvenser[​](#konsekvenser "Direct link to Konsekvenser") ### Hvem påvirkes?[​](#hvem-påvirkes "Direct link to Hvem påvirkes?") Designere som jobber med farger i Figma. Frontend-utviklere som bruker fargetokens i CSS. ### Ulemper[​](#ulemper "Direct link to Ulemper") * OKLCH gir ikke eksakt match med eksisterende merkevarefarger — fargene blir litt justert * Generering i OKLCH krever egne build-scripts (`indeks-tokens/scripts/build-colors/`) i stedet for et hyllevare-verktøy ### Tiltak mot ulemper[​](#tiltak-mot-ulemper "Direct link to Tiltak mot ulemper") * Fargetilpasning mot merkevare er et kontinuerlig arbeid * Fordi publiserte tokens er hex, er nettleserstøtte ikke et problem — `oklch()` forekommer ikke i utsendt CSS, så det trengs ingen runtime-fallback ## Forkastede alternativer[​](#forkastede-alternativer "Direct link to Forkastede alternativer") ### HSL[​](#hsl "Direct link to HSL") HSL er et utbredt fargerom og allerede godt kjent blant webutviklere. **Forkastet fordi:** HSL er ikke perseptuelt uniform — blå og gul med samme S/L-verdier oppleves som ulike lysheter. OKLCH løser dette eksplisitt og gir mer pålitelige kontrastgarantier. ### sRGB / hex-verdier direkte[​](#srgb--hex-verdier-direkte "Direct link to sRGB / hex-verdier direkte") Definere farger direkte som hex-koder uten et strukturert fargerom. **Forkastet fordi:** Gir ingen systematisk sammenheng mellom skala-trinn. Kontraster må verifiseres manuelt for hvert par. Deltakere Utarbeidet avTeam Designsystem Involvert * Kredittbanken * designsystemet.no --- # ADR-DS-008: Spacing-system (faste breakpoints + density) StatusBesluttet ## Beslutning[​](#beslutning "Direct link to Beslutning") Spacing bruker **faste px-verdier på tre mobile-first breakpoints** kombinert med et **[density-system](/docs/ordbok.md#density-system)** (Compact/Default/Comfortable). Typografi bruker en `rem`-basert geometrisk skala uten density. ### Faste spacing-steg per breakpoint[​](#faste-spacing-steg-per-breakpoint "Direct link to Faste spacing-steg per breakpoint") Spacing er faste px-verdier definert mobile-first på tre breakpoints: * **mobil** (base) * **tablet** (`min-width: 768px`) * **desktop** (`min-width: 1024px`) Trinnene `2xs`–`lg` er konstante på alle breakpoints; kun `xl`–`5xl` øker på større skjermer. Tokens eksponeres som `--ix-spacing-2xs` til `--ix-spacing-5xl`. Eksempel på Default: | Token | mobil | tablet (≥768px) | desktop (≥1024px) | | ------------------ | ----- | --------------- | ----------------- | | `--ix-spacing-md` | 16px | 16px | 16px | | `--ix-spacing-lg` | 24px | 24px | 24px | | `--ix-spacing-xl` | 32px | 40px | 48px | | `--ix-spacing-3xl` | 48px | 64px | 80px | ### Density[​](#density "Direct link to Density") Density styres med `data-density`-attributtet (`default`, `compact`, `comfortable`) på et container-element, og gir hvert nivå sitt eget sett med faste px-verdier. Compact er tettere enn Default, Comfortable er luftigere. Eksempel på `--ix-spacing-md` (mobil): | Compact | Default | Comfortable | | ------- | ------- | ----------- | | 12px | 16px | 24px | ### Typografi[​](#typografi "Direct link to Typografi") Fontstørrelser bruker en geometrisk skala med ratio 1.125, men basen er fast `1rem` og `rem`-basert — det betyr at brukeren kan endre fontstørrelse via nettleserinnstillinger og at typografien skalerer deretter. Trinnene er definert på `.ix-body` og eksponert som tokens (`--ix-font-size-xs` til `--ix-font-size-5xl`). Det finnes ingen density-nivåer og ingen breakpoint-variasjon for typografi. | Token | Verdi (base `1rem`) | | -------------------- | ------------------- | | `--ix-font-size-md` | \~16px (1rem) | | `--ix-font-size-3xl` | \~29px (1.125⁵ rem) | ### Kombinert[​](#kombinert "Direct link to Kombinert") Density settes én gang på et container-element (`data-density`) og arves av alle komponenter inni. ### Figma[​](#figma "Direct link to Figma") Designere setter skjermstørrelse på frame og density for bruksområdet, og ser de beregnede verdiene direkte. ### Drivere for beslutningen[​](#drivere-for-beslutningen "Direct link to Drivere for beslutningen") * Forutsigbar layout — faste verdier per breakpoint er enklere å resonnere om enn fluid skalering * Responsivitet uten at utviklere håndterer breakpoints manuelt (tokens bytter verdi automatisk) * Ulike bruksområder krever fundamentalt ulik spacing (density) * Spacing og fontstørrelse er bevisst uavhengige — brukeren kan endre font uten at layout brytes, i tråd med [WCAG](/docs/ordbok.md#WCAG) 1.4.4 ## Bakgrunn[​](#bakgrunn "Direct link to Bakgrunn") Designsystemet brukes av team med fundamentalt ulike behov: nettbank og bedriftsløsninger trenger informasjonstett layout, mens salgssider og markedsføring trenger luft og rom. I tillegg skal komponenter automatisk tilpasse seg skjermstørrelse uten at utviklere manuelt håndterer breakpoints. Et tradisjonelt 8px-grid løser ingen av disse problemene — det er ikke responsivt og gir ikke variasjon mellom bruksområder. ## Problemstilling[​](#problemstilling "Direct link to Problemstilling") Hvordan definerer vi et spacing-system som håndterer automatisk responsivitet og støtter fundamentalt ulike tetthetsbehov på tvers av bruksområder? ## Konsekvenser[​](#konsekvenser "Direct link to Konsekvenser") ### Hvem påvirkes?[​](#hvem-påvirkes "Direct link to Hvem påvirkes?") Designere må forstå at verdiene ikke følger 8px-grid. Utviklere får automatisk responsivitet uten ekstra arbeid. ### Ulemper[​](#ulemper "Direct link to Ulemper") * Tre density-nivåer å forholde seg til (Compact, Default, Comfortable) * Verdiene følger ikke et strengt 8px-grid på alle trinn — bevisst, men uvant for designere med 8px-bakgrunn * Kun `xl`–`5xl` varierer per breakpoint; de mindre trinnene er konstante, noe som må kommuniseres slik at man ikke forventer at all spacing skalerer med skjermstørrelse ### Tiltak mot ulemper[​](#tiltak-mot-ulemper "Direct link to Tiltak mot ulemper") * Dokumentasjon forklarer systemet og hvilke trinn som varierer per breakpoint * Figma-bibliotek gjør det enkelt å se faktiske verdier per skjermstørrelse og density ## Forkastede alternativer[​](#forkastede-alternativer "Direct link to Forkastede alternativer") ### Fluid skalering med `clamp()`[​](#fluid-skalering-med-clamp "Direct link to fluid-skalering-med-clamp") Den opprinnelige implementasjonen: spacing og fontstørrelse skalerte flytende med viewport via `clamp()` og en `calc()`-basert base. **Forkastet fordi:** Ga mindre forutsigbar layout — samme token kunne ha «alle» mellomverdier avhengig av viewport-bredde, noe som gjorde det vanskeligere å resonnere om og verifisere design. Faste steg per breakpoint gir designere og utviklere konkrete verdier å forholde seg til. (Density-systemet ble beholdt.) ### Standard 8px-grid[​](#standard-8px-grid "Direct link to Standard 8px-grid") Det dominerende spacing-systemet i designbransjen. Alle verdier er multipler av 8px. **Forkastet fordi:** Løser ikke behovet for ulik tetthet mellom nettbank og salgssider (density). Vårt system er mobile-first per breakpoint og bruker density i stedet for et fast grid. ### Kun viewport units (vw/vh)[​](#kun-viewport-units-vwvh "Direct link to Kun viewport units (vw/vh)") Bruke viewport-relative enheter direkte for spacing. **Forkastet fordi:** Gir ingen kontroll over minimums- og maksimumsstørrelser. Skaper tilgjengelighetsproblemer — brukere som bruker zoom vil ikke få økt spacing som forventet. ### Faste steg uten density[​](#faste-steg-uten-density "Direct link to Faste steg uten density") Implementere faste breakpoint-verdier uten density-nivåer. **Forkastet fordi:** Nettbank og salgssider har fundamentalt ulike tetthetsbehov som ett enkelt sett verdier ikke dekker. ### Separate spacing-systemer per bruksområde[​](#separate-spacing-systemer-per-bruksområde "Direct link to Separate spacing-systemer per bruksområde") Ulike token-sett for nettbank, bedrift og markedsføring. **Forkastet fordi:** Vanskelig å vedlikeholde — endringer må gjøres på tvers av systemer. Mister konsistensen som gjør at produkter fra SpareBank 1 ser ut som én familie. Deltakere Utarbeidet avTeam Designsystem --- # ADR-DS-009: CSS-arkitektur StatusBesluttet ## Beslutning[​](#beslutning "Direct link to Beslutning") All styling bruker ren **HTML + CSS** med `ix-`-prefix på alle klasser og custom properties. ### Tokens som CSS custom properties[​](#tokens-som-css-custom-properties "Direct link to Tokens som CSS custom properties") `--ix-color-primary`, `--ix-spacing-md` etc. Alle tokenverdier er tilgjengelige direkte i CSS uten ekstra byggesteg for konsumenter. ### Utility-klasser[​](#utility-klasser "Direct link to Utility-klasser") Inspirert av ok-css (SpareBank 1s interne CSS-rammeverk), Tailwind og nettbanken: `ix-m-md`, `ix-flex`, `ix-gap-sm`. Dekker \~80% av behovene. For avanserte behov bruker man egne CSS-filer med design tokens. Responsive varianter følger mønsteret `ix-{breakpoint}-{klasse}`, f.eks. `ix-sm-flex` for flex fra 768px og oppover. Breakpoints: sm (768px), md (1024px), lg (1280px), xl (1600px). ### Nettleserstøtte[​](#nettleserstøtte "Direct link to Nettleserstøtte") [PostCSS](/docs/ordbok.md#PostCSS) med `postcss-preset-env` og `autoprefixer` håndterer moderne CSS-syntaks ned til nettleserne i `.browserslistrc`. CSS leveres ferdig kompilert i pakken — konsumenter trenger ikke kjøre PostCSS selv. ### CSS-struktur[​](#css-struktur "Direct link to CSS-struktur") `indeks-css/css/`: components/, icons/, layout/, surface/, typography/. ### Dark mode[​](#dark-mode "Direct link to Dark mode") Temaet styres med CSS-klasser på rotelementet: `ix-light-mode` og `ix-dark-mode` tvinger henholdsvis lyst og mørkt tema. Klassen `ix-regard-color-scheme-preference` følger brukerens OS-preferanse via `prefers-color-scheme`. Åpent spørsmål Vurder om `ix-regard-color-scheme-preference` bør beholdes eller fjernes. Det er ønskelig at konsumenter eksplisitt velger tema (`ix-light-mode` / `ix-dark-mode`) fremfor å arve OS-preferanse automatisk. ### Drivere for beslutningen[​](#drivere-for-beslutningen "Direct link to Drivere for beslutningen") * Langsiktig holdbarhet — CSS fungerer uavhengig av React-versjoner og JS-trender * Ingen runtime overhead — styling er statisk CSS * Rammeverk-agnostisk — fungerer med React, Vue, Angular og ren HTML * God caching og forutsigbar ytelse * Enkel debugging med standard devtools ## Bakgrunn[​](#bakgrunn "Direct link to Bakgrunn") Vi ønsker et designsystem som varer lenge uavhengig av JavaScript-trender, støtter eldre nettlesere basert på `.browserslistrc`, og er enkelt å ta i bruk uten kompleks tooling. Et designsystem bør fungere for alle konsumenter — ikke bare de som bruker React. ## Problemstilling[​](#problemstilling "Direct link to Problemstilling") Hvilken styringstilnærming for styling sikrer at designsystemet er rammeverk-agnostisk, langsiktig holdbart og uten runtime-overhead? ## Konsekvenser[​](#konsekvenser "Direct link to Konsekvenser") ### Hvem påvirkes?[​](#hvem-påvirkes "Direct link to Hvem påvirkes?") Alle konsumenter av designsystemet — både React-konsumenter og team som kun bruker CSS. ### Ulemper[​](#ulemper "Direct link to Ulemper") * Ingen automatisk scoping — `ix-`-prefix er eneste beskyttelse mot navnekollisjoner * Ingen compile-time type checking av klassenavn * Utility-klasser dekker ikke alle behov (men dette er bevisst — 80/20-regelen) ### Tiltak mot ulemper[​](#tiltak-mot-ulemper "Direct link to Tiltak mot ulemper") * `ix-`-prefix forhindrer navnekollisjoner i praksis * TypeScript-typer for komponent-props gir type safety der det er mulig ## Forkastede alternativer[​](#forkastede-alternativer "Direct link to Forkastede alternativer") ### Tailwind CSS[​](#tailwind-css "Direct link to Tailwind CSS") Et utility-first CSS-rammeverk som genererer kun de klassene som er i bruk. Svært populært og brukes av mange designsystemer. **Forkastet fordi:** * Tailwind forutsetter at konsumenter bruker Tailwinds egen build-pipeline — et eksternt designsystem bør ikke kreve dette av sine konsumenter * Tailwinds klassemodell og vårt token-system er grunnleggende ulike paradigmer som er vanskelig å kombinere uten å tape fordelene med begge Beslutningen gjelder hvordan Indeks selv er stylet, ikke hva konsumenter kan bruke. Team som allerede har Tailwind får en generert temafil som kobler Tailwinds navnerom til `--ix-`-variablene, slik at de kan bruke tokenene våre uten å skrive `var()` overalt. Den er valgfri og påvirker ikke CSS-en vi leverer. Se [Tailwind](/docs/grunnleggende/tokens/tailwind.md). ### CSS-in-JS (styled-components, Emotion, vanilla-extract)[​](#css-in-js-styled-components-emotion-vanilla-extract "Direct link to CSS-in-JS (styled-components, Emotion, vanilla-extract)") Styling defineres i JavaScript/TypeScript, nær komponentkoden. **Forkastet fordi:** * Runtime overhead (styled-components/Emotion genererer CSS i nettleseren) * Binder designsystemet til React * Kompliserer SSR * Gjør CSS-only-bruk umulig ### CSS Modules[​](#css-modules "Direct link to CSS Modules") Modulbasert CSS der klassenavn scopet automatisk per komponent ved build. **Forkastet fordi:** Genererte hash-baserte klassenavn er vanskelig å debugge og umulige å dokumentere (klassenavnet `ix-button` er meningsfullt; `_button_a3f2c_1` er det ikke). Eksponerer ikke stabile klassenavn som konsumenter kan bruke direkte. ### Sass/Less[​](#sassless "Direct link to Sass/Less") CSS-preprocessorer med variabler, nesting og mixins. **Forkastet fordi:** Moderne CSS har nå de fleste features som motiverte Sass (custom properties, nesting, container queries). Et ekstra byggesteg gir kompleksitet uten tilsvarende verdi. Deltakere Utarbeidet avTeam Designsystem --- # ADR-DS-010: Komponentutvikling og testing StatusBesluttet ## Beslutning[​](#beslutning "Direct link to Beslutning") **[Storybook](/docs/ordbok.md#Storybook)** for isolert komponentutvikling og **[Docker](/docs/ordbok.md#Docker)-basert [Playwright](/docs/ordbok.md#Playwright)** for reproduserbare visuelle tester og accessibility-sjekk. ### Storybook[​](#storybook "Direct link to Storybook") Hver komponent har en `.stories.tsx`-fil ved siden av implementasjonen. Port 6006 i utvikling. Addons: a11y og docs. Custom preview med theme switching og device context. Publiseres til design.sparebank1.no/storybook. ### Playwright i Docker[​](#playwright-i-docker "Direct link to Playwright i Docker") Base image `mcr.microsoft.com/playwright` (SHA-pinnet i `indeks-storybook/Dockerfile`). Testene kjører mot den statisk bygde Storybooken (`storybook-static`) servert på port 9009 — en egen port slik at Storybook-dev-serveren kan kjøre på port 6006 samtidig. Docker Compose orkestrerer services. Accessibility-testing via [axe-core](/docs/ordbok.md#axe-core) kjøres eksplisitt mot [WCAG](/docs/ordbok.md#WCAG) 2.2 A og AA (`wcag2a`, `wcag2aa`, `wcag22aa`) — se `indeks-storybook/tests/scanAll.dtest.ts`. Violations blokkerer merge. Unntak kan legges til per komponent, men må dokumenteres i komponentens kode. Visuelle tester vurderes som del av PR-review. For nye komponenter godkjenner designer implementasjonen før baseline-screenshots settes. Ved feil lastes Playwright-rapporten ned som CI-artefakt for å se hvilke skjermbilder som avviker. Kommandoer (fra `indeks-storybook`): `pnpm test:playwright` for tester, `pnpm update-snapshots` for å oppdatere baseline-screenshots. ### Drivere for beslutningen[​](#drivere-for-beslutningen "Direct link to Drivere for beslutningen") * Isolert utvikling — komponenter utvikles og demonstreres uten full app-kontekst * Reproduserbare screenshots uavhengig av utviklerens OS * Accessibility-testing (WCAG 2.2 A/AA) integrert i PR-flyten — violations blokkerer merge * Industri-standard verktøy som utviklere kjenner fra før ## Bakgrunn[​](#bakgrunn "Direct link to Bakgrunn") UI-komponenter trenger et isolert utviklingsmiljø der man kan se og interagere med komponenter uten å kjøre hele appen. I tillegg trenger vi visuelle regresjonstester som fanger uønskede endringer i utseende. Problemet med visuelle tester er at screenshots varierer mellom operativsystemer — fonter rendres ulikt på macOS og Linux, og anti-aliasing varierer. Dette betyr at en test som passerer lokalt kan feile i CI, og omvendt. ## Problemstilling[​](#problemstilling "Direct link to Problemstilling") Hvordan sikrer vi reproduserbare visuelle regresjonstester på tvers av operativsystemer, og et godt isolert utviklingsmiljø for UI-komponenter? ## Konsekvenser[​](#konsekvenser "Direct link to Konsekvenser") ### Hvem påvirkes?[​](#hvem-påvirkes "Direct link to Hvem påvirkes?") Utviklere som jobber med React-komponenter bruker Storybook daglig og trenger Docker for visuelle tester. ### Ulemper[​](#ulemper "Direct link to Ulemper") * Docker kreves for visuelle tester — ikke alle har det installert * Tregere enn å kjøre Playwright native, grunnet Docker-overhead * Store Docker-image-størrelser * Storybook-versjoner må holdes oppdatert ### Tiltak mot ulemper[​](#tiltak-mot-ulemper "Direct link to Tiltak mot ulemper") * Docker Compose gjør det enkelt å kjøre tester med én kommando * Storybook-oppgraderinger inngår i løpende vedlikehold ## Forkastede alternativer[​](#forkastede-alternativer "Direct link to Forkastede alternativer") ### Chromatic / Percy[​](#chromatic--percy "Direct link to Chromatic / Percy") Skybaserte tjenester for visuell regresjonstesting som tar seg av screenshot-infrastrukturen. **Forkastet fordi:** * Ekstern tjeneste med kostnad per screenshot * Mindre kontroll over testmiljøet og data * Avhengighet av tredjepart for en kritisk del av CI-flyten ### Native Playwright (uten Docker)[​](#native-playwright-uten-docker "Direct link to Native Playwright (uten Docker)") Kjøre Playwright direkte på utviklerens maskin og i CI uten containerisering. **Forkastet fordi:** Screenshots er ikke reproduserbare på tvers av macOS, Windows og Linux. En baseline tatt på macOS vil feile i CI som kjører på Linux, og omvendt. ### Cypress[​](#cypress "Direct link to Cypress") Et E2E-testverktøy med god utvikleropplevelse og interaktiv testrunner. **Forkastet fordi:** Dårligere ytelse og mer begrenset multi-browser-støtte enn Playwright. Cypress er primært rettet mot E2E-tester, ikke komponenttesting og screenshot-sammenligning. ### Ladle / Histoire[​](#ladle--histoire "Direct link to Ladle / Histoire") Lettere alternativer til Storybook med raskere oppstartstid. **Forkastet fordi:** Mindre modne og med færre addons. a11y-integrasjon og Playwright-kobling er veldokumentert for Storybook, men ikke disse alternativene. Risikoen for å velge et verktøy som ikke er bredt adoptert er høy. Deltakere Utarbeidet avTeam Designsystem --- # ADR-DS-011: Dokumentasjon (Docusaurus, midlertidig) StatusBesluttet ## Beslutning[​](#beslutning "Direct link to Beslutning") **[Docusaurus](/docs/ordbok.md#Docusaurus) 3** brukes som midlertidig dokumentasjonsplattform med fokus på portabelt innhold. ### Docusaurus 3[​](#docusaurus-3 "Direct link to Docusaurus 3") Markdown-basert, minimal konfigurasjon, raskt å komme i gang. Hostet på Azure Static Web Apps (design.sparebank1.no). Norsk som standard. [MDX](/docs/ordbok.md#MDX) for interaktive eksempler med live React-komponenter. Vedlikeholdes av Team Designsystem. ### Struktur[​](#struktur "Direct link to Struktur") grunnleggende/, kom-i-gang/, komponenter/, monstre-og-maler/, retningslinjer/ (og adr/) ### Migreringsstrategi[​](#migreringsstrategi "Direct link to Migreringsstrategi") Markdown-innhold er portabelt og kan mappes til CMS-felter. Custom MDX-komponenter (som `AdrStatusLogg`, `AdrDeltakere`) må reimplementeres på ny plattform, men logikken er enkel og gjenbrukbar. Informasjonsarkitektur-erfaringer er plattformuavhengige. Migrering initieres når CMS-avklaringer med merkevare og redaktørene er på plass. ### Drivere for beslutningen[​](#drivere-for-beslutningen "Direct link to Drivere for beslutningen") * Raskt behov for dokumentasjon — kan ikke vente på CMS-avklaringer * Lavt risiko — Docusaurus er enkelt å bytte ut når riktig løsning er klar * Portabelt markdown-innhold — investering i innhold og IA er ikke tapt (custom komponenter må reimplementeres, men er enkle) * Mulighet for å teste og justere informasjonsarkitektur med ekte brukere ## Bakgrunn[​](#bakgrunn "Direct link to Bakgrunn") Vi trenger dokumentasjon nå for å komme i gang, teste informasjonsarkitektur og få feedback fra konsumenter. På sikt ønsker vi en egen løsning med CMS-integrasjon og tilpasset design, men det krever avklaringer med merkevare og redaktørene — og utviklingstid vi ikke har nå. Valget her er bevisst midlertidig. Målet er å komme i gang raskt uten å binde oss til en løsning vi ikke kan bytte ut. ## Problemstilling[​](#problemstilling "Direct link to Problemstilling") Hvilken dokumentasjonsplattform lar oss komme raskt i gang og teste informasjonsarkitektur, uten å låse oss til en løsning vi ikke kan bytte ut? ## Konsekvenser[​](#konsekvenser "Direct link to Konsekvenser") ### Hvem påvirkes?[​](#hvem-påvirkes "Direct link to Hvem påvirkes?") Alle som leser eller bidrar til dokumentasjonen. Ikke-tekniske bidragsytere møter git-workflow i stedet for [CMS](/docs/ordbok.md#CMS). ### Ulemper[​](#ulemper "Direct link to Ulemper") * Midlertidig — vi vil ikke investere i dype Docusaurus-tilpasninger * Ingen CMS — ikke-tekniske bidragsytere må gjennom git-workflow * React-låst (Docusaurus er React-basert) * Noe dobbeltarbeid når vi migrerer til permanent løsning * Dokumentasjonen dekker bare siste versjon av designsystemet — eldre versjoner er ikke tilgjengelige ### Tiltak mot ulemper[​](#tiltak-mot-ulemper "Direct link to Tiltak mot ulemper") * Begrenser Docusaurus-spesifikke tilpasninger * Fokus på portabelt markdown-innhold fra dag én ## Forkastede alternativer[​](#forkastede-alternativer "Direct link to Forkastede alternativer") ### Vente på egen løsning[​](#vente-på-egen-løsning "Direct link to Vente på egen løsning") Ikke publisere dokumentasjon før en permanent, tilpasset løsning er klar. **Forkastet fordi:** For lang tid uten dokumentasjon. Konsumenter trenger veiledning, og vi trenger feedback for å forbedre designsystemet. ### VitePress[​](#vitepress "Direct link to VitePress") Et statisk nettstedsrammeverk bygget på Vite og Vue, primært for dokumentasjon. **Forkastet fordi:** Vue-basert, noe som gjør det vanskelig å vise live React-komponenter i dokumentasjonen. Innholdet er portabelt, men den tekniske tilnærmingen passer dårligere med en React-basert komponentleveranse. ### Storybook Docs[​](#storybook-docs "Direct link to Storybook Docs") Docusaurus-alternativ der Storybook er dokumentasjonsplattformen. **Forkastet fordi:** Storybook er primært en komponentworkshop, ikke et dokumentasjonsverktøy. Begrenset til komponentdokumentasjon — tokens, veiledere og konsepter passer dårlig inn. ### Notion / Confluence[​](#notion--confluence "Direct link to Notion / Confluence") Veletablerte CMS-verktøy som mange i SpareBank 1 allerede bruker. **Forkastet fordi:** Ikke integrert med kode — eksempler og token-verdier må oppdateres manuelt. Vanskelig å holde synkronisert med kodebasen. ### Bygge eget fra start[​](#bygge-eget-fra-start "Direct link to Bygge eget fra start") Lage en tilpasset dokumentasjonssite med CMS-integrasjon og designet av merkevare. **Forkastet fordi:** Krever CMS-avklaringer og utviklingstid vi ikke har nå. Riktig valg på sikt, men feil prioritering akkurat nå. Deltakere Utarbeidet avTeam Designsystem --- # Kort fortalt Denne siden er snarveien. Vil du ha helheten på fem minutter — hvorfor vi bygger Indeks selv og hva vi faktisk har bestemt — så er det her. Detaljene, avveiningene og de forkastede alternativene ligger i de enkelte [ADR-ene](#de-store-valgene). ## Hvorfor et eget designsystem?[​](#hvorfor-et-eget-designsystem "Direct link to Hvorfor et eget designsystem?") Vi kunne tatt et ferdig bibliotek fra hylla. Vi lot være, av én grunn: **behovene våre passer ikke helt med det ferdige alternativene løser.** * **Vi må virke på gamle enheter.** Kundene våre bruker telefoner og nettlesere som er flere år gamle. De fleste moderne bibliotek forutsetter ferske nettlesere. Vi setter et bevisst gulv og håndhever det automatisk. * **Vi må virke overalt.** Nettsider, React-apper og hybrid-apper (webview i en mobilapp). Da kan vi ikke låse oss til ett rammeverk. * **Tilgjengelighet er ikke valgfritt.** Som bank har vi et lovkrav, men vi vil også faktisk at alle skal kunne bruke tjenestene våre. Det må være bygget inn fra bunnen, ikke limt på til slutt. * **Vi vil eie våre egne farger, avstander og mønstre.** SpareBank 1 har en visuell identitet. Et eget system lar oss uttrykke den presist, i stedet for å presse den inn i noen andres. * **Vi satser på ren web-teknologi der det er mulig.** Rammeverk kommer og går; nettleseren består. Ved å lene oss på det plattformen allerede kan, bygger vi noe som holder på lang sikt og ikke må skrives om hver gang moten skifter. ## De fem målene — og hvordan vi løser dem[​](#de-fem-målene--og-hvordan-vi-løser-dem "Direct link to De fem målene — og hvordan vi løser dem") Alt vi har bestemt kan spores tilbake til fem mål. ### 1. Mobil-først[​](#1-mobil-først "Direct link to 1. Mobil-først") Vi designer for den minste skjermen først og bygger oppover. Avstander og størrelser er faste, forutsigbare steg per skjermbredde — ikke noe som flyter ukontrollert. Berøringsflater er store nok til en tommel. → [ADR-DS-008 Spacing-system](/docs/adr/ADR-DS-008-spacing-system.md) ### 2. Virker overalt (hybrid-app-støtte)[​](#2-virker-overalt-hybrid-app-støtte "Direct link to 2. Virker overalt (hybrid-app-støtte)") Kjernen er **web components** — byggeklosser som fungerer i ren HTML, i React, og i en mobilapp uten å dra med seg et helt rammeverk. React-laget vårt er et tynt skall utenpå. Da slipper vi å vedlikeholde logikken to ganger. → [ADR-DS-004 Web components](/docs/adr/ADR-DS-004-web-components.md) · [ADR-DS-005 React-bibliotek](/docs/adr/ADR-DS-005-react-bibliotek.md) ### 3. Støtte for gamle enheter[​](#3-støtte-for-gamle-enheter "Direct link to 3. Støtte for gamle enheter") Vi har ett gulv for hvilke nettlesere vi støtter, og tre uavhengige verktøy passer på at vi ikke ved et uhell bruker noe som er for nytt. Bommer du, sier byggesteget fra — ikke kunden. ### 4. Tilgjengelighet (a11y)[​](#4-tilgjengelighet-a11y "Direct link to 4. Tilgjengelighet (a11y)") Dette er systemets sterkeste side. Hver komponent har et eget regnskap mot alle WCAG 2.2-kriteriene. Automatiske tester blokkerer sammenslåing hvis noe er utilgjengelig. Og ingen tekst er hardkodet — alt som vises eller leses opp kan settes på bokmål, nynorsk eller engelsk. → [ADR-DS-010 Komponentutvikling og testing](/docs/adr/ADR-DS-010-komponentutvikling-og-testing.md) ### 5. Egne behov[​](#5-egne-behov "Direct link to 5. Egne behov") Vi bruker **tokens** — navngitte verdier for farger, avstander og mer — som én felles kilde. Farger designes i et fargerom (OKLCH) som gir jevne, harmoniske skalaer. Og konsumenter kan trygt overstyre utseendet der de trenger det, uten å kjempe mot systemet. → [ADR-DS-006 Tokens og farger](/docs/adr/ADR-DS-006-tokens-og-farger.md) · [ADR-DS-007 Fargesystem](/docs/adr/ADR-DS-007-fargesystem.md) ## De store valgene[​](#de-store-valgene "Direct link to De store valgene") Resten av ADR-ene handler om hvordan vi jobber for at systemet skal holde over tid: * **Alt i ett repo** med felles byggverktøy — enklere å holde pakkene i takt. [ADR-DS-001](/docs/adr/ADR-DS-001-monorepo-og-byggverktoy.md) * **Versjonering og publisering** styres av changesets, med sporbar kobling mellom pakke og kildekode. [ADR-DS-002](/docs/adr/ADR-DS-002-versjonering-og-publisering.md) * **Ressurser serveres fra CDN**, slik at flere apper deler og cacher de samme filene i stedet for å laste dem på nytt hver for seg. [ADR-DS-002](/docs/adr/ADR-DS-002-versjonering-og-publisering.md) * **CI/CD** kjøres i GitHub Actions. [ADR-DS-003](/docs/adr/ADR-DS-003-ci-cd.md) * **Styling er ren CSS** — ingen runtime, virker uansett rammeverk. [ADR-DS-009](/docs/adr/ADR-DS-009-css-only-styling.md) * **Komponenter utvikles og testes** etter en fast oppskrift med visuelle tester og a11y-tester. [ADR-DS-010](/docs/adr/ADR-DS-010-komponentutvikling-og-testing.md) * **Dokumentasjon** bor sammen med koden. [ADR-DS-011](/docs/adr/ADR-DS-011-dokumentasjon.md) ## Vil du grave dypere?[​](#kilde "Direct link to Vil du grave dypere?") * Hver [ADR](/docs/adr/ADR-DS-001-monorepo-og-byggverktoy.md) forklarer bakgrunn, problemstilling, konsekvenser og hvilke alternativer vi forkastet. * [Retningslinjene](/docs/adr/retningslinjer.md) beskriver konvensjoner teamet er enige om (som ikke er arkitekturbeslutninger). --- # Retningslinjer for Indeks designsystem Disse retningslinjene er ikke arkitekturbeslutninger, men konvensjoner teamet har blitt enige om. ## Hvor en komponent bor[​](#hvor-en-komponent-bor "Direct link to Hvor en komponent bor") Indeks er en monorepo (se [ADR-DS-001](/docs/adr/ADR-DS-001-monorepo-og-byggverktoy.md)), og en komponent er ofte spredt over flere pakker etter ansvar: ``` indeks-css/css/components// # styling (.ix-) indeks-web/lib/components// # web component (hvis komponenten trenger logikk) indeks-react/lib/ui/ # tynn React-wrapper (components/, layout/, typography/, icons/) indeks-storybook/stories/ # .stories.tsx indeks-docs/docs/komponenter/.mdx # dokumentasjon ``` React-komponentene grupperes i `indeks-react/lib/ui/` etter kategori (`components/`, `layout/`, `typography/`, `icons/`). Delte React-hooks ligger i `indeks-react/lib/hooks/` og delte typer i `indeks-react/lib/types/`. ## Språkvalg i kode og dokumentasjon[​](#språkvalg-i-kode-og-dokumentasjon "Direct link to Språkvalg i kode og dokumentasjon") **Kode** skrives på engelsk: komponentnavn (`Button`, `Card`), props (`variant`, `size`), CSS-klasser (`.ix-button`), token-navn, variabelnavn. **Dokumentasjon** skrives på norsk: ADR-er, README, docs, kommentarer, commits, PR-beskrivelser, issues, code reviews. Engelske fagbegrep brukes i norsk tekst (props, tokens, hooks, components). Eksempel: *"Button-komponenten har en `variant`-prop som styrer utseendet."* ## AI-bruk og ansvar[​](#ai-bruk-og-ansvar "Direct link to AI-bruk og ansvar") Den som genererer innhold med AI er fullt ansvarlig for resultatet. AI er et verktøy, ikke en unnskyldning. * Alt AI-generert innhold skal gjennomgås og forstås før commit * Samme kvalitetskrav gjelder uavhengig av opphav * "AI skrev det" er ikke en gyldig forklaring på feil --- # ADR-DS-XXX: \[Beskrivende tittel] StatusUtkast ## Bakgrunn[​](#bakgrunn "Direct link to Bakgrunn") Beskriv konteksten en utenforstående trenger for å forstå beslutningsbehovet. Hva er situasjonen? Hvilke tidligere beslutninger eller oppgaver har utløst dette behovet? ## Problemstilling[​](#problemstilling "Direct link to Problemstilling") Den konkrete problemstillingen denne beslutningen skal svare på. Én eller to setninger. ## Beslutning[​](#beslutning "Direct link to Beslutning") Hva ble besluttet, konkret og tydelig. Bruk korte avsnitt per del av beslutningen, eller en bulletet liste hvis det er flere sidestilte punkter. Ved Utkast/Forslag: beskriv anbefalingen og mulige valg. ### Drivere for beslutningen[​](#drivere-for-beslutningen "Direct link to Drivere for beslutningen") * Driver 1: f.eks. kostnader, driftsmodell, prinsipp, kvalitetsegenskap, tid, begrensning * Driver 2 * Driver 3 ## Konsekvenser[​](#konsekvenser "Direct link to Konsekvenser") ### Hvem påvirkes?[​](#hvem-påvirkes "Direct link to Hvem påvirkes?") Scope for beslutningen og konsekvenser for ulike grupperinger. ### Ulemper[​](#ulemper "Direct link to Ulemper") Utfordringer som må løses, konsekvenser ved at man valgte bort andre løsninger. ### Tiltak mot ulemper[​](#tiltak-mot-ulemper "Direct link to Tiltak mot ulemper") * Konkret tiltak mot en spesifikk ulempe ## Forkastede alternativer[​](#forkastede-alternativer "Direct link to Forkastede alternativer") Hvert forkastet alternativ får sin egen seksjon. Beskriv alternativet kort (én setning for lesere som ikke kjenner det), og gi et konkret avvisningsargument. ### \[Navn på alternativ 1][​](#navn-på-alternativ-1 "Direct link to \[Navn på alternativ 1]") Kort beskrivelse av fordeler og ulemper. **Forkastet fordi:** Kjerneargumentet i én setning. ### \[Navn på alternativ 2][​](#navn-på-alternativ-2 "Direct link to \[Navn på alternativ 2]") ... Deltakere Utarbeidet avNavn, rolle --- # Native I hybridapper vises Indeks-komponenter i WebView-en til en native app. Brukeren skiller ikke mellom de to, og forventer derfor at web-delen oppfører seg som resten av appen. ## WebView-oppførsel[​](#webview-oppførsel "Direct link to WebView-oppførsel") Uten tilpasning oppfører innholdet seg som en vanlig nettside. Tekst kan markeres ved langt trykk, elementer kan dras, og det ligger en merkbar forsinkelse på trykk. Ingen av de tre skjer i en native app. Indeks har utility-klasser som slår av denne oppførselen, og som lar deg slippe tekstmarkering gjennom igjen der brukeren faktisk trenger å kopiere noe. Se [Native (utility-klasser)](/docs/utility-klasser/native.md) for fullstendig oversikt over klassene `.ix-native`, `.ix-selectable` og `.ix-selectable-all`. --- # Border Komponentene vi tilbyr har riktig border-radius og border-width som standard. Hvis du skal lage noe eget, kan du bruke border-variablene vi tilbyr. Bruk util-klassen `.ix-border-default` for å få standard border-stil, oftest brukt på kort. Klassen setter `--ix-border-width-default` og farge `--ix-color-border-main-default`. ## Border width[​](#border-width "Direct link to Border width") Vi har 2 forskjellige border-tykkelser. For fokus på skjemaelementer skal du bruke [outline](#outline). | CSS custom property | Verdi | Beskrivelse | | --------------------------- | ----- | ------------------------- | | `--ix-border-width-default` | 1px | Standard border-width. | | `--ix-border-width-bold` | 2px | Border-width for buttons. | ## Border radius[​](#border-radius "Direct link to Border radius") Vi har border radius i størrelsene xs, sm, md, lg og xl. I tillegg finnes egne verdier for pilleformede elementer, checkbox, input og knapp. Bruk `ix-border-radius-circle` for helt runde elementer. | CSS custom property | Verdi | Beskrivelse | | ----------------------------- | ---------------------------- | --------------------------------------------------------- | | `--ix-border-radius-circle` | 50% | Fullt avrundet border-radius. | | `--ix-border-radius-pill` | 9999px | Border-radius for pille-formede elementer. Feks. knapper. | | `--ix-border-radius-checkbox` | 4px | Border-radius for avkrysningsbokser. | | `--ix-border-radius-input` | 8px | Border-radius for input-felt. | | `--ix-border-radius-button` | var(--ix-border-radius-pill) | Border-radius for knapper. | | `--ix-border-radius-xs` | 4px | Liten border-radius. | | `--ix-border-radius-sm` | 8px | Medium border-radius. Brukes til input-felt. | | `--ix-border-radius-md` | 16px | Stor border-radius. Brukes til kort. | | `--ix-border-radius-lg` | 24px | Ekstra stor border-radius. Brukes til modaler. | | `--ix-border-radius-xl` | 40px | Ekstra stor border-radius. | ## Outline[​](#outline "Direct link to Outline") Outline brukes ved fokus på skjemaelementer. Outline burde være satt til transparent når elementet ikke er i fokus for å unngå hopping i layouten. For standard outline-stil, sett følgende CSS-egenskaper direkte på elementet: ``` element { outline: var(--ix-outline-width-default) solid transparent; outline-offset: var(--ix-outline-offset-default); } ``` Endre deretter outline-color når elementet får fokus: ``` element:focus { outline-color: var(--ix-color-border-focus-default); } ``` Stilene over er også tilgjengelige gjennom [utility-klassen](/docs/utility-klasser/oversikt.md#outline) `.ix-outline-default`. | CSS custom property | Verdi | Beskrivelse | | ---------------------------- | ----- | ------------------------ | | `--ix-outline-width-default` | 2px | Standard outline-bredde. | | CSS custom property | Verdi | Beskrivelse | | ----------------------------- | ----- | ------------------------ | | `--ix-outline-offset-default` | 3px | Standard outline-offset. | ## Farger[​](#farger "Direct link to Farger") Se [farger](/docs/retningslinjer/farger/.md#border) for å finne riktige farger til border og outline. --- # Farger for Native Fargene i Indeks kan bygges ut til native-kode. Pakken `@sb1/indeks-tokens` har en `build-colors`-kommando som genererer Kotlin-farger til Android og Color Assets til iOS, fra de samme tokenene som ligger bak [web](/docs/grunnleggende/tokens/farger-web.md). ## Hvordan bygge fargene[​](#hvordan-bygge-fargene "Direct link to Hvordan bygge fargene") Du kan kjøre kommandoen med `npx` uten å installere pakken. Signaturen er den samme uansett plattform, og du styrer resultatet med `platform=`, `path=` og valgfritt `theme=`. Commit build-scriptet Legg scriptet dere bygger fargene med i versjonskontroll. Da kan dere se hvilken tokens-versjon appen står på, og når fargene sist ble endret. ## Android[​](#android "Direct link to Android") For å bygge farger til Android (Kotlin): ``` npx @sb1/indeks-tokens build-colors platform=android path=./android/colors ``` Dette genererer en `Colors.kt`-fil med semantiske farger for både light og dark mode. ### Output-eksempel[​](#output-eksempel "Direct link to Output-eksempel") ``` object Colors { object LightDefault { val backgroundDefault = Color(0xFFFFFFFF) val backgroundNeutral = Color(0xFFF4F5F6) // ... flere farger } object DarkDefault { val backgroundDefault = Color(0xFF1E2632) val backgroundNeutral = Color(0xFF323B49) // ... flere farger } } ``` ## iOS[​](#ios "Direct link to iOS") For å bygge farger til iOS (Xcode Color Assets): ``` npx @sb1/indeks-tokens build-colors platform=ios path=./ios/colors ``` Dette genererer en `.xcassets`-mappe med semantiske farger for både light og dark mode. ### Output-struktur[​](#output-struktur "Direct link to Output-struktur") ``` SemanticColors.xcassets/ ├── Contents.json ├── README.md └── default/ ├── Contents.json ├── background.default.colorset/ │ └── Contents.json ├── background.neutral.colorset/ │ └── Contents.json └── ... flere farger ``` ## Parametere[​](#parametere "Direct link to Parametere") | Parameter | Påkrevd | Betydning | | --------------------------------------- | ------- | -------------------------------------------------------- | | `platform=android` eller `platform=ios` | Ja | Hvilken plattform du bygger for | | `path=` | Ja | Hvor filene skal lagres, for eksempel `./android/colors` | | `theme=` | Nei | Sti til egen JSON-theme-fil. Standard er `sb1` | ## Egne themes[​](#egne-themes "Direct link to Egne themes") Du kan lage ditt eget theme ved å opprette en JSON-fil med et theme-objekt. Generatoren vil da bruke dette themet i stedet for det innebygde sb1-themet. ### Eksempel på egen theme-fil[​](#eksempel-på-egen-theme-fil "Direct link to Eksempel på egen theme-fil") Lag en fil `my-theme.json`: ``` { "name": "my-custom-theme", "identityColor": "#005aa4", "colors": { "brand": "#0078D8", "success": "#00885B", "info": "#467CA4", "danger": "#C94E4F", "warning": "#AF6500", "gray": "#6D7888", "neutral": "#AF6516" }, "themeable": { "font-family": { "normal": "System", "heading": "System" }, "font-weight": { "normal": 400, "bold": 600, "heading": 700 }, "border": { "radius": { "button": 25, "checkbox": 4, "input": 8 } } } } ``` De sju basisfargene under `colors` er påkrevd. `identityColor` og `themeable` er valgfrie. ### Bruke eget theme[​](#bruke-eget-theme "Direct link to Bruke eget theme") ``` npx @sb1/indeks-tokens build-colors platform=android path=./android/colors theme=./my-theme.json ``` Eller for iOS: ``` npx @sb1/indeks-tokens build-colors platform=ios path=./ios/colors theme=./my-theme.json ``` ## Fargeskalaer[​](#fargeskalaer "Direct link to Fargeskalaer") Alle farger bygges fra fargeskalaer med 20 steg (0–950) for hver basisfarge: | Basisfarge | Brukes til | | ---------- | ---------------------------------- | | `brand` | Merkevarefargen | | `success` | Bekreftelse og positive tilstander | | `info` | Nøytral informasjon | | `warning` | Advarsel | | `danger` | Feil og destruktive handlinger | | `gray` | Nøytral grå | | `neutral` | Nøytral brun og beige | De semantiske fargene (for eksempel `background.default` og `foreground.main`) peker inn i disse skalaene og bytter trinn automatisk mellom light og dark mode. Se [Fargeskalaer](/docs/grunnleggende/tokens/fargeskalaer.md) for hvordan skalaene genereres, og hvorfor det ikke påvirker resultatet å justere mørkhetsgraden på en basisfarge. ## Oppdatere farger[​](#oppdatere-farger "Direct link to Oppdatere farger") For å oppdatere fargene i native-appen din: 1. Oppdater versjonen av `@sb1/indeks-tokens` i kommandoen 2. Kjør build-scriptet på nytt 3. Commit de genererte filene Fargene følger appen fra og med den commiten, så det er dere som bestemmer når endringen slår inn. --- # Farger for Web Pakken `@sb1/indeks-tokens` inneholder fargetokens som brukes i hele designsystemet. På web trenger de aller fleste ikke å generere farger selv, for den ferdig bygde CSS-en inneholder alt du trenger. Har du et eget theme, kan du likevel generere din egen web-CSS med den samme `build-colors`-kommandoen som brukes for [native (iOS/Android)](/docs/grunnleggende/tokens/farger-native.md). ## Standard bruk: importer ferdig CSS[​](#standard-bruk-importer-ferdig-css "Direct link to Standard bruk: importer ferdig CSS") I de aller fleste tilfeller skal du **ikke** generere farger selv. Bruk den ferdig bygde CSS-en, som allerede inneholder alle fargetokens: ``` @import url('https://cdn.sparebank1.no/indeks/css/0.22.1/index.css'); ``` Eller via npm med `@sb1/indeks-css` (som inkluderer tokens): ``` npm install @sb1/indeks-css ``` Da får du alle `--ix-color-*`-variablene ferdig satt opp med både light og dark mode. Du trenger ikke resten av denne siden. ## Egen web-CSS fra eget theme[​](#egen-web-css-fra-eget-theme "Direct link to Egen web-CSS fra eget theme") Har du behov for et eget theme (egne merkevarefarger), kan du generere web-CSS med `build-colors`: ``` npx @sb1/indeks-tokens build-colors platform=web path=./css theme=./my-theme.json ``` Uten `theme=` genereres CSS-en med standard `sb1`-theme. ### Output[​](#output "Direct link to Output") Kommandoen skriver to slags filer til `path`: ``` css/ ├── colors.css └── themes/ └── .css ``` * **`themes/.css`** — primitivene (`--ii-primitive-*`), altså de fullstendige [fargeskalaene](/docs/grunnleggende/tokens/fargeskalaer.md) generert fra basisfargene i themet ditt. Basisfargen styrer bare fargetone og metning. Lysheten settes per trinn, så å gjøre en basisfarge lysere eller mørkere endrer ikke skalaen. * **`colors.css`** — de semantiske fargene (`--ix-color-*`), som peker inn i primitivene. Denne inneholder både light mode (`:root`, `.ix-light-mode`), dark mode (`.ix-dark-mode`) og `prefers-color-scheme`-håndtering. `--ii-` vs `--ix-` `--ii-primitive-*` er interne primitiver. Ikke bruk dem direkte i applikasjonskoden din. `--ix-color-*` er det offentlige API-et du styler mot. ### Rekkefølge og overstyring[​](#rekkefølge-og-overstyring "Direct link to Rekkefølge og overstyring") Importér begge de genererte filene. **Rekkefølgen dem imellom spiller ingen rolle:** `--ix-color-*` refererer til `--ii-primitive-*` via `var()`, og CSS custom properties slås opp når de brukes, uavhengig av import- og deklarasjonsrekkefølge. ``` @import './css/themes/my-theme.css'; @import './css/colors.css'; ``` Bruker du både `@sb1/indeks-css` (for komponent-CSS) og ditt eget theme, er det derimot rekkefølgen mot indeks-css som avgjør. Den ferdige CSS-en har de samme tokenene under nøyaktig samme selektorer og spesifisitet, og ingen `@layer` skiller dem. Da er det «sist vinner» som gjelder: ``` @import url('https://cdn.sparebank1.no/indeks/css//index.css'); @import './css/themes/my-theme.css'; @import './css/colors.css'; ``` Eget theme må lastes sist Ditt eget theme overstyrer bare indeks-css hvis det importeres **etter** indeks-css. Lastes det før, vinner indeks-css og temaet får stille ingen effekt. Det samme skjer hvis du ikke har kontroll på rekkefølgen bundleren din legger opp til. ## Generere farger i runtime[​](#generere-farger-i-runtime "Direct link to Generere farger i runtime") Skal farger endres mens appen kjører, typisk ved å bytte `brand`-fargen etter brukervalg eller merkevare, kan du generere fargeskalaer i runtime. Det virker både i nettleseren og i en Node-backend, og du bruker subpath-eksporten `@sb1/indeks-tokens/generate`. Da blir `@sb1/indeks-tokens` en ekte avhengighet Ved standard bruk (ferdig CSS) trenger du bare `@sb1/indeks-css`. Men bruker du runtime-API-et, importerer applikasjonskoden din faktisk JavaScript fra `@sb1/indeks-tokens` — da må pakken ligge under `dependencies` (ikke `devDependencies`) i din `package.json`: ``` npm install @sb1/indeks-tokens ``` Fargematematikken (`colorjs.io`) er bundlet inn i eksporten, så du får ingen ekstra avhengigheter å forholde deg til. ### Slik henger det sammen[​](#slik-henger-det-sammen "Direct link to Slik henger det sammen") De semantiske fargetokenene (`--ix-color-*`) peker på primitivene via `var(--ii-primitive-*)`. For å rethem-e i runtime trenger du derfor **bare** å regenerere primitiv-skalaen for de(n) basisfargen(e) du vil endre og sette `--ii-primitive--` på et scope — alle de semantiske tokenene re-resolver seg selv via `var()`-kaskaden. Du skal **aldri** regenerere det semantiske laget selv. Eneste tillatte bruk av `--ii-` `--ii-primitive-*` er ellers interne (se over). Runtime-generering er det ene stedet der du bevisst setter dem. API-et eier navnekonvensjonen og `gray-0 = #FFFFFF`-unntaket for deg, så du slipper å hardkode dem. ### API[​](#api "Direct link to API") * **`buildColorScaleVariables(navn, farge)`** — bygger en 20-trinns skala fra én basisfarge og returnerer en ferdig CSS-variabel-map: `{ '--ii-primitive-brand-0': '#…', … }`. * **`applyColorScaleVariables(element, variabler)`** — setter map-en som inline-styling på et element (nettleser). * **`colorScaleVariablesToCss(variabler, { selector })`** — serialiserer map-en til en CSS-streng (Node-backend / injeksjon i ``). `selector` er valgfri, standard `:root`. De to hjelperne tar en variabel-map, så du kan slå sammen flere skalaer: `{ ...buildColorScaleVariables('brand', a), ...buildColorScaleVariables('info', b) }`. ### I nettleseren[​](#i-nettleseren "Direct link to I nettleseren") ``` import { buildColorScaleVariables, applyColorScaleVariables } from '@sb1/indeks-tokens/generate'; const vars = buildColorScaleVariables('brand', '#E4002B'); applyColorScaleVariables(document.documentElement, vars); // Alle --ix-color-* som bygger på brand oppdateres nå automatisk. ``` Vil du se det i praksis, kan du skrive inn en basisfarge og se hele appen bytte tema live: **[Prøv fargegenerering i eksempelappen →](https://design.sparebank1.no/eksempel/#/internTesting/generer-farger)** ### I en Node-backend[​](#i-en-node-backend "Direct link to I en Node-backend") Generér en CSS-streng du kan sende til klienten (f.eks. injisert i ``): ``` import { buildColorScaleVariables, colorScaleVariablesToCss } from '@sb1/indeks-tokens/generate'; const vars = buildColorScaleVariables('brand', '#E4002B'); const css = colorScaleVariablesToCss(vars); // ':root {\n --ii-primitive-brand-0: #…;\n … \n}' // Vil du scope temaet til et delområde i stedet for hele siden: const scoped = colorScaleVariablesToCss(vars, { selector: '.min-merkevare' }); ``` Se [Fargeskalaer](/docs/grunnleggende/tokens/fargeskalaer.md) for hva basisfargen faktisk styrer: fargetone og metning, ikke lyshet. ## Eksempel på egen theme-fil[​](#eksempel-på-egen-theme-fil "Direct link to Eksempel på egen theme-fil") Theme-fila er en JSON-fil med de sju basisfargene. `identityColor` og `themeable` (font, border-radius) er valgfrie. Lag en fil `my-theme.json`: ``` { "name": "my-custom-theme", "identityColor": "#005aa4", "colors": { "brand": "#0078D8", "success": "#00885B", "info": "#467CA4", "danger": "#C94E4F", "warning": "#AF6500", "gray": "#6D7888", "neutral": "#AF6516" }, "themeable": { "font-family": { "normal": "Inter, sans-serif", "heading": "Inter, sans-serif" } } } ``` Filnavnet på theme-CSS-en følger `name`-feltet i JSON-fila. Themet over gir `css/themes/my-custom-theme.css`. ## Parametere[​](#parametere "Direct link to Parametere") | Parameter | Påkrevd | Betydning | | -------------- | ------- | ---------------------------------------------- | | `platform=web` | Ja | Bygger web-CSS | | `path=` | Ja | Hvor filene skal lagres, for eksempel `./css` | | `theme=` | Nei | Sti til egen JSON-theme-fil. Standard er `sb1` | Den samme kommandoen bygger også farger for iOS og Android. Se [Farger for Native](/docs/grunnleggende/tokens/farger-native.md). --- # Fargeskalaer Et theme defineres med sju **basisfarger**: `brand`, `success`, `info`, `danger`, `warning`, `gray` og `neutral`. Ved build utvides hver av disse til en **20-trinns fargeskala** (0–950), som blir til primitivene (`--ii-primitive-*`) de semantiske fargene peker på. Skalaene genereres i [OKLCH](/docs/ordbok.md#OKLCH), et [perseptuelt uniform]()t fargerom. Det betyr at samme trinn gir samme opplevde lysstyrke på tvers av fargetoner, slik at de semantiske mappingene (hvilke trinn som brukes til tekst, bakgrunn osv.) fungerer likt for alle basisfarger. ## Hva basisfargen faktisk styrer[​](#hva-basisfargen-faktisk-styrer "Direct link to Hva basisfargen faktisk styrer") Generatoren bruker **ikke** basisfargen som den er og lysner/mørkner rundt den. Den konverterer basisfargen til OKLCH og bruker bare deler av den: | OKLCH-komponent | Hva generatoren gjør med den | | ------------------ | --------------------------------------------------------------------------------------------------------------------------- | | Fargetone (hue) | Beholdes uendret gjennom hele skalaen | | Metning (chroma) | Hentes fra basisfargen, men skaleres langs en fast kurve som er lav i endene og høyest midt på skalaen | | Lyshet (lightness) | Overstyres med en fast verdi per trinn, fra \~0.99 på trinn 0 til \~0.17 på trinn 950. Basisfargens egen lyshet brukes ikke | Å justere mørkhetsgraden på en basisfarge har ingen effekt Fordi lysheten settes per trinn, endrer det **ikke** skalaen om du gjør basisfargen lysere eller mørkere. To hex-verdier med samme fargetone og metning, men ulik lyshet, gir nøyaktig samme skala. Trenger du en lysere eller mørkere farge i grensesnittet, velg et **annet trinn** på skalaen. Endre selve basis-hexen kun når du vil endre **fargetone eller metning**. Ett unntak: `gray-0` tvinges alltid til `#FFFFFF`, uavhengig av `gray`-basisfargen. ## Se skalaene[​](#se-skalaene "Direct link to Se skalaene") Alle sju skalaene med alle 20 trinn vises i eksempelappen, der trinn 600 er markert som nøkkelfargen: **[Fargeskalaer i eksempelappen →](https://design.sparebank1.no/eksempel/#/internTesting/fargeskalaer-eksempler)** ## Mer om beslutningen[​](#mer-om-beslutningen "Direct link to Mer om beslutningen") [ADR-DS-007: Fargesystem](/docs/adr/ADR-DS-007-fargesystem.md) dokumenterer bakgrunnen for valget av OKLCH: hvorfor HSL ble forkastet, hvorfor de publiserte tokenene er frosne hex-verdier og ikke `oklch()` i runtime, og hvordan dark mode bruker andre trinn i samme skala. --- # Introduksjon Design tokens er de minste byggesteinene i designsystemet. Det er navngitte variabler for visuelle verdier: farger, typografi, spacing, radius, skygger. I stedet for `16px` eller `#002776` skriver du `--ix-spacing-sm` eller `--ix-color-foreground-main-default`. Poenget er at verdien bare står ett sted. Endrer vi den, endrer den seg overalt tokenet er i bruk, uten at noen må lete opp hver komponent og hver flate. ## Hvorfor bruke tokens?[​](#hvorfor-bruke-tokens "Direct link to Hvorfor bruke tokens?") * **Samme verdi over alt.** Komponenter, flater og team henter fra samme kilde. * **Ett sted å endre.** En justering slår gjennom med én gang. * **Felles språk.** Designere og utviklere snakker om `spacing-sm`, ikke om «litt luft». * **Flere temaer på samme kode.** Light og dark mode, og egne merkevarer, er samme tokens med andre verdier. ## Tokens i praksis[​](#tokens-i-praksis "Direct link to Tokens i praksis") * I Figma ligger de som variabler du velger fra, i stedet for at du skriver inn en verdi selv * I kode eksponeres de som variabler i CSS og JSON * I komponenter bruker vi bare semantiske eller kontekstuelle tokens, aldri rå verdier --- # Spacing Avstandene i Indeks er faste px-verdier med navn, ikke tall du velger selv. De justerer seg på tre breakpoints, og hele skalaen kan skrus tettere eller romsligere med en density-modus. ## Breakpoints[​](#breakpoints "Direct link to Breakpoints") Spacing er **mobile-first** og bruker tre breakpoints: * **Mobil**: grunnverdi (opptil 768px) * **Tablet**: fra `768px` * **Desktop**: fra `1024px` De minste verdiene (`2xs`–`lg`) er like på alle breakpoints. Kun de store verdiene (`xl`–`5xl`) øker på tablet og desktop, slik at layouten får mer luft på større skjermer uten at små detaljavstander endrer seg. Fontstørrelser er faste og skalerer **ikke** med skjermbredden — grunnstørrelsen er alltid 16px (`1rem`). Spacing er definert i `px`, mens typografi bruker `rem`. ## Density-moduser[​](#density-moduser "Direct link to Density-moduser") Density-modusen skalerer alle spacing-verdier samtidig, så du kan endre hvor tett en flate er uten å røre en enkelt avstand. Modusen settes med attributtet `data-density` på et element, og gjelder alt under det. | Modus | Passer til | | ------------- | ------------------------------------------------------------------------- | | `default` | De fleste flater. Bruk denne med mindre du har en grunn til noe annet | | `compact` | Flater som skal vise mye på én skjerm: rådgiverflater og interne systemer | | `comfortable` | Åpne nettsider, salgskanaler og kampanjer, der innholdet skal få plass | ```
...
...
...
``` ## Spacing-skala[​](#spacing-skala "Direct link to Spacing-skala") Spacing-tokens følger en konsistent skala fra `2xs` til `5xl`, med faste px-verdier. `2xs`–`lg` er like på alle breakpoints; kun `xl`–`5xl` øker på tablet og desktop. ### Mobil (grunnverdi)[​](#mobil-grunnverdi "Direct link to Mobil (grunnverdi)") | Token | Beskrivelse | Default | Compact | Comfortable | | ----- | -------------- | ------- | ------- | ----------- | | `2xs` | Ekstra liten | 4px | 2px | 8px | | `xs` | Liten | 8px | 4px | 12px | | `sm` | Small | 12px | 8px | 16px | | `md` | Medium | 16px | 12px | 24px | | `lg` | Large | 24px | 16px | 32px | | `xl` | Extra large | 32px | 24px | 40px | | `2xl` | 2x extra large | 40px | 32px | 48px | | `3xl` | 3x extra large | 48px | 40px | 64px | | `4xl` | 4x extra large | 64px | 48px | 80px | | `5xl` | 5x extra large | 80px | 64px | 96px | ### Tablet (fra 768px)[​](#tablet-fra-768px "Direct link to Tablet (fra 768px)") | Token | Default | Compact | Comfortable | | ----- | ------- | ------- | ----------- | | `xl` | 40px | 32px | 48px | | `2xl` | 48px | 40px | 64px | | `3xl` | 64px | 48px | 80px | | `4xl` | 80px | 64px | 96px | | `5xl` | 96px | 80px | 128px | ### Desktop (fra 1024px)[​](#desktop-fra-1024px "Direct link to Desktop (fra 1024px)") | Token | Default | Compact | Comfortable | | ----- | ------- | ------- | ----------- | | `xl` | 48px | 32px | 64px | | `2xl` | 64px | 40px | 80px | | `3xl` | 80px | 48px | 96px | | `4xl` | 96px | 64px | 128px | | `5xl` | 128px | 80px | 128px | *`Compact` har samme verdier på tablet og desktop.* ## Bruk[​](#bruk "Direct link to Bruk") ### Utility-klasser[​](#utility-klasser "Direct link to Utility-klasser") Full oversikt over utility-klassene finner du [i oversikten over utility-klasser](/docs/utility-klasser/oversikt.md#spacing). ### CSS Custom Properties[​](#css-custom-properties "Direct link to CSS Custom Properties") Spacing finnes også i variabler: `--ix-spacing-{size}`. Variablene skiller ikke mellom padding, margin eller gap. ``` .min-komponent { padding: var(--ix-spacing-md); margin-bottom: var(--ix-spacing-lg); gap: var(--ix-spacing-md); } ``` ## Migreringsguider[​](#migreringsguider "Direct link to Migreringsguider") ### Migrere fra FFE[​](#migrere-fra-ffe "Direct link to Migrere fra FFE") Indeks sine verdier på spacing-variabler justerer seg etter skjermstørrelse og [density-modus](#density-moduser). | FFE-token | Indeks-tokens | FFE verdi | Indeks verdi mobil | Indeks verdi desktop | | ----------------- | ---------------- | --------- | ------------------ | -------------------- | | `ffe-spacing-2xs` | `ix-spacing-2xs` | 4px | 4px | 4px | | `ffe-spacing-xs` | `ix-spacing-xs` | 8px | 8px | 8px | | | `ix-spacing-sm` | | 12px | 12px | | `ffe-spacing-sm` | `ix-spacing-md` | 16px | 16px | 16px | | `ffe-spacing-md` | `ix-spacing-lg` | 24px | 24px | 24px | | `ffe-spacing-lg` | `ix-spacing-xl` | 32px | 32px | 48px | | `ffe-spacing-xl` | `ix-spacing-xl` | 40px | 32px | 48px | | `ffe-spacing-2xl` | `ix-spacing-2xl` | 48px | 40px | 64px | | `ffe-spacing-3xl` | `ix-spacing-3xl` | 64px | 48px | 80px | | `ffe-spacing-4xl` | `ix-spacing-4xl` | 80px | 64px | 96px | | `ffe-spacing-5xl` | `ix-spacing-5xl` | 160px | 80px | 128px | Du kan migrere med search-replace-all, men gå gjennom resultatet etterpå, også på flere skjermstørrelser. Oversettelsen er ikke én til én for alle variablene. ### Migrere fra tailwind[​](#migrere-fra-tailwind "Direct link to Migrere fra tailwind") Hvis prosjektet ditt er satt opp med tailwind som bruker spacing-skalaen fra FFE slik som det her: ``` { 0: 0, 0.5: spacing.spacing2xs, 1: spacing.spacing, 2: spacing.spacingSm, 3: spacing.spacingMd, 4: spacing.spacingLg, 5: spacing.spacingXl, 6: spacing.spacing2xl, 8: spacing.spacing3xl, 10: spacing.spacing4xl, 20: spacing.spacing5xl } ``` Kan du ta utgangspunkt i disse tabellene: ### Padding[​](#padding "Direct link to Padding") Verdiene er de samme for `pt`, `pb`, `pl` og `pr`. Se [responsiv spacing](/docs/utility-klasser/oversikt.md#responsiv-spacing) for padding på forskjellige skjermstørrelser. | Tailwind-token | Indeks Util-klasse | FFE/Tailwind verdi | Indeks verdi mobil | Indeks desktop | | -------------- | ------------------ | ------------------ | ------------------ | -------------- | | `p-0` | `ix-p-0` | 0px | 0px | 0px | | `p-0.5` | `ix-p-2xs` | 4px | 4px | 4px | | `p-1` | `ix-p-xs` | 8px | 8px | 8px | | | `ix-p-sm` | | 12px | 12px | | `p-2` | `ix-p-md` | 16px | 16px | 16px | | `p-3` | `ix-p-lg` | 24px | 24px | 24px | | `p-4` | `ix-p-xl` | 32px | 32px | 48px | | `p-5` | `ix-p-xl` | 40px | 32px | 48px | | `p-6` | `ix-p-2xl` | 48px | 40px | 64px | | `p-8` | `ix-p-3xl` | 64px | 48px | 80px | | `p-10` | `ix-p-4xl` | 80px | 64px | 96px | | `p-20` | `ix-p-5xl` | 160px | 80px | 128px | ### Margin[​](#margin "Direct link to Margin") Verdiene er de samme for `mt`, `mb`, `ml` og `mr`. Se [responsiv spacing](/docs/utility-klasser/oversikt.md#responsiv-spacing) for margin på forskjellige skjermstørrelser. | Tailwind-token | Indeks Util-klasse | FFE/Tailwind verdi | Indeks verdi mobil | Indeks desktop | | -------------- | ------------------ | ------------------ | ------------------ | -------------- | | `m-0` | `ix-m-0` | 0px | 0px | 0px | | `m-0.5` | `ix-m-2xs` | 4px | 4px | 4px | | `m-1` | `ix-m-xs` | 8px | 8px | 8px | | | `ix-m-sm` | | 12px | 12px | | `m-2` | `ix-m-md` | 16px | 16px | 16px | | `m-3` | `ix-m-lg` | 24px | 24px | 24px | | `m-4` | `ix-m-xl` | 32px | 32px | 48px | | `m-5` | `ix-m-xl` | 40px | 32px | 48px | | `m-6` | `ix-m-2xl` | 48px | 40px | 64px | | `m-8` | `ix-m-3xl` | 64px | 48px | 80px | | `m-10` | `ix-m-4xl` | 80px | 64px | 96px | | `m-20` | `ix-m-5xl` | 160px | 80px | 128px | ### Gap[​](#gap "Direct link to Gap") | Tailwind-token | Indeks Util-klasse | FFE/Tailwind verdi | Indeks verdi mobil | Indeks desktop | | -------------- | ------------------ | ------------------ | ------------------ | -------------- | | `gap-0` | `ix-gap-0` | 0px | 0px | 0px | | `gap-0.5` | `ix-gap-2xs` | 4px | 4px | 4px | | `gap-1` | `ix-gap-xs` | 8px | 8px | 8px | | | `ix-gap-sm` | | 12px | 12px | | `gap-2` | `ix-gap-md` | 16px | 16px | 16px | | `gap-3` | `ix-gap-lg` | 24px | 24px | 24px | | `gap-4` | `ix-gap-xl` | 32px | 32px | 48px | | `gap-5` | `ix-gap-xl` | 40px | 32px | 48px | | `gap-6` | `ix-gap-2xl` | 48px | 40px | 64px | | `gap-8` | `ix-gap-3xl` | 64px | 48px | 80px | | `gap-10` | `ix-gap-4xl` | 80px | 64px | 96px | | `gap-20` | `ix-gap-5xl` | 160px | 80px | 128px | Ta gjerne kontakt med oss om du har andre behov enn det Indeks tilbyr. --- # Tailwind Indeks kommer med [egne utility-klasser](/docs/utility-klasser/oversikt.md) med `ix-`-prefiks som dekker mye av det Tailwind gir deg, uten noe byggesteg hos deg. Trenger du bare farger, spacing og typografi fra designsystemet, holder de. Bruker du Tailwind allerede, skal du ikke måtte skrive `bg-[var(--ix-color-fill-main-default)]` for å nå tokenene våre. Derfor genererer vi en Tailwind 4-temafil sammen med resten av pakkene. Den kobler Tailwinds navnerom til Indeks-variablene, slik at `bg-fill-main-default` og `p-md` gir riktige verdier. ## Kom i gang[​](#kom-i-gang "Direct link to Kom i gang") Legg importen etter `tailwindcss` i CSS-en din: ``` @import 'tailwindcss'; @import '@sb1/indeks-css/tailwind.css'; ``` Temaet krever Tailwind 4.1 eller nyere. Direktivene vi bruker finnes fra 4.0, men 4.0 er designet for Safari 16.4 og nyere, mens Indeks støtter Safari 15.4. I 4.1 kom fallbacks som gjør at Tailwinds egen CSS også virker der. Temafilen setter bare opp navnene. Selve verdiene kommer fra Indeks-CSS-en, så den må lastes som normalt: ``` @import '@sb1/indeks-css'; ``` Bruker du bare én av delpakkene, finnes temaet der også: `@sb1/indeks-tokens/tailwind.css` og `@sb1/indeks-utils/tailwind.css`. ## Hva du får[​](#hva-du-får "Direct link to Hva du får") | Tailwind-navnerom | Kommer fra | Eksempel på klasse | | ------------------------------- | --------------------------- | ----------------------------------------------------- | | `--color-*` | `--ix-color-*` | `bg-fill-main-default`, `text-foreground-main-subtle` | | `--spacing-*` | `--ix-spacing-*` | `p-md`, `gap-lg`, `mt-2xl` | | `--text-*` | `--ix-font-size-*` | `text-lg`, `text-3xl` | | `--radius-*` | `--ix-border-radius-*` | `rounded-md`, `rounded-pill` | | `--font-sans`, `--font-heading` | `--ix-font-family-*` | `font-sans`, `font-heading` | | `--font-weight-*` | `--ix-font-weight-*` | `font-regular`, `font-medium`, `font-button` | | `--breakpoint-*` | `--ix-breakpoint-*` | `sm:`, `md:`, `lg:`, `xl:` | | `--ease-default` | `--ix-transition-animation` | `ease-default` | Brødfonten ligger på `font-sans`, ikke `font-normal`. Tailwind har allerede `font-normal` som fontvekt 400, og to klasser med samme navn ville kollidert. Fargenavnene er token-navnene uendret, de samme du finner i Figma. Derfor blir det `bg-fill-main-default` og `border-border-main-default`. Den doble `border` ser rar ut, men gjør at du kan lese navnet rett av skissen. Komponent-fargene er også med, som `bg-component-button-primary-fill-default`. De finnes for at komponentene våre skal kunne temes, så de kan endre seg uten at vi regner det som et brudd. Bruk de semantiske fargene der du kan, og disse når du bygger noe som skal matche en Indeks-komponent tett. ## Bruk `var()` for det som mangler navnerom[​](#bruk-var-for-det-som-mangler-navnerom "Direct link to bruk-var-for-det-som-mangler-navnerom") Noen tokens har ingen tilsvarende tema-nøkkel i Tailwind 4. Dem bruker du med Tailwinds klammeform: | Token | Slik bruker du den | | ----------------------------- | --------------------------------------------------- | | `--ix-z-index-*` | `z-[var(--ix-z-index-dialog)]` | | `--ix-border-width-*` | `[border-width:var(--ix-border-width-bold)]` | | `--ix-outline-width-default` | `[outline-width:var(--ix-outline-width-default)]` | | `--ix-outline-offset-default` | `outline-offset-[var(--ix-outline-offset-default)]` | | `--ix-opacity-disabled` | `opacity-[var(--ix-opacity-disabled)]` | | `--ix-transition-duration` | `duration-[var(--ix-transition-duration)]` | Samme form virker for alle Indeks-variabler, også de vi ikke har tatt med i temaet. ## Regn med at `sm:` betyr 768 piksler[​](#regn-med-at-sm-betyr-768-piksler "Direct link to regn-med-at-sm-betyr-768-piksler") Breakpoint-navnene våre er de samme som Tailwinds, men verdiene er ikke. Tailwind bruker 640 piksler for `sm`, Indeks bruker 768. Når du importerer temafilen, får du Indeks-verdien: | Variant | Tailwind uten Indeks | Med Indeks-temaet | | ------- | -------------------- | ----------------- | | `sm:` | 640px | 768px | | `md:` | 768px | 1024px | | `lg:` | 1024px | 1280px | | `xl:` | 1280px | 1600px | Har du Tailwind-kode fra før, endrer den oppførsel. Gå gjennom `sm:`- og `md:`-klassene dine når du tar i bruk temaet. Breakpointene er de eneste verdiene vi skriver rett inn i temafilen. Tailwind bygger media queries på byggetid og kan ikke slå opp en CSS-variabel der. ## Du trenger ikke `dark:` for Indeks-farger[​](#du-trenger-ikke-dark-for-indeks-farger "Direct link to du-trenger-ikke-dark-for-indeks-farger") Fargeklassene peker på Indeks-variablene, og de bytter selv inne i `ix-dark-mode`. Dette blir mørkt uten at du gjør noe mer: ```

Mørk

``` Det samme gjelder density: `p-md` følger `data-density` og skjermbredden, fordi klassen peker på variabelen i stedet for en fast piksel-verdi. Temafilen setter i tillegg Tailwinds egen `dark:`-variant til å følge `ix-dark-mode` i stedet for `prefers-color-scheme`. Da havner ikke dine egne `dark:`-klasser i motsatt modus av designsystemet. ## Lås deg til Indeks-tokens[​](#lås-deg-til-indeks-tokens "Direct link to Lås deg til Indeks-tokens") Temafilen legger seg oppå Tailwinds egne skalaer. Både `bg-red-500` og `bg-fill-main-default` virker, og `p-4` virker ved siden av `p-md`. Vil du at bare Indeks-verdiene skal være tilgjengelige, nullstill navnerommene før du importerer: ``` @import 'tailwindcss'; @theme { --color-*: initial; --spacing-*: initial; } @import '@sb1/indeks-css/tailwind.css'; ``` Vær klar over hva `--spacing-*: initial` tar med seg: hele den numeriske skalaen forsvinner, altså `w-64` og `gap-2` like mye som `p-4`. ## Relatert[​](#relatert "Direct link to Relatert") * [Utility-klasser i Indeks](/docs/utility-klasser/oversikt.md) er alternativet til Tailwind i dette designsystemet * [Migrere spacing fra Tailwind](/docs/grunnleggende/tokens/spacing.md#migrere-fra-tailwind) hvis du heller vil bytte ut Tailwind-klassene med `ix-`-klasser * [Fargemodus](/docs/retningslinjer/farger/fargemodus.md) forklarer `ix-dark-mode` og `ix-light-mode` * [ADR-DS-009](/docs/adr/ADR-DS-009-css-only-styling.md) begrunner hvorfor Indeks selv ikke bruker Tailwind --- # Z Index `z-index` bestemmer hvilket element som ligger øverst når to elementer overlapper. Problemet oppstår når hvert team velger tall selv: en modal med `z-index: 100` havner under en header med `999`, og så begynner budkrigen. Derfor har vi tokens for de tilfellene som faktisk trenger stabling. Tokenet er navngitt etter formålet, og verdiene er satt slik at komponenter og overlays ikke kolliderer. Velg tokenet som beskriver det du lager, og la tallet være. | CSS custom property | Verdi | Beskrivelse | | ------------------- | ----- | -------------------------------------------------------------------------------------------------------------------- | | `--ix-z-background` | -1 | Bakgrunnselementer som alltid skal ligge bak alt annet. | | `--ix-z-default` | 0 | Standard z-index for vanlige komponenter og innhold. | | `--ix-z-elevated` | 1 | Brukes for elementer som skal ligge over standard innhold. | | `--ix-z-overlay` | 20 | Overlay som skal ligge over innholdet på siden. | | `--ix-z-dialog` | 60 | Dialoger som skal ligge over alt annet. | | `--ix-z-max` | 100 | For når du har noe som må ligge øverst uansett hva. Vi annbefaler å bruke denne like mye som !important. Dvs. aldri. | --- # Typografi ## Våre fonter[​](#våre-fonter "Direct link to Våre fonter") Skrifttypene våre er egenutviklet og bygget på det samme sirkulære formspråket som resten av merkevaren. Det er derfor de er gjenkjennelige som SpareBank 1, og en av de tydeligste forskjellene mellom våre flater og konkurrentenes. Vi bruker to fonter: SpareBank 1 Title til de største overskriftene og SpareBank 1 til all øvrig tekst. ### SpareBank 1 Title[​](#sparebank-1-title "Direct link to SpareBank 1 Title") Brukes på de største overskriftene og er derfor ekstra distinkt. Fonten kjennetegnes av tydelige, sirkulære former som skaper kontrast mot de smalere bokstavene og gir et sterkt visuelt uttrykk. SpareBank 1 Title skal ikke brukes i lengre brødtekster, da den ikke er optimalisert for lesbarhet i mengdetekst. ### SpareBank 1[​](#sparebank-1 "Direct link to SpareBank 1") I brødtekst og lengre tekster bruker vi en mindre distinkt font som er optimalisert for lesbarhet i mengdetekst. Begge fontene er tilgjengelige i Figma og kan brukes direkte, uten behov for lokal installasjon. ## Store bokstaver og kursiv[​](#store-bokstaver-og-kursiv "Direct link to Store bokstaver og kursiv") Unngå å bruke tekst med kun store bokstaver, da dette gir dårligere lesbarhet. Det samme gjelder kursiv, som ikke bør brukes i overskrifter eller i større tekstmengder, ettersom det kan gjøre teksten vanskeligere å lese. ## Fontstørrelse[​](#fontstørrelse "Direct link to Fontstørrelse") Basestørrelsen er alltid 16px (`1rem`) og skalerer ikke med skjermstørrelsen. Overskriftsstørrelsene er faste og beregnes ut fra basestørrelsen, slik at proporsjonene er konsistente på tvers av enheter og kontekster. ## Størrelse på overskrifter og semantikk[​](#størrelse-på-overskrifter-og-semantikk "Direct link to Størrelse på overskrifter og semantikk") Du står fritt til å velge størrelse på overskrifter basert på behov og kontekst. Semantikk og visuell utforming er bevisst holdt adskilt, slik at riktig HTML-struktur kan brukes uavhengig av ønsket uttrykk. ## Linjelengde[​](#linjelengde "Direct link to Linjelengde") Begrens linjelengden. WCAG anbefaler det, og både for lange og for korte linjer bryter leseflyten: er linjen for lang, mister øyet starten på den neste, og er den for kort, blir teksten hakkete. På større skjermer er 50 til 75 tegn per linje målet, mellomrom inkludert. På mobil ligger det lavere, typisk 35 til 50 tegn. Under 20 tegn blir det for mange linjeskift. ## Linjehøyde[​](#linjehøyde "Direct link to Linjehøyde") Standard linjehøyde i Indeks er 1.2. Brødtekst som er lengre enn noen få linjer trenger større linjehøyde for bedre lesbarhet, og kan overstyres til 1.4 med henholdsvis `Text/Body long/md - regular` i Figma, `long`-propen i `Text`-komponenten, eller CSS-klassen `.ix-text--long`. ## Venstrestilt tekst er standard[​](#venstrestilt-tekst-er-standard "Direct link to Venstrestilt tekst er standard") Venstrestilt tekst gir best lesbarhet. Midtstilt tekst kan på lengre tekster gjør det vanskeligere å lese, men kan brukes varsomt på korte tekstmengder, for eksempel i overskrifter med enkel støttetekst. ## Farge på tekst[​](#farge-på-tekst "Direct link to Farge på tekst") | Token | Brukes til | | ------------------------------------- | ---------------------------------------------- | | `--ix-color-foreground-main-emphasis` | Overskrifter, der hierarkiet skal være tydelig | | `--ix-color-foreground-main-default` | Brødtekst og vanlig tekst | | `--ix-color-foreground-main-subtle` | Støttetekst og sekundær informasjon | ## Stiler[​](#stiler "Direct link to Stiler") I Indeks bruker vi fire typografiske stiler: Heading, BodyLong, BodyShort og Label. Beskrivelse av når og hvordan de ulike stilene brukes, finner du under typografikomponentene. --- # Indeks Indeks er designsystemet til SpareBank 1. Her ligger komponentene, tokenene og retningslinjene teamene bygger på, slik at en løsning ser ut som SpareBank 1 og fungerer for alle som skal bruke den. ## Tilpass dokumentasjonen Velg teknologi og andre innstillinger så tilpasser vi eksemplene til stacken din. Åpne innstillinger [Utility-klasser](/docs/utility-klasser/oversikt.md) [Spacing-tokens](/docs/grunnleggende/tokens/spacing.md) [Farge-tokens og -utils](/docs/retningslinjer/farger/.md) ## Kom i gang[​](#kom-i-gang "Direct link to Kom i gang") [Designer](/docs/kom-i-gang/designer.md) [Utvikler](/docs/kom-i-gang/utvikler.md) ## Bruk Indeks med AI-assistenter[​](#bruk-indeks-med-ai-assistenter "Direct link to Bruk Indeks med AI-assistenter") Hele dokumentasjonen er tilgjengelig som ren tekst som kan limes direkte inn i Claude, ChatGPT, Cursor eller andre LLM-verktøy: * [`llms-full.txt`](/llms-full.txt) — hele dokumentasjonen i én fil, klar til å limes inn som kontekst * [`llms.txt`](/llms.txt) — hierarkisk indeks hvis du vil at AI-en skal slå opp enkeltsider Hver dokumentasjonsside finnes også som `.md`-variant — legg `.md` etter URL-en, for eksempel [`/docs/komponenter/textfield.md`](/docs/komponenter/textfield.md). --- # Bidra Enten du mangler en komponent, har forslag til forbedringer, eller har funnet noe som ikke fungerer som det skal, vil vi gjerne høre fra deg. ## Registrer feil eller mangler[​](#registrer-feil-eller-mangler "Direct link to Registrer feil eller mangler") Bruk én av disse malene i GitHub: * [Kodefeil](https://github.com/SpareBank1/indeks/issues/new?template=kodefeil.yml) — feil i en komponent, token eller stil * [Dokumentasjonsfeil](https://github.com/SpareBank1/indeks/issues/new?template=dokumentasjonsfeil.yml) — noe er feil, utdatert eller uklart i dokumentasjonen * [Ny komponent](https://github.com/SpareBank1/indeks/issues/new?template=ny-komponent.yml) — foreslå en ny komponent eller større utvidelse Du kan også bruke "Rapporter feil på denne siden"-lenken nederst på hver dokumentasjonsside for å åpne et forhåndsutfylt skjema. ## Ny komponent[​](#ny-komponent "Direct link to Ny komponent") Indeks leverer basekomponenter du setter sammen selv, ikke ferdige skjermer. Når vi vurderer en ny komponent, er spørsmålet derfor om den hører på det nivået, eller om den er noe teamet ditt bygger av delene som alt finnes. Se [Migrering](/docs/kom-i-gang/migrering.md) for arbeidsdelingen. Dette er det vi ser etter: * **Gjenbrukbarhet**: Er det flere som vil ha nytte av den? * **Verdi**: Hvilket problem løser den, og kan det løses av en annen komponent? * **Fleksibilitet**: Kan den tilpasses ulike behov? * **Konsistens**: Stemmer den overens med våre designprinsipper og helheten i systemet? Og dette er det vi spør om: * Kan komponentene brukes i flere kontekster? * Er komponenten navigerbar med tastatur og i tråd med WCAG-krav? * Har alle states god nok kontrast? * Hvordan blir opplevelsen på mobil vs desktop? Er det best med andre løsninger på mobil, som f.eks sheets eller haptic feedback? ## Mønstre og retningslinjer[​](#mønstre-og-retningslinjer "Direct link to Mønstre og retningslinjer") Er dokumentasjonen uklar, må hver leser gjette selv, og da får vi tolv varianter av samme løsning. Fant du noe som mangler, er utdatert eller er vanskelig å forstå, si det. Det er like nyttig som en kodefiks. ## Slik bidrar du[​](#slik-bidrar-du "Direct link to Slik bidrar du") Du trenger ikke et ferdig forslag. Et spørsmål eller en halv idé er nok til å starte. * **Ta kontakt med oss**: Send oss en melding på Slack eller kom bort og prat med oss når et behov, en idé eller et problem dukker opp. * **Beskriv situasjonen**: Fortell oss hva du prøver å løse for brukerne dine, eller hva som ikke fungerer som forventet. * **Vi ser på det sammen**: Sammen vurderer vi hvordan behovet best kan løses og hvordan det passer inn i helheten av Indeks. * **Vi blir enige om veien videre**: Vi avklarer om det betyr en justering, ny funksjonalitet, bedre dokumentasjon og hvem som gjør hva. * **Vi følger det til mål**: Når vi er enige om retningen, sørger vi for at arbeidet blir tatt videre på en god måte. Indeks blir bare bra av å bli brukt og sagt imot, så ta kontakt. --- # Designer ## Før du begynner[​](#før-du-begynner "Direct link to Før du begynner") 1. Les [merkevaren og vår visuelle identitet](https://merkevare.sparebank1.no/portal) for å forstå SpareBank 1s designprinsipper 2. Skaff deg tilgang til Figma. Designsystembiblioteket ligger der som standard, så du trenger ikke installere det (se [Figma](#figma) under) 3. Bli med i Slack-kanalene `#ext-design` og `#ext-designsystem` ## Viktig dokumentasjon[​](#viktig-dokumentasjon "Direct link to Viktig dokumentasjon") Før du begynner å designe, anbefaler vi at du setter deg inn i følgende: * **[Tokens](/docs/grunnleggende/tokens/introduksjon.md)** — farger, spacing, typografi og de andre designtokenene * **[Mønstre og maler](/docs/monstre-og-maler/layout.md)** — vanlige designmønstre og layoutmaler ## Verktøy[​](#verktøy "Direct link to Verktøy") ### Figma[​](#figma "Direct link to Figma") I SpareBank 1 bruker vi Figma til å designe for digitale flater. Designsystemet og illustrasjonsbiblioteket er tilgjengelig som standard i alle Figma-filer. ### Slack[​](#slack "Direct link to Slack") Vi bruker Slack til kommunikasjon på tvers av team. Viktige kanaler for designere: * **`#ext-design`** — felleskanal for alle designere * **`#ext-designsystem`** — tilbakemeldinger og hjelp fra designsystemteamet * **`#sb1u-restefest`** — bli med i kampen mot matsvinn ### Ønskeliste[​](#ønskeliste "Direct link to Ønskeliste") Vi har laget en ønskeliste i Figma hvor du kan komme med forslag og ønsker om forbedringer på komponenter. ## Samarbeid og møter[​](#samarbeid-og-møter "Direct link to Samarbeid og møter") ### Felles designmøter[​](#felles-designmøter "Direct link to Felles designmøter") * **DesignLab** (hver 4. uke). Lavterskel møteplass på tvers av team. Ta med en oppgave du står fast i, og få den diskutert med andre designere. * **Designsystem-forum** (hver 4. uke). Designere og utviklere sammen med designsystemteamet. Alt fra et konkret komponentspørsmål til hvor systemet skal videre. Meld inn saker på forhånd i `#ext-designsystem`. ### Fagtid[​](#fagtid "Direct link to Fagtid") På torsdager har alle fast ansatte i SpareBank 1 Utvikling fagtid. Før lunsj samles designerne til avdelingsmøte, hvor vi deler erfaringer fra teamene våre og tar opp andre fagrelaterte temaer. Resten av dagen kan brukes til kompetansebygging eller deltakelse i fagrupper, for eksempel faggruppen for universell utforming. --- # Kontakt Indeks blir best når vi snakker sammen. Vi setter stor pris på alle innspill, spørsmål og bidrag fra dere som bruker systemet. ## Slack[​](#slack "Direct link to Slack") Vi kan nås på: [#ext-designsystem](slack://channel?team=\&id=ext-designsystem) Her kan du stille spørsmål, dele erfaringer, rapportere bugs, eller diskutere nye ideer og forbedringer. Vi svarer gjerne på spørsmål om bruk av komponenter, konvensjoner og hjelper til med utfordringer du måtte ha. ## E-post[​](#e-post "Direct link to E-post") ## Designsystemforum[​](#designsystemforum "Direct link to Designsystemforum") Designsystemforum er et tverrfaglig møtested hver fjerde uke, hvor vi diskuterer fremtidige planer, tar opp utfordringer og deler erfaringer på tvers av team og fagfelt. Gi beskjed om du ikke har fått invitasjon, så fikser vi det! ## Teamet[​](#teamet "Direct link to Teamet") Team Designsystem består av * **Martin Gaard**: Design Lead * **Dag Frode Solberg**: Tech lead, utvikler * **Anders Johnsen**: Utvikler * **Tuva Ødegård**: Utvikler * **Karina Hammermark Holme**: Produktleder Det er bare å ta kontakt hvis det er noe, eller slå av en prat hvis du ser oss i gangene. --- # Migrering fra FFE til Indeks Indeks er ikke FFE med ny styling. Det er bygget opp på nytt, med en annen arkitektur, og på fem punkter må du derfor jobbe annerledes enn du er vant til. Denne siden går gjennom dem. ## Vi leverer byggeklosser, ikke ferdige skjermer[​](#vi-leverer-byggeklosser-ikke-ferdige-skjermer "Direct link to Vi leverer byggeklosser, ikke ferdige skjermer") Indeks tilbyr grunnleggende basekomponenter. Trenger du noe større, setter du det sammen selv av dem. Designsystemteamet eier og vedlikeholder basekomponentene; teamet ditt eier det du bygger oppå. Det er en bevisst arbeidsdeling. Skulle vi også vedlikeholdt alle de sammensatte komponentene teamene trenger, ville vi blitt flaskehalsen som alle måtte stå i kø bak. I stedet får du bestemme selv hvordan delene settes sammen. Strukturen følger atomic design, metodikken Brad Frost beskrev: * **Atoms** er de minste delene: en knapp, et input-felt, en label, et ikon. * **Molecules** er flere atoms satt sammen, for eksempel et søkefelt av et input-felt og en knapp. * **Organisms** er større sammensetninger, som en navigasjonsrad eller et kort med flere elementer. Indeks leverer atoms og enkelte molecules. Organisms bygger du. ## Webstandarder fremfor rammeverk[​](#webstandarder-fremfor-rammeverk "Direct link to Webstandarder fremfor rammeverk") FFE var React. Indeks er HTML og CSS, utvidet med web components der oppførsel krever JavaScript. Det betyr at komponentene virker i Vue, Svelte, server-rendret HTML eller ren statisk side, ikke bare i React. React-komponentene finnes fortsatt, i `@sb1/indeks-react`. De er tynne lag rundt web-komponentene og CSS-en, så du får det samme resultatet enten du bruker dem eller skriver HTML-en selv. Velger du React, må du også laste `@sb1/indeks-web`. ## Utility-klasser[​](#utility-klasser "Direct link to Utility-klasser") Indeks har utility-klasser i Tailwind-stil for layout, spacing, farger og typografi. Du kan justere avstander og plassering direkte i markupen i stedet for å skrive egen CSS til hvert tilfelle. De ligger i CSS-en du allerede laster, så du trenger ikke installere noe ekstra. Se [Utility-dokumentasjonen](/docs/utility-klasser/oversikt.md) for hele settet. ## Færre pakker enn i FFE[​](#færre-pakker-enn-i-ffe "Direct link to Færre pakker enn i FFE") I FFE lå hver komponent i sin egen npm-pakke. I Indeks er det tre du forholder deg til: | Pakke | Inneholder | Hvordan du laster den | | ---------------------------------------------------------------------- | -------------------------------------------------------------- | ------------------------- | | [`@sb1/indeks-css`](https://www.npmjs.com/package/@sb1/indeks-css) | All styling: komponent-CSS, utility-klasser og tokens i én fil | CDN (anbefalt), eller npm | | [`@sb1/indeks-web`](https://www.npmjs.com/package/@sb1/indeks-web) | Web-komponentene, altså oppførselen | CDN (anbefalt), eller npm | | [`@sb1/indeks-react`](https://www.npmjs.com/package/@sb1/indeks-react) | React-komponentene | npm | `@sb1/indeks-tokens` og `@sb1/indeks-utils` finnes på npm, men du skal normalt ikke installere dem: innholdet er inlinet i `@sb1/indeks-css`. Trenger du tokens i et native-prosjekt eller i et eget byggesteg, er det unntaket. Vi anbefaler CDN for CSS og web-komponentene, ikke npm. Alle SB1-applikasjoner deler samme URL, så nettleseren gjenbruker cachen når brukeren går mellom tjenestene våre. [Utvikler](/docs/kom-i-gang/utvikler.md) viser oppsettet, inkludert hvordan du holder CDN-versjonen i takt med `@sb1/indeks-react`. ## Færre farger[​](#færre-farger "Direct link to Færre farger") Fargepaletten i Indeks er mindre enn i FFE, og det er med vilje. Du velger farge etter hva den betyr og ikke etter hvilken kulør du vil ha: navnene er `fill-success`, `surface-main`, `border-danger`. Da havner to team som løser samme oppgave på samme farge, og et bytte av kulør senere treffer alle stedene på én gang. Se [Farge-dokumentasjonen](/docs/retningslinjer/farger/.md) for hele paletten. --- # Testing Skriver du tester mot skjemaelementene i Indeks, trenger du én linje i testoppsettet først. Uten den finner ikke testen checkboxene dine, og feilmeldingen sier ingenting om hvorfor. ## Registrer web components i testoppsettet[​](#registrer-web-components-i-testoppsettet "Direct link to Registrer web components i testoppsettet") Legg denne importen i setup-filen testkjøreren din laster, for eksempel `vitest.setup.ts`: ``` import '@sb1/indeks-web'; ``` Pakken registrerer alle `ix-*`-elementene selv når den importeres. Du skal ikke kalle `customElements.define` for hånd. `@sb1/indeks-web` hører i `devDependencies`: ``` npm install --save-dev @sb1/indeks-web ``` Har du den allerede for [TypeScript-typene](/docs/kom-i-gang/utvikler.md#3a-typescript-typer-valgfritt), er du ferdig. Koble den så på i vitest-konfigurasjonen: ``` // vitest.config.ts export default defineConfig({ test: { environment: 'jsdom', setupFiles: ['./vitest.setup.ts'], }, }); ``` ## Hvorfor oppsettet trengs[​](#hvorfor-oppsettet-trengs "Direct link to Hvorfor oppsettet trengs") React-pakken er et tynt lag oppå web components. For grupperte skjemakomponenter er det web-komponenten som kobler `