Designguide

Tidya designspråk

Et varmt grensesnitt bygget på amber og brune toner. Mørk og lys modus, tekst kontrastsjekket mot WCAG AA (4,5:1) og kontroller mot 3:1.

Farger

Mørk modus er standard, lys følger OS eller data-theme="light". Kontrast er mot --color-bg (mørk / lys). Kilde: tokens.css.

TokenMørkLysBruk
--color-bg#1a1816#f7f3eeSidebakgrunn
--color-surface#272422#ede8e1Kort, dialog
--color-text#f5efe6#1c1712Primærtekst · 15,5:1 / 16,1:1
--color-text-secondary#d4a88f#7a4a2eStøttetekst · 8,3:1 / 6,7:1
--color-text-mutedrgba(245, 239, 230, 0.55)rgba(28, 23, 18, 0.65)Metadata, placeholder · 5,5:1 / 5,3:1
--color-label#e0956d#9c3d1aEtiketter · 7,3:1 / 6,1:1
--color-accent#ffbe0b#8a5d00Aksenttekst, lenke, kant på fokusert felt · 10,7:1 / 5,2:1
--color-accent-hover#ffc933#6b4900Hover på aksenttekst · 11,5:1 / 7,4:1
--color-accent-fill#ffbe0b#ffbe0bFlater: primærknapp, markeringer
--color-accent-fill-hover#ffc933#f0a800Hover på flater
--color-on-accent#1c1712#1c1712Tekst på aksentflate · 10,7:1 / 10,7:1
--color-link= --color-accent= --color-accentLenker (alias, alltid understreket)
--color-error#ff5c5c#c41c1cFeil, slett · 5,9:1 / 5,4:1
--color-success#4cc38a#17703fSuksess · 8,0:1 / 5,6:1
--color-warning#ff9f43#9a4d00Advarsel · 8,7:1 / 5,5:1
--color-info#6cb6ff#1a5a9eInformasjon · 8,2:1 / 6,3:1
--color-*-bgrgba(farge,.12)rgba(farge,.08–.10)Bakgrunn i meldinger (error, success, warning, info)
--color-borderrgba(255, 255, 255, 0.06)rgba(28, 23, 18, 0.10)Kortkanter (dekorativ)
--color-border-subtlergba(255, 255, 255, 0.10)rgba(28, 23, 18, 0.16)Skiller (dekorativ). Ikke på inputs
--color-border-controlrgba(255, 255, 255, 0.34)rgba(28, 23, 18, 0.49)Inputs og kontroller · min. 3:1
--color-accent-borderrgba(255, 190, 11, 0.46)rgba(138, 93, 0, 0.76)Ghost-knapp, drop-sone · min. 3:1
--color-accent-bg-subtlergba(255, 190, 11, 0.05)rgba(255, 190, 11, 0.12)Hover-bakgrunn
--color-focus-halorgba(255, 190, 11, 0.18)rgba(255, 190, 11, 0.45)Glød rundt fokusert felt
--color-overlayrgba(0, 0, 0, 0.60)rgba(28, 23, 18, 0.45)Bakgrunn bak dialog

Typografi

Fonter: Manrope til all tekst. Fraunces (.display) bare der skriften er en del av uttrykket, som i en hero. På forsider og markedsføring gir class="editorial" på body eller main Fraunces også på .h1–.h3 og tall i kort (se under); apper utelater den. Selvhostet woff2 (ingen Google Fonts). Skala: --text-xs 0.8125 · sm 0.875 · base 1 · lg 1.125 · xl 1.35 · 2xl 1.5 · 3xl clamp(2, 5vw, 3) rem · --text-display clamp(2.5, 7vw, 4.5) rem. Linjehøyde --leading-tight 1.2, --leading-normal 1.6.
Display, kun der skriften er uttrykket
h1 Sidetittel
h2 Seksjonsoverskrift
h3 Underseksjon
h4 Gruppenavn
h2 i redaksjonell modus
h3 i redaksjonell modus
1 234 567,89 kr nøkkeltall: Manrope 2xl med tabular-nums
LabelSeksjonsetikettxs / 600 uppercase
BodyBrødtekst skal være lett å lese, med god linjehøyde og kontrast.base / 400
StøtteStøttetekst og metadata, 12. august 2025sm / 400
CaptionBildetekst eller kildexs / 400

Grunnlag

Avstand

--space-14px
--space-28px
--space-312px
--space-416px
--space-524px
--space-632px
--space-748px

Radius

