Designsystemets filosofi
Dette designsystemet er meningsfullt, ikke uendelig konfigurerbart. Hver komponent har én
anbefalt måte å bruke den på, ferdig satt opp med riktige design-tokens - du skal aldri trenge å style den
selv for at den skal se riktig ut. Målet er at et helt nytt skjema, en ny side eller en ny visning skal
kunne bygges med rene, semantiske lm-*-elementer og native HTML, uten en eneste linje
prosjektspesifikk CSS.
Støtter kun nyeste Chrome og Safari - det er derfor trygt å bruge moderne CSS (nesting, @layer,
@scope, :has(), container queries) og moderne JS (Web Components, native
<dialog>/popover) uten polyfill eller fallback noe sted.
Opinionerte stilvalg
- Farge: alle fargetokens er bygget på
currentColoreller egendefinerte lys/mørk-oklch()-par vialight-dark()- ikke systemfargenøkkelord somcanvas/canvasText(kun brukt der spec anbefaler det direkte, inni@media (forced-colors: active)-blokker) - lys/mørk modus følger automatisk uten en eneste@media (prefers-color-scheme)-regel noe sted. - Mellomrom: én liten skala (
--space-2xs…--space-large) for alt avgap/padding/marginsom gjentar seg - ikke fritt valgte tall. - Typografi: ferdige font-shorthands (brødtekst, overskrift, liten tekst, kode) fremfor
å sette
font-size/font-weight/line-heighthver for seg. - Bevegelse: kun
transform/opacityanimeres, alltid via de delte varighet-/easing-tokenene, og alltid null ut avprefers-reduced-motion: reduce. - Grid fremfor Flexbox som standardverktøy for plassering - Flexbox er forbeholdt ekte innholdsdrevet 1D-flyt.
- Maksimal minimalisme: flate overflater bruker
--shadow-soft: 0 0 0 transparent, radius-skalaen stopper ved--radius-large: 0.75em, og typografitoppen er strammet inn til--font-size-title-1: 1.65em,--font-size-title-2: 1.3em,--font-size-display: 2.25emog--font-size-large: 1.05em. Skygge beholdes kun for ekte overlegg via--shadow-elevated: 0 0.45em 1.1em color-mix(in srgb, var(--color-surface-text) 10%, transparent), og--blur-material: 8pxbrukes bare der lesbarhet over skrollende innhold faktisk krever det.
Målbare grenser for kompleksitet (Apples HIG)
Et grensesnitt blir uoversiktlig lenge før det blir "fullt" - derfor har designsystemet noen konkrete,
tellbare grenser for hvor mange likeverdige valg som kan vises samtidig på ett sted. Grensene er hentet
fra Apples Human Interface
Guidelines, som i flere tiår har målt nøyaktig dette for skjermer med begrenset plass og
oppmerksomhet. De er bevisst tall, ikke skjønn - et sted enten overholder grensen eller ikke, og
node designsystem/scripts/valider-kompleksitetsgrenser.js håndhever de tre tellbare radene
under automatisk mot alle sider i turer/ og okonomi/ (den siste raden, minste
tappbare areal, håndheves strukturelt via delte stilark - se forklaring under tabellen).
| Mønster | Grense | Begrunnelse |
|---|---|---|
Fane-/hovednavigasjon (<lm-nav-tabs>) | Maks 5 synlige faner | Apples Tab Bars-regel: mer enn 5 faner blir uleselig i en fanelinje og skal i stedet reorganiseres til færre, bredere kategorier - aldri løses med en "Mer"-fane eller skjuling. |
Flat handlingsrad (f.eks. en "Snarveier"-seksjon med <lm-link-button>) | Maks 5 likeverdige handlinger i én enkelt, uinndelt rad | Samme tellegrense som fanelinjen, generalisert til enhver flat liste av likeverdige valg. Trenger stedet flere enn 5 handlinger: del dem i navngitte under-seksjoner (egen <h3> per gruppe) fremfor å la alle stå i én lang rad - se Apples mønster for grupperte lister i Innstillinger-appen. |
Dialog-/varslingshandlinger (<lm-confirm-dialog>, <dialog>) | Maks 2 synlige knapper samtidig (unntak: et flerstegs veiviser-skjema kan vise "Tilbake" + "Neste"/"Fullfør" ett steg om gangen) | Apples Alerts-mønster: en bekreftelse skal ha ett klart primærvalg og ett avbryt-valg - flere samtidige knapper tvinger brukeren til å lese og veie alternativer i et øyeblikk ment for et raskt ja/nei. |
Kontekstmeny (<lm-menu-button>) | Grupper i undermenyer/skilletegn når antallet vokser forbi rundt 8 elementer | Apples Menus-anbefaling: lange, uinndelte menyer blir trege å skumme - grupper beslektede handlinger med en visuell skillelinje i stedet for én lang liste. |
Segmentert kontroll (<lm-button-group type="enkeltvalg">) | Mellom 2 og 5 segmenter | Apples Segmented Controls-regel: færre enn 2 segmenter er meningsløst (ingenting å velge mellom), flere enn 5 gjør hvert segment for smalt til å lese og treffe presist - del opp i en annen kontrolltype (f.eks. en <select>-meny) i stedet. |
| Minste tappbare areal (alle knapper, lenke-knapper, fane- og skjemakontroller) | Minst 44×44pt (token --tap-target-min, 2.75rem) | Apples Layout-krav om minimum tappbart areal: kontroller under 44×44pt er vanskelige å treffe presist, spesielt for brukere med redusert finmotorikk. Grensen er bakt inn strukturelt i delte stilark (styles.css, skjema-felt-delt.css, link-button/nav-tabs/menu-button) - alle standardkontroller overholder den uten at en forbrukende side må tenke på det selv. Unntatt: native checkbox/radio/range/color-kontroller (som skal beholde sin naturlige OS-størrelse) og rene inline-lenker i løpende tekst (som ikke er selvstendige kontroller). |
Disse grensene gjelder samtidig synlige, likeverdige valg - et flerstegs skjema der kun ett steg vises av gangen (f.eks. onboarding-veiviseren) bryter ikke grensen selv om skjemaet totalt sett har mange felt, fordi brukeren aldri ser mer enn ett steg samtidig. Et enkelt skjema for én sammenhengende ting (f.eks. å registrere ett lån med navn, beløp, rente og notat) er heller ikke omfattet - der er feltene egenskaper ved én og samme ting, ikke separate valg som konkurrerer om oppmerksomhet.
Minste tappbare areal er ikke telbart fra statisk HTML alene (den faktiske størrelsen avhenger av
rendret layout/cascade), og håndheves derfor strukturelt via delte stilark og tokens fremfor et eget
valideringsskript - verifisert manuelt mot alle sider i turer/ og okonomi/
med Playwright ved innføring av regelen.
Flere mønstre fra Apples HIG
To flere konkrete, håndhevbare regler - hentet fra samme kilde som grensene over, men om hvilken kontrolltype som skal brukes, ikke om hvor mange valg som kan vises samtidig.
| Mønster | Regel | Begrunnelse |
|---|---|---|
| Søke-/filtreringsfelt | <input type="search">, aldri type="text" | Apples Searching-anbefaling om at et søkefelt alltid skal la brukeren tømme det raskt igjen. type="search" gir en innebygd tøm-knapp og Escape-for-å-tømme helt gratis, uten en linje JS - håndheves av node designsystem/scripts/valider-kompleksitetsgrenser.js, som flagger ethvert felt med id/name/placeholder som antyder søk (inneholder «søk»/«sok»/«search») men som fortsatt bruker type="text". |
| Fremdrift med kjent prosentandel (f.eks. nedlasting av en KI-modell, budsjett brukt så langt, fremdrift mot et sparemål) | Vis alltid et native <progress value max>-element ved siden av eventuell tekst - aldri kun tekst med en prosentandel | Apples Progress Indicators-regel: når fremdriften er målbar, skal den vises som en determinert indikator - en tekstlig prosentandel alene tvinger brukeren til å lese tall i stedet for å se et øyeblikksbilde. Kan ikke telles automatisk fra statisk HTML (fremdriften kommer fra en JS-callback) - håndhevet manuelt ved innføring av regelen, se okonomi/js/features/budsjett/budsjett.js og okonomi/js/pages/formue-sparing-side.js for det etablerte mønsteret. |
One-liner-mønstre for vanlige oppgaver
Disse dekker de aller vanligste UI-behovene med én enkelt, ferdig stylet linje - ingen egen CSS-klasse trengs i det forbrukende prosjektet.
| Behov | One-liner |
|---|---|
| Enkelt tekstfelt med etikett | <label><span>E-post</span><input type="email" required></label> |
| Statistikk-/nøkkeltall | <lm-stat label="Saldo">12 450 kr</lm-stat> |
| Tom-tilstand med kall-til-handling | <lm-empty-state>…</lm-empty-state> |
| Fane-/hovednavigasjon | <lm-nav-tabs><a href="…">…</a>…</lm-nav-tabs> |
| Kort med media + handling | <lm-media-tile>…</lm-media-tile> |
| Bekreftelsesdialog | <lm-confirm-dialog>…</lm-confirm-dialog> |
| Midlertidig statusmelding | <lm-toast>…</lm-toast> |
| Punktfri liste | <ul class="liste-uten-punkter">…</ul> |
| Skrollbar tabell | <div class="tabellramme"><table>…</table></div> |
| Tilbake-navigasjon (44x44pt tappbart areal inkludert) | <nav class="tilbake"><a href="…">← Hjem</a></nav> |
| Tjenestebytter mellom appene | <span class="tjenestebytter"><button type="button" command="toggle-popover" commandfor="tjenestebytter-meny" aria-label="Bytt tjeneste"><lm-ikon navn="chevron-ned"></lm-ikon></button><menu popover id="tjenestebytter-meny">…</menu></span> |
Importer komponenter og funksjoner uten skjøre stier
Bruk alltid rot-absolutt sti (startende med /designsystem/...) - aldri
relativ sti med ../-klatring. En rot-absolutt sti fungerer identisk uansett hvor den
importerende filen ligger i mappetreet, krever ingen import-map eller byggetrinn, og er langt lettere å
lese og verifisere i en diff enn ../../../../designsystem/....
| Behov | Riktig | Unngå |
|---|---|---|
| Web component (side-effect-import som registrerer et custom element) | <script type="module" src="/designsystem/komponenter/skjema/text-field/text-field.js"></script> | <script type="module" src="../../../designsystem/komponenter/…"></script> |
| Ren funksjon | import {escapeHtml} from "/designsystem/funksjoner/sikkerhet/escape-html/escape-html.js"; | import {escapeHtml} from "../../../../designsystem/funksjoner/…"; |
Bruk <lm-ikon> - aldri emoji eller egne inline-SVG-er
Alle piktogrammer skal komme fra det delte, vendorede ikonbiblioteket via
<lm-ikon navn="..."> - aldri emoji
(🏠, 💰 ...) eller et hånd-tegnet <svg> lokalt i et
prosjekt. Emoji rendres ulikt på tvers av operativsystem/nettleser og følger ikke
currentColor, og en lokal inline-SVG dupliserer geometri som burde vært delt.
| Behov | Riktig | Unngå |
|---|---|---|
| Ikon i navigasjon/knapp | <lm-ikon navn="hjem"></lm-ikon> | <span class="ikon">🏠</span> |
| Eget, hånd-tegnet ikon | Legg navnet til i ikon-bibliotek.data.js (Lucide-geometri) og bruk <lm-ikon navn="..."> | const ICON_X = `<svg>...</svg>`; lokalt i et prosjekt |
Se ikon-siden for hele det live-rendrede biblioteket med kopierbare navn. Biblioteket er bevisst lite og kuratert - genuint generiske behov (nytt navigasjonsbegrep, ny diagramtype) legges til der; ikke lag et engangsikon lokalt i et prosjekt.
Foretrekk et ikon i hver knapp
En handling gjenkjennes raskere som et ikon+ord enn som ord alene - <button>,
<lm-link-button> og knapper i <lm-menu-button> skal derfor som
hovedregel ha et <lm-ikon navn="..."> først, deretter et mellomrom og selve
teksten - aldri kun ikonet alene (med mindre knappen har en aria-label, se
APG-dekning). Ikonet er rent dekorativt her (ordet ved
siden av sier allerede det samme), så <lm-ikon> trenger ikke noen etikett.
<button type="submit"><lm-ikon navn="lagre"></lm-ikon> Lagre</button>
Rotnivå-styles.css gjør selve oppsettet til en one-liner: button er
display: inline-flex med align-items: center og en liten gap,
slik at ikonet sentreres riktig vertikalt mot teksten uten at det forbrukende prosjektet må style noe
selv.
Unntak - disse trenger ikke ikon:
- Rene sidetalls-/paginering-knapper (
1,2,3...) - selve tallet er hele poenget, et ikon ville bare vært støy. - Et
<lm-button-group type="enkeltvalg">-segment der selve teksten er verdien som velges (f.eks. et valutavalg «NOK»/«USD»/«EUR») - ikke en handling. - Faner i
<lm-nav-tabs>(disse er<a>-elementer, ikke<button>, og har sitt eget, allerede etablerte ikon+tekst-mønster).
Når er egen CSS faktisk greit?
Kun i to tilfeller:
- Noe er genuint domenespesifikt (f.eks. fargekoding av en applikasjons egne statuskoder) og har ingen naturlig plass i et delt komponentbibliotek.
- Et helt smalt, navngitt unntak fra en global stil, alltid avgrenset med
@scopeog en kommentar som forklarer hvorfor.
Alt annet - navigasjon, kort, statistikk, tomme tilstander, dialoger, skjemaer, tabeller, lister,
bildevisning - skal dekkes av et eksisterende komponent,
token eller en utility-klasse i rot-styles.css. Mangler
noe? Utvid designsystemet fremfor å skrive lokal CSS - se
designsystem.instructions.md
for hele kontrakten (manifest-skjema, kategori-struktur, valideringsregler).