sm4px
md6px knapper
lg8px inputs
xl10px kort

Kanter

Kanter som markerer et UI-element (input, ghost-knapp, drop-sone) har minst 3:1 (WCAG 1.4.11). Dekorative kanter er unntatt.

borderkort
border-subtleskiller
border-controlinputs · 3:1
accent-borderghost, drop-sone · 3:1

Dybde

Flater skilles med --color-surface og kant. Skygge kun på flytende elementer. Native <dialog> og popover ligger i nettleserens top layer, så det trengs ingen z-index-skala.

shadow-mdDropdown, popover
shadow-lgDialog

Bevegelse

Farge og kant--t-fast: 0.15s ease
Løft--t-lift: 0.1s ease · translateY(-1px)

Med prefers-reduced-motion: reduce slås alle overganger av.

Fav-ikon

Fav-ikonet er det lille bildet i nettleserfanen, bokmerker og historikk. Her er det bare prikken, fordi hele logoen blir uleselig i 16 px. Tre filer dekker alle enheter. Hver app må lenke til dem i <head>, ellers vises ingen ikon. Denne siden bruker de samme filene, så du ser dem i fanen din nå.

Tidya designspråk
Tidya designspråk
favicon.svgFanen i moderne nettlesere. Skarp i alle størrelser.
favicon.icoFallback for eldre nettlesere og verktøy (16, 32 og 48 px).
apple-touch-icon.pngNår noen legger siden på hjemmeskjermen på iPhone (180 × 180 px, mørk bakgrunn, iOS runder hjørnene).
<link rel="icon" href="brand/favicon.ico" sizes="32x32">
<link rel="icon" href="brand/favicon.svg" type="image/svg+xml">
<link rel="apple-touch-icon" href="brand/apple-touch-icon.png">

Knapper og tilstander

Primary
Ghost
Liten og pilKom i gang
Danger
HvileHoverFokusDeaktivert Primary
Ghost
Fokus: :focus-visible. Knapper og lenker: outline: 2px solid var(--color-accent-border), offset 2px. Felt og ghost-knapp: kanten blir --color-accent pluss en 2px glød (--color-focus-halo), uten outline. Feilfelt: samme i --color-error.

Skjema

Hvert felt har synlig label, valgfri hint og feilmelding knyttet med aria-describedby. Feil settes med aria-invalid="true", ikke en egen klasse.

Vises i oversikten.

Ugyldig e-postadresse.

Varsler
HvileFokusFeilDeaktivert Input
Ugyldig verdi
<div class="field">
	<label class="label" for="navn">Navn</label>
	<input class="input" id="navn" name="navn" type="text" aria-invalid="true" aria-describedby="navn-feil">
	<p class="field-error" id="navn-feil">Navn må fylles ut.</p>
</div>

Tabell

Tall høyrejustert med tabular-nums (klassen num). Sorterbare kolonner er vanlige lenker i th med aria-sort. Tom tilstand bruker table-empty. Rammen table-wrap scroller sidelengs på smal skjerm, så den har tabindex="0", role="region" og aria-label (tastaturtilgang). Navnet må være unikt på siden, ikke det samme som seksjonens overskrift.

Prosjekter
NavnStatusBeløp
Prosjekt AAktiv1 234,50 kr
Prosjekt BPauset98 000,00 kr
Prosjekt CAvsluttet−12 500,00 kr
Tom tabell
Ingen prosjekter ennå. Legg til det første.
<div class="table-wrap" tabindex="0" role="region" aria-label="Prosjekter">
	<table class="table">
		<caption class="sr-only">Prosjekter</caption>
		<thead><tr>
			<th scope="col" aria-sort="ascending"><a href="?sort=navn&dir=desc">Navn</a></th>
			<th scope="col" class="num">Beløp</th>
		</tr></thead>
		<tbody>
			<tr><td>Prosjekt A</td><td class="num">1 234,50 kr</td></tr>
		</tbody>
	</table>
</div>

Kort, meldinger og dialog

Korttittel

Kort grupperer relatert innhold på en flate (--color-surface) med svak kant og 10px radius.

Kortvarianter

Grunnkortet card er flatt og brukes også i skjema. card-accent gir en amber tone og er det som gir variasjon i en gruppe. card-link (på <a>) løfter seg og får skygge, og bare det kortet beveger seg ved hover. card-stat er et nøkkeltall, card-wave legger en bølge nederst, og card-feature er romsligere med card-icon. Overskrifter i kortene er .h3, så editorial gir dem Fraunces.

Fremhevet kort

Amber tone gir variasjon. Bruk det på ett kort i en gruppe.

Vanlig kort

Flatt og rolig. Samme kort brukes i skjema.

96%

av totalen

Et tall med enhet og en kort forklaring.

I en section-alt

Flaten bak er --color-surface, og kortene snur til --color-bg.

Alternerer

Ingen ekstra klasser på kortene.

<div class="card-grid">
	<div class="card card-accent card-feature" data-reveal data-tilt>
		<div class="card-glow"></div>
		<div class="card-icon"><svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M2 12h4l3-9 4 18 3-9h6"/></svg></div>
		<h3 class="h3">Fremhevet kort</h3>
		<p class="card-body">Amber tone gir variasjon. Bruk det på ett kort i en gruppe.</p>
	</div>
	<div class="card card-feature" data-reveal data-tilt>
		<div class="card-glow"></div>
		<div class="card-icon"><svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><circle cx="12" cy="12" r="10"/><path d="M12 8v4l2.5 2.5"/></svg></div>
		<h3 class="h3">Vanlig kort</h3>
		<p class="card-body">Flatt og rolig. Samme kort brukes i skjema.</p>
	</div>
	<div class="card card-stat card-lift" data-reveal>
		<p class="card-stat-number"><span data-count="96">96</span><span class="card-stat-unit">%</span></p>
		<p class="h3">av totalen</p>
		<p class="card-body">Et tall med enhet og en kort forklaring.</p>
	</div>
</div>
<div class="card-grid">
	<a class="card card-accent card-link card-wave" href="#kort">
		<span class="card-tag">Kategori</span>
		<h3 class="h3">Lenkekort med bølge</h3>
		<p class="card-body">Hele kortet er en lenke. Det løfter seg ved hover, og pilen glir.</p>
		<span class="card-more">Les mer <span class="card-arrow" aria-hidden="true">→</span></span>
	</a>
	<a class="card card-link card-wave" href="#kort">
		<span class="card-tag">For kunder</span>
		<h3 class="h3">Samme uten tone</h3>
		<p class="card-body">Uten <code>card-accent</code>. Alterneringen mellom to kort ligger i den ene klassen.</p>
		<span class="card-more">Les mer <span class="card-arrow" aria-hidden="true">→</span></span>
	</a>
</div>
<!-- section-alt gir flate bak kortene, som snus til --color-bg automatisk -->
<section class="section-alt"><div class="card-grid">…</div></section>
<!-- redaksjonell modus: Fraunces på .h1–.h3, .display og tall i kort -->
<main class="editorial">…</main>

Sitatblokk for å løfte frem en viktig setning.

— Kilde

Status vises med ikon og tekst, ikke farge alene. Tittel er --color-text, brødtekst --color-text-secondary. Fargen ligger på kant og ikon.

Informasjon

Nøytral beskjed som ikke krever handling.

Lagret

Endringene er lagret.

Vær oppmerksom

Noe bør sjekkes før du går videre.

Noe gikk galt

Handlingen kunne ikke fullføres. Prøv igjen.

Dialog

Native <dialog> med showModal(): fokusfelle, Escape og bakgrunn uten JS-bibliotek. autofocus på den trygge knappen. Escape lukker, og fokus går tilbake til knappen som åpnet den.

Slett Prosjekt A?

Dette kan ikke angres.

<button class="btn-ghost" type="button" onclick="document.getElementById('slett').showModal()">Slett kunde…</button>

<dialog class="dialog" id="slett" aria-labelledby="slett-tittel" aria-describedby="slett-tekst">
	<h2 class="dialog-title" id="slett-tittel">Slett Prosjekt A?</h2>
	<p class="dialog-body" id="slett-tekst">Dette kan ikke angres.</p>
	<form class="dialog-actions" method="dialog">
		<button class="btn-ghost" value="avbryt" autofocus>Avbryt</button>
		<button class="btn-danger" value="slett">Slett</button>
	</form>
</dialog>

Skjema i dialog

Feltene ligger i et form med stack (jevn avstand), dialog-actions nederst og autofocus på første felt. Avbryt har formnovalidate, så den alltid lukker.

Ny kunde

<dialog class="dialog" id="ny-kunde" aria-labelledby="ny-kunde-tittel">
	<h2 class="dialog-title" id="ny-kunde-tittel">Ny kunde</h2>
	<form class="stack" method="dialog">
		<div class="field">
			<label class="label" for="navn">Navn</label>
			<input class="input" id="navn" name="navn" type="text" autofocus>
		</div>
		<div class="dialog-actions">
			<button class="btn-ghost" value="avbryt" formnovalidate>Avbryt</button>
			<button class="btn-primary" value="lagre">Legg til kunde</button>
		</div>
	</form>
</dialog>

htmx-mønstre

Komponentene er ren CSS og fungerer rett i server-rendret HTML. Mønstrene holder innholdet tilgjengelig når det byttes ut.

Validering og lasting

<form hx-post="/kunder" hx-target="this" hx-swap="outerHTML">
	<!-- Ved feil returnerer serveren hele skjemaet på nytt: aria-invalid, feilmelding
	     (aria-describedby) og autofocus på første ugyldige felt. -->
	<button class="btn-primary" type="submit">Lagre <span class="spinner htmx-indicator" aria-hidden="true"></span></button>
</form>

Beskjeder

<!-- Layout: regionen finnes alltid, med role og aria-live -->
<div id="status" role="status" aria-live="polite"></div>

<!-- Serversvar: bytt INNHOLDET, ikke elementet (ellers mistes role/aria-live) -->
<div hx-swap-oob="innerHTML:#status">
	<div class="alert success"><span class="alert-icon" aria-hidden="true">✓</span><div><p class="alert-title">Lagret</p></div></div>
</div>

Sletting

<!-- I layouten: overskrift som kan få fokus, og plass til dialogen -->
<h1 class="h2" id="tittel" tabindex="-1">Kunder</h1>
<div id="dialog-plass"></div>

<!-- I raden -->
<button class="btn-danger-link" hx-get="/kunder/7/bekreft" hx-target="#dialog-plass" aria-label="Slett Prosjekt A">Slett</button>

<!-- Svaret fra /kunder/7/bekreft -->
<dialog class="dialog" aria-labelledby="t" hx-on::load="this.showModal()" hx-on:close="this.remove()">
	<h2 class="dialog-title" id="t">Slett Prosjekt A?</h2>
	<form class="dialog-actions" method="dialog">
		<button class="btn-ghost" value="avbryt" autofocus>Avbryt</button>
		<button class="btn-danger" type="button" hx-delete="/kunder/7" hx-target="#rad-7" hx-swap="outerHTML"
		        hx-on::after-request="this.closest('dialog').close(); document.getElementById('tittel').focus()">Slett</button>
	</form>
</dialog>

Språk og formater

UI-tekst er bokmål, kort og konkret, og tiltaler brukeren med «du». Bruk alltid Intl med nb-NO, aldri håndformatering. Mellomrom i tall og før enheter er hardt mellomrom (U+00A0).

HvaRiktigUnngå
Dato16.10.202610/16/2026 · 2026-10-16 (i UI)
Lang dato16. oktober 2026Oct 16, 2026
Klokkeslett14:302:30 PM
Tall1 234 567,891,234,567.89
Beløp1 234,50 krkr1234.50 · $1,234.50
Negativt beløp−1 234,50 kr-1234,50
Prosent12,5 %12.5%
SorteringA, Z, Æ, Ø, ÅStandard JS-sortering
KnapperLagre · Legg til kunde · Slett utkastOK · Submit · Legg Til Kunde
FeilmeldingKunne ikke lagre. Prøv igjen.Feil! · Ugyldig input
Tom tilstandIngen kunder ennå. Legg til den første.Ingen data
Store bokstaver bare i første ord og egennavn. Verb i imperativ på knapper. Aldri æ/ø/å som ae/oe/aa i UI-tekst. <html lang="nb">.

Forsider og markedsføring

For forsider og markedsføring: sett editorial på main (Fraunces på .h1–.h3 og tall i kort) og bygg med container (innholdsbredde 1160 px, endres med --site-width), section-pad (luft mellom seksjoner), kicker (etikett over overskriften) og lead (ingress). site-footer er bunnlinjen, og grain er den valgfrie teksturen øverst på denne siden (en ekte div, aldri body::after; se iOS-regelen i DESIGN.md).

Etikett

En kort og tydelig overskrift over to linjer

Ingressen forklarer overskriften i en til to setninger.

<main class="editorial">
	<section class="section-pad">
		<div class="container">
			<p class="kicker">Etikett</p>
			<h2 class="h2">En kort og tydelig overskrift over to&nbsp;linjer</h2>
			<p class="lead">Ingress.</p>
		</div>
	</section>
</main>
<footer class="site-footer"><div class="container site-footer-inner">…</div></footer>