# Kilden Designsystem

> Et designsystem bygget med React, Tailwind CSS og semantiske design tokens.

---

# Introduksjon

> Kilden er Bekks designsystem — et komponentbibliotek med React-komponenter, design tokens og Tailwind CSS-støtte.

Kilden er Bekks designsystem. Det gir deg ferdigbygde React-komponenter, design tokens og verktøy for å bygge tilgjengelige og konsistente brukergrensesnitt — raskt.

## Hva er inkludert?

- **45+ React-komponenter** — Knapper, skjemaelementer, dialoger, navigasjon og mer. Bygget på [Radix UI](https://www.radix-ui.com/) for tilgjengelighet og [CVA](https://cva.style/) for type-sikre varianter.
- **Design tokens** — Farger, typografi, spacing, avrunding og skygger som CSS custom properties og Tailwind-klasser.
- **4 fargetemaer** — Ild, Luft, Vann og Jord — med innebygd støtte for lys og mørk modus.
- **TypeScript-støtte** — Fullstendig typet API med autocompletions for alle props og varianter.

## Designprinsipper

- **Tilgjengelig** — Alle komponenter følger WAI-ARIA-retningslinjer, bygger på Radix UI og testes automatisk med axe. Målet er å oppfylle WCAG 2.1 AA.
- **Fleksibel** — Komponenter kan tilpasses med varianter, Tailwind-klasser og `asChild`-mønsteret for full kontroll over HTML-output.
- **Konsistent** — Design tokens sikrer at farger, spacing og typografi er konsistente på tvers av alle løsninger.
- **Enkel å ta i bruk** — Installer pakken, importer Kilden i Tailwind-CSS-en din, og du er i gang.

## Pakker

Kilden består av to npm-pakker:

| Pakke                   | Beskrivelse                                           |
| ----------------------- | ----------------------------------------------------- |
| `@kilden/designsystem`  | React-komponenter og Tailwind CSS v4-oppsett          |
| `@kilden/design-tokens` | Design tokens som CSS custom properties og TypeScript |

Begge pakkene er tilgjengelige på npm og kan installeres med din foretrukne pakkebehandler.

## Kom i gang

Klar til å sette opp Kilden i prosjektet ditt? Gå til [installasjonsguiden](/docs/getting-started/installation).

---

# Installasjon

> Slik setter du opp Kilden i prosjektet ditt — med Next.js eller React Router

Denne guiden viser deg hvordan du setter opp Kilden i et nytt eller eksisterende prosjekt. Velg rammeverket du bruker for å se riktige instruksjoner.

## Krav

Kilden krever **React 19** og **Tailwind CSS v4**. Komponentene leveres som Tailwind-klasser, og appens egen Tailwind genererer kun CSS for klassene som faktisk brukes — både dine og designsystemets.

## 1. Installer pakkene

```bash
npm install @kilden/designsystem
```

Har du ikke Tailwind CSS v4 fra før, følg [Tailwinds installasjonsguide](https://tailwindcss.com/docs/installation) for rammeverket ditt først.

## 2. Sett opp CSS og tema

Importer Kilden i CSS-filen der du importerer Tailwind — **etter** `@import "tailwindcss"`. `styles.css` gir deg design tokens som CSS custom properties, Tailwind-tema (farger, fonter, tekststørrelser, skygger og bevegelse) og animasjoner, og sørger for at Tailwind finner klassene komponentene bruker. Fontene importeres separat, slik at hver merkevare kan levere sine egne.

```css
/* app.css */
@import "tailwindcss";
@import "@kilden/designsystem/styles.css";
@import "@kilden/designsystem/fonts/bekk.css";
```

  Ikke importer CSS-en fra JavaScript

`@kilden/designsystem/styles.css` må importeres fra en CSS-fil som behandles
av Tailwind. Importeres den fra `layout.tsx` eller `root.tsx`, genereres
ikke komponentenes klasser.

Temaet velges med data-attributter på `<html>`-elementet:

| Attributt           | Verdier                                           | Styrer               |
| ------------------- | ------------------------------------------------- | -------------------- |
| `data-brand`        | `bekk`                                            | Merkevaren           |
| `data-theme`        | `bekk-ild`, `bekk-luft`, `bekk-vann`, `bekk-jord` | Fargeprofilen        |
| `data-color-scheme` | `light`, `dark` (utelatt: følger systemet)        | Lys eller mørk modus |
| `data-size`         | `desktop`, `mobile` (utelatt: `desktop`)          | Størrelsesskalaen    |

Uten attributter får siden `bekk` med `bekk-ild`, som følger systeminnstillingen for lys og mørk modus.

Next.js (App Router)
React Router v7

Importer CSS-filen din (med Tailwind og Kilden) i rot-layouten, og sett temaet på `<html>`:

```tsx
// app/layout.tsx
import "./globals.css";

export default function RootLayout({
  children,
}: {
  children: React.ReactNode;
}) {
  return (
    <html lang="no" data-brand="bekk" data-theme="bekk-ild">
      <body>{children}</body>
    </html>
  );
}
```

Importer CSS-filen din (med Tailwind og Kilden) i rot-modulen, og sett temaet på `<html>`:

```tsx
// app/root.tsx
import {
  Links,
  Meta,
  Outlet,
  Scripts,
  ScrollRestoration,
} from "react-router";
import "./app.css";

export function Layout({
  children,
}: {
  children: React.ReactNode;
}) {
  return (
    <html lang="no" data-brand="bekk" data-theme="bekk-ild">
      <head>
        <meta charSet="utf-8" />
        <meta
          name="viewport"
          content="width=device-width, initial-scale=1"
        />
        <Meta />
        <Links />
      </head>
      <body>
        {children}
        <ScrollRestoration />
        <Scripts />
      </body>
    </html>
  );
}

export default function App() {
  return <Outlet />;
}
```

## 3. Bruk en komponent

Nå er du klar til å bruke Kilden-komponenter i prosjektet ditt:

```tsx
import { Button } from "@kilden/designsystem";

export function MyComponent() {
  return (
    <div style={{ padding: "2rem" }}>
      <h1>Hei, Kilden!</h1>
      <Button>Klikk meg</Button>
      <Button variant="secondary">Sekundær</Button>
      <Button variant="secondary">Sekundær</Button>
    </div>
  );
}
```

## Bruk Kilden-fargene i egne klasser

Alle semantiske farger fra design tokens er tilgjengelige som Tailwind-klasser, på samme måte som komponentene bruker dem:

```tsx
<div className="bg-neutral-background-default text-neutral-text-default">
  <p className="text-neutral-text-subtle">Dempet tekst</p>
  <div className="border border-neutral-border-subtle bg-accent-surface-tinted" />
</div>
```

Kildens tekststørrelser (`text-caption`, `text-small`, `text-body`, `text-title`, `text-heading-sm`/`-md`/`-lg`, `text-leading`, `text-action`), fonter (`font-sans`, `font-display`, `font-action`), skygger (`shadow-subtle`, `shadow-floating`, `shadow-overlay`) og bevegelse (`duration-fast`/`-default`/`-slow`, `ease-standard`/`-enter`/`-exit`) er også tilgjengelige. Tailwinds `p-4`, `rounded-md`, `font-medium` og lignende følger merkevaren automatisk. Bruk `cn()` fra `@kilden/designsystem` når du slår sammen klasser — den kjenner Kildens egne verdier.

## Hold deg til token-klassene

Tailwinds egne skalaer (`text-sm`, `shadow-lg`, `bg-blue-500` osv.) virker fortsatt, men følger ikke merkevaren eller temaet. To verktøy hjelper deg og AI-agenter å unngå dem.

**`strict.css`** fjerner Tailwinds standardskalaer for farger, fonter, tekststørrelser, radius, skygger og easing. Klasser som ikke er koblet til tokens gir da ingen CSS, så du ser det med en gang. Importer den mellom Tailwind og Kilden:

```css
@import "tailwindcss";
@import "@kilden/designsystem/strict.css";
@import "@kilden/designsystem/styles.css";
```

**`kilden-check`** følger med pakken og finner klasser som ikke følger Kilden – standardskalaer, faste verdier som `p-[12px]` og `bg-[#fff]`, rå varigheter og fontvekter som ikke lastes. For hvert funn foreslår den token-klasser som kan erstatte klassen:

```bash
npx -p @kilden/designsystem kilden-check src
```

```
src/app/page.tsx:12  text-sm
  Tailwind-standardskala som ikke følger merkevaren. Bruk en token-klasse.
  Forslag: text-action, text-body, text-caption, text-small …
```

Med pnpm kan du også kjøre `pnpm exec kilden-check src`. Uten argumenter sjekkes `src/` og `app/`. Kommandoen avslutter med kode 1 når den finner noe, så den kan kjøres i CI. Med `--json` får du funnene som JSON.

## Mørk modus

Fargene følger systeminnstillingen for lys og mørk modus. Med `data-color-scheme` kan du tvinge en bestemt modus:

| `data-color-scheme` | Beskrivelse              |
| ------------------- | ------------------------ |
| _(utelatt)_         | Følger systeminnstilling |
| `light`             | Alltid lys modus         |
| `dark`              | Alltid mørk modus        |

Attributtet kan også settes på et element lenger ned i treet, for eksempel for å vise en mørk seksjon på en lys side.

For å la brukeren bytte modus dynamisk kan du oppdatere attributtet med JavaScript, eller bruke `applyThemeToDocument(theme, colorMode)` fra `@kilden/designsystem`:

```ts
const html = document.documentElement;

// Bytt til mørk modus
html.setAttribute("data-color-scheme", "dark");

// Bytt til lys modus
html.setAttribute("data-color-scheme", "light");

// Følg systeminnstilling
html.removeAttribute("data-color-scheme");
```

  Tips

Hvis du vil unngå et "flash" av feil tema ved sidelasting, kan du sette
attributtene med et inline script i `<head>` — før resten av siden rendres.

## Neste steg

Nå som du er i gang, kan du utforske videre:

- [Se alle komponenter](/docs/components/button) — Start med Button og jobb deg videre
- [Design tokens](/docs/tokens/colors) — Lær om farger, typografi og spacing
- [Bruk med AI](/docs/getting-started/bruk-med-ai) — Gi AI-agenten din kunnskap om Kilden

---

# Bruk med AI

> Slik bruker du Kilden med AI-agenter som Claude Code, Cursor, Codex og GitHub Copilot.

Kilden leveres med en **agent skill**: en guide som lærer AI-agenter å bruke designsystemet riktig. Med den på plass vet agenten hvilke komponenter som finnes, hvilke token-klasser den skal bruke og hvilke tilgjengelighetskrav som gjelder. Den vet også at den skal sjekke koden sin etterpå.

## Hva er en agent skill?

En agent skill er en mappe med en `SKILL.md`-fil og referansefiler. Agenten leser beskrivelsen i `SKILL.md` og henter inn skillen av seg selv når du jobber med UI-kode. Referansefilene leses bare ved behov, så skillen bruker lite av kontekstvinduet.

Kildens skill består av:

- **`SKILL.md`**: oppsett, regler, token-klasser, vanlige mønstre og en oversikt over alle komponenter
- **`references/`**: én fil per komponent, token-kategori, eksempel og guide, generert fra denne dokumentasjonen. Med i oversikten er også `token-klasser.md`, som lister alle Tailwind-klassene som er koblet til design tokens

Skillen oppdateres sammen med dokumentasjonen. Installerer du den som plugin eller med `npx skills`, får du versjonen fra `main`. Versjonen i npm-pakken samsvarer med komponentene du har installert.

## Installer skillen

### Claude Code

Legg til Bekks plugin-marketplace og installer `kilden`-pluginen:

```bash
/plugin install kilden --marketplace bekk/designsystem
```

På eldre versjoner av Claude Code gjøres det i to steg:

```bash
/plugin marketplace add bekk/designsystem
/plugin install kilden@bekk
```

Claude Code bruker skillen automatisk når det er relevant. Du kan også be om den eksplisitt med `/kilden:kilden`.

### Cursor, Codex, GitHub Copilot og andre

Bruk [`skills`](https://skills.sh) til å installere skillen for agenten din. Kommandoen finner agentene du bruker, og legger skillen der de leter (`.claude/skills/`, `.cursor/skills/`, `.agents/skills/` osv.):

```bash
npx skills add bekk/designsystem
```

### Fra npm-pakken

Skillen følger også med `@kilden/designsystem`, i `node_modules/@kilden/designsystem/skills/kilden/`. Da er den alltid i samme versjon som pakken du har installert. Lenk den inn der agenten din leter etter skills:

```bash
mkdir -p .claude/skills
ln -s ../../node_modules/@kilden/designsystem/skills/kilden .claude/skills/kilden
```

## La agenten sjekke sin egen kode

Agenter skriver gjerne `text-sm`, `shadow-lg` og `bg-blue-500` av gammel vane. Skillen ber agenten kjøre `kilden-check` etter at den har skrevet UI-kode:

```bash
npx -p @kilden/designsystem kilden-check src
```

Verktøyet følger med `@kilden/designsystem`. Det finner klasser som ikke følger Kilden og foreslår token-klasser som kan erstatte dem, slik at agenten kan rette opp selv. Les mer under [Hold deg til token-klassene](/docs/getting-started/installation#hold-deg-til-token-klassene).

  Tips

Importer `@kilden/designsystem/strict.css` i nye prosjekter. Da gir
Tailwinds standardskalaer ingen CSS, og feil blir synlige med en gang, også
når agenten ikke har kjørt `kilden-check`.

## Maskinlesbar dokumentasjon

All dokumentasjon er også tilgjengelig som markdown, for agenter som henter dokumentasjon fra nettet:

- **[`/llms.txt`](/llms.txt)**: oversikt over all dokumentasjon med lenker
- **[`/llms-full.txt`](/llms-full.txt)**: all dokumentasjon i én fil
- **`/docs/:seksjon/:side.md`**: hver side som markdown. Legg til `.md` på slutten av URL-en, for eksempel [`/docs/components/button.md`](/docs/components/button.md)

## Eksempel

Når skillen er installert, kan du be agenten om å bygge et grensesnitt:

> Lag et kontaktskjema med navn, e-post og melding.

Agenten bruker `Input`, `Textarea`, `FormField` og `Button` med labels, feilmeldinger og token-klasser, og kjører `kilden-check` før den sier seg ferdig.

---

# Lag ditt eget tema

> Slik lager du et helt nytt visuelt uttrykk for Kilden – farger, typografi, form, tetthet, skygger og bevegelse – uten å endre komponentene.

Alt det visuelle i Kilden styres av design tokens: farger, statusfarger, typografi, form, tetthet, skygger og bevegelse. Komponentene bruker bare klasser som er koblet til tokenene. Et nytt tema gir derfor et nytt uttrykk uten at komponentene må endres.

Vil du se eksempler, kan du velge en av demo-merkevarene **Basic**, **Geeky** eller **Goofy** under «Tema» i menyen til venstre. De finnes bare i dokumentasjonen og publiseres ikke med pakken.

## Merkevare og variant

Et tema består av to deler:

| Attributt    | Eksempel        | Styrer                                                                  |
| ------------ | --------------- | ----------------------------------------------------------------------- |
| `data-brand` | `acme`          | Typografi, form, tetthet, skygger, bevegelse og statusfarger            |
| `data-theme` | `acme-standard` | Dominant-, aksent- og nøytralfargene. Navnet starter med merkevarens id |

En merkevare kan ha flere varianter (Bekk har `bekk-ild`, `bekk-luft`, `bekk-vann` og `bekk-jord`). Lys og mørk modus kommer automatisk, og styres med `data-color-scheme`.

```html
<html data-brand="acme" data-theme="acme-standard"></html>
```

Det holder å sette ett av attributtene:

- `data-brand` alene gir merkevarens første variant.
- `data-theme` alene gir også merkevarens typografi, form og så videre, fordi varianten alltid hører til én merkevare.

Begge kan settes på et element lenger ned i treet, for eksempel for å vise en annen merkevare i en forhåndsvisning.

## Alternativ 1: Temabyggeren

[Temabyggeren](/tema-bygger) lager et komplett tema som CSS. Velg farger, fonter, radius, tetthet, skygger og bevegelse, og trykk **Eksporter**. Temabyggeren varsler hvis fargene gir for lav kontrast.

Legg den eksporterte CSS-en etter Kilden, **utenfor** `@layer` (eller i et lag som kommer etter `kilden.design-tokens`):

```css
@import "tailwindcss";
@import "@kilden/designsystem/styles.css";
@import "./acme-fonter.css";
@import "./acme-tema.css"; /* fra temabyggeren */
```

## Alternativ 2: Skriv temaet selv

Alle variablene et tema kan sette står i token-kontrakten, `@kilden/design-tokens/contract.json`:

```json
{
  "name": "--radius-md",
  "type": "borderRadius",
  "dimensions": ["brand"],
  "colorScheme": false,
  "source": "brand.border-radius.md",
  "default": "4px"
}
```

- `dimensions` sier om variabelen hører til merkevaren (`brand`), varianten (`theme`) eller størrelsen (`size`). Et tema setter variablene for `brand` og `theme`.
- `colorScheme: true` betyr at verdien er ulik i lys og mørk modus. Bruk `light-dark(lys, mørk)`.
- `source` er tokenet verdien kommer fra. Variabler med samme kilde bør ha samme verdi.
- `default` er verdien Bekk-temaet bruker.

## Alternativ 3: I designsystemet

Temaer som skal ligge i Kilden, lages i Tokens Studio og sjekkes inn i `packages/design-tokens/src`:

1. Lag merkevaresettet `brands/<id>.json` med de samme tokenene som `brands/bekk.json`.
2. Lag variantsettet `themes/<id>-<variant>.json`, og eventuelle nye primitive farger.
3. Legg temaene til i gruppene `brand` og `variant` i Tokens Studio.

Testene sjekker at alle merkevarer har de samme tokenene, at den ferdige CSS-en stemmer med tokenene, og at tekst og kantlinjer har nok kontrast etter WCAG.

## Ting å vite

- **Fonter:** Kilden laster bare Bekks fonter (`@kilden/designsystem/fonts/bekk.css`). Andre merkevarer må deklarere sine egne `@font-face`. Komponentene bruker vektene light, regular og medium.
- **Størrelse:** Tokens som avhenger av både merkevare og `data-size`, krever at begge attributtene står på samme element (i praksis `<html>`).
- **Tetthet:** Tailwinds `p-4`, `h-10` og lignende er multipler av `--spacing-unit`. Det gjelder også appens egne klasser.
- **Logo:** `Logo`-komponenten er Bekk-spesifikk. Bruk din egen logo for andre merkevarer.

---

# Migreringsguide

> Slik oppgraderer du @kilden/designsystem og @kilden/design-tokens mellom versjoner med brytende endringer.

## Fra 0.2 til 0.3

Versjon 0.3 oppdaterer avhengighetene. Den eneste brytende endringen gjelder `Calendar`.

### Bytt utgåtte props på `Calendar`

`Calendar` bruker nå `react-day-picker` 10, som har fjernet props som var markert som utgått. TypeScript sier fra hvis du bruker noen av dem:

| Før                   | Nå                                                           |
| --------------------- | ------------------------------------------------------------ |
| `fromMonth`/`toMonth` | `startMonth`/`endMonth`                                      |
| `fromYear`/`toYear`   | `startMonth={new Date(år, 0)}`/`endMonth={new Date(år, 11)}` |
| `fromDate`/`toDate`   | `hidden={{ before: … }}`/`hidden={{ after: … }}`             |
| `initialFocus`        | `autoFocus`                                                  |

## Fra 0.1 til 0.2

Versjon 0.2 gjør det mulig å endre hele uttrykket med et tema – farger, typografi, form, tetthet, skygger og bevegelse. Temaet er delt i merkevare og fargevariant, og lys/mørk modus har fått sitt eget attributt.

Ingenting krasjer hvis du ikke migrerer, men flere av endringene gir **stille visuelle feil**. Gå gjennom stegene under.

### Nye krav

Fargene bruker CSS-funksjonen [`light-dark()`](https://developer.mozilla.org/en-US/docs/Web/CSS/color_value/light-dark). Den krever **Chrome/Edge 123, Safari 17.5 eller Firefox 120** (fra mai 2024) eller nyere. I eldre nettlesere vises ingen farger fra designsystemet.

### 1. Importer fontene

Fontene ligger ikke lenger i `styles.css`, slik at hver merkevare kan levere sine egne. Uten importen faller all tekst tilbake til systemfonten.

```css
@import "tailwindcss";
@import "@kilden/designsystem/styles.css";
@import "@kilden/designsystem/fonts/bekk.css"; /* ny */
```

### 2. Bytt tema-attributtene

`data-theme` har fått merkevaren som prefiks, og lys/mørk settes med `data-color-scheme`:

| Før                       | Nå                                                                   |
| ------------------------- | -------------------------------------------------------------------- |
| `data-theme="ild"`        | `data-brand="bekk" data-theme="bekk-ild"`                            |
| `data-theme="vann"`       | `data-brand="bekk" data-theme="bekk-vann"`                           |
| `data-theme="jord"`       | `data-brand="bekk" data-theme="bekk-jord"`                           |
| `data-theme="luft"`       | `data-brand="bekk" data-theme="bekk-luft"`                           |
| `data-theme="vann-light"` | `data-brand="bekk" data-theme="bekk-vann" data-color-scheme="light"` |
| `data-theme="vann-dark"`  | `data-brand="bekk" data-theme="bekk-vann" data-color-scheme="dark"`  |

De gamle verdiene treffer ingen regler lenger. Uten migrering får siden standardtemaet `bekk-ild`, og Vann, Jord og Luft blir stille til Ild. `-light` og `-dark` ignoreres, så fargene følger operativsystemet.

Bruker du `ThemeSwitcher` eller `applyThemeToDocument()`, settes de nye attributtene automatisk. Husk også attributtene du setter i server-rendret HTML og i inline-skript i `<head>`.

### 3. Sjekk egen CSS og egne temaer

- **Selektorer på gamle attributter**, som `[data-theme="ild-dark"] .noe`, treffer ikke lenger. Bruk `[data-theme="bekk-ild"]` og `[data-color-scheme="dark"]`, eller enda bedre: CSS-variablene, som allerede følger temaet.
- **`font-expanded` heter nå `font-action`.** Klassen `font-expanded` finnes ikke lenger, og gir vanlig font.
- **Temaer fra den gamle temabyggeren** (`:root { … }` og `.dark { … }`) virker i lys modus, men mørk modus virker bare hvis appen selv setter `.dark`. Lag temaet på nytt i [temabyggeren](/tema-bygger), som nå eksporterer et komplett tema for `data-brand`. Se [Lag ditt eget tema](/docs/getting-started/egne-temaer).

### 4. Sjekk visuelle endringer

- **`Ingress` og `ActionText` får riktig størrelse** (32 og 14 px på desktop, 24 og 12 px på mobil) også uten `data-size`. Tidligere var størrelses-tokenene udefinerte uten `data-size`, så tekstene arvet størrelsen fra omgivelsene og ble ofte for små. Uten `data-size` brukes nå desktop-størrelsene.
- **`Calendar`** har 12 px tekst i stedet for 12,8 px.
- `text-body`, `text-leading` og `text-action` setter nå linjehøyde 1,5. Det er det samme som de arvet fra før i de fleste tilfeller.

Alle andre komponenter ser ut som før.

### Dette trenger du ikke gjøre noe med

- **CSS-variablene** har samme navn og verdi som før. Det er bare kommet nye i tillegg (se `@kilden/design-tokens/contract.json`).
- **Tailwind-klassene i appen din** (`p-4`, `rounded-md`, `font-medium`, `text-sm`, `shadow-lg` …) gir samme resultat for Bekk. `p-4`, `rounded-*` og `font-*` følger nå merkevarens tokens.
- **Komponent-API-ene** er uendret.

### Nytt i denne versjonen

- Egne merkevarer med `data-brand`: se [Lag ditt eget tema](/docs/getting-started/egne-temaer).
- Token-klasser for typografi, skygger og bevegelse: `text-caption`, `text-small`, `text-title`, `text-heading-sm`/`-md`/`-lg`, `font-display`, `font-action`, `shadow-subtle`, `shadow-floating`, `shadow-overlay`, `duration-fast`/`-default`/`-slow`/`-slower` og `ease-standard`/`-enter`/`-exit`. Bruk dem i stedet for Tailwinds standardskala når noe skal følge merkevaren.
- `data-theme` alene gir hele merkevaren, og attributtene kan settes på elementer lenger ned i treet.

## Fra 0.0.x til 0.1.0

Denne guiden gjelder deg som oppgraderer fra `@kilden/designsystem` **0.0.x til 0.1.0**. Versjon 0.1.0 gjør designsystemet klart for produksjon, og inneholder noen endringer som krever at du gjør små justeringer i prosjektet ditt.

Se [endringsloggen](/docs/getting-started/endringslogg) for en fullstendig liste over endringer.

### Nye krav

| Avhengighet  | Før          | Nå             |
| ------------ | ------------ | -------------- |
| React        | 18 eller 19  | **19**         |
| Tailwind CSS | Ikke påkrevd | **v4 (≥ 4.1)** |

Har du ikke Tailwind CSS v4 fra før, følg [Tailwinds installasjonsguide](https://tailwindcss.com/docs/installation) først.

### 1. Flytt CSS-importen inn i Tailwind-CSS-en

`@kilden/designsystem/styles.css` er ikke lenger ferdig kompilert CSS, men et Tailwind v4-oppsett. Appen din genererer nå bare klassene som faktisk brukes, i stedet for å laste ca. 140 KB med alle klassene – og du slipper dobbel preflight.

**Før** – importert fra JavaScript:

```tsx
// app/layout.tsx eller app/root.tsx
import "@kilden/designsystem/styles.css";
```

**Nå** – importert fra CSS-filen der du importerer Tailwind:

```css
/* app.css / globals.css */
@import "tailwindcss";
@import "@kilden/designsystem/styles.css";
```

Fjern importen fra `layout.tsx`/`root.tsx`. Har du egne `@theme`-variabler for Kilden-fargene (f.eks. `--color-on-page`), kan du fjerne dem – alle semantiske farger, fonter og tekststørrelser er nå registrert i Tailwind-temaet.

### 2. Fjern bruk av `styles`

Eksporten `styles` (CSS-in-JS-objekter) er fjernet. Bruk Tailwind-klasser eller CSS-variablene fra design tokens i stedet:

```tsx
// Før
<button style={styles.button.primary}>Send</button>

// Nå
<Button>Send</Button>
// eller
<button className="bg-dominant-base-default text-dominant-base-contrast-default">
  Send
</button>
```

### 3. Sjekk visuelle endringer

Ingen av disse krever kodeendringer, men de kan påvirke utseendet:

- **Skjemafelt** (`Input`, `Textarea`, `Select`, `Combobox`, `DateInput`, `TimePicker`, `DatePicker`) har felles boksstil. `Input` hadde tidligere understrek.
- **`DatePicker`** fyller nå hele bredden i stedet for å være 280 px. Legg til `className="w-[280px]"` hvis du vil beholde den gamle bredden.
- **`Label`** er like stor som feltets innebygde label (`text-base`). Det gjelder også labels for `Checkbox`, `Switch` og `RadioGroup`.
- **`FormMessage`** vises med en rød prikk og nøytral tekst, som feilmeldingen i `Input`.
- **Overskrifter og titler** bruker `font-medium`, siden GT America bare lastes i vektene 300–500.
- **Fokusmarkering** er lik i alle komponenter, og fungerer i mørke temaer og høykontrastmodus.

### 4. Sjekk endret oppførsel

- **`Button` med `isLoading`** uten `loadingText` viser nå children ved siden av spinneren, slik at knappen beholder sitt tilgjengelige navn. Tidligere ble children skjult.
- **`Combobox`** nullstiller ikke lenger valgt verdi når den åpnes og lukkes uten endring.
- **`TimePicker`** setter bare `aria-label="Klokkeslett"` når feltet mangler label.
- **`AlertTitle`** rendres som `div` i stedet for `h5`. Trenger du en overskrift, bruk f.eks. ``.
- **`DatePicker`** setter ikke lenger `aria-required` på trigger-knappen (det er ikke tillatt på knapper). Påkrevd vises i labelen.
- **`ThemeSwitcher`** setter fortsatt `data-theme` på `<html>` som standard. Styrer appen temaet selv, kan du slå det av med `applyToDocument={false}`.

### 5. Valgfritt: forenkle skjemaene dine

`FormField` kobler nå `Label`, skjemaelement, `FormDescription` og `FormMessage` automatisk. Manuelle `id`/`htmlFor`/`aria-describedby` fungerer fortsatt, men kan fjernes:

```tsx
// Før
<FormField>
  <Label htmlFor="name">Navn</Label>
  <Input id="name" aria-invalid="true" aria-describedby="name-error" />
  <FormMessage id="name-error">Navn er påkrevd.</FormMessage>
</FormField>

// Nå
<FormField>
  <Label>Navn</Label>
  <Input />
  <FormMessage>Navn er påkrevd.</FormMessage>
</FormField>
```

`Textarea`, `Combobox`, `DateInput`, `TimePicker` og `DatePicker` har også fått `label`- og `error`-props på samme måte som `Input`.

### Nytt i denne versjonen

- `toast()`, `useToast()` og `` for å vise varsler uten egen tilstand. Se [Toast](/docs/components/toast).
- `useFormControl()` for å koble egne skjemaelementer til `FormField`. Se [FormField](/docs/components/form-field).
- `require("@kilden/designsystem")` fungerer på Node 20.19+ og 22.12+, og pakken kan lastes direkte av Node (f.eks. ved SSR og i Vitest).

---

# Endringslogg

> Alle endringer i @kilden/designsystem og @kilden/design-tokens, versjon for versjon.

Endringsloggen genereres automatisk fra [Changesets](https://github.com/changesets/changesets) når nye versjoner publiseres. Skal du oppgradere fra en eldre versjon, se [migreringsguiden](/docs/getting-started/migrering).

Du kan også følge med på nye versjoner via [RSS-feeden](/feed.xml).

## @kilden/designsystem

### 0.4.0

#### Minor Changes

- 3977043: Legg til `position`-prop på `Toaster` og `ToastViewport` (`"top-left" | "top-right" | "bottom-left" | "bottom-right"`). Inn- og utanimasjon og sveiperetning følger posisjonen.

  **Endret standard:** toasts vises nå øverst til høyre (`"top-right"`) i stedet for nederst til høyre fra `sm` og oppover. Sett `position="bottom-right"` for å beholde gammel plassering på desktop. På mobil er plasseringen uendret (øverst).

### 0.3.1

#### Patch Changes

- 65c5828: Legg til `warning`-variant på `Toast` og `toast()`.

### 0.3.0

#### Minor Changes

- 529ae7c: Bedre støtte for AI-agenter.

  **Brytende:** Agent-skillen er flyttet fra `skill/kilden.md` til `skills/kilden/SKILL.md` og er nå en ekte agent skill med referansefiler for hver komponent, token-kategori, eksempel og guide. Lenker du til den gamle filen, må du oppdatere stien. Den kan også installeres som Claude Code-plugin (`/plugin install kilden --marketplace bekk/designsystem`) eller med `npx skills add bekk/designsystem`.

  Nytt:
  - `kilden-check` finner Tailwind-klasser som ikke følger Kilden (standardskalaer som `text-sm` og `bg-blue-500`, faste px/rem-verdier, rå farger, varigheter og fontvekter som ikke lastes) og foreslår token-klasser. Kjør `npx -p @kilden/designsystem kilden-check src`.
  - `@kilden/designsystem/strict.css` fjerner Tailwinds standardskalaer, slik at klasser som ikke er koblet til tokens ikke gir noen CSS. Valgfri; importeres mellom `tailwindcss` og `styles.css`.
  - `@tailwindcss/node` er nå en avhengighet (brukes av `kilden-check`). De fleste Tailwind v4-oppsett har den allerede.

- 9285979: Oppdaterte avhengigheter, blant annet alle Radix-primitivene, `lucide-react` 1 og `react-day-picker` 10.

  **Brytende:** `CalendarProps` er typene til `react-day-picker` 10, som har fjernet props som var markert som utgått. Bruker du noen av disse på `Calendar`, må du bytte dem ut:
  - `fromMonth`/`toMonth` → `startMonth`/`endMonth`
  - `fromYear`/`toYear` → `startMonth={new Date(år, 0)}`/`endMonth={new Date(år, 11)}`
  - `fromDate`/`toDate` → `hidden={{ before: … }}`/`hidden={{ after: … }}`
  - `initialFocus` → `autoFocus`

  Ikonene fra `lucide-react` har nå `aria-hidden="true"` som standard.

### 0.2.0

#### Minor Changes

- 2cc7d25: **Brytende:** Fontene er flyttet ut av `styles.css` og må importeres separat, slik at hver merkevare kan levere sine egne fonter:

```css
@import "tailwindcss";
@import "@kilden/designsystem/styles.css";
@import "@kilden/designsystem/fonts/bekk.css";
```

- 686a517: Tailwinds skalaer er koblet til design-tokens, slik at merkevaren styrer typografi, form, tetthet, elevation og bevegelse – både i komponentene og i appens egne klasser.
  - `p-4`, `h-10`, `gap-2` osv. er multipler av `--spacing-unit`. `rounded-*` og `font-light`/`-normal`/`-medium` følger merkevarens tokens.
  - Nye klasser: `text-caption`, `text-small`, `text-title`, `text-heading-sm`/`-md`/`-lg`, `font-display`, `font-action`, `shadow-subtle`, `shadow-floating`, `shadow-overlay`, `duration-fast`/`-default`/`-slow`/`-slower`, `ease-standard`/`-enter`/`-exit` og `bg-backdrop`.
  - `text-body`, `text-leading` og `text-action` setter nå også linjehøyde (1.5).
  - `transition-*` bruker `--motion-duration-fast` og `--motion-easing-standard` som standard.
  - **Brytende:** `font-expanded` er fjernet. Bruk `font-action`.
  - Komponentene bruker bare token-klasser. Utseendet er det samme som før.

- 0bdf0d3: Nye tema-attributter: temaet er delt i merkevare (`data-brand`) og fargevariant (`data-theme`), og lys/mørk modus har fått eget attributt (`data-color-scheme`).
  - `data-theme="ild-dark"` blir `data-brand="bekk" data-theme="bekk-ild" data-color-scheme="dark"`. Uten `data-color-scheme` følger fargene systeminnstillingen.
  - Fargene skrives ut med `light-dark()`, så lys og mørk modus kan nøstes fritt i treet.
  - Uten attributter får siden `bekk` / `bekk-ild` og desktop-størrelser. Før var fargene og størrelsene udefinerte.
  - `Ingress` og `ActionText` får nå riktig tekststørrelse uten `data-size` (de arvet tidligere størrelsen fra omgivelsene).
  - `applyThemeToDocument()` og `ThemeSwitcher` setter de nye attributtene.
  - Hver token-verdi skrives ut under selektoren til dimensjonen den faktisk varierer over, og `dist/index.css` er gått fra ca. 2400 til ca. 380 linjer.

  Se migreringsguiden for detaljer.

#### Patch Changes

- Updated dependencies [4dfcaf2]
- Updated dependencies [a6f5eb5]
- Updated dependencies [0bdf0d3]
- Updated dependencies [14e65d7]
- Updated dependencies [3cd3c01]
  - @kilden/design-tokens@0.2.0

### 0.1.0

#### Minor Changes

- 7e64308: **Bryter bakoverkompatibilitet:** Designsystemet krever nå Tailwind CSS v4 og React 19.

  `@kilden/designsystem/styles.css` er ikke lenger ferdig kompilert CSS, men et Tailwind v4-oppsett (tokens, tema, fonter og `@source` mot komponentene). Konsumenten genererer dermed bare klassene som faktisk brukes, og slipper å laste en dobbel preflight og ~140 KB ubrukte klasser.

  Migrering – flytt importen fra JS/TS til CSS-filen der du importerer Tailwind:

```css
@import "tailwindcss";
@import "@kilden/designsystem/styles.css";
```

  Fjern eventuell `import "@kilden/designsystem/styles.css"` fra rot-layouten.

  Andre endringer:
  - `tailwindcss` (^4.1) er lagt til som peer dependency, og React 18 støttes ikke lenger.
  - `tailwindcss-animate` er byttet ut med `tw-animate-css`.
  - Fargene defineres med `@theme inline reference`, slik at Tailwind ikke lenger skriver ut selvrefererende CSS-variabler.
  - Den ubrukte `dark`-varianten (`.dark`) er fjernet fra pakkens CSS.
  - Pakken inneholder ikke lenger testoppsett, utdaterte byggefiler eller sourcemaps som pekte på upubliserte kildefiler.

#### Patch Changes

- b8ad65c: - `AlertTitle` rendres som `div` som standard i stedet for en fast `h5`, som brøt overskriftshierarkiet de fleste steder. Bruk den nye `as`-propen (f.eks. `as="h3"`) når tittelen skal være en overskrift.
  - `DatePicker` setter ikke lenger `aria-required` på trigger-knappen, siden attributtet ikke er tillatt på knapper. Påkrevd-markeringen vises i labelen.
- 3e979f5: - `Button` med `asChild` og `isLoading` samtidig krasjer ikke lenger. Spinneren legges inn i barn-elementet, og deaktivert tilstand settes med `aria-disabled`.
  - `Button` med `isLoading` uten `loadingText` viser nå children ved siden av spinneren, slik at knappen beholder sitt tilgjengelige navn.
  - `Chip` viser kryss-ikonet også i ukontrollert modus (`defaultPressed` / klikk), og bruker et ikon i stedet for bokstaven «x».
- 0ba29a2: Rett feil der `Combobox` nullstilte valgt verdi når den ble åpnet og lukket uten at brukeren skrev noe (f.eks. ved å klikke i feltet og trykke Escape, eller tabbe forbi). Verdien nullstilles nå bare når brukeren faktisk har tømt søkefeltet.
- ea088d3: Skyggetokenene (elevation) genereres nå som ferdige `box-shadow`-verdier: `--elevation-raised-default`, `--elevation-raised-hover` og `--elevation-raised-pressed`, med riktige farger for lys og mørk modus. Tidligere ble bare fargen per skyggelag eksportert (`--elevation-raised-default-1-color` osv.), og variablene dokumentasjonen viste til fantes ikke. De gamle `-N-color`-variablene er fjernet.

  I `@kilden/designsystem` er skyggene registrert i Tailwind-temaet som `shadow-raised`, `shadow-raised-hover` og `shadow-raised-pressed`.

- df5e679: Rett feil ved lasting av pakken utenfor bundlere:
  - Importene i `dist/` har nå `.js`-endelse. Tidligere feilet `import "@kilden/designsystem"` med `ERR_MODULE_NOT_FOUND` når Node lastet pakken direkte – f.eks. ved SSR i React Router/Vite, i Vitest og i Node-skript – og TypeScript med `moduleResolution: node16/nodenext` fant ikke typene.
  - `exports` har fått en `default`-betingelse, slik at `require("@kilden/designsystem")` fungerer på Node 20.19+/22.12+ i stedet for å feile med `ERR_PACKAGE_PATH_NOT_EXPORTED`.

- 9687a53: Felles fokusmarkering i alle komponenter. Tidligere brukte komponentene tre ulike farger og en blanding av `focus:` og `focus-visible:`, og `ring-offset-white` ga en hvit kant rundt fokusringen i mørke temaer. Nå brukes `outline` med `outline-offset` i dominant-farge overalt, som også vises i Windows høykontrastmodus. `outline-none` er byttet til `outline-hidden` av samme grunn.
- 6849148: Bytt `font-bold` og `font-semibold` med `font-medium` i `Heading`, `CardTitle`, `DialogTitle`, `SheetTitle`, `SelectLabel`, `DropdownMenuLabel`, `TabsTrigger` og `ToastTitle`. Bare vektene 300–500 av GT America lastes, så nettleseren lagde en kunstig fet variant.
- 31e3bb4: Bruk ikoner fra `lucide-react` i stedet for dupliserte inline-SVG-er i Accordion, Checkbox, Combobox, Dialog, DropdownMenu, NavigationMenu, Select, Sheet, TimePicker og Toast.
- 9377a80: `Heading`, `BodyText`, `ActionText`, `Ingress`, `Badge` og `Skeleton` tar nå imot `ref` (React 19-stil, uten `forwardRef`).
- a6d75f9: Opprydding i det offentlige API-et:
  - **Fjernet** `styles`-eksporten (CSS-in-JS-objekter). `"&:hover"` fungerer ikke i inline-stiler, så den var aldri brukbar. Bruk Tailwind-klasser eller CSS-variablene fra tokens i stedet.
  - `tokens` eksporteres nå bare én gang.
  - `ThemeSwitcher` har fått `applyToDocument` (standard `true`). Sett den til `false` hvis appen styrer `data-theme` selv. Logikken er også tilgjengelig som `applyThemeToDocument(theme, colorMode)`.

- c2051a6: Skjemafelt har fått felles boksstil og automatisk kobling via `FormField`.
  - `Input`, `Textarea`, `SelectTrigger`, `Combobox`, `DateInput`, `TimePicker` og `DatePicker` bruker nå samme boksstil (tidligere hadde `Input` understrek, mens de andre var bokser med ulik tekststørrelse). `Label` har fått samme stil som Inputs innebygde label.
  - `FormField` kobler automatisk `Label`, skjemaelement, `FormDescription` og `FormMessage` med `id`/`htmlFor`, `aria-describedby` og `aria-invalid`, og tar imot `required`, `disabled`, `invalid` og `controlId`. Den nye hooken `useFormControl` lar egne skjemaelementer koble seg på.
  - `Textarea`, `Combobox`, `DateInput`, `TimePicker` og `DatePicker` støtter `label` og `error` på samme måte som `Input`.
  - `DateInput` og `TimePicker` sender alle input-props videre (`name`, `id`, `aria-*`, `onBlur` osv.). `TimePicker` setter bare `aria-label="Klokkeslett"` når feltet mangler label.
  - `DatePicker` tar imot knappe-props og `name`; med `name` sendes datoen med skjemaet i ISO-format via et skjult felt. Triggeren er nå et skjemafelt i full bredde i stedet for en 280 px knapp.
  - `Combobox` respekterer `id` og `aria-labelledby`, og `aria-expanded` følger faktisk åpen/lukket-tilstand (cmdk satte den alltid til `true`).
  - `FormMessage` viser samme feilmarkering (rød prikk) som `Input`.

- c50d390: `Slider` sender nå `aria-label` og `aria-labelledby` videre til håndtakene. Tidligere havnet de på roten, og skjermlesere fant ikke noe navn på slideren.
- 749444b: `cn()` bruker nå tailwind-merge v3 (Tailwind v4-kompatibel) og kjenner Kildens egne skriftstørrelser og fontfamilier. Tidligere fjernet `cn("text-body", "text-neutral-text-subtle")` skriftstørrelsen fordi `text-body` ble tolket som en tekstfarge. Samtidig er `clsx` oppgradert til v2 og `class-variance-authority` til 0.7.
- 6f2e454: Nytt imperativt toast-API: `toast()`, `dismissToast()`, `useToast()` og ``. Plasser `` én gang i rot-layouten og kall `toast({ title, description, variant, action })` hvor som helst. Lukkeknappen i `ToastClose` har også fått et tilgjengelig navn («Lukk»).
- 19e71bb: Rett Tailwind-klasser som ikke genererte CSS: `Heading`, `BodyText`, `Ingress` og `ActionText` får nå tekstfarge (`text-on-page` fantes bare i docs-appen), aktiv `Toggle` får riktig kontrastfarge, og `NavigationMenuViewport` bruker en gyldig `origin`-klasse. En ny test feiler hvis komponentene bruker klasser som ikke finnes.
- Updated dependencies [ea088d3]
  - @kilden/design-tokens@0.1.0

### 0.0.21

#### Patch Changes

- 00f5644: fix(button): gi `default`, `md` og `sm` eksplisitt høyde (`h-10`/`h-9`/`h-8`) slik at de får samme høyde som `icon`-varianten når de står ved siden av hverandre

### 0.0.20

#### Patch Changes

- ac3854d: Fjern `forceMount` fra `Combobox` sitt popover-innhold. Innholdet unmountes nå fra DOM når dropdownen er lukket, i stedet for å bli liggende skjult med en `hidden`-klasse, slik Radix Popover er designet for.

### 0.0.19

#### Patch Changes

- 2812e45: Oppdaterer skill-dokumentasjonen (`kilden.md`): dokumenterer `required`-proppen (stjerne + skjult "(påkrevd)"-tekst) og fikser et utdatert eksempel som hardkodet stjernen manuelt i label-teksten. Fikser også tre andre feil: manglende `md`-størrelse på `Button`, manglende `SheetFooter`/`SheetClose` i `Sheet`-eksemplet, og et CSS-eksempel som brukte en ikke-eksisterende `--spacing-4`-token.

### 0.0.18

#### Patch Changes

- 5e91eed: feat: vis rød stjerne-indikasjon for påkrevde felt via `required`-prop på `Label` og `Input`, og legg til `required`-prop på `DatePicker`, `DateInput` og `TimePicker`

### 0.0.17

#### Patch Changes

- 9a3f4c9: fix(badge): sentrer tekst vertikalt ved å bruke leading-none
- 8cc3a4e: Legg til dark mode-støtte i favicon.svg via `prefers-color-scheme` — hvit i mørk modus, mørk i lys modus.
- Updated dependencies [3c835ec]
  - @kilden/design-tokens@0.0.5

### 0.0.16

#### Patch Changes

- 66885ad: Legg til `md`-størrelse for Button-komponenten, mellom `sm` (12px) og `default` (16px). Bruker `text-sm` (14px) med `px-[11px] py-1`.

### 0.0.15

#### Patch Changes

- a19d292: Refaktorer komponentkategorier: legg til "Dato og tid"- og "Tekst"-kategorier, flytt Chip til Skjema
- fe9ce62: UU-forbedringer: øker berøringsflate for Checkbox og RadioGroupItem fra 16px til 24px (WCAG 2.2 SC 2.5.8), utvider klikkareal på Dialog close-knapp til 32px, og endrer Badge fra `<div>` til `<span>` for korrekt semantikk.
- Updated dependencies [d0d09f8]
  - @kilden/design-tokens@0.0.4

### 0.0.14

#### Patch Changes

- f5f3737: Legg til DotsLoader-komponent for inline og aktive lastetilstander
- 12f3f2a: Legg til Spinner-komponent for ubestemte lastetilstander
- be0ceac: Erstatt Text-komponent med tre fokuserte tekstkomponenter: `Ingress` (leading), `BodyText` (body) og `ActionText` (action)
- 6fdc872: Fikser vertikal sentrering av tekst i liten (`sm`) Button-variant ved å bruke symmetrisk vertikal padding.
- 9bfc8b5: Legg til TypeScript-typedeklarasjon for CSS-exporten slik at `import "@kilden/designsystem/styles.css"` er en gyldig side-effect import uten behov for `declare module "*.css" {}` hos konsumenter.
- 0f55230: Nedgrader jsdom til ^26.1.0 og legg til eksplisitt vitest-avhengighet for å fikse testoppsett
- 6106879: Fiks favicon.svg: kvadratisk viewBox og currentColor

  favicon.svg hadde en ikke-kvadratisk viewBox (`0 0 12 17`) og hardkodet
  farge (`#1A1A1A`). Oppdatert til kvadratisk viewBox (`-4 -1.5 20 20`) med
  B-merket sentrert og litt luft rundt, og `fill="currentColor"` for
  fleksibel fargebruk.

  Fikser også en bug i build-scriptet der `cp -r ./src/logos ./dist/logos`
  nested feil når `dist/logos/` allerede eksisterte — favicon.svg havnet i
  `dist/logos/logos/` i stedet for `dist/logos/`.

- 5608369: Oppdater tsconfig til ES2024-target og moduleResolution bundler

### 0.0.13

#### Patch Changes

- cd97631: Fiks build-infrastruktur: erstatt mkdtempDisposable med mkdtemp+rm i design-tokens, og bruk lokal tailwindcss-binær i designsystem
- 57fd219: Legg til favicon-SVG (Bekk B-logo) som publisert fil: `@kilden/designsystem/logos/favicon.svg`
- Updated dependencies [cd97631]
- Updated dependencies [228e107]
- Updated dependencies [c148130]
  - @kilden/design-tokens@0.0.3

### 0.0.12

#### Patch Changes

- 558ae56: Inkluder skill-mappen i publisert pakke og oppdater skill-filen med nye komponenter

### 0.0.11

#### Patch Changes

- d73c271: Legg til "use client" direktiv på alle komponenter som bruker React hooks, interaktive Radix UI-primitiver eller browser-APIer. Dette fikser kompatibilitet med Next.js og andre React Server Component-rammeverk.

### 0.0.10

#### Patch Changes

- d0dff72: Forbedret tabs-komponenten med tydeligere aktiv state og sterkere temaforskjeller uten å endre API-et.
- 08ac952: Tillat React 19 som peer dependency i tillegg til React 18.

### 0.0.9

#### Patch Changes

- 159272e: Legg til Bekk-logoer som både publiserte SVG-filer og React-komponenter.

### 0.0.8

#### Patch Changes

- 7e14450: Legg til PasswordInput-komponent med vis/skjul-knapp og autoComplete-støtte
- 596bd56: Forenklet Breadcrumb API — fjernet BreadcrumbList, BreadcrumbItem og BreadcrumbSeparator. Separatorer settes nå inn automatisk. BreadcrumbLink støtter nå asChild for bruk med React Router o.l.
- 61a1a79: Redesignet Button-komponent med nytt visuelt uttrykk.

  **Breaking changes:**
  - Fjernet varianter `outline` og `link`
  - Fjernet størrelse `lg`

  **Endringer:**
  - `default`-varianten er nå border-only (ikke fylt), med accent-farge ved hover
  - `secondary`-varianten er nå kun tekst (ingen border/bakgrunn), med border ved hover
  - `ghost`-varianten bruker nå semantic surface-tokens for hover/active
  - `destructive`-varianten bruker nå semantic danger-tokens for alle states
  - Lagt til `danger-base-hover`, `danger-base-active` og `neutral-surface-active` tokens i Tailwind-tema

### 0.0.7

#### Patch Changes

- e8c91ff: Legg til SearchInput-komponent med søkeikon, clear-knapp, label og feilmelding
- 9303249: Legg til prefix/suffix-props på Input for ikoner og knapper. Refaktorer SearchInput, DateInput og TimePicker til å bruke Input med prefix/suffix internt.
- b69f56d: Redesign Input med underline-stil, innebygd label- og error-prop

### 0.0.6

#### Patch Changes

- c23544c: Legg til Chip-komponent for filtrering og valg, bygget på Radix UI Toggle
- 4c645a5: Legg til JumpToMainContent-komponent for tastaturnavigasjon
- 1aff57a: Legg til TextLink-komponent for inline tekstlenker med automatisk eksternt-lenke-ikon og tilgjengelighet
- c36c2f3: Legg til Combobox-komponent med input som trigger og backspace-støtte

### 0.0.5

#### Patch Changes

- 36c355f: Legg til TimePicker-komponent for tidsvalg
- d24cad2: Legg til Calendar, DatePicker og DateInput-komponenter for datovalg

### 0.0.4

#### Patch Changes

- 1deb022: Endre font på button og badges
- 3157cc0: Fiks noen mindre problemer med Skeleton og NavigationMenu
- 0d50a18: En liten ytelsesforbedring for Slider-komponenten

### 0.0.3

#### Patch Changes

- 13be719: Legg til JSDoc-dokumentasjon på Button, Heading og ThemeSwitcher
- d5d7c1b: Bytt til oxlint og oxfmt

### 0.0.2

#### Patch Changes

- Updated dependencies [881085c]
  - @kilden/design-tokens@0.0.2

## @kilden/design-tokens

### 0.2.0

#### Minor Changes

- 0bdf0d3: Nye tema-attributter: temaet er delt i merkevare (`data-brand`) og fargevariant (`data-theme`), og lys/mørk modus har fått eget attributt (`data-color-scheme`).
  - `data-theme="ild-dark"` blir `data-brand="bekk" data-theme="bekk-ild" data-color-scheme="dark"`. Uten `data-color-scheme` følger fargene systeminnstillingen.
  - Fargene skrives ut med `light-dark()`, så lys og mørk modus kan nøstes fritt i treet.
  - Uten attributter får siden `bekk` / `bekk-ild` og desktop-størrelser. Før var fargene og størrelsene udefinerte.
  - `Ingress` og `ActionText` får nå riktig tekststørrelse uten `data-size` (de arvet tidligere størrelsen fra omgivelsene).
  - `applyThemeToDocument()` og `ThemeSwitcher` setter de nye attributtene.
  - Hver token-verdi skrives ut under selektoren til dimensjonen den faktisk varierer over, og `dist/index.css` er gått fra ca. 2400 til ca. 380 linjer.

  Se migreringsguiden for detaljer.

#### Patch Changes

- 4dfcaf2: `data-brand` alene gir nå merkevarens standardvariant, og `data-theme` alene gir også merkevarens tokens, slik at et tema kan settes med ett attributt.
- a6f5eb5: Nye merkevaretokens: typografi, form, tetthet, elevation, bevegelse og statusfarger går nå via merkevaren (`brands/bekk`), slik at en ny merkevare kan endre hele uttrykket. Alle eksisterende variabler har samme verdi som før.

  Nye CSS-variabler:
  - `--font-family-base`, `--font-family-display`, `--font-family-action`
  - `--font-weight-light`, `--font-weight-regular`, `--font-weight-medium`
  - `--spacing-unit`
  - `--elevation-subtle`, `--elevation-floating`, `--elevation-overlay`
  - `--motion-duration-fast|default|slow|slower`, `--motion-easing-standard|enter|exit`
  - Typografiroller: `caption`, `small`, `title`, `heading-sm`, `heading-md`, `heading-lg` (`--typography-<rolle>-font-size`, `-line-height` osv.)
  - `--typography-<rolle>-line-height` for alle typografiroller

- 14e65d7: Token-kontrakten har fått `source` (merkevare- eller varianttokenet en variabel kommer fra) og `default` (standardverdien), slik at verktøy kan lage komplette temaer.
- 3cd3c01: Ny token-kontrakt i `@kilden/design-tokens/contract.json`. Den lister alle CSS-variabler med type, hvilken dimensjon de hører til (merkevare, variant eller størrelse), om de varierer med lys/mørk modus, hvilket merkevare- eller varianttoken de kommer fra, og standardverdien. Bruk den når du lager et eget tema.

### 0.1.0

#### Minor Changes

- ea088d3: Skyggetokenene (elevation) genereres nå som ferdige `box-shadow`-verdier: `--elevation-raised-default`, `--elevation-raised-hover` og `--elevation-raised-pressed`, med riktige farger for lys og mørk modus. Tidligere ble bare fargen per skyggelag eksportert (`--elevation-raised-default-1-color` osv.), og variablene dokumentasjonen viste til fantes ikke. De gamle `-N-color`-variablene er fjernet.

  I `@kilden/designsystem` er skyggene registrert i Tailwind-temaet som `shadow-raised`, `shadow-raised-hover` og `shadow-raised-pressed`.

### 0.0.5

#### Patch Changes

- 3c835ec: Justerer typografiskala på desktop: brødtekst 20px → 16px, action 16px → 14px, display 56px → 64px

### 0.0.4

#### Patch Changes

- d0d09f8: Endrer `color-success-base-contrast-default` til hvit (#ffffff, 4.5:1 ✓ AA) og `color-success-base-contrast-subtle` til grønn-toned off-hvit (#ecf6f1, 4.1:1) i light mode for å rette opp visuell inkonsistens.

### 0.0.3

#### Patch Changes

- cd97631: Fiks build-infrastruktur: erstatt mkdtempDisposable med mkdtemp+rm i design-tokens, og bruk lokal tailwindcss-binær i designsystem
- 228e107: Fiks typography.action-token til å bruke 'GT America Expanded' som font-family og 500 som font-weight.

  Tidligere pekte `typography.action` på `primitive.font-family` ('GT America') og brukte `font-weight.expanded` ('Expanded Medium') som er ugyldig CSS. Nå er det lagt til en `primitive.font-family-expanded`-token ('GT America Expanded'), og action-stilen bruker denne direkte med `font-weight.medium` (500) — som matcher den faktiske @font-face-filen.

  Dette betyr at `--typography-action-font-family` nå korrekt resolves til `'GT America Expanded'` og `--typography-action-font-weight` til `500`.

  Fiks også en pre-eksisterende build-feil i build-tokens.ts der `mkdtempDisposable` ble importert fra `node:fs/promises` (eksisterer ikke). Nå er den definert lokalt.

- c148130: Legg til `--line-height-sm`, `--line-height-md` og `--line-height-lg` som CSS-variabler. Disse mangler i forrige versjon til tross for at dokumentasjonen nevner dem.

### 0.0.2

#### Patch Changes

- 881085c: Første release

---

# Button

> En allsidig knapp-komponent med flere varianter og størrelser.

[Kildekode på GitHub](https://github.com/bekk/designsystem/blob/main/packages/designsystem/src/ui/button.tsx)

Knapp-komponenten brukes til handlinger og navigasjon. Den støtter flere varianter, størrelser og kan brukes som en lenke med `asChild`.

## Utforsk

*Se interaktiv playground på dokumentasjonssiden.*

## Importering

```tsx
import { Button } from "@kilden/designsystem";
```

## Varianter

- **`default`** – primærknapp med kant, fylles ved hover. Brukes for hovedhandlingen.
- **`secondary`** – kun tekst, får kant ved hover. Brukes for sekundære handlinger.
- **`ghost`** – usynlig knapp med bakgrunn ved hover. Passer i verktøylinjer og menyer.
- **`destructive`** – for handlinger som sletter eller ikke kan angres.

```tsx
<div style={{ display: "flex", gap: "8px", flexWrap: "wrap" }}>
  <Button>Default</Button>
  <Button variant="secondary">Secondary</Button>
  <Button variant="ghost">Ghost</Button>
  <Button variant="destructive">Destructive</Button>
</div>
```

## Størrelser

`icon` er for knapper som bare inneholder et ikon. De **må** ha en `aria-label`, ellers har knappen ikke noe tilgjengelig navn.

```tsx
<div style={{ display: "flex", gap: "8px", alignItems: "center", flexWrap: "wrap" }}>
  <Button size="sm">Liten</Button>
  <Button size="md">Medium</Button>
  <Button size="default">Standard</Button>
  <Button size="icon" aria-label="Varsler">
    <Bell className="h-4 w-4" />
  </Button>
</div>
```

## Lastetilstand

Bruk `isLoading` for å vise en spinner og deaktivere knappen. Uten `loadingText` vises children ved siden av spinneren, slik at knappen beholder sitt tilgjengelige navn. Sett `loadingText` for å endre teksten under lasting.

`isLoading` fungerer også sammen med `asChild` — spinneren legges da inn i barn-elementet, og deaktivert tilstand settes med `aria-disabled` (siden f.eks. lenker ikke har et `disabled`-attributt). `loadingText` brukes ikke med `asChild`.

```tsx
<Button isLoading>Send inn</Button>
```

```tsx
<Button isLoading loadingText="Sender...">
  Send inn
</Button>
```

```tsx
<div style={{ display: "flex", gap: "8px", flexWrap: "wrap" }}>
    <Button isLoading>Send inn</Button>
    <Button isLoading loadingText="Sender...">Send inn</Button>
    <Button variant="secondary" isLoading loadingText="Lagrer...">Lagre</Button>
    <Button variant="ghost" isLoading>Laster</Button>
</div>
```

## Som lenke

Med `asChild` kan knappen brukes som wrapper rundt en lenke:

```tsx
<Button asChild>
  <a href="/side">Gå til side</a>
</Button>
```

## Bruksanbefalinger

- **Gjør: Bruk primary for hovedhandlinger** — Bruk default-varianten for den viktigste handlingen på siden.
- **Unngå: Ikke bruk flere primary-knapper** — Unngå å ha flere default-knapper ved siden av hverandre. Bruk secondary for sekundære handlinger.

## Tilgjengelighet

- Rendres som et native `<button>`, og aktiveres med `Enter` og `Space`
- `disabled` bruker det native attributtet, slik at knappen ikke kan fokuseres eller aktiveres
- `isLoading` deaktiverer knappen og setter `aria-busy`. Uten `loadingText` beholder knappen teksten sin som tilgjengelig navn
- Med `asChild` (f.eks. som lenke) uttrykkes deaktivert tilstand med `aria-disabled`, siden lenker ikke har et `disabled`-attributt
- Knapper med `size="icon"` må ha `aria-label`
- Synlig fokusmarkering ved tastaturnavigasjon
- Bruk `asChild` med `<a>` for navigasjon og `<button>` for handlinger – ikke omvendt

## Props

| Prop          | Type                                                   | Default     | Beskrivelse                                                     |
| ------------- | ------------------------------------------------------ | ----------- | --------------------------------------------------------------- |
| `variant`     | `"default" \| "secondary" \| "ghost" \| "destructive"` | `"default"` | Visuell stil                                                    |
| `size`        | `"default" \| "md" \| "sm" \| "icon"`                  | `"default"` | Størrelse                                                       |
| `asChild`     | `boolean`                                              | `false`     | Bruk child-elementet som rot                                    |
| `disabled`    | `boolean`                                              | `false`     | Deaktiver knappen                                               |
| `isLoading`   | `boolean`                                              | `false`     | Viser en spinner og deaktiverer knappen                         |
| `loadingText` | `string`                                               | —           | Tekst som erstatter children under lasting (ikke med `asChild`) |

---

# Calendar

> Kalenderkomponent for datovisning og datovalg, med norsk locale og full tastaturnavigasjon.

[Kildekode på GitHub](https://github.com/bekk/designsystem/blob/main/packages/designsystem/src/ui/calendar.tsx)

Calendar er en kalenderkomponent for å vise og velge datoer. Den viser norske dag- og månedsnavn, har mandag som første ukedag, og støtter full tastaturnavigasjon. Bygget på [react-day-picker](https://daypicker.dev).

## Importering

```tsx
import { Calendar } from "@kilden/designsystem";
```

## Grunnleggende bruk

```tsx
function CalendarDemo() {
  const [date, setDate] = React.useState(undefined);
  return (
    <Calendar
      mode="single"
      selected={date}
      onSelect={setDate}
    />
  );
}
```

## Med forhåndsvalgt dato

```tsx
function CalendarPreselected() {
  const [date, setDate] = React.useState(new Date());
  return (
    <Calendar
      mode="single"
      selected={date}
      onSelect={setDate}
    />
  );
}
```

## Deaktiverte datoer

Bruk `disabled`-propen for å hindre valg av bestemte datoer, for eksempel helger eller datoer i fortiden.

```tsx
function CalendarDisabled() {
  const [date, setDate] = React.useState(undefined);
  return (
    <Calendar
      mode="single"
      selected={date}
      onSelect={setDate}
      disabled={[
        { dayOfWeek: [0, 6] },
        { before: new Date() },
      ]}
    />
  );
}
```

## Datoperiode (range)

Calendar støtter valg av datoperioder med `mode="range"`.

```tsx
function CalendarRange() {
  const [range, setRange] = React.useState(undefined);
  return (
    <Calendar
      mode="range"
      selected={range}
      onSelect={setRange}
    />
  );
}
```

## Bruksanbefalinger

- **Gjør: Bruk DatePicker for skjemaer** — Når brukeren skal velge en dato i et skjema, bruk DatePicker som kombinerer Calendar med en Popover og knapp.
- **Unngå: Ikke bruk Calendar alene i skjemaer** — En frittstående kalender tar mye plass. Bruk den kun når kalenderen alltid skal være synlig, for eksempel i en bookingside.

## Tilgjengelighet

- Full tastaturnavigasjon: piltaster for å flytte mellom dager, PageUp/PageDown for måneder
- Norske aria-labels for alle elementer (dager, navigasjonsknapper, månedsnavn)
- `disabled`-datoer kommuniseres via `aria-disabled`
- Semantisk `<table>`-oppsett med `role="grid"`

## Props

Calendar godtar alle props fra [react-day-picker](https://daypicker.dev/api/interfaces/PropsBase) i tillegg til:

| Prop              | Type                                | Default | Beskrivelse                            |
| ----------------- | ----------------------------------- | ------- | -------------------------------------- |
| `mode`            | `"single" \| "multiple" \| "range"` | —       | Valgmodus                              |
| `selected`        | `Date \| Date[] \| DateRange`       | —       | Valgt(e) dato(er)                      |
| `onSelect`        | `(value) => void`                   | —       | Kalles når valg endres                 |
| `disabled`        | `Matcher \| Matcher[]`              | —       | Datoer som ikke kan velges             |
| `defaultMonth`    | `Date`                              | Nå      | Startet måned                          |
| `showOutsideDays` | `boolean`                           | `true`  | Vis dager fra forrige/neste måned      |
| `className`       | `string`                            | —       | Ekstra CSS-klasser                     |
| `classNames`      | `object`                            | —       | Overstyr klasser for interne elementer |

---

# Breadcrumb

> Brødsmulesti for navigasjonshierarki.

Breadcrumb,
  BreadcrumbLink,
  BreadcrumbPage,
  BreadcrumbEllipsis,
} from "@kilden/designsystem";

# Breadcrumb

[Kildekode på GitHub](https://github.com/bekk/designsystem/blob/main/packages/designsystem/src/ui/breadcrumb.tsx)

Breadcrumb viser brukerens posisjon i et navigasjonshierarki med klikkbare lenker tilbake til overordnede sider. Rendres som en semantisk `<nav>` med ordnet liste. Separatorer settes inn automatisk.

## Importering

```tsx
import {
  Breadcrumb,
  BreadcrumbLink,
  BreadcrumbPage,
  BreadcrumbEllipsis,
} from "@kilden/designsystem";
```

## Grunnleggende bruk

```tsx
<Breadcrumb>
  <BreadcrumbLink href="#">Hjem</BreadcrumbLink>
  <BreadcrumbLink href="#">Komponenter</BreadcrumbLink>
  <BreadcrumbPage>Breadcrumb</BreadcrumbPage>
</Breadcrumb>
```

## Med ellipsis

For dype hierarkier kan du bruke `BreadcrumbEllipsis` for å skjule mellomliggende nivåer.

```tsx
<Breadcrumb>
  <BreadcrumbLink href="#">Hjem</BreadcrumbLink>
  <BreadcrumbEllipsis />
  <BreadcrumbLink href="#">Komponenter</BreadcrumbLink>
  <BreadcrumbPage>Breadcrumb</BreadcrumbPage>
</Breadcrumb>
```

## Med asChild

Bruk `asChild` for å rendre en annen komponent som lenke, for eksempel med React Router.

```tsx
<Breadcrumb>
  <BreadcrumbLink asChild>
    <a href="#">Hjem</a>
  </BreadcrumbLink>
  <BreadcrumbPage>Side</BreadcrumbPage>
</Breadcrumb>
```

## Tilpasset separator

```tsx
<Breadcrumb separator=">">
  <BreadcrumbLink href="#">Hjem</BreadcrumbLink>
  <BreadcrumbLink href="#">Komponenter</BreadcrumbLink>
  <BreadcrumbPage>Breadcrumb</BreadcrumbPage>
</Breadcrumb>
```

## Lang sti

```tsx
<Breadcrumb>
  <BreadcrumbLink href="#">Hjem</BreadcrumbLink>
  <BreadcrumbLink href="#">Dokumentasjon</BreadcrumbLink>
  <BreadcrumbLink href="#">Komponenter</BreadcrumbLink>
  <BreadcrumbLink href="#">Navigasjon</BreadcrumbLink>
  <BreadcrumbPage>Breadcrumb</BreadcrumbPage>
</Breadcrumb>
```

## Bruksanbefalinger

- **Gjør: Bruk for hierarkisk navigasjon** — Breadcrumb viser tydelig hvor brukeren er i informasjonsarkitekturen og gir rask tilgang til overordnede sider.
- **Unngå: Ikke bruk for flat navigasjon** — Hvis sidene ikke har et hierarki, er breadcrumbs forvirrende. Bruk vanlige lenker eller tabs i stedet.

## Tilgjengelighet

- Rendres som `<nav>` med `aria-label="Brødsmulesti"`
- Bruker ordnet liste (`<ol>`) for korrekt semantikk
- Gjeldende side markeres med `aria-current="page"`
- Separatorer settes inn automatisk og er skjult med `aria-hidden="true"`
- Ellipsis har `sr-only`-tekst for skjermlesere

## Props

### Breadcrumb

Arver alle props fra `<nav>`. Setter automatisk `aria-label="Brødsmulesti"`. Separatorer settes inn automatisk mellom children.

| Prop        | Type              | Standard | Beskrivelse                        |
| ----------- | ----------------- | -------- | ---------------------------------- |
| `separator` | `React.ReactNode` | `"/"`    | Separator mellom brødsmuler        |
| `className` | `string`          | —        | CSS-klasser for den interne `<ol>` |

### BreadcrumbLink

Arver alle props fra `<a>`. Understreket som standard, fjerner understreking ved hover.

| Prop      | Type      | Standard | Beskrivelse                                             |
| --------- | --------- | -------- | ------------------------------------------------------- |
| `asChild` | `boolean` | `false`  | Rendrer barn-elementet som lenke i stedet for en `<a>`. |

### BreadcrumbPage

Arver alle props fra `<span>`. Markert med `aria-current="page"` og `aria-disabled="true"`.

### BreadcrumbEllipsis

Arver alle props fra `<span>`. Viser `…` med `sr-only`-tekst "Flere sider".

---

# Dialog

> Modal-vindu for viktige handlinger som krever brukerens oppmerksomhet.

Dialog,
  DialogTrigger,
  DialogContent,
  DialogHeader,
  DialogFooter,
  DialogTitle,
  DialogDescription,
  DialogClose,
  Button,
} from "@kilden/designsystem";

# Dialog

[Kildekode på GitHub](https://github.com/bekk/designsystem/blob/main/packages/designsystem/src/ui/dialog.tsx)

Dialog (modal) viser innhold over resten av siden og krever at brukeren interagerer med det eller lukker det. Bygget på [Radix UI Dialog](https://www.radix-ui.com/primitives/docs/components/dialog) med fokusfelle, Escape-lukking og bakgrunnsoverlegg.

## Importering

```tsx
import {
  Dialog,
  DialogTrigger,
  DialogContent,
  DialogHeader,
  DialogFooter,
  DialogTitle,
  DialogDescription,
  DialogClose,
} from "@kilden/designsystem";
```

## Grunnleggende bruk

```tsx
<Dialog>
  <DialogTrigger asChild>
    <Button>Åpne dialog</Button>
  </DialogTrigger>
  <DialogContent>
    <DialogHeader>
      <DialogTitle>Er du sikker?</DialogTitle>
      <DialogDescription>
        Denne handlingen kan ikke angres. Er du sikker på at du vil fortsette?
      </DialogDescription>
    </DialogHeader>
    <DialogFooter>
      <DialogClose asChild>
        <Button variant="secondary">Avbryt</Button>
      </DialogClose>
      <DialogClose asChild>
        <Button>Bekreft</Button>
      </DialogClose>
    </DialogFooter>
  </DialogContent>
</Dialog>
```

## Med skjemainnhold

Dialog kan brukes for å vise skjemaer som krever fokus.

```tsx
<Dialog>
  <DialogTrigger asChild>
    <Button variant="secondary">Rediger profil</Button>
  </DialogTrigger>
  <DialogContent>
    <DialogHeader>
      <DialogTitle>Rediger profil</DialogTitle>
      <DialogDescription>
        Gjør endringer i profilen din her. Klikk lagre når du er ferdig.
      </DialogDescription>
    </DialogHeader>
    <div style={{ display: "grid", gap: "1rem", padding: "1rem 0" }}>
      <div style={{ display: "grid", gap: "0.25rem" }}>
        <label htmlFor="name" style={{ fontSize: "0.875rem", fontWeight: 500 }}>Navn</label>
        <input
          id="name"
          defaultValue="Ola Nordmann"
          style={{
            padding: "0.5rem",
            borderRadius: "0.375rem",
            border: "1px solid var(--color-neutral-border-default)",
            background: "var(--color-neutral-background-default)",
          }}
        />
      </div>
      <div style={{ display: "grid", gap: "0.25rem" }}>
        <label htmlFor="email" style={{ fontSize: "0.875rem", fontWeight: 500 }}>E-post</label>
        <input
          id="email"
          defaultValue="ola@bekk.no"
          style={{
            padding: "0.5rem",
            borderRadius: "0.375rem",
            border: "1px solid var(--color-neutral-border-default)",
            background: "var(--color-neutral-background-default)",
          }}
        />
      </div>
    </div>
    <DialogFooter>
      <DialogClose asChild>
        <Button variant="secondary">Avbryt</Button>
      </DialogClose>
      <Button>Lagre endringer</Button>
    </DialogFooter>
  </DialogContent>
</Dialog>
```

## Bruksanbefalinger

- **Gjør: Bruk for viktige handlinger** — Dialog egner seg for bekreftelser, skjemaer og innhold som krever brukerens fulle oppmerksomhet før de fortsetter.
- **Unngå: Ikke bruk for enkel informasjon** — Unngå dialog for meldinger som ikke krever en handling. Bruk heller en toast, banner, eller inline-melding.

- **Gjør: Inkluder alltid en tittel** — Bruk DialogTitle for å gi dialogen en tydelig overskrift. Dette er viktig for tilgjengelighet.
- **Unngå: Ikke nestl dialoger** — Unngå å åpne en dialog fra en annen dialog. Det skaper en forvirrende brukeropplevelse.

## Tilgjengelighet

- Fokus flyttes automatisk inn i dialogen når den åpnes
- Fokus er fanget inni dialogen (fokusfelle)
- `Escape` lukker dialogen
- Klikk på overlegget lukker dialogen
- `DialogTitle` kobles automatisk til `aria-labelledby`
- `DialogDescription` kobles automatisk til `aria-describedby`
- Lukkeknappen har `sr-only`-teksten «Lukk»

## Props

### Dialog (rot)

| Prop           | Type                      | Default | Beskrivelse                  |
| -------------- | ------------------------- | ------- | ---------------------------- |
| `open`         | `boolean`                 | —       | Kontrollert åpen/lukket      |
| `onOpenChange` | `(open: boolean) => void` | —       | Kalles når tilstanden endres |
| `defaultOpen`  | `boolean`                 | `false` | Om dialogen starter åpen     |

### DialogContent

Arver alle props fra `<div>`. Rendres i en portal over alt annet innhold.

### DialogTitle

Arver alle props fra `<h2>`. Påkrevd for tilgjengelighet.

### DialogDescription

Arver alle props fra `<p>`. Gir kontekst til skjermlesere.

### DialogTrigger

| Prop      | Type      | Default | Beskrivelse                      |
| --------- | --------- | ------- | -------------------------------- |
| `asChild` | `boolean` | `false` | Bruk child-elementet som trigger |

### DialogClose

| Prop      | Type      | Default | Beskrivelse                         |
| --------- | --------- | ------- | ----------------------------------- |
| `asChild` | `boolean` | `false` | Bruk child-elementet som lukkeknapp |

---

# Alert

> Fremhevede meldingsbokser for tips, advarsler og viktig informasjon.

Alert,
  AlertTitle,
  AlertDescription,
  Button,
} from "@kilden/designsystem";

# Alert

[Kildekode på GitHub](https://github.com/bekk/designsystem/blob/main/packages/designsystem/src/ui/alert.tsx)

Alert brukes for å vise kontekstuell informasjon som krever brukerens oppmerksomhet. Den støtter flere semantiske varianter for ulike typer meldinger — fra nøytral informasjon til feilmeldinger.

## Utforsk

*Se interaktiv playground på dokumentasjonssiden.*

## Importering

```tsx
import { Alert, AlertTitle, AlertDescription } from "@kilden/designsystem";
```

## Grunnleggende bruk

```tsx
<Alert>
  <AlertTitle>Merknad</AlertTitle>
  <AlertDescription>
    Dette er en standard alert for generell informasjon.
  </AlertDescription>
</Alert>
```

## Varianter

### Info

```tsx
<Alert variant="info">
  <AlertTitle>Tips</AlertTitle>
  <AlertDescription>
    Du kan bruke tastatursnarvei Ctrl+K for å åpne søk.
  </AlertDescription>
</Alert>
```

### Success

```tsx
<Alert variant="success">
  <AlertTitle>Fullført</AlertTitle>
  <AlertDescription>
    Endringene dine er lagret og publisert.
  </AlertDescription>
</Alert>
```

### Warning

```tsx
<Alert variant="warning">
  <AlertTitle>Advarsel</AlertTitle>
  <AlertDescription>
    Denne komponenten er eksperimentell og kan endres uten forvarsel.
  </AlertDescription>
</Alert>
```

### Danger

```tsx
<Alert variant="danger">
  <AlertTitle>Feil</AlertTitle>
  <AlertDescription>
    Kunne ikke lagre endringene. Prøv igjen senere.
  </AlertDescription>
</Alert>
```

## Alle varianter samlet

```tsx
<div style={{ display: "flex", flexDirection: "column", gap: "1rem" }}>
  <Alert>
    <AlertTitle>Default</AlertTitle>
    <AlertDescription>Nøytral informasjon.</AlertDescription>
  </Alert>
  <Alert variant="info">
    <AlertTitle>Info</AlertTitle>
    <AlertDescription>Nyttig informasjon å vite om.</AlertDescription>
  </Alert>
  <Alert variant="success">
    <AlertTitle>Success</AlertTitle>
    <AlertDescription>Handlingen var vellykket.</AlertDescription>
  </Alert>
  <Alert variant="warning">
    <AlertTitle>Warning</AlertTitle>
    <AlertDescription>Vær oppmerksom på dette.</AlertDescription>
  </Alert>
  <Alert variant="danger">
    <AlertTitle>Danger</AlertTitle>
    <AlertDescription>Noe gikk galt.</AlertDescription>
  </Alert>
</div>
```

## Kun beskrivelse

AlertTitle er valgfri — du kan bruke Alert med kun en beskrivelse.

```tsx
<Alert variant="info">
  <AlertDescription>
    Husk å kjøre testene før du pusher koden din.
  </AlertDescription>
</Alert>
```

## Bruksanbefalinger

- **Gjør: Bruk riktig variant for konteksten** — Velg variant basert på meldingstypen: info for tips, success for bekreftelser, warning for advarsler og danger for feil.
- **Unngå: Ikke bruk danger for advarsler** — Danger er for faktiske feil. Advarsler som 'eksperimentell' eller 'deprecated' bør bruke warning.

- **Gjør: Hold teksten kortfattet** — Alerts bør ha en tydelig tittel og kort beskrivelse. Lengre innhold hører hjemme i selve siden.
- **Unngå: Ikke stable for mange alerts** — For mange alerts på én side reduserer effekten. Prioriter de viktigste meldingene.

## Tilgjengelighet

- Bruker `role="alert"` som signaliserer viktig innhold til skjermlesere
- Skjermlesere vil typisk annonsere innholdet umiddelbart
- Bruk `AlertTitle` for å gi en tydelig overskrift

## Props

### Alert

| Prop      | Type                                                        | Default     | Beskrivelse           |
| --------- | ----------------------------------------------------------- | ----------- | --------------------- |
| `variant` | `"default" \| "info" \| "success" \| "warning" \| "danger"` | `"default"` | Visuell stil og farge |

### AlertTitle

| Prop | Type                                                   | Default | Beskrivelse                                                                                                     |
| ---- | ------------------------------------------------------ | ------- | --------------------------------------------------------------------------------------------------------------- |
| `as` | `"div" \| "p" \| "h2" \| "h3" \| "h4" \| "h5" \| "h6"` | `"div"` | Elementet som rendres. Velg et overskriftsnivå som passer sidens hierarki hvis tittelen skal være en overskrift |

Arver ellers alle props fra `<div>`.

### AlertDescription

Arver alle props fra `<div>`.

---

# Accordion

> Sammenleggbare seksjoner for å vise og skjule innhold.

Accordion,
  AccordionItem,
  AccordionTrigger,
  AccordionContent,
} from "@kilden/designsystem";

# Accordion

[Kildekode på GitHub](https://github.com/bekk/designsystem/blob/main/packages/designsystem/src/ui/accordion.tsx)

Accordion (trekkspill) lar brukeren utvide og lukke seksjoner med innhold. Nyttig for FAQ-er, prop-dokumentasjon, og andre tilfeller der mye innhold skal være tilgjengelig uten å ta opp plass. Bygget på [Radix UI Accordion](https://www.radix-ui.com/primitives/docs/components/accordion).

## Importering

```tsx
import {
  Accordion,
  AccordionItem,
  AccordionTrigger,
  AccordionContent,
} from "@kilden/designsystem";
```

## Grunnleggende bruk

Bruk `type="single"` med `collapsible` for å la brukeren åpne én seksjon om gangen, med mulighet til å lukke den igjen.

```tsx
<Accordion type="single" collapsible>
  <AccordionItem value="item-1">
    <AccordionTrigger>Hva er et designsystem?</AccordionTrigger>
    <AccordionContent>
      Et designsystem er en samling av gjenbrukbare komponenter,
      retningslinjer og designtokens som sikrer konsistens på tvers
      av produkter.
    </AccordionContent>
  </AccordionItem>
  <AccordionItem value="item-2">
    <AccordionTrigger>Hvorfor bruke Radix UI?</AccordionTrigger>
    <AccordionContent>
      Radix UI gir tilgjengelige, ustylt primitiver som lar deg
      bygge komponentbiblioteker med full kontroll over utseendet.
    </AccordionContent>
  </AccordionItem>
  <AccordionItem value="item-3">
    <AccordionTrigger>Støttes tastaturnavigasjon?</AccordionTrigger>
    <AccordionContent>
      Ja! Du kan bruke piltaster for å navigere mellom seksjoner,
      og Enter/Space for å åpne og lukke dem.
    </AccordionContent>
  </AccordionItem>
</Accordion>
```

## Flere åpne samtidig

Med `type="multiple"` kan flere seksjoner være åpne samtidig.

```tsx
<Accordion type="multiple" defaultValue={["item-1", "item-2"]}>
  <AccordionItem value="item-1">
    <AccordionTrigger>Seksjon 1 (åpen)</AccordionTrigger>
    <AccordionContent>
      Denne seksjonen er åpen som standard.
    </AccordionContent>
  </AccordionItem>
  <AccordionItem value="item-2">
    <AccordionTrigger>Seksjon 2 (åpen)</AccordionTrigger>
    <AccordionContent>
      Denne er også åpen. Begge kan lukkes uavhengig.
    </AccordionContent>
  </AccordionItem>
  <AccordionItem value="item-3">
    <AccordionTrigger>Seksjon 3 (lukket)</AccordionTrigger>
    <AccordionContent>
      Denne starter lukket, men kan åpnes uten at de andre lukkes.
    </AccordionContent>
  </AccordionItem>
</Accordion>
```

## Forhåndsåpnet seksjon

Bruk `defaultValue` for å styre hvilken seksjon som er åpen ved oppstart.

```tsx
<Accordion type="single" collapsible defaultValue="item-2">
  <AccordionItem value="item-1">
    <AccordionTrigger>Første seksjon</AccordionTrigger>
    <AccordionContent>Innhold i første seksjon.</AccordionContent>
  </AccordionItem>
  <AccordionItem value="item-2">
    <AccordionTrigger>Andre seksjon (forhåndsåpnet)</AccordionTrigger>
    <AccordionContent>
      Denne seksjonen er åpen når siden lastes.
    </AccordionContent>
  </AccordionItem>
</Accordion>
```

## Bruksanbefalinger

- **Gjør: Bruk for valgfritt innhold** — Accordion er perfekt for FAQ-er, tilleggsdetaljer og annet innhold brukeren kanskje vil se, men ikke trenger å se.
- **Unngå: Ikke skjul viktig innhold** — Ikke legg kritisk informasjon brukeren trenger i en lukket accordion. Innhold som er nødvendig for å fullføre en oppgave bør alltid være synlig.

## Tilgjengelighet

- Hvert element har `role="region"` med `aria-labelledby` koblet til triggeren
- Piltaster navigerer mellom seksjoner
- `Enter` eller `Space` åpner/lukker en seksjon
- Chevron-ikon roterer for å vise åpen/lukket tilstand

## Props

### Accordion (rot)

| Prop           | Type                     | Default | Beskrivelse                                            |
| -------------- | ------------------------ | ------- | ------------------------------------------------------ |
| `type`         | `"single" \| "multiple"` | —       | Om én eller flere seksjoner kan være åpne samtidig     |
| `collapsible`  | `boolean`                | `false` | Om den åpne seksjonen kan lukkes (kun `type="single"`) |
| `defaultValue` | `string \| string[]`     | —       | Seksjon(er) som er åpne ved oppstart                   |
| `value`        | `string \| string[]`     | —       | Kontrollert åpne seksjon(er)                           |

### AccordionItem

| Prop    | Type     | Default | Beskrivelse                      |
| ------- | -------- | ------- | -------------------------------- |
| `value` | `string` | —       | Unik identifikator for elementet |

### AccordionTrigger

Arver alle props fra `<button>`.

### AccordionContent

Arver alle props fra `<div>`. Innholdet animeres med en glidende overgang.

---

# Heading

> Overskriftskomponent med konfigurerbart semantisk nivå og visuell størrelse.

[Kildekode på GitHub](https://github.com/bekk/designsystem/blob/main/packages/designsystem/src/ui/heading.tsx)

Heading brukes til overskrifter og titler. Semantisk nivå (`h1`–`h6`) og visuell størrelse er uavhengige — du kan for eksempel ha en `h2` som ser ut som en `h1`.

## Utforsk

*Se interaktiv playground på dokumentasjonssiden.*

## Importering

```tsx
import { Heading } from "@kilden/designsystem";
```

## Størrelser

```tsx
<div style={{ display: "flex", flexDirection: "column", gap: "1rem" }}>
  <Heading size="3xl">Størrelse 3xl</Heading>
  <Heading size="2xl">Størrelse 2xl (standard)</Heading>
  <Heading size="xl">Størrelse xl</Heading>
  <Heading size="lg">Størrelse lg</Heading>
</div>
```

## Semantisk nivå

Bruk `as`-prop for å kontrollere HTML-elementet uavhengig av visuell størrelse:

```tsx
<div style={{ display: "flex", flexDirection: "column", gap: "0.5rem" }}>
  <Heading as="h1" size="3xl">h1 — størrelse 3xl</Heading>
  <Heading as="h2" size="2xl">h2 — størrelse 2xl</Heading>
  <Heading as="h3" size="xl">h3 — størrelse xl</Heading>
  <Heading as="h4" size="lg">h4 — størrelse lg</Heading>
</div>
```

## Frikobling av nivå og størrelse

Semantisk og visuell størrelse kan settes uavhengig av hverandre:

```tsx
{
  /* Semantisk h2, men ser ut som en h1 */
}
<Heading as="h2" size="3xl">
  Stor seksjonstittel
</Heading>;

{
  /* Semantisk h1, men kompakt visuell størrelse */
}
<Heading as="h1" size="lg">
  Liten sidetopp
</Heading>;
```

## Bruksanbefalinger

- **Gjør: Bruk riktig semantisk nivå** — Overskriftshierarkiet (h1 → h2 → h3) skal alltid følge dokumentstrukturen, uavhengig av visuell størrelse.
- **Unngå: Ikke hopp over nivåer** — Unngå å hoppe fra h1 direkte til h3 — det bryter med tilgjengelighetsstandarden.

## Tilgjengelighet

- Rendres som `h2` som standard — husk å sette `as` til riktig nivå for konteksten
- Semantisk nivå bestemmer skjermleseropplesing og dokumentoversikt

## Props

| Prop      | Type                                           | Default | Beskrivelse                                          |
| --------- | ---------------------------------------------- | ------- | ---------------------------------------------------- |
| `size`    | `"3xl" \| "2xl" \| "xl" \| "lg"`               | `"2xl"` | Visuell størrelse                                    |
| `as`      | `"h1" \| "h2" \| "h3" \| "h4" \| "h5" \| "h6"` | `"h2"`  | Semantisk HTML-element                               |
| `asChild` | `boolean`                                      | `false` | Rendrer barn-elementet som overskriften (Radix Slot) |

---

# Checkbox

> Avkryssingsboks for binære valg og flervalg i skjemaer.

Checkbox,
  Label,
  FormField,
  FormDescription,
  FormMessage,
} from "@kilden/designsystem";

# Checkbox

[Kildekode på GitHub](https://github.com/bekk/designsystem/blob/main/packages/designsystem/src/ui/checkbox.tsx)

Checkbox er en avkryssingsboks for binære valg. Til forskjell fra Switch er den ment for skjemaer der endringen ikke trer i kraft umiddelbart, men først ved innsending. Bygget på [Radix UI Checkbox](https://www.radix-ui.com/primitives/docs/components/checkbox).

## Utforsk

*Se interaktiv playground på dokumentasjonssiden.*

## Importering

```tsx
import { Checkbox } from "@kilden/designsystem";
```

## Grunnleggende bruk

```tsx
<div style={{ display: "flex", alignItems: "center", gap: "0.5rem" }}>
  <Checkbox id="terms" />
  <Label htmlFor="terms">Jeg godtar vilkårene</Label>
</div>
```

## Flere checkboxer

```tsx
<div style={{ display: "grid", gap: "0.75rem" }}>
  <div style={{ display: "flex", alignItems: "center", gap: "0.5rem" }}>
    <Checkbox id="email-notif" defaultChecked />
    <Label htmlFor="email-notif">E-postvarsler</Label>
  </div>
  <div style={{ display: "flex", alignItems: "center", gap: "0.5rem" }}>
    <Checkbox id="sms-notif" />
    <Label htmlFor="sms-notif">SMS-varsler</Label>
  </div>
  <div style={{ display: "flex", alignItems: "center", gap: "0.5rem" }}>
    <Checkbox id="push-notif" defaultChecked />
    <Label htmlFor="push-notif">Push-varsler</Label>
  </div>
</div>
```

## Med FormField

```tsx
<FormField>
  <div style={{ display: "flex", alignItems: "center", gap: "0.5rem" }}>
    <Checkbox id="newsletter" />
    <Label htmlFor="newsletter">Meld meg på nyhetsbrevet</Label>
  </div>
  <FormDescription>Du kan melde deg av når som helst.</FormDescription>
</FormField>
```

## Deaktivert

```tsx
<div style={{ display: "grid", gap: "0.75rem" }}>
  <div style={{ display: "flex", alignItems: "center", gap: "0.5rem" }}>
    <Checkbox id="disabled-unchecked" disabled />
    <Label htmlFor="disabled-unchecked" style={{ opacity: 0.5 }}>Deaktivert (av)</Label>
  </div>
  <div style={{ display: "flex", alignItems: "center", gap: "0.5rem" }}>
    <Checkbox id="disabled-checked" disabled defaultChecked />
    <Label htmlFor="disabled-checked" style={{ opacity: 0.5 }}>Deaktivert (på)</Label>
  </div>
</div>
```

## Bruksanbefalinger

- **Gjør: Bruk i skjemaer med lagre-knapp** — Checkbox passer for valg som sendes inn med et skjema, som samtykke, preferanser eller flervalg.
- **Unngå: Ikke bruk for umiddelbare endringer** — For innstillinger som trer i kraft med en gang (mørk modus, varsler av/på), bruk Switch i stedet.

- **Gjør: Bruk for flervalg** — Når brukeren kan velge flere alternativer fra en liste, bruk checkboxer.
- **Unngå: Ikke bruk for enkeltvalg mellom alternativer** — Når brukeren skal velge ett alternativ fra en liste, bruk RadioGroup eller Select.

## Tilgjengelighet

- `Space` toggler checkboxen
- Støtter `checked`, `unchecked` og `indeterminate` tilstander
- Kobles til label via `id`/`htmlFor`
- Deaktivert tilstand kommuniseres via `aria-disabled`

## Props

| Prop              | Type                                            | Default | Beskrivelse                           |
| ----------------- | ----------------------------------------------- | ------- | ------------------------------------- |
| `checked`         | `boolean \| "indeterminate"`                    | —       | Kontrollert tilstand                  |
| `defaultChecked`  | `boolean`                                       | `false` | Starttilstand (ukontrollert)          |
| `onCheckedChange` | `(checked: boolean \| "indeterminate") => void` | —       | Kalles når tilstanden endres          |
| `disabled`        | `boolean`                                       | `false` | Deaktiver checkboxen                  |
| `required`        | `boolean`                                       | `false` | Marker som påkrevd i skjema           |
| `name`            | `string`                                        | —       | Navn for skjemainnsending             |
| `value`           | `string`                                        | `"on"`  | Verdi som sendes ved skjemainnsending |

---

# DateInput

> Datovelger med tekstfelt for manuell inntasting og kalender-popover.

[Kildekode på GitHub](https://github.com/bekk/designsystem/blob/main/packages/designsystem/src/ui/date-input.tsx)

DateInput er en datovelger der brukeren kan skrive inn datoen direkte i et tekstfelt (dd.mm.åååå) eller velge fra en kalender-popover. Viser den valgte datoen i lesbart norsk format under inputfeltet.

DateInput støtter de samme propsene som [Input](/docs/components/input) — blant annet `label`, `error`, `name`, `id` og `aria-*` — og kobles automatisk til `FormField`.

## Utforsk

*Se interaktiv playground på dokumentasjonssiden.*

## Importering

```tsx
import { DateInput } from "@kilden/designsystem";
```

## Grunnleggende bruk

```tsx
function DateInputDemo() {
  const [date, setDate] = React.useState(undefined);
  return (
    <DateInput
      value={date}
      onValueChange={setDate}
    />
  );
}
```

## Med forhåndsvalgt dato

```tsx
function DateInputPreselected() {
  const [date, setDate] = React.useState(new Date());
  return (
    <DateInput
      value={date}
      onValueChange={setDate}
    />
  );
}
```

## Custom plassholder

```tsx
function DateInputPlaceholder() {
  const [date, setDate] = React.useState(undefined);
  return (
    <DateInput
      value={date}
      onValueChange={setDate}
      placeholder="Fødselsdato"
    />
  );
}
```

## Med label og feilmelding

```tsx
function DateInputLabel() {
  const [date, setDate] = React.useState(undefined);
  return (
    <DateInput
      label="Startdato"
      name="startdato"
      required
      error={date ? undefined : "Velg en startdato"}
      value={date}
      onValueChange={setDate}
    />
  );
}
```

## Deaktivert

```tsx
<DateInput disabled />
```

## Med begrensede datoer

Bruk `calendarProps` for å sende props videre til Calendar-komponenten.

```tsx
function DateInputConstrained() {
  const [date, setDate] = React.useState(undefined);
  return (
    <DateInput
      value={date}
      onValueChange={setDate}
      calendarProps={{
        disabled: [
          { dayOfWeek: [0, 6] },
          { before: new Date() },
        ],
      }}
    />
  );
}
```

## Bruksanbefalinger

- **Gjør: Bruk når brukeren kjenner datoen** — DateInput er ideell når brukeren sannsynligvis vet datoen og vil skrive den inn direkte, men også kan bruke kalenderen som støtte.
- **Unngå: Ikke bruk når datoen er ukjent** — Hvis brukeren må bla i kalenderen for å finne riktig dato (f.eks. neste ledige time), bruk DatePicker eller Calendar direkte.

- **Gjør: Bruk i skjemaer med label** — Gi alltid DateInput en label — via label-propen eller en FormField med Label.
- **Unngå: Ikke bruk for datoperioder** — For valg av en periode (fra–til) bør du bruke to separate DateInput-felt eller Calendar med mode='range'.

## Tilgjengelighet

- Inputfeltet støtter direkte tastaturinntasting med `inputMode="numeric"`
- `label`-propen (eller en `FormField` med ``) kobler label til inputfeltet
- `error`-propen setter `aria-invalid` og kobler feilmelding via `aria-describedby`
- Kalender-knappen har `aria-label="Åpne kalender"`
- Full tastaturnavigasjon inne i kalenderen
- `Escape` lukker popoveren og returnerer fokus
- Deaktivert tilstand kommuniseres via `disabled`-attributt på både input og knapp
- Lesbar dato under feltet gir visuell bekreftelse på tolket verdi

## Props

| Prop            | Type                                | Default        | Beskrivelse                                   |
| --------------- | ----------------------------------- | -------------- | --------------------------------------------- |
| `value`         | `Date`                              | —              | Den valgte datoen                             |
| `onValueChange` | `(date: Date \| undefined) => void` | —              | Kalles når datoen endres                      |
| `label`         | `ReactNode`                         | —              | Synlig label over feltet                      |
| `error`         | `ReactNode`                         | —              | Feilmelding under feltet                      |
| `name`          | `string`                            | —              | Navn for skjemainnsending (verdi: dd.mm.åååå) |
| `placeholder`   | `string`                            | `"dd.mm.åååå"` | Plassholdertekst for inputfeltet              |
| `disabled`      | `boolean`                           | `false`        | Deaktiver datovelgeren                        |
| `required`      | `boolean`                           | `false`        | Marker som påkrevd                            |
| `className`     | `string`                            | —              | Ekstra CSS-klasser for container-elementet    |
| `calendarProps` | `CalendarProps`                     | —              | Props som sendes videre til Calendar          |

I tillegg sendes øvrige input-props (f.eks. `id`, `aria-*`, `onBlur`) videre til inputfeltet.

---

# JumpToMainContent

> En usynlig lenke som lar tastaturbrukere hoppe rett til hovedinnholdet på siden.

[Kildekode på GitHub](https://github.com/bekk/designsystem/blob/main/packages/designsystem/src/ui/jump-to-main-content.tsx)

En «hopp til hovedinnhold»-lenke som er usynlig til den mottar fokus via Tab-tasten. Dette er et viktig tilgjengelighetsmønster som lar tastaturbrukere og skjermleserbrukere hoppe over repeterende navigasjon og gå rett til sidens hovedinnhold.

## Utforsk

*Se interaktiv playground på dokumentasjonssiden.*

## Importering

```tsx
import { JumpToMainContent } from "@kilden/designsystem";
```

## Grunnleggende bruk

Plassér komponenten som det aller første elementet i `<body>`, og gi hovedinnholdet en matchende `id`:

```tsx
<body>
  <JumpToMainContent />
  <header>...</header>
  <main id="main">Hovedinnhold her</main>
</body>
```

Komponenten er helt usynlig — trykk **Tab** for å se den.

## Egendefinert mål

Du kan peke lenken til et annet element enn `#main`:

```tsx
<JumpToMainContent targetId="#content" />
```

## Egendefinert tekst

Standardteksten er «Hopp til hovedinnhold», men du kan tilpasse den:

```tsx
<JumpToMainContent>Gå til innhold</JumpToMainContent>
```

## Tilgjengelighet

- Komponenten bruker `sr-only` for å skjule seg visuelt, men den er alltid tilgjengelig for skjermlesere
- Ved fokus (Tab) vises den som en tydelig lenke med god kontrast øverst i venstre hjørne
- Den følger [WCAG 2.4.1 — Bypass Blocks](https://www.w3.org/WAI/WCAG21/Understanding/bypass-blocks.html)
- Alle sider bør ha denne komponenten som det første fokuserbare elementet

## Bruksanbefalinger

## Props

| Prop       | Type     | Default                   | Beskrivelse                              |
| ---------- | -------- | ------------------------- | ---------------------------------------- |
| `targetId` | `string` | `"#main"`                 | ID-selector til elementet man hopper til |
| `children` | `node`   | `"Hopp til hovedinnhold"` | Teksten som vises når lenken er fokusert |

---

# DropdownMenu

> Kontekstmeny med handlinger, avkrysning og tastaturnavigasjon.

DropdownMenu,
  DropdownMenuTrigger,
  DropdownMenuContent,
  DropdownMenuItem,
  DropdownMenuCheckboxItem,
  DropdownMenuRadioItem,
  DropdownMenuRadioGroup,
  DropdownMenuLabel,
  DropdownMenuSeparator,
  DropdownMenuShortcut,
  DropdownMenuGroup,
  DropdownMenuSub,
  DropdownMenuSubTrigger,
  DropdownMenuSubContent,
  Button,
} from "@kilden/designsystem";

# DropdownMenu

[Kildekode på GitHub](https://github.com/bekk/designsystem/blob/main/packages/designsystem/src/ui/dropdown-menu.tsx)

DropdownMenu viser en liste med handlinger eller valg i en flytende meny. Bygget på [Radix UI DropdownMenu](https://www.radix-ui.com/primitives/docs/components/dropdown-menu) med full tastaturnavigasjon og tilgjengelighet.

## Importering

```tsx
import {
  DropdownMenu,
  DropdownMenuTrigger,
  DropdownMenuContent,
  DropdownMenuItem,
  DropdownMenuSeparator,
  DropdownMenuLabel,
} from "@kilden/designsystem";
```

## Grunnleggende bruk

```tsx
<DropdownMenu>
  <DropdownMenuTrigger asChild>
    <Button variant="secondary">Åpne meny</Button>
  </DropdownMenuTrigger>
  <DropdownMenuContent>
    <DropdownMenuLabel>Min konto</DropdownMenuLabel>
    <DropdownMenuSeparator />
    <DropdownMenuItem>Profil</DropdownMenuItem>
    <DropdownMenuItem>Innstillinger</DropdownMenuItem>
    <DropdownMenuItem>Fakturering</DropdownMenuItem>
    <DropdownMenuSeparator />
    <DropdownMenuItem>Logg ut</DropdownMenuItem>
  </DropdownMenuContent>
</DropdownMenu>
```

## Med hurtigtaster

```tsx
<DropdownMenu>
  <DropdownMenuTrigger asChild>
    <Button variant="secondary">Rediger</Button>
  </DropdownMenuTrigger>
  <DropdownMenuContent style={{ minWidth: "10rem" }}>
    <DropdownMenuItem>
      Klipp ut
      <DropdownMenuShortcut>⌘X</DropdownMenuShortcut>
    </DropdownMenuItem>
    <DropdownMenuItem>
      Kopier
      <DropdownMenuShortcut>⌘C</DropdownMenuShortcut>
    </DropdownMenuItem>
    <DropdownMenuItem>
      Lim inn
      <DropdownMenuShortcut>⌘V</DropdownMenuShortcut>
    </DropdownMenuItem>
    <DropdownMenuSeparator />
    <DropdownMenuItem>
      Velg alt
      <DropdownMenuShortcut>⌘A</DropdownMenuShortcut>
    </DropdownMenuItem>
  </DropdownMenuContent>
</DropdownMenu>
```

## Med deaktiverte elementer

```tsx
<DropdownMenu>
  <DropdownMenuTrigger asChild>
    <Button variant="secondary">Handlinger</Button>
  </DropdownMenuTrigger>
  <DropdownMenuContent>
    <DropdownMenuItem>Rediger</DropdownMenuItem>
    <DropdownMenuItem>Dupliser</DropdownMenuItem>
    <DropdownMenuSeparator />
    <DropdownMenuItem disabled>Arkiver</DropdownMenuItem>
    <DropdownMenuItem disabled>Slett</DropdownMenuItem>
  </DropdownMenuContent>
</DropdownMenu>
```

## Bruksanbefalinger

- **Gjør: Bruk for kontekstuelle handlinger** — DropdownMenu egner seg for handlinger knyttet til et spesifikt element — som en rad i en tabell, et kort eller en bruker.
- **Unngå: Ikke bruk for navigasjon** — For navigasjon mellom sider, bruk NavigationMenu eller vanlige lenker. DropdownMenu er for handlinger, ikke navigering.

## Tilgjengelighet

- Bruker `role="menu"` og `role="menuitem"` automatisk
- Piltaster navigerer mellom elementer
- `Enter` eller `Space` aktiverer det fokuserte elementet
- `Escape` lukker menyen og returnerer fokus til trigger
- Bokstavtaster hopper til matchende element (typeahead)
- Deaktiverte elementer hoppes over ved tastaturnavigasjon

## Props

### DropdownMenu (rot)

| Prop           | Type                      | Default | Beskrivelse                |
| -------------- | ------------------------- | ------- | -------------------------- |
| `open`         | `boolean`                 | —       | Kontrollert åpen-tilstand  |
| `onOpenChange` | `(open: boolean) => void` | —       | Kalles når tilstand endres |
| `defaultOpen`  | `boolean`                 | `false` | Standard åpen-tilstand     |

### DropdownMenuContent

| Prop         | Type                                     | Default    | Beskrivelse              |
| ------------ | ---------------------------------------- | ---------- | ------------------------ |
| `side`       | `"top" \| "right" \| "bottom" \| "left"` | `"bottom"` | Hvilken side av trigger  |
| `sideOffset` | `number`                                 | `4`        | Avstand fra trigger i px |
| `align`      | `"start" \| "center" \| "end"`           | `"center"` | Justering langs siden    |

### DropdownMenuItem

| Prop       | Type      | Default | Beskrivelse                |
| ---------- | --------- | ------- | -------------------------- |
| `disabled` | `boolean` | `false` | Deaktiver elementet        |
| `inset`    | `boolean` | `false` | Innrykk for ikon-justering |

---

# Progress

> Fremdriftsindikator for lasting og prosesser.

[Kildekode på GitHub](https://github.com/bekk/designsystem/blob/main/packages/designsystem/src/ui/progress.tsx)

Progress viser hvor langt en prosess har kommet. Bygget på [Radix UI Progress](https://www.radix-ui.com/primitives/docs/components/progress) med riktig `role="progressbar"` og ARIA-attributter.

## Importering

```tsx
import { Progress } from "@kilden/designsystem";
```

## Grunnleggende bruk

```tsx
<div style={{ maxWidth: "24rem" }}>
  <Progress value={60} aria-label="Fremdrift" />
</div>
```

## Ulike verdier

```tsx
<div style={{ display: "grid", gap: "1rem", maxWidth: "24rem" }}>
  <div>
    <p style={{ fontSize: "0.875rem", marginBottom: "0.25rem" }}>0%</p>
    <Progress value={0} aria-label="0 prosent" />
  </div>
  <div>
    <p style={{ fontSize: "0.875rem", marginBottom: "0.25rem" }}>25%</p>
    <Progress value={25} aria-label="25 prosent" />
  </div>
  <div>
    <p style={{ fontSize: "0.875rem", marginBottom: "0.25rem" }}>50%</p>
    <Progress value={50} aria-label="50 prosent" />
  </div>
  <div>
    <p style={{ fontSize: "0.875rem", marginBottom: "0.25rem" }}>75%</p>
    <Progress value={75} aria-label="75 prosent" />
  </div>
  <div>
    <p style={{ fontSize: "0.875rem", marginBottom: "0.25rem" }}>100%</p>
    <Progress value={100} aria-label="100 prosent" />
  </div>
</div>
```

## Med label

```tsx
<FormField style={{ maxWidth: "24rem" }}>
  <div style={{ display: "flex", justifyContent: "space-between" }}>
    <Label>Opplasting</Label>
    <span style={{ fontSize: "0.875rem", opacity: 0.7 }}>73%</span>
  </div>
  <Progress value={73} aria-label="Opplasting" />
</FormField>
```

## Bruksanbefalinger

- **Gjør: Bruk for bestemte prosesser** — Progress passer for prosesser der du kjenner fremdriften — opplasting, nedlasting, flersteg-skjemaer.
- **Unngå: Ikke bruk for ubestemt lasting** — For lasting der du ikke vet hvor lang tid det tar, bruk Skeleton eller en spinner i stedet.

## Tilgjengelighet

- Bruker `role="progressbar"` automatisk
- `aria-valuenow`, `aria-valuemin` og `aria-valuemax` settes automatisk
- Legg til `aria-label` eller koble til en label med `aria-labelledby`

## Props

| Prop            | Type                                     | Default | Beskrivelse                      |
| --------------- | ---------------------------------------- | ------- | -------------------------------- |
| `value`         | `number`                                 | `0`     | Nåværende verdi (0–100)          |
| `max`           | `number`                                 | `100`   | Maksimumsverdi                   |
| `getValueLabel` | `(value: number, max: number) => string` | —       | Tilpasset label for skjermlesere |

---

# Avatar

> Brukerbilder med fallback til initialer.

[Kildekode på GitHub](https://github.com/bekk/designsystem/blob/main/packages/designsystem/src/ui/avatar.tsx)

Avatar viser et brukerbilde med automatisk fallback til initialer når bildet ikke kan lastes. Bygget på [Radix UI Avatar](https://www.radix-ui.com/primitives/docs/components/avatar).

## Importering

```tsx
import { Avatar, AvatarImage, AvatarFallback } from "@kilden/designsystem";
```

## Grunnleggende bruk

```tsx
<div style={{ display: "flex", gap: "1rem", alignItems: "center" }}>
  <Avatar>
    <AvatarFallback>ON</AvatarFallback>
  </Avatar>
  <Avatar>
    <AvatarFallback>KN</AvatarFallback>
  </Avatar>
  <Avatar>
    <AvatarFallback>AB</AvatarFallback>
  </Avatar>
</div>
```

## Ulike størrelser

Bruk Tailwind-klasser for å endre størrelse.

```tsx
<div style={{ display: "flex", gap: "1rem", alignItems: "center" }}>
  <Avatar className="h-8 w-8">
    <AvatarFallback className="text-xs">S</AvatarFallback>
  </Avatar>
  <Avatar>
    <AvatarFallback>M</AvatarFallback>
  </Avatar>
  <Avatar className="h-14 w-14">
    <AvatarFallback className="text-lg">L</AvatarFallback>
  </Avatar>
  <Avatar className="h-20 w-20">
    <AvatarFallback className="text-xl">XL</AvatarFallback>
  </Avatar>
</div>
```

## Gruppe av avatarer

```tsx
<div style={{ display: "flex" }}>
  <Avatar className="border-2 border-white">
    <AvatarFallback>A</AvatarFallback>
  </Avatar>
  <Avatar className="-ml-3 border-2 border-white">
    <AvatarFallback>B</AvatarFallback>
  </Avatar>
  <Avatar className="-ml-3 border-2 border-white">
    <AvatarFallback>C</AvatarFallback>
  </Avatar>
  <Avatar className="-ml-3 border-2 border-white">
    <AvatarFallback className="text-xs">+3</AvatarFallback>
  </Avatar>
</div>
```

## Bruksanbefalinger

- **Gjør: Inkluder alltid en fallback** — Bilder kan feile. Bruk alltid AvatarFallback med initialer eller et ikon som reserveløsning.
- **Unngå: Ikke bruk for dekorative bilder** — Avatar er ment for brukeridentifisering. For generelle bilder, bruk vanlige img-elementer.

## Tilgjengelighet

- `AvatarImage` aksepterer `alt`-tekst for skjermlesere
- Fallback vises umiddelbart hvis det ikke er noe bilde, eller etter en kort forsinkelse mens bildet lastes
- For rent dekorative avatarer, bruk `alt=""` på bildet

## Props

### Avatar

Arver alle props fra Radix `Avatar.Root`. Standard størrelse er `h-10 w-10`.

### AvatarImage

| Prop  | Type     | Default | Beskrivelse                 |
| ----- | -------- | ------- | --------------------------- |
| `src` | `string` | —       | URL til brukerbildet        |
| `alt` | `string` | —       | Alternativ tekst for bildet |

### AvatarFallback

| Prop       | Type        | Default | Beskrivelse                         |
| ---------- | ----------- | ------- | ----------------------------------- |
| `delayMs`  | `number`    | —       | Forsinkelse i ms før fallback vises |
| `children` | `ReactNode` | —       | Innhold (initialer, ikon)           |

---

# Ingress

> Ingresstekst som introduserer og fremhever innhold, basert på leading-typografi-tokenet.

[Kildekode på GitHub](https://github.com/bekk/designsystem/blob/main/packages/designsystem/src/ui/ingress.tsx)

Ingress brukes til ingresstekst og fremhevet brødtekst som introduserer innholdet på en side.

## Utforsk

*Se interaktiv playground på dokumentasjonssiden.*

## Importering

```tsx
import { Ingress } from "@kilden/designsystem";
```

## Eksempel

```tsx
<Ingress>Ingress som introduserer resten av innholdet på siden.</Ingress>
```

## HTML-element

Bruk `as`-prop for å velge element uavhengig av størrelse:

```tsx
<div style={{ display: "flex", flexDirection: "column", gap: "0.5rem" }}>
  <Ingress as="p">Avsnitt (standard)</Ingress>
  <Ingress as="span">Inline span</Ingress>
</div>
```

## Tilgjengelighet

- Rendres som `<p>` som standard — sett `as` til `span` eller `div` ved behov for inline- eller blokk-kontekst

## Props

| Prop      | Type                                         | Default | Beskrivelse                                     |
| --------- | -------------------------------------------- | ------- | ----------------------------------------------- |
| `as`      | `"p" \| "span" \| "div" \| "strong" \| "em"` | `"p"`   | HTML-element                                    |
| `asChild` | `boolean`                                    | `false` | Rendrer barn-elementet som teksten (Radix Slot) |

---

# Chip

> Interaktiv chip for filtrering og valg med toggle-funksjonalitet.

[Kildekode på GitHub](https://github.com/bekk/designsystem/blob/main/packages/designsystem/src/ui/chip.tsx)

Chip er en interaktiv komponent for filtrering og valg. Den fungerer som en toggle-knapp med tydelig visuell tilbakemelding — ved valg får den mørk bakgrunn og viser et kryss-ikon for å indikere at den kan fjernes. Krysset vises både i kontrollert og ukontrollert modus (`defaultPressed`). Bygget på [Radix UI Toggle](https://www.radix-ui.com/primitives/docs/components/toggle).

## Utforsk

*Se interaktiv playground på dokumentasjonssiden.*

## Importering

```tsx
import { Chip } from "@kilden/designsystem";
```

## Grunnleggende bruk

```tsx
<Chip>Filter</Chip>
```

## Kontrollert bruk

```tsx
function ChipDemo() {
  const [pressed, setPressed] = React.useState(false);
  return (
    <Chip pressed={pressed} onPressedChange={setPressed}>
      Aktiv
    </Chip>
  );
}
```

## Filtergruppe

```tsx
function FilterDemo() {
  const [filters, setFilters] = React.useState({
    design: false,
    utvikling: true,
    strategi: false,
  });
  const toggle = (key) =>
    setFilters((prev) => ({ ...prev, [key]: !prev[key] }));
  return (
    <div style={{ display: "flex", gap: "0.5rem", flexWrap: "wrap" }}>
      <Chip pressed={filters.design} onPressedChange={() => toggle("design")}>
        Design
      </Chip>
      <Chip pressed={filters.utvikling} onPressedChange={() => toggle("utvikling")}>
        Utvikling
      </Chip>
      <Chip pressed={filters.strategi} onPressedChange={() => toggle("strategi")}>
        Strategi
      </Chip>
    </div>
  );
}
```

## Bruksanbefalinger

- **Gjør: Bruk for filtrering og valg** — Chip passer for filterknapper der brukeren kan velge og fjerne aktive filtre.
- **Unngå: Ikke bruk for statisk metadata** — For informasjon som ikke kan togles, bruk Badge i stedet.

- **Gjør: Bruk korte, beskrivende labels** — Hold chip-teksten kort og tydelig — ett eller to ord er ideelt.
- **Unngå: Ikke bruk for lange tekster** — Chip er ment for korte labels. For lengre innhold, vurder andre komponenter.

## Tilgjengelighet

- Bruker `aria-pressed` for å kommunisere av/på-tilstand til skjermlesere
- `Enter` eller `Space` toggler chippen
- Fokusring med tydelig kontrast for tastaturnavigasjon
- Deaktivert tilstand kommuniseres via `disabled`

## Props

| Prop              | Type                         | Default | Beskrivelse                  |
| ----------------- | ---------------------------- | ------- | ---------------------------- |
| `pressed`         | `boolean`                    | —       | Kontrollert av/på-tilstand   |
| `defaultPressed`  | `boolean`                    | `false` | Starttilstand (ukontrollert) |
| `onPressedChange` | `(pressed: boolean) => void` | —       | Kalles når tilstanden endres |
| `disabled`        | `boolean`                    | `false` | Deaktiver chippen            |

---

# DatePicker

> Datovelger som kombinerer en kalender i en popover med en trigger-knapp.

[Kildekode på GitHub](https://github.com/bekk/designsystem/blob/main/packages/designsystem/src/ui/date-picker.tsx)

DatePicker er en datovelger som viser en knapp med den valgte datoen formatert på norsk. Ved klikk åpnes en kalender-popover for å velge dato. Kombinerer Calendar og Popover med en trigger i samme boksstil som de andre skjemafeltene, i full bredde.

DatePicker støtter `label` og `error` som [Input](/docs/components/input), og kobles automatisk til `FormField`. Med `name` sendes valgt dato med skjemaet i ISO-format (`åååå-mm-dd`) via et skjult inputfelt.

## Utforsk

*Se interaktiv playground på dokumentasjonssiden.*

## Importering

```tsx
import { DatePicker } from "@kilden/designsystem";
```

## Grunnleggende bruk

```tsx
function DatePickerDemo() {
  const [date, setDate] = React.useState(undefined);
  return (
    <DatePicker
      value={date}
      onValueChange={setDate}
    />
  );
}
```

## Med forhåndsvalgt dato

```tsx
function DatePickerPreselected() {
  const [date, setDate] = React.useState(new Date());
  return (
    <DatePicker
      value={date}
      onValueChange={setDate}
    />
  );
}
```

## Custom plassholder

```tsx
function DatePickerPlaceholder() {
  const [date, setDate] = React.useState(undefined);
  return (
    <DatePicker
      value={date}
      onValueChange={setDate}
      placeholder="Velg fødselsdato"
    />
  );
}
```

## Med label, navn og feilmelding

```tsx
function DatePickerLabel() {
  const [date, setDate] = React.useState(undefined);
  return (
    <div style={{ maxWidth: "20rem" }}>
      <DatePicker
        label="Dato"
        name="dato"
        required
        error={date ? undefined : "Velg en dato"}
        value={date}
        onValueChange={setDate}
      />
    </div>
  );
}
```

## Deaktivert

```tsx
<DatePicker disabled placeholder="Ikke tilgjengelig" />
```

## Med begrensede datoer

Bruk `calendarProps` for å sende props videre til Calendar-komponenten, for eksempel for å deaktivere helger eller datoer i fortiden.

```tsx
function DatePickerConstrained() {
  const [date, setDate] = React.useState(undefined);
  return (
    <DatePicker
      value={date}
      onValueChange={setDate}
      placeholder="Velg arbeidsdag"
      calendarProps={{
        disabled: [
          { dayOfWeek: [0, 6] },
          { before: new Date() },
        ],
      }}
    />
  );
}
```

## Bruksanbefalinger

- **Gjør: Bruk for enkeltdatoer i skjemaer** — DatePicker er ideell for skjemafelt der brukeren skal velge én dato, som fødselsdato eller startdato.
- **Unngå: Ikke bruk for datoperioder** — For valg av en periode (fra–til) bør du bruke Calendar med mode='range' direkte, eller en egen DateRangePicker.

- **Gjør: Formater datoen for brukeren** — DatePicker viser datoen på norsk automatisk (f.eks. '13. mars 2026'). Brukeren trenger aldri å skrive inn datoen manuelt.
- **Unngå: Ikke bruk for kjent dato** — Hvis brukeren allerede vet datoen (som et ordrenummer eller fakturadato), bruk et vanlig Input-felt i stedet.

## Tilgjengelighet

- Trigger-knappen er en standard `<button>` med synlig tekst
- Kalender-popoveren åpnes med klikk eller Enter/Space
- Full tastaturnavigasjon inne i kalenderen
- `Escape` lukker popoveren og returnerer fokus til knappen
- Valgt dato annonseres som knappetekst
- Deaktivert tilstand kommuniseres via `disabled`-attributt
- `label`-propen (eller en `FormField` med ``) gir triggeren et tilgjengelig navn, og `error` kobles via `aria-describedby`

## Props

| Prop            | Type                                | Default       | Beskrivelse                                                                                                                |
| --------------- | ----------------------------------- | ------------- | -------------------------------------------------------------------------------------------------------------------------- |
| `value`         | `Date`                              | —             | Den valgte datoen                                                                                                          |
| `onValueChange` | `(date: Date \| undefined) => void` | —             | Kalles når brukeren velger en dato                                                                                         |
| `label`         | `ReactNode`                         | —             | Synlig label over feltet                                                                                                   |
| `error`         | `ReactNode`                         | —             | Feilmelding under feltet                                                                                                   |
| `name`          | `string`                            | —             | Navn for skjemainnsending. Valgt dato sendes i ISO-format (`åååå-mm-dd`) via et skjult felt                                |
| `placeholder`   | `string`                            | `"Velg dato"` | Plassholdertekst når ingen dato er valgt                                                                                   |
| `disabled`      | `boolean`                           | `false`       | Deaktiver datovelgeren                                                                                                     |
| `required`      | `boolean`                           | `false`       | Viser påkrevd-markering i labelen. Gir ingen nativ nettleser-validering siden triggeren er en knapp, ikke et skjemaelement |
| `className`     | `string`                            | —             | Ekstra CSS-klasser for trigger-knappen                                                                                     |
| `calendarProps` | `CalendarProps`                     | —             | Props som sendes videre til Calendar                                                                                       |

I tillegg sendes øvrige knappe-props (f.eks. `id`, `aria-*`) videre til triggeren.

---

# NavigationMenu

> Toppnivå-navigasjonsmeny med dropdowns.

NavigationMenu,
  NavigationMenuList,
  NavigationMenuItem,
  NavigationMenuContent,
  NavigationMenuTrigger,
  NavigationMenuLink,
} from "@kilden/designsystem";

# NavigationMenu

[Kildekode på GitHub](https://github.com/bekk/designsystem/blob/main/packages/designsystem/src/ui/navigation-menu.tsx)

NavigationMenu er en toppnivå-navigasjonsmeny med støtte for dropdowns og rikt innhold. Bygget på [Radix UI NavigationMenu](https://www.radix-ui.com/primitives/docs/components/navigation-menu) med full tastaturnavigasjon.

## Importering

```tsx
import {
  NavigationMenu,
  NavigationMenuList,
  NavigationMenuItem,
  NavigationMenuContent,
  NavigationMenuTrigger,
  NavigationMenuLink,
} from "@kilden/designsystem";
```

## Grunnleggende bruk

```tsx
<NavigationMenu>
  <NavigationMenuList>
    <NavigationMenuItem>
      <NavigationMenuTrigger>Kom i gang</NavigationMenuTrigger>
      <NavigationMenuContent>
        <div style={{ padding: "1rem", width: "300px" }}>
          <p style={{ fontWeight: 600, marginBottom: "0.5rem" }}>Kom i gang</p>
          <p style={{ fontSize: "0.875rem", opacity: 0.7 }}>Lær hvordan du installerer og bruker designsystemet.</p>
        </div>
      </NavigationMenuContent>
    </NavigationMenuItem>
    <NavigationMenuItem>
      <NavigationMenuTrigger>Komponenter</NavigationMenuTrigger>
      <NavigationMenuContent>
        <div style={{ padding: "1rem", width: "300px", display: "grid", gap: "0.5rem" }}>
          <p style={{ fontWeight: 600 }}>UI-komponenter</p>
          <a href="#" style={{ fontSize: "0.875rem" }}>Button</a>
          <a href="#" style={{ fontSize: "0.875rem" }}>Card</a>
          <a href="#" style={{ fontSize: "0.875rem" }}>Dialog</a>
          <a href="#" style={{ fontSize: "0.875rem" }}>Se alle →</a>
        </div>
      </NavigationMenuContent>
    </NavigationMenuItem>
    <NavigationMenuItem>
      <NavigationMenuLink href="#" style={{ display: "inline-flex", height: "2.5rem", alignItems: "center", padding: "0 1rem", fontSize: "0.875rem", fontWeight: 500 }}>
        Dokumentasjon
      </NavigationMenuLink>
    </NavigationMenuItem>
  </NavigationMenuList>
</NavigationMenu>
```

## Med rikt innhold

```tsx
<NavigationMenu>
  <NavigationMenuList>
    <NavigationMenuItem>
      <NavigationMenuTrigger>Ressurser</NavigationMenuTrigger>
      <NavigationMenuContent>
        <div style={{ padding: "1.5rem", width: "400px", display: "grid", gridTemplateColumns: "1fr 1fr", gap: "1rem" }}>
          <div>
            <p style={{ fontWeight: 600, fontSize: "0.875rem", marginBottom: "0.25rem" }}>Dokumentasjon</p>
            <p style={{ fontSize: "0.75rem", opacity: 0.7 }}>Kom i gang med designsystemet.</p>
          </div>
          <div>
            <p style={{ fontWeight: 600, fontSize: "0.875rem", marginBottom: "0.25rem" }}>Komponenter</p>
            <p style={{ fontSize: "0.75rem", opacity: 0.7 }}>Utforsk alle tilgjengelige komponenter.</p>
          </div>
          <div>
            <p style={{ fontWeight: 600, fontSize: "0.875rem", marginBottom: "0.25rem" }}>Farger</p>
            <p style={{ fontSize: "0.75rem", opacity: 0.7 }}>Se fargepaletten og tokens.</p>
          </div>
          <div>
            <p style={{ fontWeight: 600, fontSize: "0.875rem", marginBottom: "0.25rem" }}>GitHub</p>
            <p style={{ fontSize: "0.75rem", opacity: 0.7 }}>Bidra til kodebasen.</p>
          </div>
        </div>
      </NavigationMenuContent>
    </NavigationMenuItem>
  </NavigationMenuList>
</NavigationMenu>
```

## Bruksanbefalinger

- **Gjør: Bruk for hovednavigasjon** — NavigationMenu egner seg for toppnivå-navigasjon med få, veldefinerte kategorier og eventuelt dropdowns med rikt innhold.
- **Unngå: Ikke bruk for handlingsmenyer** — For kontekstuelle handlinger (rediger, slett, kopier), bruk DropdownMenu. NavigationMenu er for sidenavigasjon.

## Tilgjengelighet

- Bruker `role="navigation"` automatisk
- Piltaster navigerer mellom elementer
- `Enter` eller `Space` åpner dropdowns
- `Escape` lukker åpne dropdowns
- Støtter `aria-current` for å markere aktiv side
- Tab navigerer gjennom synlige lenker og trigger-elementer

## Props

### NavigationMenu (rot)

| Prop            | Type                      | Default | Beskrivelse                         |
| --------------- | ------------------------- | ------- | ----------------------------------- |
| `value`         | `string`                  | —       | Kontrollert åpent element           |
| `onValueChange` | `(value: string) => void` | —       | Kalles når åpent element endres     |
| `defaultValue`  | `string`                  | —       | Standard åpent element              |
| `delayDuration` | `number`                  | `200`   | Forsinkelse i ms før dropdown åpnes |

### NavigationMenuTrigger

Arver alle props fra `<button>`. Trigger for en dropdown.

### NavigationMenuContent

Arver alle props fra Radix `NavigationMenu.Content`. Innholdet i en dropdown.

### NavigationMenuLink

| Prop     | Type      | Default | Beskrivelse               |
| -------- | --------- | ------- | ------------------------- |
| `active` | `boolean` | `false` | Markerer lenken som aktiv |
| `href`   | `string`  | —       | URL for lenken            |

---

# Popover

> Flytende innholdsboks som åpnes ved klikk, med støtte for interaktive elementer.

Popover,
  PopoverTrigger,
  PopoverContent,
  Button,
  Input,
  Separator,
} from "@kilden/designsystem";

# Popover

[Kildekode på GitHub](https://github.com/bekk/designsystem/blob/main/packages/designsystem/src/ui/popover.tsx)

Popover viser rikt innhold i en flytende boks som åpnes ved klikk. Til forskjell fra Tooltip kan en popover inneholde interaktive elementer som knapper, lenker og skjemaer. Bygget på [Radix UI Popover](https://www.radix-ui.com/primitives/docs/components/popover).

## Importering

```tsx
import { Popover, PopoverTrigger, PopoverContent } from "@kilden/designsystem";
```

## Grunnleggende bruk

```tsx
<Popover>
  <PopoverTrigger asChild>
    <Button variant="secondary">Åpne popover</Button>
  </PopoverTrigger>
  <PopoverContent>
    <div style={{ display: "grid", gap: "0.5rem" }}>
      <h4 style={{ fontWeight: 600, margin: 0 }}>Dimensjoner</h4>
      <p style={{ fontSize: "0.875rem", margin: 0, opacity: 0.7 }}>
        Sett dimensjonene for elementet.
      </p>
      <Separator />
      <div style={{ display: "grid", gap: "0.25rem" }}>
        <label htmlFor="width" style={{ fontSize: "0.875rem" }}>Bredde</label>
        <Input id="width" defaultValue="100%" />
      </div>
      <div style={{ display: "grid", gap: "0.25rem" }}>
        <label htmlFor="height" style={{ fontSize: "0.875rem" }}>Høyde</label>
        <Input id="height" defaultValue="auto" />
      </div>
    </div>
  </PopoverContent>
</Popover>
```

## Posisjonering

Popover støtter ulike plasseringer via `side` og `align`.

```tsx
<div style={{ display: "flex", gap: "1rem", flexWrap: "wrap", justifyContent: "center" }}>
  <Popover>
    <PopoverTrigger asChild>
      <Button variant="secondary">Over</Button>
    </PopoverTrigger>
    <PopoverContent side="top">
      <p style={{ margin: 0, fontSize: "0.875rem" }}>Popover over triggeren</p>
    </PopoverContent>
  </Popover>
  <Popover>
    <PopoverTrigger asChild>
      <Button variant="secondary">Under</Button>
    </PopoverTrigger>
    <PopoverContent side="bottom">
      <p style={{ margin: 0, fontSize: "0.875rem" }}>Popover under triggeren</p>
    </PopoverContent>
  </Popover>
  <Popover>
    <PopoverTrigger asChild>
      <Button variant="secondary">Venstre</Button>
    </PopoverTrigger>
    <PopoverContent side="left">
      <p style={{ margin: 0, fontSize: "0.875rem" }}>Popover til venstre</p>
    </PopoverContent>
  </Popover>
  <Popover>
    <PopoverTrigger asChild>
      <Button variant="secondary">Høyre</Button>
    </PopoverTrigger>
    <PopoverContent side="right">
      <p style={{ margin: 0, fontSize: "0.875rem" }}>Popover til høyre</p>
    </PopoverContent>
  </Popover>
</div>
```

## Med tilpasset bredde

Standard bredde er `w-72` (18rem). Du kan overstyre med `className`.

```tsx
<Popover>
  <PopoverTrigger asChild>
    <Button variant="secondary">Bred popover</Button>
  </PopoverTrigger>
  <PopoverContent className="w-96">
    <div style={{ display: "grid", gap: "0.5rem" }}>
      <h4 style={{ fontWeight: 600, margin: 0 }}>Innstillinger</h4>
      <p style={{ fontSize: "0.875rem", margin: 0, opacity: 0.7 }}>
        Her kan du konfigurere ulike innstillinger for komponenten.
        Denne popoveren er bredere enn standard.
      </p>
    </div>
  </PopoverContent>
</Popover>
```

## Bruksanbefalinger

- **Gjør: Bruk for interaktivt innhold** — Popover passer for skjemaer, innstillinger og innhold brukeren skal interagere med — altså innhold som trenger å forbli synlig mens du bruker det.
- **Unngå: Ikke bruk for enkel tekst** — For ren informasjon uten interaksjon, bruk Tooltip i stedet. Popover er overkill for en kort beskrivelse.

- **Gjør: Hold innholdet fokusert** — En popover bør ha ett tydelig formål. Hold innholdet relevant og begrenset.
- **Unngå: Ikke legg for mye innhold i en popover** — Hvis innholdet er komplekst nok til å trenge scrolling, bør du bruke en Dialog i stedet.

## Tilgjengelighet

- Åpnes med klikk eller Enter/Space på triggeren
- Fokus flyttes inn i popoveren når den åpnes
- `Escape` lukker popoveren og returnerer fokus til triggeren
- Innhold utenfor popoveren er utilgjengelig mens den er åpen

## Props

### Popover (rot)

| Prop           | Type                      | Default | Beskrivelse                  |
| -------------- | ------------------------- | ------- | ---------------------------- |
| `open`         | `boolean`                 | —       | Kontrollert åpen/lukket      |
| `onOpenChange` | `(open: boolean) => void` | —       | Kalles når tilstanden endres |
| `defaultOpen`  | `boolean`                 | `false` | Om popoveren starter åpen    |

### PopoverTrigger

| Prop      | Type      | Default | Beskrivelse                      |
| --------- | --------- | ------- | -------------------------------- |
| `asChild` | `boolean` | `false` | Bruk child-elementet som trigger |

### PopoverContent

| Prop         | Type                                     | Default    | Beskrivelse                               |
| ------------ | ---------------------------------------- | ---------- | ----------------------------------------- |
| `side`       | `"top" \| "right" \| "bottom" \| "left"` | `"bottom"` | Hvilken side av triggeren popoveren vises |
| `sideOffset` | `number`                                 | `4`        | Avstand fra triggeren i piksler           |
| `align`      | `"start" \| "center" \| "end"`           | `"center"` | Justering langs siden                     |

---

# Skeleton

> Plassholder-animasjon for innhold som lastes.

[Kildekode på GitHub](https://github.com/bekk/designsystem/blob/main/packages/designsystem/src/ui/skeleton.tsx)

Skeleton viser formen av innhold som ennå ikke er lastet. Den gir en bedre brukeropplevelse enn en tom side ved å indikere hva som kommer.

## Importering

```tsx
import { Skeleton } from "@kilden/designsystem";
```

## Grunnleggende bruk

```tsx
<div style={{ display: "grid", gap: "0.5rem", maxWidth: "24rem" }}>
  <Skeleton className="h-4 w-3/4" />
  <Skeleton className="h-4 w-full" />
  <Skeleton className="h-4 w-5/6" />
</div>
```

## Profil-plassholder

```tsx
<div style={{ display: "flex", alignItems: "center", gap: "1rem" }}>
  <Skeleton className="h-12 w-12 rounded-full" />
  <div style={{ display: "grid", gap: "0.5rem" }}>
    <Skeleton className="h-4 w-[180px]" />
    <Skeleton className="h-4 w-[120px]" />
  </div>
</div>
```

## Kort-plassholder

```tsx
<Card style={{ maxWidth: "24rem" }}>
  <CardHeader>
    <Skeleton className="h-5 w-2/3" />
    <Skeleton className="h-4 w-full" />
  </CardHeader>
  <CardContent>
    <div style={{ display: "grid", gap: "0.5rem" }}>
      <Skeleton className="h-4 w-full" />
      <Skeleton className="h-4 w-full" />
      <Skeleton className="h-4 w-3/4" />
    </div>
  </CardContent>
</Card>
```

## Liste-plassholder

```tsx
<div style={{ display: "grid", gap: "1rem", maxWidth: "24rem" }}>
  {[1, 2, 3].map((i) => (
    <div key={i} style={{ display: "flex", alignItems: "center", gap: "0.75rem" }}>
      <Skeleton className="h-10 w-10 rounded-md" />
      <div style={{ display: "grid", gap: "0.25rem", flex: 1 }}>
        <Skeleton className="h-4 w-3/4" />
        <Skeleton className="h-3 w-1/2" />
      </div>
    </div>
  ))}
</div>
```

## Bruksanbefalinger

- **Gjør: Match formen av innholdet** — Skeletons bør ligne innholdet de erstatter — bruk riktig størrelse, avrunding og layout.
- **Unngå: Ikke bruk for rask lasting** — Hvis innholdet lastes på under 200ms, viser skeleton bare et flimmer. Bruk den kun for merkbar ventetid.

## Tilgjengelighet

- Skeleton er dekorativ og trenger ingen spesiell ARIA-markering
- For skjermlesere, sørg for at lasting-tilstanden kommuniseres via `aria-busy="true"` på containeren
- Bruk `aria-live="polite"` på området som oppdateres når innholdet er ferdig lastet

## Props

Arver alle props fra `<div>`. Viktige utility-klasser:

| Klasse         | Beskrivelse                  |
| -------------- | ---------------------------- |
| `h-4`          | Høyde (tekst-linje)          |
| `w-[250px]`    | Fast bredde                  |
| `w-3/4`        | Relativ bredde               |
| `rounded-full` | Sirkulær form (avatar)       |
| `rounded-md`   | Avrundede hjørner (standard) |

---

# Badge

> Små visuelle markører for statuser, kategorier og metadata.

[Kildekode på GitHub](https://github.com/bekk/designsystem/blob/main/packages/designsystem/src/ui/badge.tsx)

Badge brukes for å merke elementer med status, kategori eller annen metadata. Den er kompakt og visuelt distinkt med avrundede hjørner og semantiske farger.

## Utforsk

*Se interaktiv playground på dokumentasjonssiden.*

## Importering

```tsx
import { Badge } from "@kilden/designsystem";
```

## Varianter

- **`default`**, **`secondary`** og **`outline`** – nøytrale markører, f.eks. kategorier og versjonsnumre.
- **`success`**, **`warning`**, **`danger`** og **`info`** – semantiske statuser.

```tsx
<div style={{ display: "flex", gap: "8px", flexWrap: "wrap", alignItems: "center" }}>
  <Badge>Default</Badge>
  <Badge variant="secondary">Secondary</Badge>
  <Badge variant="outline">v2.1.0</Badge>
  <Badge variant="success">Publisert</Badge>
  <Badge variant="warning">Beta</Badge>
  <Badge variant="danger">Avviklet</Badge>
  <Badge variant="info">Ny</Badge>
</div>
```

## Typiske bruksområder

```tsx
<div style={{ display: "flex", flexDirection: "column", gap: "1rem" }}>
  {/* Versjonsnummer */}
  <div style={{ display: "flex", alignItems: "center", gap: "0.5rem" }}>
    <span style={{ fontWeight: 600 }}>Button</span>
    <Badge variant="outline">v1.2.3</Badge>
  </div>
  {/* Status */}
  <div style={{ display: "flex", alignItems: "center", gap: "0.5rem" }}>
    <span style={{ fontWeight: 600 }}>Komponent</span>
    <Badge variant="success">Stabil</Badge>
  </div>
  {/* Kombinert med knapp */}
  <div style={{ display: "flex", alignItems: "center", gap: "0.5rem" }}>
    <Button variant="secondary">
      Varsler <Badge style={{ marginLeft: "0.5rem" }}>3</Badge>
    </Button>
  </div>
</div>
```

## Bruksanbefalinger

- **Gjør: Bruk for kortfattet metadata** — Badge passer for statuser, versjoner, kategorier og antall — altså kort og beskrivende informasjon.
- **Unngå: Ikke bruk for lange tekster** — Badge er ment for korte labels. For lengre meldinger, bruk Alert eller en vanlig tekst.

- **Gjør: Bruk semantiske farger bevisst** — Bruk success for positive statuser, warning for ting som krever oppmerksomhet, og danger for feil eller fjerning.
- **Unngå: Ikke bruk for mange badges sammen** — For mange badges skaper visuell støy. Velg de viktigste og hold det ryddig.

## Tilgjengelighet

- Badge er et `<span>` uten egen rolle, og leses opp som vanlig tekst
- Ikke la fargen alene bære betydningen. Teksten i badgen må beskrive statusen (f.eks. «Avviklet», ikke bare en rød badge)
- Badges med tall eller forkortelser trenger ofte mer kontekst for skjermlesere, f.eks. `3<span className="sr-only"> uleste varsler</span>`
- Badge er ikke interaktiv. Bruk `Button` eller `Chip` hvis brukeren skal kunne klikke på den
- De semantiske variantene bruker kontrastfarger fra design tokens

## Props

| Prop      | Type                                                                                    | Default     | Beskrivelse  |
| --------- | --------------------------------------------------------------------------------------- | ----------- | ------------ |
| `variant` | `"default" \| "secondary" \| "outline" \| "success" \| "warning" \| "danger" \| "info"` | `"default"` | Visuell stil |

---

# BodyText

> Brødtekst for løpende innhold, basert på body-typografi-tokenet.

[Kildekode på GitHub](https://github.com/bekk/designsystem/blob/main/packages/designsystem/src/ui/body-text.tsx)

BodyText brukes til all løpende tekst og ordinært innhold.

## Utforsk

*Se interaktiv playground på dokumentasjonssiden.*

## Importering

```tsx
import { BodyText } from "@kilden/designsystem";
```

## Eksempel

```tsx
<BodyText>Løpende brødtekst som utdyper innholdet videre.</BodyText>
```

## HTML-element

Bruk `as`-prop for å velge element uavhengig av størrelse:

```tsx
<div style={{ display: "flex", flexDirection: "column", gap: "0.5rem" }}>
  <BodyText as="p">Avsnitt (standard)</BodyText>
  <BodyText as="span">Inline span</BodyText>
  <BodyText as="strong">Uthevet tekst</BodyText>
</div>
```

## Tilgjengelighet

- Rendres som `<p>` som standard — sett `as` til `span` eller `div` ved behov for inline- eller blokk-kontekst
- Bruk `as="strong"` eller `as="em"` for å formidle semantisk vekt

## Props

| Prop      | Type                                         | Default | Beskrivelse                                     |
| --------- | -------------------------------------------- | ------- | ----------------------------------------------- |
| `as`      | `"p" \| "span" \| "div" \| "strong" \| "em"` | `"p"`   | HTML-element                                    |
| `asChild` | `boolean`                                    | `false` | Rendrer barn-elementet som teksten (Radix Slot) |

---

# Combobox

> Søkbar nedtrekksmeny for å velge blant mange alternativer.

Combobox,
  ComboboxItem,
  ComboboxGroup,
  ComboboxSeparator,
  Label,
} from "@kilden/designsystem";

# Combobox

[Kildekode på GitHub](https://github.com/bekk/designsystem/blob/main/packages/designsystem/src/ui/combobox.tsx)

Combobox er en søkbar nedtrekksmeny som kombinerer et tekstfelt med en filtrerbar liste. Ideell for tilfeller der brukeren må velge blant mange alternativer, for eksempel byer, ansatte eller produkter. Bygget på [cmdk](https://cmdk.paco.me/) for fuzzy-søk og [Radix UI Popover](https://www.radix-ui.com/primitives/docs/components/popover) for posisjonering.

## Importering

```tsx
import {
  Combobox,
  ComboboxItem,
  ComboboxGroup,
  ComboboxSeparator,
} from "@kilden/designsystem";
```

## Grunnleggende bruk

```tsx
function ComboboxDemo() {
  const [value, setValue] = React.useState("");
  return (
    <Combobox value={value} onValueChange={setValue} placeholder="Velg frukt..." className="w-[240px]">
      <ComboboxItem value="eple">Eple</ComboboxItem>
      <ComboboxItem value="banan">Banan</ComboboxItem>
      <ComboboxItem value="appelsin">Appelsin</ComboboxItem>
      <ComboboxItem value="jordbær">Jordbær</ComboboxItem>
      <ComboboxItem value="mango">Mango</ComboboxItem>
    </Combobox>
  );
}
```

## Med grupper

Bruk `ComboboxGroup` og `ComboboxSeparator` for å organisere alternativer.

```tsx
function GroupedComboboxDemo() {
  const [value, setValue] = React.useState("");
  return (
    <Combobox value={value} onValueChange={setValue} placeholder="Velg mat..." className="w-[240px]">
      <ComboboxGroup heading="Frukt">
        <ComboboxItem value="eple">Eple</ComboboxItem>
        <ComboboxItem value="banan">Banan</ComboboxItem>
        <ComboboxItem value="appelsin">Appelsin</ComboboxItem>
      </ComboboxGroup>
      <ComboboxSeparator />
      <ComboboxGroup heading="Grønnsaker">
        <ComboboxItem value="gulrot">Gulrot</ComboboxItem>
        <ComboboxItem value="brokkoli">Brokkoli</ComboboxItem>
        <ComboboxItem value="spinat">Spinat</ComboboxItem>
      </ComboboxGroup>
    </Combobox>
  );
}
```

## Asynkront søk

For datadrevne lister kan du deaktivere innebygd filtrering med `shouldFilter={false}` og styre resultatene selv via `onSearchChange`.

```tsx
function AsyncComboboxDemo() {
  const [value, setValue] = React.useState("");
  const [results, setResults] = React.useState([]);
  const [loading, setLoading] = React.useState(false);

const allItems = [
{ id: "oslo", name: "Oslo" },
{ id: "bergen", name: "Bergen" },
{ id: "trondheim", name: "Trondheim" },
{ id: "stavanger", name: "Stavanger" },
{ id: "tromsø", name: "Tromsø" },
{ id: "drammen", name: "Drammen" },
{ id: "kristiansand", name: "Kristiansand" },
];

function handleSearch(search) {
setLoading(true);
// Simuler API-kall
setTimeout(() => {
setResults(
search
? allItems.filter((item) =>
item.name.toLowerCase().includes(search.toLowerCase())
)
: allItems
);
setLoading(false);
}, 300);
}

return (

<Combobox
      value={value}
      onValueChange={setValue}
      placeholder="Søk etter by..."
      onSearchChange={handleSearch}
      loading={loading}
      shouldFilter={false}
      emptyMessage="Ingen byer funnet."
      className="w-[240px]"
    >
{results.map((item) => (
<ComboboxItem key={item.id} value={item.id}>
{item.name}
</ComboboxItem>
))}
</Combobox>
);
}
```

## Med label og feilmelding

Combobox støtter `label` og `error` som [Input](/docs/components/input), og kobles automatisk til `FormField`. `id`, `name` og `aria-*` sendes videre til søkefeltet.

```tsx
function ComboboxLabel() {
  const [value, setValue] = React.useState("");
  return (
    <div style={{ maxWidth: "20rem" }}>
      <Combobox
        label="Frukt"
        error={value ? undefined : "Velg en frukt"}
        value={value}
        onValueChange={setValue}
      >
        <ComboboxItem value="eple">Eple</ComboboxItem>
        <ComboboxItem value="banan">Banan</ComboboxItem>
      </Combobox>
    </div>
  );
}
```

## Bruksanbefalinger

- **Gjør: Bruk for store lister** — Combobox egner seg når det er mange alternativer og brukeren trenger å søke. For korte lister (under 7 elementer) er vanlig Select ofte tilstrekkelig.
- **Unngå: Ikke bruk for korte lister** — Når det er få, oversiktlige alternativer er Select enklere å bruke. Combobox tilfører unødvendig kompleksitet for korte lister.

## Tilgjengelighet

- Følger [WAI-ARIA Combobox](https://www.w3.org/WAI/ARIA/apg/patterns/combobox/)-mønsteret
- Full tastaturnavigasjon: piltaster, Enter, Escape
- `role="combobox"` settes på søkefeltet, og `aria-expanded` følger faktisk åpen/lukket-tilstand
- `label`-propen (eller en `FormField` med ``) kobler label til søkefeltet
- Valgt verdi beholdes når listen åpnes og lukkes uten at brukeren skriver; den nullstilles bare hvis brukeren tømmer feltet
- `aria-autocomplete="list"` på søkefeltet
- Skjermlesere annonserer antall filtrerte resultater
- Fokus returneres til trigger ved lukking

## Props

### Combobox

| Prop             | Type                       | Default               | Beskrivelse                                     |
| ---------------- | -------------------------- | --------------------- | ----------------------------------------------- |
| `value`          | `string`                   | —                     | Valgt verdi (kontrollert)                       |
| `onValueChange`  | `(value: string) => void`  | —                     | Kalles når bruker velger et alternativ          |
| `label`          | `ReactNode`                | —                     | Synlig label over feltet                        |
| `error`          | `ReactNode`                | —                     | Feilmelding under feltet                        |
| `placeholder`    | `string`                   | `"Velg..."`           | Plassholdertekst i trigger og søkefelt          |
| `disabled`       | `boolean`                  | `false`               | Deaktiverer hele komponenten                    |
| `required`       | `boolean`                  | `false`               | Marker som påkrevd                              |
| `className`      | `string`                   | —                     | Ekstra CSS-klasser for trigger                  |
| `emptyMessage`   | `string`                   | `"Ingen resultater."` | Tekst vist ved ingen treff                      |
| `onSearchChange` | `(search: string) => void` | —                     | Kalles ved hvert tastetrykk (for asynkront søk) |
| `loading`        | `boolean`                  | `false`               | Viser lasteindikator                            |
| `shouldFilter`   | `boolean`                  | `true`                | cmdk-filtrering. Sett `false` for asynkront søk |
| `open`           | `boolean`                  | —                     | Kontrollert åpne-tilstand                       |
| `onOpenChange`   | `(open: boolean) => void`  | —                     | Kalles når åpne-tilstand endres                 |

### ComboboxItem

| Prop       | Type      | Default | Beskrivelse                  |
| ---------- | --------- | ------- | ---------------------------- |
| `value`    | `string`  | —       | Verdien til alternativet     |
| `disabled` | `boolean` | `false` | Deaktiver dette alternativet |

### ComboboxGroup

| Prop      | Type     | Default | Beskrivelse            |
| --------- | -------- | ------- | ---------------------- |
| `heading` | `string` | —       | Overskrift for gruppen |

---

# TimePicker

> Tidsvelger med tekstfelt for manuell inntasting og opp/ned-knapper.

[Kildekode på GitHub](https://github.com/bekk/designsystem/blob/main/packages/designsystem/src/ui/time-picker.tsx)

TimePicker er en tidsvelger der brukeren kan skrive inn klokkeslett direkte (tt:mm) eller bruke opp/ned-knapper for å justere tiden i konfigurerbare intervaller. Støtter 24-timersformat.

TimePicker støtter de samme propsene som [Input](/docs/components/input) — blant annet `label`, `error`, `name`, `id` og `aria-*` — og kobles automatisk til `FormField`.

## Utforsk

*Se interaktiv playground på dokumentasjonssiden.*

## Importering

```tsx
import { TimePicker } from "@kilden/designsystem";
```

## Grunnleggende bruk

```tsx
function TimePickerDemo() {
  const [hours, setHours] = React.useState(9);
  const [minutes, setMinutes] = React.useState(0);
  return (
    <TimePicker
      hours={hours}
      minutes={minutes}
      onValueChange={(time) => {
        setHours(time?.hours);
        setMinutes(time?.minutes);
      }}
    />
  );
}
```

## Uten startverdi

```tsx
function TimePickerEmpty() {
  const [time, setTime] = React.useState(undefined);
  return (
    <TimePicker
      hours={time?.hours}
      minutes={time?.minutes}
      onValueChange={setTime}
    />
  );
}
```

## Custom minutt-intervall

Bruk `minuteStep` for å endre intervallet opp/ned-knappene og piltastene justerer med. Standard er 15 minutter.

```tsx
function TimePickerStep() {
  const [hours, setHours] = React.useState(12);
  const [minutes, setMinutes] = React.useState(0);
  return (
    <div style={{ display: "grid", gap: "1rem" }}>
      <div>
        <p style={{ fontSize: "0.875rem", marginBottom: "0.25rem" }}>5-minutters intervall:</p>
        <TimePicker
          hours={hours}
          minutes={minutes}
          minuteStep={5}
          onValueChange={(time) => {
            setHours(time?.hours);
            setMinutes(time?.minutes);
          }}
        />
      </div>
      <div>
        <p style={{ fontSize: "0.875rem", marginBottom: "0.25rem" }}>30-minutters intervall:</p>
        <TimePicker
          hours={hours}
          minutes={minutes}
          minuteStep={30}
          onValueChange={(time) => {
            setHours(time?.hours);
            setMinutes(time?.minutes);
          }}
        />
      </div>
    </div>
  );
}
```

## Med label og feilmelding

```tsx
function TimePickerLabel() {
  const [time, setTime] = React.useState(undefined);
  return (
    <TimePicker
      label="Starttid"
      name="starttid"
      error={time ? undefined : "Velg et klokkeslett"}
      hours={time?.hours}
      minutes={time?.minutes}
      onValueChange={setTime}
    />
  );
}
```

## Deaktivert

```tsx
<TimePicker hours={9} minutes={30} disabled />
```

## Bruksanbefalinger

- **Gjør: Bruk for klokkeslett-input** — TimePicker passer for skjemafelt der brukeren skal velge et klokkeslett, som møtetidspunkt eller åpningstider.
- **Unngå: Ikke bruk for varighet** — For tidsintervaller (f.eks. '2 timer og 30 minutter') bør du bruke separate Input-felt for timer og minutter.

- **Gjør: Velg passende minutt-intervall** — Bruk minuteStep som matcher konteksten — 15 min for møter, 5 min for presis tid, 30 min for grove estimater.
- **Unngå: Ikke bruk for dato+tid** — For valg av både dato og klokkeslett, kombiner DatePicker eller DateInput med TimePicker i stedet for én komponent.

## Tilgjengelighet

- Bruk `label`-propen eller en `FormField` med `` for å gi feltet et synlig navn
- Uten label, `id`, `aria-label` eller `aria-labelledby` får inputfeltet `aria-label="Klokkeslett"`
- `error`-propen setter `aria-invalid` og kobler feilmelding via `aria-describedby`
- Opp/ned-knappene har `aria-label` ("Øk tid" / "Reduser tid")
- `ArrowUp` og `ArrowDown` justerer tiden når inputfeltet har fokus
- Støtter manuell inntasting med `inputMode="numeric"`
- Deaktivert tilstand kommuniseres via `disabled` på alle elementer
- 24-timersformat unngår AM/PM-forvirring

## Props

| Prop            | Type                                                                | Default   | Beskrivelse                              |
| --------------- | ------------------------------------------------------------------- | --------- | ---------------------------------------- |
| `hours`         | `number`                                                            | —         | Valgt time (0–23)                        |
| `minutes`       | `number`                                                            | —         | Valgt minutt (0–59)                      |
| `onValueChange` | `(time: \{ hours: number; minutes: number \} \| undefined) => void` | —         | Kalles når klokkeslettet endres          |
| `label`         | `ReactNode`                                                         | —         | Synlig label over feltet                 |
| `error`         | `ReactNode`                                                         | —         | Feilmelding under feltet                 |
| `name`          | `string`                                                            | —         | Navn for skjemainnsending (verdi: tt:mm) |
| `placeholder`   | `string`                                                            | `"tt:mm"` | Plassholdertekst for inputfeltet         |
| `disabled`      | `boolean`                                                           | `false`   | Deaktiver tidsvelgeren                   |
| `required`      | `boolean`                                                           | `false`   | Marker som påkrevd                       |
| `minuteStep`    | `number`                                                            | `15`      | Minutt-intervall for opp/ned-knapper     |
| `className`     | `string`                                                            | —         | Ekstra CSS-klasser for feltet            |

I tillegg sendes øvrige input-props (f.eks. `id`, `aria-*`, `onBlur`, `onKeyDown`) videre til inputfeltet.

---

# Tabs

> Fanebasert navigasjon for å bytte mellom ulike visninger av relatert innhold.

Tabs,
  TabsList,
  TabsTrigger,
  TabsContent,
  Button,
} from "@kilden/designsystem";

# Tabs

[Kildekode på GitHub](https://github.com/bekk/designsystem/blob/main/packages/designsystem/src/ui/tabs.tsx)

Tabs lar brukeren bytte mellom ulike paneler med relatert innhold. Komponenten er bygget på [Radix UI Tabs](https://www.radix-ui.com/primitives/docs/components/tabs) og støtter full tastaturnavigasjon og tilgjengelighet.

## Importering

```tsx
import { Tabs, TabsList, TabsTrigger, TabsContent } from "@kilden/designsystem";
```

## Grunnleggende bruk

Den aktive fanen bruker nå dominante temafarger, mens de inaktive fanene holder seg nøytrale. Det gjør valgt state tydeligere, og gir mer karakter når brukeren bytter mellom ild, luft, jord og vann.

```tsx
<Tabs defaultValue="preview">
  <TabsList>
    <TabsTrigger value="preview">Forhåndsvisning</TabsTrigger>
    <TabsTrigger value="code">Kode</TabsTrigger>
  </TabsList>
  <TabsContent value="preview">
    <p>Her ser du en forhåndsvisning av komponenten.</p>
  </TabsContent>
  <TabsContent value="code">
    <pre style={{ padding: "1rem", background: "var(--color-neutral-surface-default)", borderRadius: "0.5rem" }}>
      {'<Button variant="secondary">Klikk</Button>'}
    </pre>
  </TabsContent>
</Tabs>
```

## Temastyrt uttrykk

Tabs fungerer best når valgt fane får eie uttrykket. Derfor bruker komponenten dominante tokens i aktiv state, mens railen og de inaktive fanene ligger i nøytrale toner.

```tsx
<div style={{ display: "grid", gap: "1rem" }}>
  <Tabs defaultValue="oversikt">
    <TabsList>
      <TabsTrigger value="oversikt">Oversikt</TabsTrigger>
      <TabsTrigger value="detaljer">Detaljer</TabsTrigger>
      <TabsTrigger value="aktivitet">Aktivitet</TabsTrigger>
    </TabsList>
    <TabsContent value="oversikt">
      <p>Den valgte fanen tar opp mer av temaets identitet og skaper et tydeligere fokuspunkt.</p>
    </TabsContent>
    <TabsContent value="detaljer">
      <p>Inaktive faner holder seg roligere, slik at aktiv state popper tydeligere.</p>
    </TabsContent>
    <TabsContent value="aktivitet">
      <p>Bytt tema i dokumentasjonen for å se hvordan uttrykket skifter mellom ild, luft, jord og vann.</p>
    </TabsContent>
  </Tabs>
</div>
```

## Flere faner

```tsx
<Tabs defaultValue="react">
  <TabsList>
    <TabsTrigger value="react">React</TabsTrigger>
    <TabsTrigger value="vue">Vue</TabsTrigger>
    <TabsTrigger value="svelte">Svelte</TabsTrigger>
  </TabsList>
  <TabsContent value="react">
    <p>Importer komponenten med <code>import { "{ Button }" } from "@kilden/designsystem"</code></p>
  </TabsContent>
  <TabsContent value="vue">
    <p>Vue-støtte kommer snart.</p>
  </TabsContent>
  <TabsContent value="svelte">
    <p>Svelte-støtte er ikke planlagt ennå.</p>
  </TabsContent>
</Tabs>
```

## Deaktivert fane

Individuelle faner kan deaktiveres med `disabled`-prop-en.

```tsx
<Tabs defaultValue="aktiv">
  <TabsList>
    <TabsTrigger value="aktiv">Aktiv fane</TabsTrigger>
    <TabsTrigger value="deaktivert" disabled>Deaktivert</TabsTrigger>
  </TabsList>
  <TabsContent value="aktiv">
    <p>Denne fanen er aktiv. Den deaktiverte fanen kan ikke klikkes.</p>
  </TabsContent>
</Tabs>
```

## Bruksanbefalinger

- **Gjør: Bruk for relatert innhold** — Tabs egner seg best når innholdet i de ulike panelene er relatert og på samme nivå — f.eks. kode vs. forhåndsvisning, eller ulike varianter av en komponent. La den aktive fanen få tydelig vekt, og la de øvrige være roligere.
- **Unngå: Ikke bruk som navigasjon** — Tabs skal ikke brukes for sidenavigasjon. Bruk lenker eller en meny for det. Tabs bytter innhold innenfor samme side.

## Tilgjengelighet

- Bruker `role="tablist"`, `role="tab"` og `role="tabpanel"` automatisk
- Piltaster navigerer mellom faner
- `Home` og `End` hopper til første/siste fane
- `Enter` eller `Space` aktiverer en fane
- Deaktiverte faner hoppes over ved tastaturnavigasjon
- Sterkere fargeforskjeller supplerer state, men erstatter ikke ARIA-semantikken
- Fokusmarkering beholdes også når en fane allerede er valgt

## Props

### Tabs (rot)

| Prop            | Type                      | Default | Beskrivelse                                 |
| --------------- | ------------------------- | ------- | ------------------------------------------- |
| `defaultValue`  | `string`                  | —       | Verdien til fanen som er aktiv ved oppstart |
| `value`         | `string`                  | —       | Kontrollert aktiv fane                      |
| `onValueChange` | `(value: string) => void` | —       | Kalles når aktiv fane endres                |

### TabsTrigger

| Prop       | Type      | Default | Beskrivelse                               |
| ---------- | --------- | ------- | ----------------------------------------- |
| `value`    | `string`  | —       | Unik verdi som knytter fanen til et panel |
| `disabled` | `boolean` | `false` | Deaktiver fanen                           |

### TabsContent

| Prop    | Type     | Default | Beskrivelse                             |
| ------- | -------- | ------- | --------------------------------------- |
| `value` | `string` | —       | Verdien som knytter panelet til en fane |

---

# Sheet

> Sidepanel-overlay som glir inn fra en kant av skjermen.

Sheet,
  SheetTrigger,
  SheetContent,
  SheetHeader,
  SheetFooter,
  SheetTitle,
  SheetDescription,
  SheetClose,
  Button,
  Input,
  Label,
  FormField,
} from "@kilden/designsystem";

# Sheet

[Kildekode på GitHub](https://github.com/bekk/designsystem/blob/main/packages/designsystem/src/ui/sheet.tsx)

Sheet er et sidepanel som glir inn fra en kant av skjermen. Bygget på [Radix UI Dialog](https://www.radix-ui.com/primitives/docs/components/dialog) med fokusfelle, Escape-lukking og bakgrunnsoverlegg. Perfekt for mobilnavigasjon, filtre og redigeringspaneler.

## Importering

```tsx
import {
  Sheet,
  SheetTrigger,
  SheetContent,
  SheetHeader,
  SheetTitle,
  SheetDescription,
} from "@kilden/designsystem";
```

## Grunnleggende bruk

```tsx
<Sheet>
  <SheetTrigger asChild>
    <Button variant="secondary">Åpne panel</Button>
  </SheetTrigger>
  <SheetContent>
    <SheetHeader>
      <SheetTitle>Innstillinger</SheetTitle>
      <SheetDescription>
        Endre innstillingene dine her. Klikk lagre når du er ferdig.
      </SheetDescription>
    </SheetHeader>
    <div style={{ padding: "1rem 0", display: "grid", gap: "1rem" }}>
      <FormField>
        <Label>Navn</Label>
        <Input defaultValue="Ola Nordmann" />
      </FormField>
      <FormField>
        <Label>E-post</Label>
        <Input defaultValue="ola@bekk.no" />
      </FormField>
    </div>
    <SheetFooter>
      <SheetClose asChild>
        <Button>Lagre</Button>
      </SheetClose>
    </SheetFooter>
  </SheetContent>
</Sheet>
```

## Fra venstre

```tsx
<Sheet>
  <SheetTrigger asChild>
    <Button variant="secondary">Åpne fra venstre</Button>
  </SheetTrigger>
  <SheetContent side="left">
    <SheetHeader>
      <SheetTitle>Navigasjon</SheetTitle>
      <SheetDescription>Velg en side fra menyen.</SheetDescription>
    </SheetHeader>
    <nav style={{ display: "grid", gap: "0.5rem", paddingTop: "1rem" }}>
      <a href="#" style={{ padding: "0.5rem 0" }}>Hjem</a>
      <a href="#" style={{ padding: "0.5rem 0" }}>Komponenter</a>
      <a href="#" style={{ padding: "0.5rem 0" }}>Farger</a>
      <a href="#" style={{ padding: "0.5rem 0" }}>Ikoner</a>
    </nav>
  </SheetContent>
</Sheet>
```

## Fra topp og bunn

```tsx
<div style={{ display: "flex", gap: "0.5rem" }}>
  <Sheet>
    <SheetTrigger asChild>
      <Button variant="secondary">Fra toppen</Button>
    </SheetTrigger>
    <SheetContent side="top">
      <SheetHeader>
        <SheetTitle>Melding</SheetTitle>
        <SheetDescription>Dette panelet glir inn fra toppen.</SheetDescription>
      </SheetHeader>
    </SheetContent>
  </Sheet>
  <Sheet>
    <SheetTrigger asChild>
      <Button variant="secondary">Fra bunnen</Button>
    </SheetTrigger>
    <SheetContent side="bottom">
      <SheetHeader>
        <SheetTitle>Melding</SheetTitle>
        <SheetDescription>Dette panelet glir inn fra bunnen.</SheetDescription>
      </SheetHeader>
    </SheetContent>
  </Sheet>
</div>
```

## Bruksanbefalinger

- **Gjør: Bruk for sekundært innhold** — Sheet egner seg for innhold som ikke trenger hele skjermen — mobilmeny, filtre, redigeringspaneler.
- **Unngå: Ikke bruk for viktige beslutninger** — For handlinger som krever brukerens fulle oppmerksomhet (bekreftelse, feilmelding), bruk Dialog i stedet.

## Tilgjengelighet

- Bygget på Dialog-primitiven med full fokusfelle
- `Escape` lukker panelet
- Klikk på bakgrunnsoverlegget lukker panelet
- Fokus returneres til trigger-elementet ved lukking
- `SheetTitle` brukes av skjermlesere til å identifisere panelet

## Props

### SheetContent

| Prop   | Type                                     | Default   | Beskrivelse                       |
| ------ | ---------------------------------------- | --------- | --------------------------------- |
| `side` | `"top" \| "right" \| "bottom" \| "left"` | `"right"` | Hvilken side panelet glir inn fra |

### Sheet (rot)

| Prop           | Type                      | Default | Beskrivelse                |
| -------------- | ------------------------- | ------- | -------------------------- |
| `open`         | `boolean`                 | —       | Kontrollert åpen-tilstand  |
| `onOpenChange` | `(open: boolean) => void` | —       | Kalles når tilstand endres |
| `defaultOpen`  | `boolean`                 | `false` | Standard åpen-tilstand     |

---

# DotsLoader

> Animert tre-prikker lasteindikator for inline og aktive prosesser.

[Kildekode på GitHub](https://github.com/bekk/designsystem/blob/main/packages/designsystem/src/ui/dots-loader.tsx)

DotsLoader viser tre animerte prikker som bouncer i sekvens. Den passer for inline lastetilstander, chat-typing-indikatorer og streaming-tilstander der `Spinner` ville vært for dominerende visuelt.

## Utforsk

*Se interaktiv playground på dokumentasjonssiden.*

## Importering

```tsx
import { DotsLoader } from "@kilden/designsystem";
```

## Grunnleggende bruk

```tsx
<DotsLoader />
```

## Størrelser

```tsx
<div style={{ display: "flex", alignItems: "center", gap: "1.5rem" }}>
  <DotsLoader size="sm" />
  <DotsLoader size="md" />
  <DotsLoader size="lg" />
</div>
```

## Egendefinert label

`aria-label` leses opp av skjermlesere. Standard er `"Laster..."`.

```tsx
<DotsLoader label="Skriver..." />
```

## Inline i tekst

```tsx
<div style={{ display: "flex", alignItems: "center", gap: "0.5rem" }}>
  <span>Henter data</span>
  <DotsLoader size="sm" />
</div>
```

## Chat-typing-indikator

```tsx
<div style={{ display: "inline-flex", alignItems: "center", gap: "0.5rem", padding: "0.5rem 0.75rem", borderRadius: "0.75rem", background: "var(--color-neutral-surface-default)" }}>
  <DotsLoader size="sm" label="Skriver..." />
</div>
```

## Bruksanbefalinger

- **Gjør: Bruk for inline og aktive prosesser** — DotsLoader passer for chat-typing-indikatorer, streaming-svar og inline lastetilstander i tekst.
- **Unngå: Ikke bruk for generell lasting av sider eller store seksjoner** — Bruk Spinner for generell lasting, og Skeleton når du kjenner formen av innholdet som lastes.

## Tilgjengelighet

- Har `role="status"` som annonserer lastetilstanden til skjermlesere
- `aria-label` (standard: `"Laster..."`) leses opp automatisk — tilpass den til konteksten
- Prikkene er skjult for skjermlesere med `aria-hidden`

## Props

| Prop        | Type                   | Default       | Beskrivelse                         |
| ----------- | ---------------------- | ------------- | ----------------------------------- |
| `size`      | `"sm" \| "md" \| "lg"` | `"md"`        | Størrelse på prikkene               |
| `label`     | `string`               | `"Laster..."` | Tilgjengelig label for skjermlesere |
| `className` | `string`               | —             | Ekstra CSS-klasser                  |

---

# Card

> Container-komponent for å gruppere relatert innhold med header, innhold og footer.

Card,
  CardHeader,
  CardTitle,
  CardDescription,
  CardContent,
  CardFooter,
  Button,
  Badge,
  Input,
  Label,
  Separator,
} from "@kilden/designsystem";

# Card

[Kildekode på GitHub](https://github.com/bekk/designsystem/blob/main/packages/designsystem/src/ui/card.tsx)

Card er en container-komponent som grupperer relatert innhold med en tydelig visuell avgrensning. Brukes for å vise features, eksempler, profiler og annet innhold som hører sammen.

## Importering

```tsx
import {
  Card,
  CardHeader,
  CardTitle,
  CardDescription,
  CardContent,
  CardFooter,
} from "@kilden/designsystem";
```

## Grunnleggende bruk

```tsx
<Card className="w-[350px]">
  <CardHeader>
    <CardTitle>Opprett prosjekt</CardTitle>
    <CardDescription>Lag et nytt prosjekt med et par klikk.</CardDescription>
  </CardHeader>
  <CardContent>
    <div style={{ display: "grid", gap: "1rem" }}>
      <div style={{ display: "grid", gap: "0.25rem" }}>
        <Label htmlFor="project-name">Prosjektnavn</Label>
        <Input id="project-name" placeholder="Mitt prosjekt" />
      </div>
    </div>
  </CardContent>
  <CardFooter style={{ justifyContent: "space-between" }}>
    <Button variant="secondary">Avbryt</Button>
    <Button>Opprett</Button>
  </CardFooter>
</Card>
```

## Kun innhold

Card kan brukes uten header og footer for enklere bruk.

```tsx
<Card className="w-[350px]">
  <CardContent style={{ paddingTop: "1.5rem" }}>
    <p style={{ margin: 0, fontSize: "0.875rem" }}>
      Dette er et enkelt kort med kun innhold. Nyttig for korte meldinger eller
      informasjonsbokser.
    </p>
  </CardContent>
</Card>
```

## Komponent-showcase

```tsx
<div style={{ display: "grid", gridTemplateColumns: "repeat(auto-fit, minmax(250px, 1fr))", gap: "1rem" }}>
  <Card>
    <CardHeader>
      <div style={{ display: "flex", justifyContent: "space-between", alignItems: "center" }}>
        <CardTitle style={{ fontSize: "1.125rem" }}>Button</CardTitle>
        <Badge variant="success">Stabil</Badge>
      </div>
      <CardDescription>Allsidig knapp med varianter og størrelser.</CardDescription>
    </CardHeader>
    <CardFooter>
      <Button variant="secondary" size="sm">Dokumentasjon</Button>
    </CardFooter>
  </Card>
  <Card>
    <CardHeader>
      <div style={{ display: "flex", justifyContent: "space-between", alignItems: "center" }}>
        <CardTitle style={{ fontSize: "1.125rem" }}>Dialog</CardTitle>
        <Badge variant="success">Stabil</Badge>
      </div>
      <CardDescription>Modal-vindu for viktige handlinger.</CardDescription>
    </CardHeader>
    <CardFooter>
      <Button variant="secondary" size="sm">Dokumentasjon</Button>
    </CardFooter>
  </Card>
  <Card>
    <CardHeader>
      <div style={{ display: "flex", justifyContent: "space-between", alignItems: "center" }}>
        <CardTitle style={{ fontSize: "1.125rem" }}>Select</CardTitle>
        <Badge variant="warning">Beta</Badge>
      </div>
      <CardDescription>Nedtrekksmeny for valg mellom alternativer.</CardDescription>
    </CardHeader>
    <CardFooter>
      <Button variant="secondary" size="sm">Dokumentasjon</Button>
    </CardFooter>
  </Card>
</div>
```

## Med skillelinje

```tsx
<Card className="w-[350px]">
  <CardHeader>
    <CardTitle>Ola Nordmann</CardTitle>
    <CardDescription>Utvikler hos Bekk</CardDescription>
  </CardHeader>
  <CardContent>
    <Separator />
    <div style={{ display: "grid", gap: "0.5rem", marginTop: "1rem", fontSize: "0.875rem" }}>
      <div style={{ display: "flex", justifyContent: "space-between" }}>
        <span style={{ opacity: 0.7 }}>E-post</span>
        <span>ola@bekk.no</span>
      </div>
      <div style={{ display: "flex", justifyContent: "space-between" }}>
        <span style={{ opacity: 0.7 }}>Team</span>
        <span>Designsystem</span>
      </div>
      <div style={{ display: "flex", justifyContent: "space-between" }}>
        <span style={{ opacity: 0.7 }}>Rolle</span>
        <span>Tech lead</span>
      </div>
    </div>
  </CardContent>
</Card>
```

## Bruksanbefalinger

- **Gjør: Bruk for å gruppere relatert innhold** — Card gir visuell avgrensning og struktur. Bruk det for innhold som henger sammen — som en feature, en profil eller et skjema.
- **Unngå: Ikke bruk for enkeltlinjer** — Card med lite innhold føles overdrevet. Bruk det bare når innholdet har nok substans til å trenge visuell gruppering.

## Tilgjengelighet

- Card bruker standard HTML-elementer (`<div>`, `<h3>`, `<p>`) med riktig semantikk
- `CardTitle` rendres som `<h3>` for overskriftshierarki
- Legg til `role="region"` og `aria-label` om kortet representerer en selvstendig seksjon

## Props

Alle delkomponenter arver standard `<div>`-props (unntatt CardTitle som arver `<h3>` og CardDescription som arver `<p>`). De mest relevante:

### Card

Arver alle props fra `<div>`. Ingen ekstra props.

### CardHeader / CardContent / CardFooter

Arver alle props fra `<div>`. Gir konsistent padding og layout.

### CardTitle

Arver alle props fra `<h3>`.

### CardDescription

Arver alle props fra `<p>`. Rendres med dempet farge.

---

# ActionText

> Liten tekst for etiketter, hjelpetekst og UI-elementer, basert på action-typografi-tokenet.

[Kildekode på GitHub](https://github.com/bekk/designsystem/blob/main/packages/designsystem/src/ui/action-text.tsx)

ActionText brukes til etiketter, hjelpetekst og annen støttetekst i UI-elementer.

## Utforsk

*Se interaktiv playground på dokumentasjonssiden.*

## Importering

```tsx
import { ActionText } from "@kilden/designsystem";
```

## Eksempel

```tsx
<ActionText>Hjelpetekst under et skjemafelt.</ActionText>
```

## HTML-element

Bruk `as`-prop for å velge element uavhengig av størrelse:

```tsx
<div style={{ display: "flex", flexDirection: "column", gap: "0.5rem" }}>
  <ActionText as="p">Avsnitt (standard)</ActionText>
  <ActionText as="span">Inline etikett</ActionText>
</div>
```

## Tilgjengelighet

- Rendres som `<p>` som standard — sett `as` til `span` for inline-kontekst
- Unngå å bruke ActionText alene uten tilhørende, lesbart innhold

## Props

| Prop      | Type                                         | Default | Beskrivelse                                     |
| --------- | -------------------------------------------- | ------- | ----------------------------------------------- |
| `as`      | `"p" \| "span" \| "div" \| "strong" \| "em"` | `"p"`   | HTML-element                                    |
| `asChild` | `boolean`                                    | `false` | Rendrer barn-elementet som teksten (Radix Slot) |

---

# FormField

> Hjelpere for å strukturere skjemafelt med label, hjelpetekst og feilmelding.

FormField,
  FormDescription,
  FormMessage,
  Label,
  Input,
  Textarea,
  Checkbox,
  Select,
  SelectTrigger,
  SelectValue,
  SelectContent,
  SelectItem,
  Button,
} from "@kilden/designsystem";

# FormField

[Kildekode på GitHub](https://github.com/bekk/designsystem/blob/main/packages/designsystem/src/ui/form-field.tsx)

FormField, FormDescription og FormMessage er hjelpere for å bygge konsistente skjemafelt. De gir riktig layout og mellomrom mellom label, skjemaelement, hjelpetekst og feilmelding.

FormField kobler i tillegg automatisk sammen `Label`, skjemaelementet (`Input`, `Textarea`, `SelectTrigger`, `Combobox`, `DateInput`, `TimePicker`, `DatePicker`), `FormDescription` og `FormMessage` med `id`/`htmlFor`, `aria-describedby` og `aria-invalid`. Du trenger altså ikke sette id-er manuelt.

## Importering

```tsx
import {
  FormField,
  FormDescription,
  FormMessage,
  useFormControl,
} from "@kilden/designsystem";
```

## Grunnleggende bruk

```tsx
<FormField style={{ maxWidth: "24rem" }}>
  <Label>Navn</Label>
  <Input placeholder="Ola Nordmann" />
</FormField>
```

## Med hjelpetekst

```tsx
<FormField style={{ maxWidth: "24rem" }}>
  <Label>E-post</Label>
  <Input type="email" placeholder="ola@bekk.no" />
  <FormDescription>Vi deler aldri e-posten din med andre.</FormDescription>
</FormField>
```

## Med feilmelding

Når en `FormMessage` med innhold vises, markeres skjemaelementet automatisk med `aria-invalid="true"` og kobles til feilmeldingen via `aria-describedby`.

```tsx
<FormField style={{ maxWidth: "24rem" }}>
  <Label>Passord</Label>
  <Input

    type="password"
  />
  <FormMessage>Passordet må være minst 8 tegn.</FormMessage>
</FormField>
```

## Med påkrevd felt

Sett `required` på `FormField`. Labelen viser da en rød stjerne, og skjemaelementet får `required` for nativ nettleser-validering.

```tsx
<FormField required style={{ maxWidth: "24rem" }}>
  <Label>E-post</Label>
  <Input type="email" placeholder="ola@bekk.no" />
</FormField>
```

## Komplett eksempel

```tsx
<form style={{ display: "grid", gap: "1.5rem", maxWidth: "24rem" }}>
  <FormField>
    <Label>Fullt navn</Label>
    <Input placeholder="Ola Nordmann" />
  </FormField>

<FormField>
  <Label>E-post</Label>
  <Input type="email" placeholder="ola@bekk.no" />
  <FormDescription>Bruk jobb-e-posten din.</FormDescription>
</FormField>

<FormField>
  <Label>Bio</Label>
  <Textarea placeholder="Fortell om deg selv..." rows={3} />
  <FormDescription>Maks 160 tegn.</FormDescription>
</FormField>

<FormField>
  <Label>Rolle</Label>
  <Select>
    <SelectTrigger>
      <SelectValue placeholder="Velg rolle" />
    </SelectTrigger>
    <SelectContent>
      <SelectItem value="dev">Utvikler</SelectItem>
      <SelectItem value="designer">Designer</SelectItem>
      <SelectItem value="pm">Prosjektleder</SelectItem>
    </SelectContent>
  </Select>
</FormField>

<FormField>
  <div style={{ display: "flex", alignItems: "center", gap: "0.5rem" }}>
    <Checkbox id="agree-terms" />
    <Label htmlFor="agree-terms">Jeg godtar vilkårene</Label>
  </div>
</FormField>

  <Button type="submit">Send inn</Button>
</form>
```

## Med validering

```tsx
<form style={{ display: "grid", gap: "1.5rem", maxWidth: "24rem" }}>
  <FormField>
    <Label>Navn</Label>
    <Input />
    <FormMessage>Navn er påkrevd.</FormMessage>
  </FormField>

<FormField>
  <Label>E-post</Label>
  <Input type="email" defaultValue="ugyldig-epost" />
  <FormMessage>Ugyldig e-postadresse.</FormMessage>
</FormField>

<FormField>
  <Label>Bio</Label>
  <Textarea placeholder="Skriv noe..." />
  <FormDescription>Valgfritt felt — ingen feil her.</FormDescription>
</FormField>

  <Button type="submit">Send inn</Button>
</form>
```

## Egne skjemaelementer

Bruk `useFormControl` for å koble et eget skjemaelement til `FormField`. Hooken slår sammen dine props med konteksten — eksplisitte props vinner alltid.

```tsx
function MyControl(props: React.ComponentProps<"input">) {
  const controlProps = useFormControl(props);
  return <input {...props} {...controlProps} />;
}
```

## Bruksanbefalinger

- **Gjør: Bruk FormField for konsistent layout** — FormField gir automatisk riktig mellomrom mellom label, felt, hjelpetekst og feilmelding.
- **Unngå: Ikke bland hjelpetekst og feilmelding** — Vis enten hjelpetekst eller feilmelding, ikke begge. Feilmeldingen erstatter hjelpeteksten.

## Tilgjengelighet

- FormField kobler automatisk label til skjemaelementet, og FormDescription/FormMessage via `aria-describedby`
- Skjemaelementet får `aria-invalid="true"` når en FormMessage vises (eller når `invalid` er satt)
- Eksplisitte props som `id`, `aria-describedby` og `aria-invalid` på skjemaelementet vinner over konteksten
- FormMessage rendres med `role="alert"` slik at skjermlesere annonserer feil
- FormMessage rendrer ingenting hvis `children` er tom — trygt å alltid inkludere

## Props

### FormField

Arver alle props fra `<div>`.

| Prop        | Type      | Default  | Beskrivelse                                                                 |
| ----------- | --------- | -------- | --------------------------------------------------------------------------- |
| `controlId` | `string`  | generert | Id for skjemaelementet i feltet                                             |
| `required`  | `boolean` | —        | Markerer feltet som påkrevd (stjerne på label, `required` på skjemaelement) |
| `disabled`  | `boolean` | —        | Deaktiverer skjemaelementet                                                 |
| `invalid`   | `boolean` | —        | Markerer feltet som ugyldig. Settes automatisk når FormMessage vises        |

### FormDescription

Arver alle props fra `<p>`. Rendres med dempet farge og liten tekst.

### FormMessage

Arver alle props fra `<p>`. Rendres med `role="alert"` og en rød prikk foran teksten.

| Prop       | Type        | Default | Beskrivelse                                   |
| ---------- | ----------- | ------- | --------------------------------------------- |
| `children` | `ReactNode` | —       | Feilmeldingstekst. Rendrer ingenting hvis tom |

### useFormControl

`useFormControl(props)` returnerer `id`, `required`, `disabled`, `aria-describedby` og `aria-invalid` slått sammen med konteksten fra nærmeste FormField.

---

# TextLink

> Inline tekstlenke med automatisk eksternt-lenke-ikon og tilgjengelighet.

[Kildekode på GitHub](https://github.com/bekk/designsystem/blob/main/packages/designsystem/src/ui/text-link.tsx)

TextLink er en inline lenkekomponent med understreket tekst som fjerner understrekingen ved hover. Komponenten oppdager automatisk eksterne URLer og viser et eksternt-lenke-ikon, setter `target="_blank"` og legger til skjermlesertekst for tilgjengelighet.

## Utforsk

*Se interaktiv playground på dokumentasjonssiden.*

## Importering

```tsx
import { TextLink } from "@kilden/designsystem";
```

## Grunnleggende bruk

```tsx
<TextLink href="/komponenter">Gå til komponenter</TextLink>
```

## Ekstern lenke

Eksterne lenker (http/https) får automatisk et trailing-ikon og åpnes i nytt vindu.

```tsx
<TextLink href="https://bekk.no">Besøk Bekk</TextLink>
```

## Ikon-varianter

Du kan styre ikonplassering med `iconPlacement`-propen, og sende inn et custom ikon med `icon`-propen.

```tsx
<div style={{ display: "flex", flexDirection: "column", gap: "0.75rem" }}>
  <TextLink href="/test" iconPlacement="none">Uten ikon</TextLink>
  <TextLink href="/test" iconPlacement="leading">Med leading-ikon</TextLink>
  <TextLink href="/test" iconPlacement="trailing">Med trailing-ikon</TextLink>
  <TextLink href="https://bekk.no" iconPlacement="auto">Auto (ekstern = trailing)</TextLink>
</div>
```

## Custom ikon

Send inn et eget ikon med `icon`-propen. Ikonet erstatter standard ExternalLink-ikonet.

```tsx
<div style={{ display: "flex", flexDirection: "column", gap: "0.75rem" }}>
  <TextLink href="/neste-side" icon={<ArrowRight className="h-[1em] w-[1em]" />} iconPlacement="trailing">
    Neste side
  </TextLink>
  <TextLink href="/last-ned" icon={<Download className="h-[1em] w-[1em]" />} iconPlacement="leading">
    Last ned rapport
  </TextLink>
</div>
```

## Med asChild

Bruk `asChild` for å rendre en annen komponent som rot-element, for eksempel React Router sin `Link`.

```tsx
<TextLink asChild>
  <a href="/komponenter">React Router Link</a>
</TextLink>
```

## Bruksanbefalinger

- **Gjør: Bruk for inline-lenker i tekst** — TextLink passer for lenker som er del av en setning eller et avsnitt.
- **Unngå: Ikke bruk for handlinger** — For handlinger som utfører noe (lagre, sende, slette), bruk Button i stedet.

- **Gjør: Bruk beskrivende lenketekst** — Lenketeksten bør forklare hva brukeren navigerer til.
- **Unngå: Unngå generisk lenketekst** — Tekst som 'klikk her' eller 'les mer' gir lite kontekst for skjermlesere.

## Tilgjengelighet

- Understreking som standard gir visuell indikasjon på at teksten er klikkbar
- Eksterne lenker annonseres med "(åpner i nytt vindu)" for skjermlesere
- `target="_blank"` settes automatisk med `rel="noopener noreferrer"` for sikkerhet
- Fokusring med tydelig kontrast for tastaturnavigasjon
- Ikoner har `aria-hidden="true"` for å unngå duplikat annonsering

## Props

| Prop            | Type                                          | Default  | Beskrivelse                                                      |
| --------------- | --------------------------------------------- | -------- | ---------------------------------------------------------------- |
| `href`          | `string`                                      | —        | URL-en lenken peker til                                          |
| `icon`          | `React.ReactNode`                             | —        | Custom ikon. Standard er ExternalLink for eksterne lenker        |
| `iconPlacement` | `"none" \| "leading" \| "trailing" \| "auto"` | `"auto"` | Ikonplassering. `"auto"` viser trailing-ikon for eksterne lenker |
| `asChild`       | `boolean`                                     | `false`  | Rendrer child-elementet som rot via Radix Slot                   |
| `target`        | `string`                                      | —        | Overstyrer automatisk `_blank` for eksterne lenker               |
| `rel`           | `string`                                      | —        | Overstyrer automatisk `noopener noreferrer` for eksterne lenker  |
| `className`     | `string`                                      | —        | Ekstra CSS-klasser som merges med standard-klasser               |

---

# Tooltip

> Kontekstuell informasjon som vises ved hover eller fokus.

Tooltip,
  TooltipTrigger,
  TooltipContent,
  TooltipProvider,
  Button,
} from "@kilden/designsystem";

# Tooltip

[Kildekode på GitHub](https://github.com/bekk/designsystem/blob/main/packages/designsystem/src/ui/tooltip.tsx)

Tooltip viser en liten informasjonsboble når brukeren holder musepekeren over eller fokuserer på et element. Bygget på [Radix UI Tooltip](https://www.radix-ui.com/primitives/docs/components/tooltip) med automatisk posisjonering og animasjon.

## Importering

```tsx
import {
  Tooltip,
  TooltipTrigger,
  TooltipContent,
  TooltipProvider,
} from "@kilden/designsystem";
```

> **Merk:** `TooltipProvider` må wrappe alle tooltips i applikasjonen. Legg den typisk rundt rot-komponenten.

## Grunnleggende bruk

```tsx
<TooltipProvider>
  <Tooltip>
    <TooltipTrigger asChild>
      <Button variant="secondary">Hold over meg</Button>
    </TooltipTrigger>
    <TooltipContent>
      <p>Nyttig tilleggsinformasjon</p>
    </TooltipContent>
  </Tooltip>
</TooltipProvider>
```

## Posisjonering

Tooltip kan plasseres på ulike sider med `side`-prop-en.

```tsx
<TooltipProvider>
  <div style={{ display: "flex", gap: "1rem", flexWrap: "wrap", justifyContent: "center" }}>
    <Tooltip>
      <TooltipTrigger asChild>
        <Button variant="secondary">Over</Button>
      </TooltipTrigger>
      <TooltipContent side="top">Tooltip over</TooltipContent>
    </Tooltip>
    <Tooltip>
      <TooltipTrigger asChild>
        <Button variant="secondary">Under</Button>
      </TooltipTrigger>
      <TooltipContent side="bottom">Tooltip under</TooltipContent>
    </Tooltip>
    <Tooltip>
      <TooltipTrigger asChild>
        <Button variant="secondary">Venstre</Button>
      </TooltipTrigger>
      <TooltipContent side="left">Tooltip til venstre</TooltipContent>
    </Tooltip>
    <Tooltip>
      <TooltipTrigger asChild>
        <Button variant="secondary">Høyre</Button>
      </TooltipTrigger>
      <TooltipContent side="right">Tooltip til høyre</TooltipContent>
    </Tooltip>
  </div>
</TooltipProvider>
```

## Med forsinkelse

`TooltipProvider` styrer forsinkelse for alle tooltips. `delayDuration` angir millisekunder før tooltip vises.

```tsx
<TooltipProvider delayDuration={800}>
  <Tooltip>
    <TooltipTrigger asChild>
      <Button variant="secondary">Lang forsinkelse (800ms)</Button>
    </TooltipTrigger>
    <TooltipContent>
      Denne tok litt tid å vise!
    </TooltipContent>
  </Tooltip>
</TooltipProvider>
```

## Bruksanbefalinger

- **Gjør: Bruk for tilleggsinformasjon** — Tooltip egner seg for å forklare ikoner, forkorte tekst, eller gi kontekst som ikke er kritisk.
- **Unngå: Ikke legg viktig innhold i tooltip** — Informasjon som er nødvendig for å bruke grensesnittet bør alltid være synlig. Tooltip er utilgjengelig for touch-brukere og skjermlesere i noen kontekster.

- **Gjør: Kort og konsis tekst** — Hold tooltip-teksten kort — helst én linje. For lengre forklaringer, bruk en popover eller inline-tekst.
- **Unngå: Ikke bruk interaktivt innhold** — Tooltip lukkes når brukeren flytter musen. Ikke legg lenker, knapper eller annet klikkbart innhold inni en tooltip.

## Tilgjengelighet

- Tooltip-innholdet kobles til triggeren via `aria-describedby`
- Vises ved både hover og fokus, slik at tastaturbrukere også får tilgang
- Lukkes med `Escape`
- Følger [WAI-ARIA Tooltip-mønsteret](https://www.w3.org/WAI/ARIA/apg/patterns/tooltip/)

## Props

### TooltipProvider

| Prop                | Type     | Default | Beskrivelse                                           |
| ------------------- | -------- | ------- | ----------------------------------------------------- |
| `delayDuration`     | `number` | `700`   | Millisekunder før tooltip vises                       |
| `skipDelayDuration` | `number` | `300`   | Millisekunder før forsinkelsen resettes etter lukking |

### Tooltip (rot)

| Prop           | Type                      | Default | Beskrivelse                  |
| -------------- | ------------------------- | ------- | ---------------------------- |
| `open`         | `boolean`                 | —       | Kontrollert åpen/lukket      |
| `onOpenChange` | `(open: boolean) => void` | —       | Kalles når tilstanden endres |
| `defaultOpen`  | `boolean`                 | `false` | Om tooltip starter åpen      |

### TooltipTrigger

| Prop      | Type      | Default | Beskrivelse                      |
| --------- | --------- | ------- | -------------------------------- |
| `asChild` | `boolean` | `false` | Bruk child-elementet som trigger |

### TooltipContent

| Prop         | Type                                     | Default    | Beskrivelse                                |
| ------------ | ---------------------------------------- | ---------- | ------------------------------------------ |
| `side`       | `"top" \| "right" \| "bottom" \| "left"` | `"top"`    | Hvilken side av triggeren tooltip vises på |
| `sideOffset` | `number`                                 | `4`        | Avstand fra triggeren i piksler            |
| `align`      | `"start" \| "center" \| "end"`           | `"center"` | Justering langs siden                      |

---

# Spinner

> Roterende lasteindikator for ubestemte lastetilstander.

[Kildekode på GitHub](https://github.com/bekk/designsystem/blob/main/packages/designsystem/src/ui/spinner.tsx)

Spinner viser at noe lastes når du ikke vet hvor lang tid det tar. Bruk `Skeleton` i stedet hvis du kjenner formen på innholdet som lastes.

## Utforsk

*Se interaktiv playground på dokumentasjonssiden.*

## Importering

```tsx
import { Spinner } from "@kilden/designsystem";
```

## Grunnleggende bruk

```tsx
<Spinner />
```

## Størrelser

```tsx
<div style={{ display: "flex", alignItems: "center", gap: "1.5rem" }}>
  <Spinner size="sm" />
  <Spinner size="md" />
  <Spinner size="lg" />
</div>
```

## Egendefinert label

Spinnerens `aria-label` leses opp av skjermlesere. Standard er `"Laster..."`.

```tsx
<Spinner label="Henter resultater..." />
```

## Sentrert i en container

```tsx
<div style={{ display: "flex", alignItems: "center", justifyContent: "center", padding: "2rem" }}>
  <Spinner />
</div>
```

## Med tekst

```tsx
<div style={{ display: "flex", alignItems: "center", gap: "0.5rem" }}>
  <Spinner size="sm" />
  <span>Laster inn data...</span>
</div>
```

## Bruksanbefalinger

- **Gjør: Bruk for ubestemt lasting** — Spinner passer når du ikke vet hvor lang tid en operasjon tar — f.eks. API-kall eller innlogging.
- **Unngå: Ikke bruk når du kjenner formen av innholdet** — Hvis du vet hva som lastes (tekst, bilder, kort), bruk Skeleton i stedet — det gir en mer forutsigbar opplevelse.

## Tilgjengelighet

- Har `role="status"` som annonserer lastetilstanden til skjermlesere
- `aria-label` (standard: `"Laster..."`) leses opp automatisk — tilpass den til konteksten
- SVG-grafikken er skjult for skjermlesere

## Props

| Prop        | Type                   | Default       | Beskrivelse                         |
| ----------- | ---------------------- | ------------- | ----------------------------------- |
| `size`      | `"sm" \| "md" \| "lg"` | `"md"`        | Størrelse på spinnerens diameter    |
| `label`     | `string`               | `"Laster..."` | Tilgjengelig label for skjermlesere |
| `className` | `string`               | —             | Ekstra CSS-klasser                  |

---

# Logo

> Bekk-logoer som React-komponenter og publiserte SVG-filer.

[Kildekode på GitHub](https://github.com/bekk/designsystem/blob/main/packages/designsystem/src/ui/logo.tsx)

Designsystemet eksponerer Bekk-logoene både som React-komponenter og som publiserte SVG-filer. Det gjør det enkelt å bruke logoene i UI-et og å hente de samme filene i andre flater som trenger rå SVG.

## Utforsk

*Se interaktiv playground på dokumentasjonssiden.*

## Importering

```tsx
import { Logo } from "@kilden/designsystem";
```

## React-komponenter

  <div style={{ background: "#111827", padding: "1rem", display: "inline-flex" }}>

  </div>

  <div style={{ background: "#111827", padding: "1rem", display: "inline-flex" }}>

  </div>
</div>`}
  scope={{ Logo }}
/>

Komponentene arver vanlige props for `<svg>`, så du kan styre størrelse, klasse og tilgjengelig navn direkte:

```tsx
<Logo variant="secondary" color="black" className="w-48" aria-label="Bekk" />
<Logo variant="main" color="white" className="w-40" title="Bekk hovedlogo" />
```

## Rå SVG-filer

Følgende filer publiseres også fra pakken:

```text
@kilden/designsystem/logos/bekk-main-black.svg
@kilden/designsystem/logos/bekk-main-white.svg
@kilden/designsystem/logos/bekk-secondary-black.svg
@kilden/designsystem/logos/bekk-secondary-white.svg
```

Hvordan disse hentes i kode avhenger av byggeverktøyet ditt, men subpathene over er de stabile, publiserte filbanene.

## Favicon

En ferdig favicon-SVG er tilgjengelig fra pakken og kan brukes direkte i HTML-hodet:

```text
@kilden/designsystem/logos/favicon.svg
```

### React Router / Vite

Kopier filen til `public/favicon.svg` og legg til lenker i rotlayouten:

```tsx
export const links: Route.LinksFunction = () => [
  { rel: "icon", type: "image/svg+xml", href: "/favicon.svg" },
];
```

### Vanilla HTML

```html
<link rel="icon" type="image/svg+xml" href="/favicon.svg" />
```

SVG-favikoner støttes av alle moderne nettlesere og skalerer skarpt på alle skjermoppløsninger. Legg til en `.ico`-fallback for eldre nettlesere:

```tsx
export const links: Route.LinksFunction = () => [
  { rel: "icon", type: "image/svg+xml", href: "/favicon.svg" },
  { rel: "icon", type: "image/x-icon", href: "/favicon.ico" },
];
```

## Tilgjengelighet

- Sett `aria-label` eller `title` når logoen formidler identitet eller fungerer som innhold.
- La logoen være dekorativ når den bare støtter omgivelsene visuelt; da blir den skjult for skjermlesere automatisk.
- Sørg for tilstrekkelig kontrast rundt de hvite variantene ved bruk på mørk bakgrunn.

## Props

Logo arver alle standard SVG-props (`className`, `aria-label`, `aria-hidden`, m.m.), pluss disse:

| Prop      | Type                    | Default   | Beskrivelse                                                             |
| --------- | ----------------------- | --------- | ----------------------------------------------------------------------- |
| `variant` | `"main" \| "secondary"` | `"main"`  | Hvilken logovariant som vises                                           |
| `color`   | `"black" \| "white"`    | `"black"` | Fargevariant                                                            |
| `title`   | `string`                | —         | Tilgjengelig navn — setter `role="img"` og kobler via `aria-labelledby` |

---

# Input

> Tekstfelt for brukerinput med støtte for label, feilmelding og ulike typer.

[Kildekode på GitHub](https://github.com/bekk/designsystem/blob/main/packages/designsystem/src/ui/input.tsx)

Input er et tekstfelt for brukerinput, stylet som en boks i samme stil som de andre skjemafeltene. Støtter innebygd label og feilmelding, samt alle standard HTML input-typer.

## Utforsk

*Se interaktiv playground på dokumentasjonssiden.*

## Importering

```tsx
import { Input } from "@kilden/designsystem";
```

## Grunnleggende bruk

```tsx
<Input placeholder="Skriv noe..." />
```

## Med label

Bruk `label`-propen for å legge til en synlig label koblet til inputfeltet.

```tsx
<Input label="Navn" placeholder="Ola Nordmann" />
```

## Med feilmelding

Bruk `error`-propen for å vise en feilmelding under feltet. Dette setter også visuell error-state med rød bunnlinje og `aria-invalid` på inputen.

```tsx
<Input label="E-post" type="email" error="E-post er obligatorisk" />
```

## Med ikon

Bruk `prefix` og `suffix`-propene for å legge til ikoner eller knapper inne i feltet.

```tsx
<Input label="E-post" prefix={<Mail size={20} />} placeholder="ola@bekk.no" />`}
  scope={{ Mail }}
/>

<Playground
  code={`<Input label="Beløp" suffix={<span className="text-neutral-text-subtle">NOK</span>} placeholder="0,00" />
```

## Påkrevd felt

Bruk `required`-propen for å vise en rød stjerne etter label-teksten, i tillegg til nativ nettleser-validering.

```tsx
<Input label="Navn" required placeholder="Ola Nordmann" />
```

## Ulike typer

```tsx
<div style={{ display: "grid", gap: "1.5rem", maxWidth: "24rem" }}>
  <Input label="E-post" type="email" placeholder="ola@bekk.no" />
  <Input label="Passord" type="password" placeholder="••••••••" />
  <Input label="Antall" type="number" placeholder="0" />
  <Input label="Søk" type="search" placeholder="Søk etter komponenter..." />
</div>
```

## Deaktivert

```tsx
<Input label="Navn" placeholder="Kan ikke redigeres" disabled />
```

## Fil-input

```tsx
<Input label="Last opp fil" type="file" />
```

## Med knapp

```tsx
<div style={{ display: "flex", gap: "0.5rem", maxWidth: "24rem", alignItems: "end" }}>
  <Input label="Nyhetsbrev" placeholder="E-postadresse" type="email" />
  <Button>Abonner</Button>
</div>
```

## Bruksanbefalinger

- **Gjør: Bruk alltid en label** — Alle input-felt må ha en synlig label som forklarer hva feltet er til. Bruk label-propen for enkel kobling.
- **Unngå: Ikke bruk placeholder som label** — Placeholder forsvinner når brukeren begynner å skrive. Bruk label-propen i tillegg til placeholder.

- **Gjør: Bruk riktig type** — Sett type='email' for e-post, type='number' for tall osv. Dette gir riktig tastatur på mobil og validering i nettleseren.
- **Unngå: Ikke bruk text-type for alt** — Å bruke type='text' for e-post, tall eller telefonnummer gir dårligere brukeropplevelse, spesielt på mobil.

## Tilgjengelighet

- `label`-propen kobler automatisk label til input via `id`/`htmlFor`
- `required`-propen viser en rød stjerne etter labelen, pluss en skjult "(påkrevd)"-tekst for skjermlesere
- `error`-propen setter `aria-invalid` og kobler feilmelding via `aria-describedby`
- Feilmelding har `role="alert"` for skjermlesere
- Tydelig fokusindikator (outline) som også vises i høykontrastmodus
- Inni en `FormField` kobles Input automatisk til `Label`, `FormDescription` og `FormMessage`
- Disabled-tilstand kommuniseres via HTML `disabled`-attributtet

## Props

Input arver alle props fra `<input>`, pluss disse:

| Prop          | Type                       | Default  | Beskrivelse                                       |
| ------------- | -------------------------- | -------- | ------------------------------------------------- |
| `label`       | `ReactNode`                | —        | Synlig label over inputfeltet                     |
| `error`       | `ReactNode`                | —        | Feilmelding under feltet (aktiverer error-state)  |
| `prefix`      | `ReactNode`                | —        | Innhold til venstre for inputfeltet (f.eks. ikon) |
| `suffix`      | `ReactNode`                | —        | Innhold til høyre for inputfeltet (f.eks. knapp)  |
| `type`        | `string`                   | `"text"` | Input-type (text, email, password, number osv.)   |
| `placeholder` | `string`                   | —        | Plassholdertekst                                  |
| `disabled`    | `boolean`                  | `false`  | Deaktiver feltet                                  |
| `required`    | `boolean`                  | `false`  | Marker som påkrevd                                |
| `name`        | `string`                   | —        | Navn for skjemainnsending                         |
| `value`       | `string`                   | —        | Kontrollert verdi                                 |
| `onChange`    | `(e: ChangeEvent) => void` | —        | Kalles ved endring                                |

---

# ThemeSwitcher

> Radiogruppe for å bytte mellom designsystemets fargetemaer.

[Kildekode på GitHub](https://github.com/bekk/designsystem/blob/main/packages/designsystem/src/ui/theme-switcher.tsx)

ThemeSwitcher er en kontrollert radiogruppe for å velge mellom de fire fargetemaene i designsystemet: Ild, Luft, Vann og Jord. Ved valg settes tema-attributtene (`data-brand`, `data-theme` og `data-color-scheme`) på `<html>`-elementet automatisk. Sett `applyToDocument={false}` hvis appen styrer temaet selv (f.eks. via cookie og server-rendering).

## Importering

```tsx
import { ThemeSwitcher, applyThemeToDocument } from "@kilden/designsystem";
```

## Grunnleggende bruk

  );
}
render()`}
/>

## Størrelser

### Standard

```tsx
<ThemeSwitcher theme={theme} onThemeChange={setTheme} />
```

### Kompakt

  );
}
render()`}
/>

## Fargemodus

`colorMode`-prop styrer om temaet settes med lys, mørk eller systemets fargemodus. Standard er `"system"`.

```tsx
{
  /* Tving lys modus */
}
<ThemeSwitcher theme={theme} onThemeChange={setTheme} colorMode="light" />;

{
  /* Tving mørk modus */
}
<ThemeSwitcher theme={theme} onThemeChange={setTheme} colorMode="dark" />;
```

ThemeSwitcher setter `data-brand="bekk"` og `data-theme` (f.eks. `"bekk-ild"`) på `<html>`. Når `colorMode` er `"light"` eller `"dark"`, settes også `data-color-scheme`. Med `"system"` fjernes det, og operativsystemet bestemmer.

## Styr temaet selv

Med `applyToDocument={false}` endrer ikke ThemeSwitcher `<html>`. Bruk hjelperen `applyThemeToDocument(theme, colorMode)` når du selv vil sette temaet, f.eks. etter å ha lagret valget:

```tsx
<ThemeSwitcher
  theme={theme}
  applyToDocument={false}
  onThemeChange={(next) => {
    setTheme(next);
    saveThemeCookie(next);
    applyThemeToDocument(next, colorMode); // setter f.eks. data-theme="bekk-ild" og data-color-scheme="dark"
  }}
/>
```

## Bruksanbefalinger

- **Gjør: Bruk som kontrollert komponent** — ThemeSwitcher er alltid kontrollert — hold tema-tilstanden i en overordnet komponent og synkroniser den med f.eks. en cookie eller localStorage.
- **Unngå: Ikke vis flere ThemeSwitchere samtidig** — Ha kun én ThemeSwitcher i grensesnittet, ellers kan bruker ende opp med motstridende tema-valg.

## Tilgjengelighet

- Rendres som en `role="radiogroup"` med `aria-label="Velg tema"`
- Hvert tema er en `role="radio"`-knapp med `aria-checked`
- Fullt tastaturtilgjengelig — Tab for fokus, Enter/Space for valg

## Props

| Prop              | Type                                  | Default     | Beskrivelse                                       |
| ----------------- | ------------------------------------- | ----------- | ------------------------------------------------- |
| `theme`           | `"ild" \| "luft" \| "vann" \| "jord"` | —           | Gjeldende valgt tema (påkrevd)                    |
| `onThemeChange`   | `(theme: Theme) => void`              | —           | Kalles når brukeren velger et nytt tema (påkrevd) |
| `size`            | `"default" \| "sm"`                   | `"default"` | Størrelse                                         |
| `colorMode`       | `"light" \| "dark" \| "system"`       | `"system"`  | Fargemodus som settes som `data-color-scheme`     |
| `applyToDocument` | `boolean`                             | `true`      | Om temaet skal settes på `<html>` ved valg        |

---

# Toast

> Flyktige varsler som dukker opp og forsvinner automatisk.

ToastProvider,
  Toast,
  ToastTitle,
  ToastDescription,
  ToastAction,
  ToastClose,
  ToastViewport,
  Toaster,
  toast,
  Button,
} from "@kilden/designsystem";

# Toast

[Kildekode på GitHub](https://github.com/bekk/designsystem/blob/main/packages/designsystem/src/ui/toast.tsx)

Toast viser korte, flyktige meldinger til brukeren — for eksempel bekreftelser, advarsler eller feilmeldinger. Bygget på [Radix UI Toast](https://www.radix-ui.com/primitives/docs/components/toast) med sveip-for-å-lukke og full tastaturnavigasjon.

Den anbefalte måten å bruke toasts på er det imperative API-et: plasser `` én gang i rot-layouten, og kall `toast()` hvor som helst i appen. Lavnivå-komponentene (`ToastProvider`, `Toast` osv.) er fortsatt tilgjengelige hvis du trenger full kontroll.

## Importering

```tsx
// Anbefalt
import { Toaster, toast, useToast, dismissToast } from "@kilden/designsystem";

// Lavnivå-komponenter
import {
  ToastProvider,
  Toast,
  ToastTitle,
  ToastDescription,
  ToastAction,
  ToastClose,
  ToastViewport,
} from "@kilden/designsystem";
```

## Med Toaster og toast()

Plasser `` én gang, typisk nederst i rot-layouten:

```tsx
export default function RootLayout({ children }) {
  return (
    <body>
      {children}
      <Toaster />
    </body>
  );
}
```

Vis deretter varsler med `toast()`:

<div style={{ display: "flex", gap: "0.5rem" }}>
toast({ title: "Lagret!", description: "Endringene dine er lagret." })}>
Vis toast

toast({ variant: "danger", title: "Noe gikk galt", description: "Prøv igjen senere." })}
>
Vis feil

</div>

</>
  );
}
render();`}
/>

### Plassering

Bruk `position` på `Toaster` for å velge hjørne. Standard er `"top-right"`. På mobil går toasts over hele bredden, og `position` styrer bare om de ligger øverst eller nederst.

```tsx
<Toaster position="top-right" />
```

`toast()` returnerer et håndtak med `id`, `dismiss()` og `update()`:

```tsx
const handle = toast({ title: "Laster opp..." });
// ...
handle.update({ title: "Ferdig!", variant: "success" });
```

Bruk `useToast()` i komponenter for å lese aktive toasts, eller `dismissToast()` for å lukke én (med `id`) eller alle toasts.

## Lavnivå-komponenter

Wrap applikasjonen din med `ToastProvider` og plasser `ToastViewport` nederst. Bruk `open`-prop for å kontrollere synlighet.

setOpen(true)}>Vis toast

<div style={{ display: "grid", gap: "0.25rem" }}>
Lagret!
Endringene dine er lagret.
</div>

  );
}
render();`}
/>

## Med handling

setOpen(true)}>Send melding

<div style={{ display: "grid", gap: "0.25rem" }}>
Melding sendt
Meldingen ble sendt til mottakeren.
</div>
Angre

  );
}
render();`}
/>

## Feil-variant

setOpen(true)}>Utløs feil

<div style={{ display: "grid", gap: "0.25rem" }}>
Noe gikk galt
Kunne ikke lagre endringene. Prøv igjen.
</div>

  );
}
render();`}
/>

## Suksess-variant

setOpen(true)}>Publiser artikkel

<div style={{ display: "grid", gap: "0.25rem" }}>
Publisert!
Artikkelen er nå synlig for alle.
</div>

  );
}
render();`}
/>

## Advarsel-variant

setOpen(true)}>Lagre utkast

<div style={{ display: "grid", gap: "0.25rem" }}>
Utkastet er ikke publisert
Artikkelen er lagret, men er ikke synlig for andre ennå.
</div>

  );
}
render();`}
/>

## Bruksanbefalinger

- **Gjør: Bruk for flyktige bekreftelser** — Toast passer for korte meldinger som ikke krever brukerhandling — f.eks. 'Lagret', 'Kopiert' eller 'Sendt'.
- **Unngå: Ikke bruk for viktig informasjon** — Toast forsvinner automatisk. For kritiske meldinger som krever handling, bruk Alert eller Dialog i stedet.

## Tilgjengelighet

- Toast rendres med `role="status"` og `aria-live="polite"` som standard
- Danger-toasts bør bruke `type="foreground"` for å annonseres umiddelbart
- `ToastAction` krever `altText` som beskriver handlingen for skjermlesere
- Tastaturnavigasjon: `F8` fokuserer toast-området, `Escape` lukker
- Sveip-for-å-lukke på touch-enheter
- `ToastClose` har et skjult tilgjengelig navn («Lukk»)

## Props

### toast(options)

| Option        | Type                                              | Default     | Beskrivelse               |
| ------------- | ------------------------------------------------- | ----------- | ------------------------- |
| `title`       | `ReactNode`                                       | —           | Tittelen på varselet      |
| `description` | `ReactNode`                                       | —           | Utfyllende tekst          |
| `variant`     | `"default" \| "success" \| "warning" \| "danger"` | `"default"` | Visuell variant           |
| `action`      | `ReactElement` (`ToastAction`)                    | —           | Handlingsknapp            |
| `duration`    | `number`                                          | `5000`      | Tid i ms før auto-lukking |

Øvrige props fra `Toast` kan også sendes med. Maks tre toasts vises samtidig.

### Toaster

| Prop       | Type                                                           | Default       | Beskrivelse                                                      |
| ---------- | -------------------------------------------------------------- | ------------- | ---------------------------------------------------------------- |
| `position` | `"top-left" \| "top-right" \| "bottom-left" \| "bottom-right"` | `"top-right"` | Hjørnet toasts vises i. På mobil styrer den bare topp eller bunn |

Tar i tillegg imot propsene til `ToastProvider` (f.eks. `duration`). `swipeDirection` følger `position` med mindre du setter den selv.

### Toast

| Prop           | Type                                              | Default        | Beskrivelse                      |
| -------------- | ------------------------------------------------- | -------------- | -------------------------------- |
| `variant`      | `"default" \| "success" \| "warning" \| "danger"` | `"default"`    | Visuell variant                  |
| `open`         | `boolean`                                         | —              | Kontrollert åpen-tilstand        |
| `onOpenChange` | `(open: boolean) => void`                         | —              | Kalles når tilstand endres       |
| `duration`     | `number`                                          | `5000`         | Tid i ms før auto-lukking        |
| `type`         | `"foreground" \| "background"`                    | `"foreground"` | Påvirker skjermleser-annonsering |

### ToastAction

| Prop      | Type     | Default | Beskrivelse                                          |
| --------- | -------- | ------- | ---------------------------------------------------- |
| `altText` | `string` | —       | Beskrivelse av handlingen for skjermlesere (påkrevd) |

### ToastProvider

| Prop             | Type                                  | Default   | Beskrivelse                       |
| ---------------- | ------------------------------------- | --------- | --------------------------------- |
| `duration`       | `number`                              | `5000`    | Standard varighet for alle toasts |
| `swipeDirection` | `"right" \| "left" \| "up" \| "down"` | `"right"` | Sveiperetning for å lukke         |

---

# ScrollArea

> Tilpasset scrollbar for containere med begrenset høyde.

[Kildekode på GitHub](https://github.com/bekk/designsystem/blob/main/packages/designsystem/src/ui/scroll-area.tsx)

ScrollArea gir tilpassede scrollbarer som ser like ut på tvers av operativsystemer. Bygget på [Radix UI ScrollArea](https://www.radix-ui.com/primitives/docs/components/scroll-area). Perfekt for sidebarer, lister og containere med begrenset høyde.

## Importering

```tsx
import { ScrollArea } from "@kilden/designsystem";
```

## Grunnleggende bruk

```tsx
<ScrollArea className="h-[200px] w-[300px] rounded-md border border-neutral-border-default p-4">
  <div>
    <h4 style={{ marginBottom: "1rem", fontWeight: 600, fontSize: "0.875rem" }}>Komponenter</h4>
    {["Accordion", "Alert", "Avatar", "Badge", "Breadcrumb", "Button", "Card", "Checkbox", "Dialog", "DropdownMenu", "Input", "Label", "Progress", "Select", "Separator", "Skeleton", "Slider", "Switch", "Table", "Tabs", "Textarea", "Toast", "Toggle", "Tooltip"].map((component) => (
      <div key={component}>
        <div style={{ padding: "0.25rem 0", fontSize: "0.875rem" }}>{component}</div>
        <Separator />
      </div>
    ))}
  </div>
</ScrollArea>
```

## Med horisontal scrolling

```tsx
<ScrollArea className="w-[400px] whitespace-nowrap rounded-md border border-neutral-border-default p-4">
  <div style={{ display: "flex", gap: "1rem" }}>
    {["Januar", "Februar", "Mars", "April", "Mai", "Juni", "Juli", "August", "September", "Oktober", "November", "Desember"].map((month) => (
      <div key={month} style={{ padding: "0.5rem 1rem", borderRadius: "0.375rem", fontSize: "0.875rem", border: "1px solid var(--color-neutral-border-default)" }}>
        {month}
      </div>
    ))}
  </div>
</ScrollArea>
```

## Bruksanbefalinger

- **Gjør: Bruk for begrensede containere** — ScrollArea passer for innhold som må begrenses i høyde eller bredde — sidebarer, dropdown-innhold, lange lister.
- **Unngå: Ikke bruk for hele sider** — For sidescrolling, bruk nettleserens innebygde scrollbar. ScrollArea er for delvis innhold innenfor en side.

## Tilgjengelighet

- Bevarer nettleserens standard scroll-oppførsel for mus og tastatur
- Tilpassede scrollbarer er kun visuelle — all funksjonalitet er den samme
- Støtter touch-scrolling på mobile enheter
- Piltaster og Page Up/Down fungerer som vanlig

## Props

### ScrollArea

Arver alle props fra Radix `ScrollArea.Root`. Viktige props:

| Prop              | Type                                        | Default   | Beskrivelse                            |
| ----------------- | ------------------------------------------- | --------- | -------------------------------------- |
| `type`            | `"auto" \| "always" \| "scroll" \| "hover"` | `"hover"` | Når scrollbar vises                    |
| `scrollHideDelay` | `number`                                    | `600`     | Forsinkelse i ms før scrollbar skjules |

### ScrollBar

| Prop          | Type                         | Default      | Beskrivelse            |
| ------------- | ---------------------------- | ------------ | ---------------------- |
| `orientation` | `"vertical" \| "horizontal"` | `"vertical"` | Retning på scrollbaren |

---

# Label

> Tilgjengelig label-komponent for skjemaelementer.

[Kildekode på GitHub](https://github.com/bekk/designsystem/blob/main/packages/designsystem/src/ui/label.tsx)

Label er en tilgjengelig label-komponent som kobler tekst til skjemaelementer. Bygget på [Radix UI Label](https://www.radix-ui.com/primitives/docs/components/label) med automatisk håndtering av klikk-fokus og riktig semantikk.

Inni en [FormField](/docs/components/form-field) kobles Label automatisk til skjemaelementet, og viser påkrevd-markering når feltet er `required` — da trenger du ikke `htmlFor`.

## Utforsk

*Se interaktiv playground på dokumentasjonssiden.*

## Importering

```tsx
import { Label } from "@kilden/designsystem";
```

## Grunnleggende bruk

```tsx
<div style={{ display: "grid", gap: "0.5rem", maxWidth: "24rem" }}>
  <Label htmlFor="name">Navn</Label>
  <Input id="name" placeholder="Ola Nordmann" />
</div>
```

## Med ulike skjemaelementer

```tsx
<div style={{ display: "grid", gap: "1.5rem", maxWidth: "24rem" }}>
  <div style={{ display: "grid", gap: "0.5rem" }}>
    <Label htmlFor="email">E-post</Label>
    <Input id="email" type="email" placeholder="ola@bekk.no" />
  </div>
  <div style={{ display: "grid", gap: "0.5rem" }}>
    <Label htmlFor="bio">Biografi</Label>
    <Textarea id="bio" placeholder="Fortell om deg selv..." />
  </div>
  <div style={{ display: "flex", alignItems: "center", gap: "0.5rem" }}>
    <Switch id="notifications" />
    <Label htmlFor="notifications">Aktiver varsler</Label>
  </div>
</div>
```

## Påkrevd felt

Bruk `required`-propen for å vise en rød stjerne etter labelteksten. Skjermlesere får i tillegg en skjult "(påkrevd)"-tekst.

```tsx
<div style={{ display: "grid", gap: "0.5rem", maxWidth: "24rem" }}>
  <Label htmlFor="required-name" required>Navn</Label>
  <Input id="required-name" required placeholder="Ola Nordmann" />
</div>
```

## Bruksanbefalinger

- **Gjør: Koble label til skjemaelementet** — Bruk htmlFor og id for å koble label til input. Da kan brukeren klikke på labelen for å fokusere feltet.
- **Unngå: Ikke bruk label uten kobling** — En label uten htmlFor gir ingen tilgjengelighetsforbedring. Skjermlesere kan ikke koble teksten til feltet.

## Tilgjengelighet

- Klikk på labelen fokuserer det tilknyttede skjemaelementet
- Skjermlesere leser opp labelen når feltet får fokus
- Redusert opasitet og `cursor-not-allowed` når tilknyttet felt er disabled (via CSS `peer`)
- Med `required` vises en rød stjerne visuelt, pluss en skjult "(påkrevd)"-tekst for skjermlesere

## Props

| Prop       | Type      | Default | Beskrivelse                                                                    |
| ---------- | --------- | ------- | ------------------------------------------------------------------------------ |
| `htmlFor`  | `string`  | —       | ID-en til skjemaelementet labelen tilhører. Settes automatisk inni `FormField` |
| `required` | `boolean` | `false` | Viser en rød stjerne som markerer feltet som påkrevd. Arves fra `FormField`    |

---

# Separator

> Visuell skillelinje for å dele opp innhold i seksjoner.

[Kildekode på GitHub](https://github.com/bekk/designsystem/blob/main/packages/designsystem/src/ui/separator.tsx)

Separator er en enkel visuell skillelinje som brukes for å dele opp innhold. Bygget på [Radix UI Separator](https://www.radix-ui.com/primitives/docs/components/separator) med riktig semantikk og ARIA-attributter.

## Utforsk

*Se interaktiv playground på dokumentasjonssiden.*

## Importering

```tsx
import { Separator } from "@kilden/designsystem";
```

## Horisontal (standard)

```tsx
<div>
  <div style={{ marginBottom: "1rem" }}>
    <h4 style={{ fontWeight: 600, margin: "0 0 0.25rem 0" }}>Bekk Designsystem</h4>
    <p style={{ margin: 0, fontSize: "0.875rem", opacity: 0.7 }}>
      Et åpent designsystem for Bekk-produkter.
    </p>
  </div>
  <Separator />
  <div style={{ display: "flex", gap: "1rem", marginTop: "1rem", fontSize: "0.875rem" }}>
    <span>Dokumentasjon</span>
    <span>Kildekode</span>
    <span>Changelog</span>
  </div>
</div>
```

## Vertikal

Bruk `orientation="vertical"` for en vertikal skillelinje. Nyttig mellom elementer i en horisontal layout.

```tsx
<div style={{ display: "flex", alignItems: "center", gap: "1rem", height: "2rem" }}>
  <span style={{ fontSize: "0.875rem" }}>Blog</span>
  <Separator orientation="vertical" />
  <span style={{ fontSize: "0.875rem" }}>Dokumentasjon</span>
  <Separator orientation="vertical" />
  <span style={{ fontSize: "0.875rem" }}>Kildekode</span>
</div>
```

## I en liste

```tsx
<div style={{ maxWidth: "20rem" }}>
  <div style={{ padding: "0.75rem 0" }}>
    <p style={{ fontWeight: 500, margin: "0 0 0.25rem 0" }}>Profil</p>
    <p style={{ fontSize: "0.875rem", margin: 0, opacity: 0.7 }}>Vis og rediger profilen din</p>
  </div>
  <Separator />
  <div style={{ padding: "0.75rem 0" }}>
    <p style={{ fontWeight: 500, margin: "0 0 0.25rem 0" }}>Innstillinger</p>
    <p style={{ fontSize: "0.875rem", margin: 0, opacity: 0.7 }}>Tilpass applikasjonen</p>
  </div>
  <Separator />
  <div style={{ padding: "0.75rem 0" }}>
    <p style={{ fontWeight: 500, margin: "0 0 0.25rem 0" }}>Varsler</p>
    <p style={{ fontSize: "0.875rem", margin: 0, opacity: 0.7 }}>Konfigurer varslingsinnstillinger</p>
  </div>
</div>
```

## Bruksanbefalinger

- **Gjør: Bruk for å gruppere relatert innhold** — Separator gir visuell struktur og hjelper brukeren å skille mellom ulike seksjoner.
- **Unngå: Ikke overbruk separatorer** — For mange skillelinjer skaper visuell støy. Bruk mellomrom (spacing) som primær metode for å skille innhold, og separator kun der det trengs en tydelig visuell grense.

## Tilgjengelighet

- Dekorativ som standard (`decorative={true}`) — rendrer med `role="none"` og ignoreres av skjermlesere
- Sett `decorative={false}` for semantisk separering, som gir `role="separator"` og `aria-orientation`
- Bruk dekorativ modus for visuell separering og semantisk modus når skillelinjen markerer en meningsfull grense

## Props

| Prop          | Type                         | Default        | Beskrivelse                                            |
| ------------- | ---------------------------- | -------------- | ------------------------------------------------------ |
| `orientation` | `"horizontal" \| "vertical"` | `"horizontal"` | Retningen på skillelinjen                              |
| `decorative`  | `boolean`                    | `true`         | Om separatoren er dekorativ (ignorert av skjermlesere) |

---

# PasswordInput

> Passordfelt med vis/skjul-knapp og støtte for autoComplete.

[Kildekode på GitHub](https://github.com/bekk/designsystem/blob/main/packages/designsystem/src/ui/password-input.tsx)

PasswordInput er et passordfelt med innebygd vis/skjul-knapp. Stylet som en boks i samme stil som de andre skjemafeltene. Støtter innebygd label og feilmelding, samt alle standard HTML input-attributter.

## Utforsk

*Se interaktiv playground på dokumentasjonssiden.*

## Importering

```tsx
import { PasswordInput } from "@kilden/designsystem";
```

## Grunnleggende bruk

```tsx
<PasswordInput label="Passord" />
```

## Med autoComplete

Bruk `autoComplete`-propen for å angi om feltet er for et eksisterende passord (`current-password`, default) eller et nytt passord (`new-password`).

```tsx
<PasswordInput label="Nytt passord" autoComplete="new-password" />
```

## Med feilmelding

Bruk `error`-propen for å vise en feilmelding under feltet. Dette setter også visuell error-state med rød bunnlinje og `aria-invalid` på inputen.

```tsx
<PasswordInput label="Passord" error="Passord er påkrevd" />
```

## Påkrevd felt

Bruk `required`-propen for å vise en rød stjerne etter label-teksten, i tillegg til nativ nettleser-validering.

```tsx
<PasswordInput label="Passord" required />
```

## Deaktivert

```tsx
<PasswordInput label="Passord" placeholder="Kan ikke skrive" disabled />
```

## Bruksanbefalinger

- **Gjør: Bruk alltid en label** — Alle passordfelt må ha en synlig label som forklarer hva feltet er til.
- **Unngå: Ikke bruk placeholder som label** — Placeholder forsvinner når brukeren begynner å skrive. Bruk label-propen i tillegg.

- **Gjør: Sett riktig autoComplete** — Bruk current-password for innlogging og new-password for registrering eller passordbytte. Dette hjelper passordadministratorer.
- **Unngå: Ikke bruk vanlig Input for passord** — Bruk PasswordInput for passordfelt. Den gir vis/skjul-knapp og riktig autoComplete ut av boksen.

## Tilgjengelighet

- `label`-propen kobler automatisk label til input via `id`/`htmlFor`
- `error`-propen setter `aria-invalid` og kobler feilmelding via `aria-describedby`
- Feilmelding har `role="alert"` for skjermlesere
- Vis/skjul-knappen har `aria-label` som oppdateres ("Vis passord" / "Skjul passord")
- Vis/skjul-knappen har `type="button"` for å unngå utilsiktet form-submit
- Tydelig fokusindikator med tykkere bunnlinje
- Disabled-tilstand kommuniseres via HTML `disabled`-attributtet

## Props

PasswordInput arver alle props fra `<input>` (unntatt `type` og `suffix`), pluss disse:

| Prop           | Type                                   | Default              | Beskrivelse                                       |
| -------------- | -------------------------------------- | -------------------- | ------------------------------------------------- |
| `label`        | `string`                               | —                    | Synlig label over passordfeltet                   |
| `error`        | `string`                               | —                    | Feilmelding under feltet (aktiverer error-state)  |
| `autoComplete` | `"new-password" \| "current-password"` | `"current-password"` | autoComplete-attributt for passordfeltet          |
| `placeholder`  | `string`                               | —                    | Plassholdertekst                                  |
| `disabled`     | `boolean`                              | `false`              | Deaktiver feltet                                  |
| `required`     | `boolean`                              | `false`              | Marker som påkrevd — viser rød stjerne på labelen |
| `value`        | `string`                               | —                    | Kontrollert verdi                                 |
| `onChange`     | `(e: ChangeEvent) => void`             | —                    | Kalles ved endring                                |

---

# Table

> Strukturert datatabell med header, body, footer og styling.

Table,
  TableHeader,
  TableBody,
  TableFooter,
  TableRow,
  TableHead,
  TableCell,
  TableCaption,
  Badge,
} from "@kilden/designsystem";

# Table

[Kildekode på GitHub](https://github.com/bekk/designsystem/blob/main/packages/designsystem/src/ui/table.tsx)

Table er en strukturert datatabell med konsistent styling for header, body, footer og caption. Ren HTML-semantikk med Tailwind-styling.

## Importering

```tsx
import {
  Table,
  TableHeader,
  TableBody,
  TableFooter,
  TableRow,
  TableHead,
  TableCell,
  TableCaption,
} from "@kilden/designsystem";
```

## Grunnleggende bruk

```tsx
<Table>
  <TableCaption>Liste over ansatte</TableCaption>
  <TableHeader>
    <TableRow>
      <TableHead>Navn</TableHead>
      <TableHead>Rolle</TableHead>
      <TableHead>Avdeling</TableHead>
    </TableRow>
  </TableHeader>
  <TableBody>
    <TableRow>
      <TableCell>Ola Nordmann</TableCell>
      <TableCell>Utvikler</TableCell>
      <TableCell>Teknologi</TableCell>
    </TableRow>
    <TableRow>
      <TableCell>Kari Nordmann</TableCell>
      <TableCell>Designer</TableCell>
      <TableCell>Design</TableCell>
    </TableRow>
    <TableRow>
      <TableCell>Per Hansen</TableCell>
      <TableCell>Prosjektleder</TableCell>
      <TableCell>Ledelse</TableCell>
    </TableRow>
  </TableBody>
</Table>
```

## Med footer

```tsx
<Table>
  <TableHeader>
    <TableRow>
      <TableHead>Produkt</TableHead>
      <TableHead style={{ textAlign: "right" }}>Pris</TableHead>
    </TableRow>
  </TableHeader>
  <TableBody>
    <TableRow>
      <TableCell>Designsystem-lisens</TableCell>
      <TableCell style={{ textAlign: "right" }}>5 000 kr</TableCell>
    </TableRow>
    <TableRow>
      <TableCell>Konsulentbistand</TableCell>
      <TableCell style={{ textAlign: "right" }}>15 000 kr</TableCell>
    </TableRow>
    <TableRow>
      <TableCell>Opplæring</TableCell>
      <TableCell style={{ textAlign: "right" }}>3 000 kr</TableCell>
    </TableRow>
  </TableBody>
  <TableFooter>
    <TableRow>
      <TableCell style={{ fontWeight: 600 }}>Totalt</TableCell>
      <TableCell style={{ textAlign: "right", fontWeight: 600 }}>23 000 kr</TableCell>
    </TableRow>
  </TableFooter>
</Table>
```

## Med badges

```tsx
<Table>
  <TableHeader>
    <TableRow>
      <TableHead>Oppgave</TableHead>
      <TableHead>Ansvarlig</TableHead>
      <TableHead>Status</TableHead>
    </TableRow>
  </TableHeader>
  <TableBody>
    <TableRow>
      <TableCell>Implementer Button</TableCell>
      <TableCell>Ola</TableCell>
      <TableCell><Badge variant="success">Ferdig</Badge></TableCell>
    </TableRow>
    <TableRow>
      <TableCell>Design tokens</TableCell>
      <TableCell>Kari</TableCell>
      <TableCell><Badge variant="warning">Pågår</Badge></TableCell>
    </TableRow>
    <TableRow>
      <TableCell>Dokumentasjon</TableCell>
      <TableCell>Per</TableCell>
      <TableCell><Badge variant="outline">Planlagt</Badge></TableCell>
    </TableRow>
  </TableBody>
</Table>
```

## Bruksanbefalinger

- **Gjør: Bruk for strukturert data** — Table egner seg for data med tydelige kolonner — som lister, priser, statuser eller sammenligninger.
- **Unngå: Ikke bruk for layout** — Tabeller er for data, ikke layout. Bruk CSS Grid eller Flexbox for visuell plassering av elementer.

## Tilgjengelighet

- Bruker semantisk HTML (`<table>`, `<thead>`, `<tbody>`, `<th>`, `<td>`)
- `TableCaption` gir en beskrivelse av tabellen for skjermlesere
- `TableHead` (`<th>`) gir kontekst til dataceller i samme kolonne
- Wrapper med `overflow-auto` for horisontal scrolling på smale skjermer

## Props

Alle komponenter arver standard HTML-attributter for sine respektive elementer:

| Komponent      | HTML-element | Beskrivelse                     |
| -------------- | ------------ | ------------------------------- |
| `Table`        | `<table>`    | Rot-tabellen med scroll-wrapper |
| `TableHeader`  | `<thead>`    | Header-seksjon                  |
| `TableBody`    | `<tbody>`    | Body-seksjon                    |
| `TableFooter`  | `<tfoot>`    | Footer-seksjon                  |
| `TableRow`     | `<tr>`       | Tabellrad med hover-effekt      |
| `TableHead`    | `<th>`       | Header-celle                    |
| `TableCell`    | `<td>`       | Data-celle                      |
| `TableCaption` | `<caption>`  | Tabellbeskrivelse               |

---

# RadioGroup

> Radioknapper for enkeltvalg fra en synlig liste med alternativer.

RadioGroup,
  RadioGroupItem,
  Label,
  FormField,
  FormDescription,
  FormMessage,
} from "@kilden/designsystem";

# RadioGroup

[Kildekode på GitHub](https://github.com/bekk/designsystem/blob/main/packages/designsystem/src/ui/radio-group.tsx)

RadioGroup lar brukeren velge ett alternativ fra en synlig liste. Bygget på [Radix UI RadioGroup](https://www.radix-ui.com/primitives/docs/components/radio-group) med full tastaturnavigasjon.

Bruk RadioGroup for 2–5 synlige alternativer. For flere alternativer, bruk Select.

## Importering

```tsx
import { RadioGroup, RadioGroupItem } from "@kilden/designsystem";
```

## Grunnleggende bruk

```tsx
<RadioGroup defaultValue="comfortable" aria-label="Tetthet">
  <div style={{ display: "flex", alignItems: "center", gap: "0.5rem" }}>
    <RadioGroupItem value="compact" id="compact" />
    <Label htmlFor="compact">Kompakt</Label>
  </div>
  <div style={{ display: "flex", alignItems: "center", gap: "0.5rem" }}>
    <RadioGroupItem value="comfortable" id="comfortable" />
    <Label htmlFor="comfortable">Komfortabel</Label>
  </div>
  <div style={{ display: "flex", alignItems: "center", gap: "0.5rem" }}>
    <RadioGroupItem value="spacious" id="spacious" />
    <Label htmlFor="spacious">Romslig</Label>
  </div>
</RadioGroup>
```

## Med FormField

```tsx
<FormField>
  <Label>Foretrukket kontaktmetode</Label>
  <RadioGroup defaultValue="email">
    <div style={{ display: "flex", alignItems: "center", gap: "0.5rem" }}>
      <RadioGroupItem value="email" id="contact-email" />
      <Label htmlFor="contact-email">E-post</Label>
    </div>
    <div style={{ display: "flex", alignItems: "center", gap: "0.5rem" }}>
      <RadioGroupItem value="phone" id="contact-phone" />
      <Label htmlFor="contact-phone">Telefon</Label>
    </div>
    <div style={{ display: "flex", alignItems: "center", gap: "0.5rem" }}>
      <RadioGroupItem value="sms" id="contact-sms" />
      <Label htmlFor="contact-sms">SMS</Label>
    </div>
  </RadioGroup>
  <FormDescription>Vi bruker dette for å sende deg oppdateringer.</FormDescription>
</FormField>
```

## Horisontal layout

```tsx
<RadioGroup defaultValue="medium" aria-label="Størrelse" style={{ display: "flex", gap: "1rem" }}>
  <div style={{ display: "flex", alignItems: "center", gap: "0.5rem" }}>
    <RadioGroupItem value="small" id="size-s" />
    <Label htmlFor="size-s">S</Label>
  </div>
  <div style={{ display: "flex", alignItems: "center", gap: "0.5rem" }}>
    <RadioGroupItem value="medium" id="size-m" />
    <Label htmlFor="size-m">M</Label>
  </div>
  <div style={{ display: "flex", alignItems: "center", gap: "0.5rem" }}>
    <RadioGroupItem value="large" id="size-l" />
    <Label htmlFor="size-l">L</Label>
  </div>
  <div style={{ display: "flex", alignItems: "center", gap: "0.5rem" }}>
    <RadioGroupItem value="xlarge" id="size-xl" />
    <Label htmlFor="size-xl">XL</Label>
  </div>
</RadioGroup>
```

## Med deaktiverte alternativer

```tsx
<RadioGroup defaultValue="free" aria-label="Plan">
  <div style={{ display: "flex", alignItems: "center", gap: "0.5rem" }}>
    <RadioGroupItem value="free" id="plan-free" />
    <Label htmlFor="plan-free">Gratis</Label>
  </div>
  <div style={{ display: "flex", alignItems: "center", gap: "0.5rem" }}>
    <RadioGroupItem value="pro" id="plan-pro" />
    <Label htmlFor="plan-pro">Pro</Label>
  </div>
  <div style={{ display: "flex", alignItems: "center", gap: "0.5rem" }}>
    <RadioGroupItem value="enterprise" id="plan-enterprise" disabled />
    <Label htmlFor="plan-enterprise" style={{ opacity: 0.5 }}>Enterprise (kommer snart)</Label>
  </div>
</RadioGroup>
```

## Bruksanbefalinger

- **Gjør: Bruk for 2–5 synlige alternativer** — RadioGroup viser alle alternativene samtidig, noe som gir god oversikt for et begrenset antall valg.
- **Unngå: Ikke bruk for mange alternativer** — For 6+ alternativer tar radioknapper for mye plass. Bruk Select i stedet.

## Tilgjengelighet

- Bruker `role="radiogroup"` og `role="radio"` automatisk
- Piltaster navigerer mellom alternativer
- `Space` velger det fokuserte alternativet
- Koble til label via `aria-label`/`aria-labelledby` på RadioGroup
- Individuelle items kobles til label via `id`/`htmlFor`

## Props

### RadioGroup (rot)

| Prop            | Type                         | Default      | Beskrivelse                    |
| --------------- | ---------------------------- | ------------ | ------------------------------ |
| `value`         | `string`                     | —            | Kontrollert valgt verdi        |
| `defaultValue`  | `string`                     | —            | Standard valgt verdi           |
| `onValueChange` | `(value: string) => void`    | —            | Kalles når valgt verdi endres  |
| `disabled`      | `boolean`                    | `false`      | Deaktiver hele gruppen         |
| `required`      | `boolean`                    | `false`      | Marker som påkrevd             |
| `orientation`   | `"horizontal" \| "vertical"` | `"vertical"` | Retning for tastaturnavigasjon |
| `name`          | `string`                     | —            | Navn for skjemainnsending      |

### RadioGroupItem

| Prop       | Type      | Default | Beskrivelse                  |
| ---------- | --------- | ------- | ---------------------------- |
| `value`    | `string`  | —       | Verdien til alternativet     |
| `disabled` | `boolean` | `false` | Deaktiver dette alternativet |

---

# SearchInput

> Søkefelt med søkeikon og tøm-knapp, med støtte for label og feilmelding.

[Kildekode på GitHub](https://github.com/bekk/designsystem/blob/main/packages/designsystem/src/ui/search-input.tsx)

SearchInput er et søkefelt med innebygd søkeikon og tøm-knapp. Stylet som en boks i samme stil som de andre skjemafeltene. Støtter innebygd label og feilmelding, samt alle standard HTML input-attributter.

## Utforsk

*Se interaktiv playground på dokumentasjonssiden.*

## Importering

```tsx
import { SearchInput } from "@kilden/designsystem";
```

## Grunnleggende bruk

```tsx
<SearchInput placeholder="Søk..." />
```

## Med label

Bruk `label`-propen for å legge til en synlig label koblet til søkefeltet.

```tsx
<SearchInput label="Søk" placeholder="Søk etter komponenter..." />
```

## Med feilmelding

Bruk `error`-propen for å vise en feilmelding under feltet. Dette setter også visuell error-state med rød bunnlinje og `aria-invalid` på inputen.

```tsx
<SearchInput label="Søk" error="Du må skrive et søkeord" />
```

## Påkrevd felt

Bruk `required`-propen for å vise en rød stjerne etter label-teksten, i tillegg til nativ nettleser-validering.

```tsx
<SearchInput label="Søk" required />
```

## Deaktivert

```tsx
<SearchInput label="Søk" placeholder="Kan ikke søke" disabled />
```

## Bruksanbefalinger

- **Gjør: Bruk alltid en label** — Alle søkefelt må ha en synlig label eller en aria-label som forklarer hva feltet er til.
- **Unngå: Ikke bruk placeholder som label** — Placeholder forsvinner når brukeren begynner å skrive. Bruk label-propen eller aria-label i tillegg.

- **Gjør: Bruk SearchInput for søk** — Bruk SearchInput for søkefelt, ikke en vanlig Input med type='search'. SearchInput gir søkeikon og tøm-knapp ut av boksen.
- **Unngå: Ikke bruk for vanlig tekstinput** — Bruk Input for vanlige tekstfelt. SearchInput er kun ment for søkefunksjonalitet.

## Tilgjengelighet

- `label`-propen kobler automatisk label til input via `id`/`htmlFor`
- `error`-propen setter `aria-invalid` og kobler feilmelding via `aria-describedby`
- Feilmelding har `role="alert"` for skjermlesere
- Tøm-knappen har `aria-label="Tøm søk"` for skjermlesere
- Input har `type="search"` som gir riktig semantikk
- Tydelig fokusindikator med tykkere bunnlinje
- Disabled-tilstand kommuniseres via HTML `disabled`-attributtet

## Props

SearchInput arver alle props fra `<input>` (unntatt `type`), pluss disse:

| Prop          | Type                       | Default | Beskrivelse                                       |
| ------------- | -------------------------- | ------- | ------------------------------------------------- |
| `label`       | `string`                   | —       | Synlig label over søkefeltet                      |
| `error`       | `string`                   | —       | Feilmelding under feltet (aktiverer error-state)  |
| `onClear`     | `() => void`               | —       | Kalles når brukeren klikker tøm-knappen           |
| `placeholder` | `string`                   | —       | Plassholdertekst                                  |
| `disabled`    | `boolean`                  | `false` | Deaktiver feltet                                  |
| `required`    | `boolean`                  | `false` | Marker som påkrevd — viser rød stjerne på labelen |
| `value`       | `string`                   | —       | Kontrollert verdi                                 |
| `onChange`    | `(e: ChangeEvent) => void` | —       | Kalles ved endring                                |

---

# Select

> Tilgjengelig nedtrekksmeny for å velge blant alternativer.

Select,
  SelectTrigger,
  SelectValue,
  SelectContent,
  SelectItem,
  SelectGroup,
  SelectLabel,
  SelectSeparator,
  Label,
} from "@kilden/designsystem";

# Select

[Kildekode på GitHub](https://github.com/bekk/designsystem/blob/main/packages/designsystem/src/ui/select.tsx)

Select er en tilgjengelig nedtrekksmeny som erstatter native `<select>` med full kontroll over utseende og oppførsel. Bygget på [Radix UI Select](https://www.radix-ui.com/primitives/docs/components/select) med tastaturnavigasjon, typeahead-søk og automatisk posisjonering.

## Importering

```tsx
import {
  Select,
  SelectTrigger,
  SelectValue,
  SelectContent,
  SelectItem,
  SelectGroup,
  SelectLabel,
  SelectSeparator,
} from "@kilden/designsystem";
```

## Grunnleggende bruk

```tsx
<Select>
  <SelectTrigger className="w-[200px]" aria-label="Rammeverk">
    <SelectValue placeholder="Velg et alternativ" />
  </SelectTrigger>
  <SelectContent>
    <SelectItem value="react">React</SelectItem>
    <SelectItem value="vue">Vue</SelectItem>
    <SelectItem value="svelte">Svelte</SelectItem>
    <SelectItem value="angular">Angular</SelectItem>
  </SelectContent>
</Select>
```

## Med label

```tsx
<div style={{ display: "grid", gap: "0.5rem", maxWidth: "16rem" }}>
  <Label htmlFor="framework">Rammeverk</Label>
  <Select>
    <SelectTrigger id="framework">
      <SelectValue placeholder="Velg rammeverk" />
    </SelectTrigger>
    <SelectContent>
      <SelectItem value="react">React</SelectItem>
      <SelectItem value="vue">Vue</SelectItem>
      <SelectItem value="svelte">Svelte</SelectItem>
    </SelectContent>
  </Select>
</div>
```

## Med grupper

Bruk `SelectGroup` og `SelectLabel` for å organisere alternativer i grupper.

```tsx
<Select>
  <SelectTrigger className="w-[240px]" aria-label="Teknologi">
    <SelectValue placeholder="Velg teknologi" />
  </SelectTrigger>
  <SelectContent>
    <SelectGroup>
      <SelectLabel>Frontend</SelectLabel>
      <SelectItem value="react">React</SelectItem>
      <SelectItem value="vue">Vue</SelectItem>
      <SelectItem value="svelte">Svelte</SelectItem>
    </SelectGroup>
    <SelectSeparator />
    <SelectGroup>
      <SelectLabel>Backend</SelectLabel>
      <SelectItem value="node">Node.js</SelectItem>
      <SelectItem value="kotlin">Kotlin</SelectItem>
      <SelectItem value="python">Python</SelectItem>
    </SelectGroup>
  </SelectContent>
</Select>
```

## Deaktiverte alternativer

Individuelle alternativer kan deaktiveres med `disabled`.

```tsx
<Select>
  <SelectTrigger className="w-[200px]" aria-label="Plan">
    <SelectValue placeholder="Velg plan" />
  </SelectTrigger>
  <SelectContent>
    <SelectItem value="free">Gratis</SelectItem>
    <SelectItem value="pro">Pro</SelectItem>
    <SelectItem value="enterprise" disabled>Enterprise (kommer snart)</SelectItem>
  </SelectContent>
</Select>
```

## Bruksanbefalinger

- **Gjør: Bruk for 4+ alternativer** — Select egner seg når det er mange alternativer å velge mellom. For 2–3 alternativer er radioknapper ofte bedre.
- **Unngå: Ikke bruk for binære valg** — For av/på-valg, bruk Switch. For ja/nei, bruk radioknapper eller en checkbox.

- **Gjør: Bruk en tydelig placeholder** — Placeholder-teksten bør fortelle brukeren hva de skal velge.
- **Unngå: Ikke bruk generisk placeholder** — Unngå vage placeholdere som 'Velg...' uten kontekst.

## Tilgjengelighet

- Full tastaturnavigasjon: piltaster, Home, End, Enter, Space, Escape
- Typeahead-søk: begynn å skrive for å hoppe til matchende alternativ
- `aria-expanded`, `aria-haspopup` og `aria-activedescendant` settes automatisk
- Støtter `aria-label` og `aria-labelledby` via Label-kobling
- Hake-ikon indikerer valgt element visuelt

## Props

### Select (rot)

| Prop            | Type                      | Default | Beskrivelse                   |
| --------------- | ------------------------- | ------- | ----------------------------- |
| `value`         | `string`                  | —       | Kontrollert valgt verdi       |
| `defaultValue`  | `string`                  | —       | Standard valgt verdi          |
| `onValueChange` | `(value: string) => void` | —       | Kalles når valgt verdi endres |
| `disabled`      | `boolean`                 | `false` | Deaktiver hele select-en      |
| `required`      | `boolean`                 | `false` | Marker som påkrevd            |
| `name`          | `string`                  | —       | Navn for skjemainnsending     |

### SelectTrigger

Arver alle props fra `<button>`.

### SelectValue

| Prop          | Type     | Default | Beskrivelse                              |
| ------------- | -------- | ------- | ---------------------------------------- |
| `placeholder` | `string` | —       | Tekst som vises når ingen verdi er valgt |

### SelectItem

| Prop       | Type      | Default | Beskrivelse                  |
| ---------- | --------- | ------- | ---------------------------- |
| `value`    | `string`  | —       | Verdien til alternativet     |
| `disabled` | `boolean` | `false` | Deaktiver dette alternativet |

---

# Slider

> Slider for å velge numeriske verdier innenfor et intervall.

Slider,
  Label,
  FormField,
  FormDescription,
} from "@kilden/designsystem";

# Slider

[Kildekode på GitHub](https://github.com/bekk/designsystem/blob/main/packages/designsystem/src/ui/slider.tsx)

Slider lar brukeren velge en numerisk verdi ved å dra en håndtak langs en linje. Bygget på [Radix UI Slider](https://www.radix-ui.com/primitives/docs/components/slider) med støtte for enkelt- og flerverdier, trinnstørrelse og full tastaturnavigasjon.

## Utforsk

*Se interaktiv playground på dokumentasjonssiden.*

## Importering

```tsx
import { Slider } from "@kilden/designsystem";
```

## Grunnleggende bruk

```tsx
<div style={{ maxWidth: "24rem" }}>
  <Slider defaultValue={[50]} max={100} step={1} aria-label="Volum" />
</div>
```

## Med label og FormField

```tsx
<FormField style={{ maxWidth: "24rem" }}>
  <div style={{ display: "flex", justifyContent: "space-between" }}>
    <Label>Volum</Label>
    <span style={{ fontSize: "0.875rem", opacity: 0.7 }}>75%</span>
  </div>
  <Slider defaultValue={[75]} max={100} step={1} aria-label="Volum" />
</FormField>
```

## Med trinnstørrelse

Bruk `step` for å begrense verdiene til bestemte intervaller.

```tsx
<FormField style={{ maxWidth: "24rem" }}>
  <Label>Temperatur (steg på 5)</Label>
  <Slider defaultValue={[20]} min={0} max={40} step={5} aria-label="Temperatur" />
  <FormDescription>Velg en temperatur mellom 0 og 40°C.</FormDescription>
</FormField>
```

## Range-slider

Med to verdier i `defaultValue` får du en range-slider for å velge et intervall.

```tsx
<FormField style={{ maxWidth: "24rem" }}>
  <Label>Prisintervall</Label>
  <Slider defaultValue={[200, 800]} min={0} max={1000} step={50} aria-label="Prisintervall" />
  <FormDescription>Velg minimum og maksimum pris.</FormDescription>
</FormField>
```

## Deaktivert

```tsx
<div style={{ maxWidth: "24rem" }}>
  <Slider defaultValue={[50]} max={100} disabled aria-label="Deaktivert" />
</div>
```

## Bruksanbefalinger

- **Gjør: Bruk for relative verdier** — Slider egner seg best når den eksakte verdien er mindre viktig enn den relative posisjonen — som volum, lysstyrke eller filtreringsintervaller.
- **Unngå: Ikke bruk for presise verdier** — For nøyaktige tall (alder, mengde, beløp) er Input med type='number' bedre. Slider er upresis for eksakte verdier.

## Tilgjengelighet

- Bruker `role="slider"` med `aria-valuemin`, `aria-valuemax`, `aria-valuenow`
- Piltaster justerer verdien (høyre/opp øker, venstre/ned minsker)
- `Home` setter minimum, `End` setter maksimum
- Legg til `aria-label` eller koble til en label med `aria-labelledby`
- `aria-orientation` settes automatisk

## Props

| Prop            | Type                         | Default        | Beskrivelse                                                  |
| --------------- | ---------------------------- | -------------- | ------------------------------------------------------------ |
| `defaultValue`  | `number[]`                   | —              | Startverdi(er) — bruk `[50]` for enkel, `[25, 75]` for range |
| `value`         | `number[]`                   | —              | Kontrollert verdi(er)                                        |
| `onValueChange` | `(value: number[]) => void`  | —              | Kalles når verdien endres                                    |
| `onValueCommit` | `(value: number[]) => void`  | —              | Kalles når brukeren slipper håndtaket                        |
| `min`           | `number`                     | `0`            | Minimumsverdi                                                |
| `max`           | `number`                     | `100`          | Maksimumsverdi                                               |
| `step`          | `number`                     | `1`            | Trinnstørrelse                                               |
| `disabled`      | `boolean`                    | `false`        | Deaktiver slideren                                           |
| `orientation`   | `"horizontal" \| "vertical"` | `"horizontal"` | Retning                                                      |
| `name`          | `string`                     | —              | Navn for skjemainnsending                                    |

---

# Switch

> Toggle-bryter for å slå av og på innstillinger.

[Kildekode på GitHub](https://github.com/bekk/designsystem/blob/main/packages/designsystem/src/ui/switch.tsx)

Switch er en toggle-bryter for binære valg (av/på). Den er visuelt tydelig og gir umiddelbar feedback. Bygget på [Radix UI Switch](https://www.radix-ui.com/primitives/docs/components/switch).

## Utforsk

*Se interaktiv playground på dokumentasjonssiden.*

## Importering

```tsx
import { Switch } from "@kilden/designsystem";
```

## Grunnleggende bruk

```tsx
<div style={{ display: "flex", alignItems: "center", gap: "0.5rem" }}>
  <Switch id="airplane-mode" />
  <label htmlFor="airplane-mode">Flymodus</label>
</div>
```

## Med label

Koble alltid en `<label>` til switchen via `id`/`htmlFor` for tilgjengelighet.

```tsx
<div style={{ display: "flex", flexDirection: "column", gap: "1rem" }}>
  <div style={{ display: "flex", alignItems: "center", gap: "0.5rem" }}>
    <Switch id="notifications" defaultChecked />
    <label htmlFor="notifications">Varsler</label>
  </div>
  <div style={{ display: "flex", alignItems: "center", gap: "0.5rem" }}>
    <Switch id="marketing" />
    <label htmlFor="marketing">Markedsføringseposter</label>
  </div>
  <div style={{ display: "flex", alignItems: "center", gap: "0.5rem" }}>
    <Switch id="analytics" disabled />
    <label htmlFor="analytics" style={{ opacity: 0.5 }}>Analyse (deaktivert)</label>
  </div>
</div>
```

## Bruksanbefalinger

- **Gjør: Bruk for umiddelbare innstillinger** — Switch passer best for innstillinger som trer i kraft umiddelbart, uten å måtte trykke 'Lagre'.
- **Unngå: Ikke bruk i skjemaer med lagre-knapp** — Hvis endringen ikke trer i kraft umiddelbart, bruk en checkbox i stedet. Brukere forventer at en switch har umiddelbar effekt.

- **Gjør: Koble til en synlig label** — Bruk alltid en synlig label som forklarer hva switchen styrer. Ikke stol kun på aria-label.
- **Unngå: Ikke bruk uten kontekst** — En switch uten label gir ingen informasjon om hva den gjør.

## Tilgjengelighet

- Bruker `role="switch"` med `aria-checked` automatisk
- `Space` toggler switchen
- Støtter `aria-label` og `aria-labelledby` for skjermlesere
- Deaktivert tilstand kommuniseres via `aria-disabled`
- Koble alltid til en `<label>` for best mulig tilgjengelighet

## Props

| Prop              | Type                         | Default | Beskrivelse                           |
| ----------------- | ---------------------------- | ------- | ------------------------------------- |
| `checked`         | `boolean`                    | —       | Kontrollert av/på-tilstand            |
| `defaultChecked`  | `boolean`                    | `false` | Starttilstand (ukontrollert)          |
| `onCheckedChange` | `(checked: boolean) => void` | —       | Kalles når tilstanden endres          |
| `disabled`        | `boolean`                    | `false` | Deaktiver switchen                    |
| `required`        | `boolean`                    | `false` | Marker som påkrevd i skjema           |
| `name`            | `string`                     | —       | Navn for skjemainnsending             |
| `value`           | `string`                     | `"on"`  | Verdi som sendes ved skjemainnsending |

---

# Textarea

> Flerlinjers tekstfelt for lengre brukerinput.

[Kildekode på GitHub](https://github.com/bekk/designsystem/blob/main/packages/designsystem/src/ui/textarea.tsx)

Textarea er et flerlinjers tekstfelt som matcher Input-komponenten i stil. Brukes for lengre tekster som kommentarer, beskrivelser og meldinger. Støtter innebygd `label` og `error` på samme måte som Input, og kobles automatisk til `FormField`.

## Utforsk

*Se interaktiv playground på dokumentasjonssiden.*

## Importering

```tsx
import { Textarea } from "@kilden/designsystem";
```

## Grunnleggende bruk

```tsx
<Textarea placeholder="Skriv en melding..." />
```

## Med label

```tsx
<div style={{ maxWidth: "24rem" }}>
  <Textarea label="Melding" placeholder="Skriv meldingen din her..." />
</div>
```

## Med feilmelding

```tsx
<div style={{ maxWidth: "24rem" }}>
  <Textarea label="Melding" error="Meldingen kan ikke være tom" required />
</div>
```

## Med tilpasset høyde

Bruk `rows` for å styre antall synlige linjer.

```tsx
<div style={{ display: "grid", gap: "1.5rem", maxWidth: "24rem" }}>
  <Textarea label="Kort (2 rader)" rows={2} placeholder="Kort tekst..." />
  <Textarea label="Lang (6 rader)" rows={6} placeholder="Lengre tekst..." />
</div>
```

## Med knapp

```tsx
<div style={{ display: "grid", gap: "0.75rem", maxWidth: "24rem" }}>
  <Textarea label="Kommentar" placeholder="Legg til en kommentar..." />
  <div style={{ display: "flex", justifyContent: "flex-end" }}>
    <Button>Send</Button>
  </div>
</div>
```

## Deaktivert

```tsx
<Textarea placeholder="Kan ikke redigeres" disabled />
```

## Bruksanbefalinger

- **Gjør: Bruk for flerlinjers tekst** — Textarea egner seg for kommentarer, beskrivelser, meldinger og annet innhold som typisk er mer enn én linje.
- **Unngå: Ikke bruk for korte input** — For enkeltlinjer som navn, e-post eller telefon, bruk Input i stedet. Textarea tar unødvendig plass for kort tekst.

## Tilgjengelighet

- Bruk `label`-propen, eller plasser Textarea i en `FormField` med ``, så kobles label automatisk
- `error`-propen setter `aria-invalid` og kobler feilmelding via `aria-describedby`
- Tydelig fokusindikator (outline) som også vises i høykontrastmodus
- Disabled-tilstand kommuniseres via HTML `disabled`-attributtet
- Brukere kan endre størrelse med resize-håndtaket i nettleseren

## Props

Textarea arver alle props fra `<textarea>`. De mest relevante:

| Prop          | Type                       | Default | Beskrivelse               |
| ------------- | -------------------------- | ------- | ------------------------- |
| `label`       | `ReactNode`                | —       | Synlig label over feltet  |
| `error`       | `ReactNode`                | —       | Feilmelding under feltet  |
| `placeholder` | `string`                   | —       | Plassholdertekst          |
| `rows`        | `number`                   | —       | Antall synlige linjer     |
| `disabled`    | `boolean`                  | `false` | Deaktiver feltet          |
| `required`    | `boolean`                  | `false` | Marker som påkrevd        |
| `name`        | `string`                   | —       | Navn for skjemainnsending |
| `value`       | `string`                   | —       | Kontrollert verdi         |
| `onChange`    | `(e: ChangeEvent) => void` | —       | Kalles ved endring        |

---

# Toggle

> Trykknapp med av/på-tilstand for verktøylinjer og handlinger.

[Kildekode på GitHub](https://github.com/bekk/designsystem/blob/main/packages/designsystem/src/ui/toggle.tsx)

Toggle er en trykknapp som holder av/på-tilstand. Til forskjell fra Switch ser den ut som en vanlig knapp og egner seg for verktøylinjer, favoritt-markering og lignende. Bygget på [Radix UI Toggle](https://www.radix-ui.com/primitives/docs/components/toggle).

## Utforsk

*Se interaktiv playground på dokumentasjonssiden.*

## Importering

```tsx
import { Toggle } from "@kilden/designsystem";
```

## Grunnleggende bruk

```tsx
<Toggle aria-label="Fet skrift">
  <strong>B</strong>
</Toggle>
```

## Varianter

### Default

```tsx
<Toggle aria-label="Kursiv">
  <em>I</em>
</Toggle>
```

### Outline

```tsx
<Toggle variant="outline" aria-label="Understreking">
  <span style={{ textDecoration: "underline" }}>U</span>
</Toggle>
```

## Størrelser

```tsx
<div style={{ display: "flex", gap: "0.5rem", alignItems: "center" }}>
  <Toggle size="sm" aria-label="Liten">
    <strong>B</strong>
  </Toggle>
  <Toggle size="default" aria-label="Standard">
    <strong>B</strong>
  </Toggle>
  <Toggle size="lg" aria-label="Stor">
    <strong>B</strong>
  </Toggle>
</div>
```

## Verktøylinje-eksempel

```tsx
<div style={{ display: "flex", alignItems: "center", gap: "0.25rem", border: "1px solid var(--color-neutral-border-default)", borderRadius: "0.375rem", padding: "0.25rem" }}>
  <Toggle size="sm" aria-label="Fet skrift">
    <strong>B</strong>
  </Toggle>
  <Toggle size="sm" aria-label="Kursiv">
    <em>I</em>
  </Toggle>
  <Toggle size="sm" aria-label="Understreking">
    <span style={{ textDecoration: "underline" }}>U</span>
  </Toggle>
  <Separator orientation="vertical" style={{ height: "1.5rem", margin: "0 0.25rem" }} decorative={false} />
  <Toggle size="sm" aria-label="Venstrejuster">
    ≡
  </Toggle>
  <Toggle size="sm" aria-label="Midtstill">
    ≡
  </Toggle>
</div>
```

## Med tekst

```tsx
<div style={{ display: "flex", gap: "0.5rem" }}>
  <Toggle variant="outline" aria-label="Favoritt">
    ★ Favoritt
  </Toggle>
  <Toggle variant="outline" aria-label="Følg">
    Følg
  </Toggle>
</div>
```

## Bruksanbefalinger

- **Gjør: Bruk for verktøylinjer og handlinger** — Toggle passer for formateringsknapper, favoritter og andre handlinger der brukerens valg skal være synlig.
- **Unngå: Ikke bruk for innstillinger** — For innstillinger som 'mørk modus' eller 'varsler', bruk Switch. Toggle ser ut som en knapp og kommuniserer en handling.

- **Gjør: Bruk alltid aria-label** — Toggle har ofte kun et ikon — da er aria-label essensielt for skjermlesere.
- **Unngå: Ikke bruk uten tilgjengelig navn** — En toggle uten aria-label eller tekst er utilgjengelig for skjermlesere.

## Tilgjengelighet

- Bruker `aria-pressed` for å kommunisere av/på-tilstand
- `Enter` eller `Space` toggler knappen
- Støtter `aria-label` for skjermlesere
- Deaktivert tilstand kommuniseres via `disabled`

## Props

| Prop              | Type                         | Default     | Beskrivelse                  |
| ----------------- | ---------------------------- | ----------- | ---------------------------- |
| `variant`         | `"default" \| "outline"`     | `"default"` | Visuell stil                 |
| `size`            | `"default" \| "sm" \| "lg"`  | `"default"` | Størrelse                    |
| `pressed`         | `boolean`                    | —           | Kontrollert av/på-tilstand   |
| `defaultPressed`  | `boolean`                    | `false`     | Starttilstand (ukontrollert) |
| `onPressedChange` | `(pressed: boolean) => void` | —           | Kalles når tilstanden endres |
| `disabled`        | `boolean`                    | `false`     | Deaktiver knappen            |

---

# Farger

> Designsystemets fargetokens og hvordan du bruker dem.

Designsystemet bruker semantiske fargetokens som tilpasser seg automatisk basert på valgt tema og fargemodus (lys/mørk). Tokensene er tilgjengelige som CSS-variabler og via TypeScript.

## Oversikt

*Se interaktiv tabell på dokumentasjonssiden.*

## Kategorier

Fargetokensene er delt inn i åtte kategorier:

### Temaavhengige kategorier

Disse kategoriene endrer farge basert på valgt Bekk-tema (vann, jord, ild, luft):

- **dominant** — Hovedfargen i temaet. Brukes til primærflater, bakgrunner og hovedelementer.
- **accent** — Aksentfargen i temaet. Brukes til fremhevede elementer og lenker.
- **neutral** — Nøytralfargen i temaet. Brukes til generell tekst, bakgrunner og kantlinjer.

### Globale kategorier

Disse kategoriene er like på tvers av alle temaer:

- **info** — Informasjon (blå). Brukes til informasjonsmeldinger og -flater.
- **success** — Suksess (grønn). Brukes til bekreftelser og positive tilbakemeldinger.
- **warning** — Advarsel (oransje). Brukes til advarsler og varselmeldinger.
- **danger** — Fare/feil (rød). Brukes til feilmeldinger og destruktive handlinger.
- **interactive** — Interaktive elementer (lilla). Brukes til fokusringer og interaktive tilstander.

## Tokens per kategori

Hver kategori har 16 tokens organisert i fem grupper:

| Gruppe         | Tokens                                                                                       | Bruksområde                         |
| -------------- | -------------------------------------------------------------------------------------------- | ----------------------------------- |
| **background** | `background-default`, `background-tinted`                                                    | Side- og seksjons-bakgrunner        |
| **surface**    | `surface-default`, `surface-tinted`, `surface-hover`, `surface-active`                       | Kort, paneler og interaktive flater |
| **border**     | `border-subtle`, `border-default`, `border-strong`                                           | Kantlinjer og skillelinjer          |
| **text**       | `text-subtle`, `text-default`                                                                | Tekst og ikoner                     |
| **base**       | `base-default`, `base-hover`, `base-active`, `base-contrast-subtle`, `base-contrast-default` | Knapper og fremhevede elementer     |

## dominant

*Se interaktiv tabell på dokumentasjonssiden.*

## accent

*Se interaktiv tabell på dokumentasjonssiden.*

## neutral

*Se interaktiv tabell på dokumentasjonssiden.*

## info

*Se interaktiv tabell på dokumentasjonssiden.*

## success

*Se interaktiv tabell på dokumentasjonssiden.*

## warning

*Se interaktiv tabell på dokumentasjonssiden.*

## danger

*Se interaktiv tabell på dokumentasjonssiden.*

## interactive

*Se interaktiv tabell på dokumentasjonssiden.*

## Bruk i CSS

```css
.card {
  background-color: var(--color-neutral-surface-default);
  border: 1px solid var(--color-neutral-border-default);
  color: var(--color-neutral-text-default);
}

.alert-error {
  background-color: var(--color-danger-surface-default);
  border-color: var(--color-danger-border-default);
  color: var(--color-danger-text-default);
}
```

## Bruk med Tailwind

Fargetokensene er tilgjengelige som Tailwind-klasser via CSS-variablene:

```html
<div
  class="bg-[var(--color-neutral-surface-default)] text-[var(--color-neutral-text-default)]"
>
  Innhold
</div>
```

## Bruk i TypeScript

```tsx
import { tokens } from "@kilden/design-tokens";

// Hent CSS-variabelreferanse
const bgColor = tokens.color.neutral["surface-default"];
// → "var(--color-neutral-surface-default)"
```

---

# Typografi

> Designsystemets typografi-tokens for skriftstørrelser, vekter og stiler.

Designsystemet har et sett med typografi-tokens som dekker alle tekststiler. Tokensene er responsive og tilpasser seg automatisk mellom desktop og mobil.

## Skrifttyper

Skriftfamiliene hører til merkevaren. Bekk bruker **GT America** for brødtekst (`font-family.base`) og overskrifter (`font-family.display`), og **GT America Expanded** for knapper og handlinger (`font-family.action`).

| Tailwind-klasse | CSS-variabel            |
| --------------- | ----------------------- |
| `font-sans`     | `--font-family-base`    |
| `font-display`  | `--font-family-display` |
| `font-action`   | `--font-family-action`  |

## Skriftvekter

| Token                 | Verdi   | Bruksområde                   |
| --------------------- | ------- | ----------------------------- |
| `font-weight.light`   | Light   | Dekorativ tekst               |
| `font-weight.regular` | Regular | Brødtekst og overskrifter     |
| `font-weight.medium`  | Medium  | Fremhevet tekst og handlinger |

## Tekststiler

Designsystemet definerer syv semantiske tekststiler som kombinerer skriftstørrelse, vekt og linjehøyde:

*Se interaktiv tabell på dokumentasjonssiden.*

## Størrelser per breakpoint

Tekststørrelsene endrer seg mellom mobil og desktop:

*Se interaktiv tabell på dokumentasjonssiden.*

## Tekststørrelser i Tailwind

Komponentene bruker bare tekststørrelser som er koblet til tokens. Tailwinds egne størrelser (`text-sm`, `text-lg` osv.) følger ikke merkevaren.

| Klasse            | Bekk                | Bruksområde                          |
| ----------------- | ------------------- | ------------------------------------ |
| `text-caption`    | 12 px               | Etiketter, badges og metatekst       |
| `text-small`      | 14 px               | Sekundærtekst og kompakte kontroller |
| `text-body`       | 16 px               | Brødtekst og skjemafelt              |
| `text-title`      | 18 px               | Titler i dialoger og paneler         |
| `text-heading-sm` | 20 px               | Små overskrifter                     |
| `text-heading-md` | 24 px               | Mellomstore overskrifter             |
| `text-heading-lg` | 30 px               | Store overskrifter                   |
| `text-leading`    | 32 px (24 px mobil) | Ingress                              |
| `text-action`     | 14 px (12 px mobil) | Handlingstekst                       |

## Linjehøyder

| Token            | Verdi | Bruksområde                           |
| ---------------- | ----- | ------------------------------------- |
| `line-height.sm` | 130%  | Kompakt tekst                         |
| `line-height.md` | 150%  | Standard (brukes av alle tekststiler) |
| `line-height.lg` | 170%  | Luftig tekst                          |

## Bruk i CSS

```css
.display-heading {
  font-family: var(--typography-heading-display-font-family);
  font-size: var(--typography-heading-display-font-size);
  font-weight: var(--typography-heading-display-font-weight);
}

.body-text {
  font-family: var(--typography-body-font-family);
  font-size: var(--typography-body-font-size);
  font-weight: var(--typography-body-font-weight);
}

.action-text {
  font-family: var(--typography-action-font-family); /* GT America Expanded */
  font-size: var(--typography-action-font-size);
  font-weight: var(--typography-action-font-weight); /* 500 */
  text-transform: uppercase;
}
```

## Bruk i TypeScript

```tsx
import { tokens } from "@kilden/design-tokens";

const displayFontSize = tokens.typography.heading.display["font-size"];
// → "var(--typography-heading-display-font-size)"
```

---

# Størrelser

> Designsystemets størrelsestokens for spacing, padding og layout.

Designsystemet bruker en beregnet størrelsesskala basert på en grunnverdi. Verdiene er responsive og tilpasser seg mellom desktop og mobil via `data-size`-attributtet.

## Størrelsesskala

Skalaen går fra 0 til 86, med noen ekstra steg for spesielle størrelser. Hver verdi beregnes ut fra en fast formel, noe som gir en konsistent og harmonisk skala.

*Se interaktiv tabell på dokumentasjonssiden.*

## Spesielle størrelser

I tillegg til den numeriske skalaen finnes det to navngitte tokens for innholdsbredde:

## Responsivitet

Størrelsene endrer seg basert på `data-size`-attributtet på et overliggende element:

- **desktop** — Større verdier (base: 21px figma-font-size)
- **mobile** — Mindre verdier (base: 16px figma-font-size)

## Tetthet og Tailwinds spacing-skala

Tailwinds spacing-skala (`p-4`, `h-10`, `gap-2` osv.) er multipler av `--spacing-unit`, som hører til merkevaren (`0.25rem` for Bekk). En merkevare kan dermed gjøre hele grensesnittet tettere eller luftigere ved å endre én verdi.

## Bruk med Tailwind

Størrelsestokenene over (`--dimension-*`) følger `data-size` og er ikke koblet til Tailwinds spacing-skala. Bruk dem med Tailwinds syntaks for CSS-variabler når du vil at avstanden skal følge `data-size`:

```tsx
<div className="p-(--dimension-4) gap-(--dimension-2)">…</div>
<main className="max-w-(--dimension-max-width)">…</main>
```

## Bruk i CSS

```css
.card {
  padding: var(--dimension-4);
  gap: var(--dimension-2);
}

.container {
  max-width: var(--dimension-max-width);
  min-width: var(--dimension-min-width);
}
```

## Bruk i TypeScript

```tsx
import { tokens } from "@kilden/design-tokens";

const spacing = tokens.dimension[4];
// → "var(--dimension-4)"
```

---

# Avrunding

> Designsystemets border-radius-tokens for avrundede hjørner.

Designsystemet definerer seks nivåer av avrunding (border-radius) som brukes konsistent på tvers av alle komponenter.

## Tokens

*Se interaktiv tabell på dokumentasjonssiden.*

## Når bruker jeg hva?

- **`sm`** – små elementer som badges inni tekst og avkrysningsbokser
- **`md`** – standard for skjemafelt, knapper og menyer
- **`lg`** – kort, dialoger og større flater
- **`xl`** – store, frittstående flater som hero-seksjoner
- **`full`** – piller, avatarer og runde ikonknapper

## Bruk med Tailwind

Tailwinds `rounded-*`-klasser bruker de samme variablene, så de følger tokenene automatisk:

```tsx
<div className="rounded-md">…</div>   {/* var(--radius-md) */}
<div className="rounded-lg">…</div>   {/* var(--radius-lg) */}
<div className="rounded-full">…</div> {/* helt rund */}
```

## Bruk i CSS

```css
.card {
  border-radius: var(--radius-lg);
}

.badge {
  border-radius: var(--radius-full);
}

.button {
  border-radius: var(--radius-md);
}
```

## Bruk i TypeScript

```tsx
import { tokens } from "@kilden/design-tokens";

const radius = tokens.radius.lg;
// → "var(--radius-lg)"
```

---

# Kantlinjer

> Designsystemets tokens for kantlinjebredder og -stiler.

Designsystemet har tokens for kantlinjebredder og forhåndsdefinerte kantlinjestiler som kombinerer bredde, stil og farge.

## Kantlinjebredder

## Kantlinjestiler

Designsystemet definerer fem sammensatte kantlinjestiler som kombinerer bredde, stil og farge:

## Bruk med Tailwind

Fargene finnes som vanlige Tailwind-klasser, og bredden kan hentes fra tokenene med variabelsyntaks:

```tsx
<div className="border border-neutral-border-default">…</div>
<div className="border-(length:--border-width-focus) border-dominant-border-default">…</div>
```

## Bruk i CSS

```css
.card {
  border-width: var(--border-width-default);
  border-style: solid;
  border-color: var(--color-neutral-border-default);
}

.focused-element {
  border-width: var(--border-width-focus);
  border-style: solid;
  border-color: var(--color-dominant-border-default);
}
```

## Bruk i TypeScript

```tsx
import { tokens } from "@kilden/design-tokens";

const borderWidth = tokens["border-width"].default;
// → "var(--border-width-default)"
```

---

# Skygger

> Designsystemets elevation-tokens for skygger og dybde.

Designsystemet definerer skygger (elevation) som gir visuell dybde til elementer: `raised-*` for opphøyde, klikkbare flater, og `subtle`, `floating` og `overlay` for kort, flytende elementer og lag over siden. Skyggene hører til merkevaren.

## Tokens

*Se interaktiv tabell på dokumentasjonssiden.*

## Interaksjonsmønster

Skyggene er designet for å brukes sammen for å gi visuell tilbakemelding ved interaksjon:

1. **raised-default** — Standardtilstanden for et opphøyd element
2. **raised-hover** — Når brukeren holder musepekeren over elementet (økt dybde)
3. **raised-pressed** — Når elementet er trykket ned (redusert dybde)

## Bruk med Tailwind

Skyggene er registrert i Tailwind-temaet når du importerer `@kilden/designsystem/styles.css`:

```tsx
<div className="shadow-raised hover:shadow-raised-hover active:shadow-raised-pressed">
  …
</div>
<div className="shadow-subtle">Kort</div>
<div className="shadow-floating">Meny</div>
<div className="shadow-overlay">Dialog</div>
```

Tailwinds egne skygger (`shadow-sm`, `shadow-lg` osv.) følger ikke merkevaren. Komponentene bruker bare klassene over.

## Bruk i CSS

```css
.card {
  box-shadow: var(--elevation-raised-default);
}

.card:hover {
  box-shadow: var(--elevation-raised-hover);
}

.card:active {
  box-shadow: var(--elevation-raised-pressed);
}
```

## Bruk i TypeScript

```tsx
import { tokens } from "@kilden/design-tokens";

const shadow = tokens.elevation["raised-default"];
// → "var(--elevation-raised-default)"
```

---

# Bevegelse

> Designsystemets tokens for varighet og easing i overganger og animasjoner.

Varighet og easing hører til merkevaren, slik at en merkevare kan gjøre grensesnittet raskere, roligere eller mer sprettent uten å endre komponentene.

## Varighet

| Token                     | Tailwind-klasse    | Bekk   | Bruksområde                                   |
| ------------------------- | ------------------ | ------ | --------------------------------------------- |
| `motion.duration.fast`    | `duration-fast`    | 150 ms | Fargeoverganger (standard for `transition-*`) |
| `motion.duration.default` | `duration-default` | 200 ms | Små elementer som åpnes og lukkes             |
| `motion.duration.slow`    | `duration-slow`    | 300 ms | Større flater, som sheets                     |
| `motion.duration.slower`  | `duration-slower`  | 500 ms | Store flater som åpnes                        |

## Easing

| Token                    | Tailwind-klasse | Bruksområde                 |
| ------------------------ | --------------- | --------------------------- |
| `motion.easing.standard` | `ease-standard` | Bevegelse innenfor skjermen |
| `motion.easing.enter`    | `ease-enter`    | Elementer som kommer inn    |
| `motion.easing.exit`     | `ease-exit`     | Elementer som forsvinner    |

Tailwinds `transition`, `transition-colors` og lignende bruker `motion.duration.fast` og `motion.easing.standard` som standard.

## Bruk i CSS

```css
.panel {
  transition: transform var(--motion-duration-slow) var(--motion-easing-enter);
}
```

---

# Innloggingsside

> Et komplett innloggingsskjema med Card, FormField, Input, Label, Checkbox og Button.

Et klassisk innloggingsskjema bygget med Kilden-komponenter. Eksempelet viser hvordan `Card`, `FormField`, `Input`, `Label`, `Checkbox` og `Button` fungerer sammen.

## Komplett eksempel

```tsx
<div className="flex min-h-[500px] items-center justify-center bg-page p-4">
  <Card className="w-full max-w-sm">
    <CardHeader>
      <CardTitle>Logg inn</CardTitle>
      <CardDescription>
        Skriv inn e-post og passord for å logge inn på kontoen din.
      </CardDescription>
    </CardHeader>
    <CardContent className="flex flex-col gap-4">
      <FormField>
        <Label required>E-post</Label>
        <Input type="email" required placeholder="navn@bekk.no" />
      </FormField>
      <FormField>
        <Label required>Passord</Label>
        <Input type="password" required placeholder="••••••••" />
      </FormField>
      <div className="flex items-center gap-2">
        <Checkbox id="remember" />
        <Label htmlFor="remember" className="text-sm font-normal">
          Husk meg
        </Label>
      </div>
    </CardContent>
    <CardFooter className="flex flex-col gap-3">
      <Button className="w-full">Logg inn</Button>
      <p className="text-center text-sm text-muted">
        Har du ikke konto?{" "}
        <a href="#" className="underline underline-offset-4 hover:opacity-80">
          Registrer deg
        </a>
      </p>
    </CardFooter>
  </Card>
</div>
```

## Komponenter som brukes

| Komponent   | Rolle                                                  |
| ----------- | ------------------------------------------------------ |
| `Card`      | Ramme rundt skjemaet med header, innhold og footer     |
| `FormField` | Grupperer label og input med riktig spacing            |
| `Label`     | Ledetekst, koblet automatisk til input via `FormField` |
| `Input`     | Tekstfelt for e-post og passord                        |
| `Checkbox`  | Avkrysningsboks for "Husk meg"                         |
| `Button`    | Primærhandling for å sende skjemaet                    |

## Med feilmelding

Slik kan du vise valideringsfeil med `FormMessage`:

```tsx
<div className="flex items-center justify-center bg-page p-4">
  <Card className="w-full max-w-sm">
    <CardHeader>
      <CardTitle>Logg inn</CardTitle>
      <CardDescription>
        Skriv inn e-post og passord for å logge inn på kontoen din.
      </CardDescription>
    </CardHeader>
    <CardContent className="flex flex-col gap-4">
      <FormField>
        <Label required>E-post</Label>
        <Input type="email" required placeholder="navn@bekk.no" />
        <FormMessage>Du må skrive inn en gyldig e-postadresse.</FormMessage>
      </FormField>
      <FormField>
        <Label required>Passord</Label>
        <Input type="password" required placeholder="••••••••" />
        <FormMessage>Passordet må være minst 8 tegn.</FormMessage>
      </FormField>
    </CardContent>
    <CardFooter>
      <Button className="w-full">Logg inn</Button>
    </CardFooter>
  </Card>
</div>
```

## Med lastestatus

Bruk `isLoading` på `Button` for å vise at skjemaet sendes:

```tsx
<div className="flex items-center justify-center bg-page p-4">
  <Card className="w-full max-w-sm">
    <CardHeader>
      <CardTitle>Logg inn</CardTitle>
      <CardDescription>
        Skriv inn e-post og passord for å logge inn på kontoen din.
      </CardDescription>
    </CardHeader>
    <CardContent className="flex flex-col gap-4">
      <FormField>
        <Label>E-post</Label>
        <Input type="email" value="ola@bekk.no" readOnly />
      </FormField>
      <FormField>
        <Label>Passord</Label>
        <Input type="password" value="hemmelig123" readOnly />
      </FormField>
    </CardContent>
    <CardFooter>
      <Button className="w-full" isLoading loadingText="Logger inn...">
        Logg inn
      </Button>
    </CardFooter>
  </Card>
</div>
```

---

# Kontaktskjema

> Et kontaktskjema med Input, Textarea, Select, RadioGroup og validering.

Et kontaktskjema som viser hvordan flere skjemaelementer fungerer sammen — tekstfelt, nedtrekksliste, radioknapper og tekstområde.

## Komplett eksempel

```tsx
<div className="flex items-center justify-center bg-page p-4">
  <Card className="w-full max-w-lg">
    <CardHeader>
      <CardTitle>Kontakt oss</CardTitle>
      <CardDescription>
        Fyll ut skjemaet så tar vi kontakt med deg innen 2 virkedager.
      </CardDescription>
    </CardHeader>
    <CardContent className="flex flex-col gap-4">
      <div className="grid grid-cols-2 gap-4">
        <FormField>
          <Label required>Fornavn</Label>
          <Input required placeholder="Ola" />
        </FormField>
        <FormField>
          <Label required>Etternavn</Label>
          <Input required placeholder="Nordmann" />
        </FormField>
      </div>
      <FormField>
        <Label required>E-post</Label>
        <Input type="email" required placeholder="ola@eksempel.no" />
      </FormField>
      <FormField>
        <Label>Emne</Label>
        <Select>
          <SelectTrigger>
            <SelectValue placeholder="Velg et emne" />
          </SelectTrigger>
          <SelectContent>
            <SelectItem value="general">Generell henvendelse</SelectItem>
            <SelectItem value="support">Teknisk støtte</SelectItem>
            <SelectItem value="sales">Salg og samarbeid</SelectItem>
            <SelectItem value="feedback">Tilbakemelding</SelectItem>
          </SelectContent>
        </Select>
      </FormField>
      <FormField>
        <Label>Hvordan vil du bli kontaktet?</Label>
        <RadioGroup defaultValue="email" className="flex gap-4 mt-1">
          <div className="flex items-center gap-2">
            <RadioGroupItem value="email" id="contact-email-radio" />
            <Label htmlFor="contact-email-radio" className="font-normal">E-post</Label>
          </div>
          <div className="flex items-center gap-2">
            <RadioGroupItem value="phone" id="contact-phone" />
            <Label htmlFor="contact-phone" className="font-normal">Telefon</Label>
          </div>
        </RadioGroup>
      </FormField>
      <FormField>
        <Label>Melding</Label>
        <Textarea placeholder="Skriv meldingen din her..." rows={4} />
        <FormDescription>Maks 500 tegn.</FormDescription>
      </FormField>
    </CardContent>
    <CardFooter className="flex justify-between">
      <Button variant="secondary">Avbryt</Button>
      <Button>Send melding</Button>
    </CardFooter>
  </Card>
</div>
```

## Komponenter som brukes

| Komponent         | Rolle                                 |
| ----------------- | ------------------------------------- |
| `Card`            | Ramme rundt skjemaet                  |
| `FormField`       | Grupperer label, input og hjelpetekst |
| `FormDescription` | Hjelpetekst under et felt             |
| `Label`           | Tilgjengelig ledetekst for hvert felt |
| `Input`           | Tekstfelt for navn og e-post          |
| `Select`          | Nedtrekksliste for emnevalg           |
| `RadioGroup`      | Radioknapper for kontaktpreferanse    |
| `Textarea`        | Tekstområde for meldingen             |
| `Button`          | Primær- og sekundærhandling           |

## Med validering

Slik ser skjemaet ut med valideringsfeil:

```tsx
<div className="flex items-center justify-center bg-page p-4">
  <Card className="w-full max-w-lg">
    <CardHeader>
      <CardTitle>Kontakt oss</CardTitle>
      <CardDescription>
        Fyll ut skjemaet så tar vi kontakt med deg innen 2 virkedager.
      </CardDescription>
    </CardHeader>
    <CardContent className="flex flex-col gap-4">
      <div className="grid grid-cols-2 gap-4">
        <FormField>
          <Label required>Fornavn</Label>
          <Input required />
          <FormMessage>Fornavn er påkrevd.</FormMessage>
        </FormField>
        <FormField>
          <Label required>Etternavn</Label>
          <Input required />
          <FormMessage>Etternavn er påkrevd.</FormMessage>
        </FormField>
      </div>
      <FormField>
        <Label required>E-post</Label>
        <Input type="email" required value="ola@" />
        <FormMessage>Du må skrive inn en gyldig e-postadresse.</FormMessage>
      </FormField>
      <FormField>
        <Label>Melding</Label>
        <Textarea />
        <FormMessage>Du må skrive en melding.</FormMessage>
      </FormField>
    </CardContent>
    <CardFooter className="flex justify-between">
      <Button variant="secondary">Avbryt</Button>
      <Button>Send melding</Button>
    </CardFooter>
  </Card>
</div>
```

## Med suksessmelding

Bruk `Alert` for å bekrefte at skjemaet ble sendt:

```tsx
<div className="flex items-center justify-center bg-page p-4">
  <Card className="w-full max-w-lg">
    <CardContent className="pt-6">
      <Alert variant="success">
        <AlertTitle>Meldingen er sendt!</AlertTitle>
        <AlertDescription>
          Takk for at du tok kontakt. Vi svarer deg innen 2 virkedager.
        </AlertDescription>
      </Alert>
    </CardContent>
    <CardFooter>
      <Button variant="secondary" className="w-full">Send en ny melding</Button>
    </CardFooter>
  </Card>
</div>
```

---

# Innstillingsside

> En innstillingsside med Tabs, Switch, Select, FormField og Separator.

En typisk innstillingsside med faner for å organisere ulike kategorier av innstillinger. Viser hvordan `Tabs`, `Switch`, `Select` og skjemakomponenter fungerer sammen.

## Komplett eksempel

```tsx
<div className="bg-page p-4 w-full max-w-2xl mx-auto">
  <div className="mb-6">
    <Heading as="h2" size="2xl">Innstillinger</Heading>
    <p className="text-sm text-muted mt-1">Administrer konto og preferanser.</p>
  </div>
  <Tabs defaultValue="general">
    <TabsList className="w-full">
      <TabsTrigger value="general">Generelt</TabsTrigger>
      <TabsTrigger value="notifications">Varsler</TabsTrigger>
      <TabsTrigger value="display">Visning</TabsTrigger>
    </TabsList>
    <TabsContent value="general" className="mt-6">
      <Card>
        <CardHeader>
          <CardTitle>Profil</CardTitle>
          <CardDescription>Oppdater profilinformasjonen din.</CardDescription>
        </CardHeader>
        <CardContent className="flex flex-col gap-4">
          <FormField>
            <Label required>Visningsnavn</Label>
            <Input required value="Ola Nordmann" />
          </FormField>
          <FormField>
            <Label required>E-post</Label>
            <Input type="email" required value="ola@bekk.no" />
            <FormDescription>Denne brukes for å logge inn.</FormDescription>
          </FormField>
          <FormField>
            <Label>Rolle</Label>
            <Select defaultValue="developer">
              <SelectTrigger>
                <SelectValue />
              </SelectTrigger>
              <SelectContent>
                <SelectItem value="developer">Utvikler</SelectItem>
                <SelectItem value="designer">Designer</SelectItem>
                <SelectItem value="manager">Prosjektleder</SelectItem>
                <SelectItem value="consultant">Rådgiver</SelectItem>
              </SelectContent>
            </Select>
          </FormField>
        </CardContent>
        <CardFooter className="flex justify-end">
          <Button>Lagre endringer</Button>
        </CardFooter>
      </Card>
    </TabsContent>
    <TabsContent value="notifications" className="mt-6">
      <Card>
        <CardHeader>
          <CardTitle>Varsler</CardTitle>
          <CardDescription>Velg hvilke varsler du vil motta.</CardDescription>
        </CardHeader>
        <CardContent className="flex flex-col gap-0">
          <div className="flex items-center justify-between py-4">
            <div>
              <p className="text-sm font-medium">E-postvarsler</p>
              <p className="text-sm text-muted">Motta varsler om nye meldinger på e-post.</p>
            </div>
            <Switch defaultChecked />
          </div>
          <Separator />
          <div className="flex items-center justify-between py-4">
            <div>
              <p className="text-sm font-medium">Prosjektoppdateringer</p>
              <p className="text-sm text-muted">Bli varslet når prosjekter du følger oppdateres.</p>
            </div>
            <Switch defaultChecked />
          </div>
          <Separator />
          <div className="flex items-center justify-between py-4">
            <div>
              <p className="text-sm font-medium">Markedsføring</p>
              <p className="text-sm text-muted">Motta nyhetsbrev og produktoppdateringer.</p>
            </div>
            <Switch />
          </div>
        </CardContent>
      </Card>
    </TabsContent>
    <TabsContent value="display" className="mt-6">
      <Card>
        <CardHeader>
          <CardTitle>Visning</CardTitle>
          <CardDescription>Tilpass hvordan appen ser ut for deg.</CardDescription>
        </CardHeader>
        <CardContent className="flex flex-col gap-4">
          <FormField>
            <Label>Språk</Label>
            <Select defaultValue="no">
              <SelectTrigger>
                <SelectValue />
              </SelectTrigger>
              <SelectContent>
                <SelectItem value="no">Norsk</SelectItem>
                <SelectItem value="en">English</SelectItem>
                <SelectItem value="sv">Svenska</SelectItem>
              </SelectContent>
            </Select>
          </FormField>
          <div className="flex items-center justify-between py-2">
            <div>
              <p className="text-sm font-medium">Kompakt visning</p>
              <p className="text-sm text-muted">Vis mer innhold med mindre mellomrom.</p>
            </div>
            <Switch />
          </div>
        </CardContent>
        <CardFooter className="flex justify-end">
          <Button>Lagre endringer</Button>
        </CardFooter>
      </Card>
    </TabsContent>
  </Tabs>
</div>
```

## Komponenter som brukes

| Komponent   | Rolle                                        |
| ----------- | -------------------------------------------- |
| `Tabs`      | Organiserer innstillinger i kategorier       |
| `Card`      | Ramme rundt hver innstillingskategori        |
| `FormField` | Grupperer label og input                     |
| `Input`     | Tekstfelt for navn og e-post                 |
| `Select`    | Nedtrekksliste for rolle og språk            |
| `Switch`    | Av/på-bryter for varsler og preferanser      |
| `Separator` | Visuell skillelinje mellom innstillingsrader |
| `Heading`   | Sidetittel                                   |
| `Button`    | Lagre-handling                               |

---

# Dashboard

> Et dashboard med Card, Table, Badge, Progress, Avatar og Tabs.

Et enkelt dashboard som viser nøkkeltall, en tabell med oppgaver og en prosjektoversikt. Viser hvordan `Card`, `Table`, `Badge`, `Progress` og `Avatar` fungerer sammen i en datadrevet kontekst.

## Nøkkeltall

```tsx
<div className="bg-page p-4 w-full">
  <div className="grid grid-cols-1 sm:grid-cols-3 gap-4">
    <Card>
      <CardHeader className="pb-2">
        <CardDescription>Aktive prosjekter</CardDescription>
        <CardTitle className="text-3xl">12</CardTitle>
      </CardHeader>
      <CardContent>
        <p className="text-xs text-muted">+2 fra forrige måned</p>
      </CardContent>
    </Card>
    <Card>
      <CardHeader className="pb-2">
        <CardDescription>Timer denne uken</CardDescription>
        <CardTitle className="text-3xl">34,5</CardTitle>
      </CardHeader>
      <CardContent>
        <Progress value={86} className="mt-1" />
        <p className="text-xs text-muted mt-1">86 % av mål</p>
      </CardContent>
    </Card>
    <Card>
      <CardHeader className="pb-2">
        <CardDescription>Åpne oppgaver</CardDescription>
        <CardTitle className="text-3xl">7</CardTitle>
      </CardHeader>
      <CardContent>
        <p className="text-xs text-muted">3 forfaller denne uken</p>
      </CardContent>
    </Card>
  </div>
</div>
```

## Oppgavetabell

```tsx
<div className="bg-page p-4 w-full">
  <Card>
    <CardHeader>
      <CardTitle>Oppgaver</CardTitle>
      <CardDescription>Oversikt over aktive oppgaver i teamet.</CardDescription>
    </CardHeader>
    <CardContent>
      <Table>
        <TableHeader>
          <TableRow>
            <TableHead>Oppgave</TableHead>
            <TableHead>Ansvarlig</TableHead>
            <TableHead>Status</TableHead>
            <TableHead>Frist</TableHead>
          </TableRow>
        </TableHeader>
        <TableBody>
          <TableRow>
            <TableCell className="font-medium">Redesign forsiden</TableCell>
            <TableCell>
              <div className="flex items-center gap-2">
                <Avatar className="h-6 w-6">
                  <AvatarFallback className="text-xs">ON</AvatarFallback>
                </Avatar>
                Ola
              </div>
            </TableCell>
            <TableCell><Badge>Pågår</Badge></TableCell>
            <TableCell>15. mar</TableCell>
          </TableRow>
          <TableRow>
            <TableCell className="font-medium">Implementer søkefunksjon</TableCell>
            <TableCell>
              <div className="flex items-center gap-2">
                <Avatar className="h-6 w-6">
                  <AvatarFallback className="text-xs">KH</AvatarFallback>
                </Avatar>
                Kari
              </div>
            </TableCell>
            <TableCell><Badge variant="secondary">Til review</Badge></TableCell>
            <TableCell>12. mar</TableCell>
          </TableRow>
          <TableRow>
            <TableCell className="font-medium">Fikse ytelsesproblem</TableCell>
            <TableCell>
              <div className="flex items-center gap-2">
                <Avatar className="h-6 w-6">
                  <AvatarFallback className="text-xs">PJ</AvatarFallback>
                </Avatar>
                Per
              </div>
            </TableCell>
            <TableCell><Badge variant="danger">Blokkert</Badge></TableCell>
            <TableCell>10. mar</TableCell>
          </TableRow>
          <TableRow>
            <TableCell className="font-medium">Skrive dokumentasjon</TableCell>
            <TableCell>
              <div className="flex items-center gap-2">
                <Avatar className="h-6 w-6">
                  <AvatarFallback className="text-xs">LB</AvatarFallback>
                </Avatar>
                Lisa
              </div>
            </TableCell>
            <TableCell><Badge variant="outline">Ikke startet</Badge></TableCell>
            <TableCell>20. mar</TableCell>
          </TableRow>
        </TableBody>
      </Table>
    </CardContent>
  </Card>
</div>
```

## Prosjektoversikt

```tsx
<div className="bg-page p-4 w-full">
  <Card>
    <CardHeader>
      <CardTitle>Prosjekter</CardTitle>
      <CardDescription>Fremdrift for aktive prosjekter.</CardDescription>
    </CardHeader>
    <CardContent className="flex flex-col gap-5">
      <div>
        <div className="flex items-center justify-between mb-1">
          <p className="text-sm font-medium">Designsystem</p>
          <span className="text-sm text-muted">75 %</span>
        </div>
        <Progress value={75} />
      </div>
      <Separator />
      <div>
        <div className="flex items-center justify-between mb-1">
          <p className="text-sm font-medium">Kundeportal</p>
          <span className="text-sm text-muted">42 %</span>
        </div>
        <Progress value={42} />
      </div>
      <Separator />
      <div>
        <div className="flex items-center justify-between mb-1">
          <p className="text-sm font-medium">Intern dashboard</p>
          <span className="text-sm text-muted">91 %</span>
        </div>
        <Progress value={91} />
      </div>
    </CardContent>
  </Card>
</div>
```

## Komponenter som brukes

| Komponent   | Rolle                                              |
| ----------- | -------------------------------------------------- |
| `Card`      | Ramme rundt nøkkeltall, tabell og prosjektoversikt |
| `Table`     | Strukturert visning av oppgaver                    |
| `Badge`     | Statusindikator for oppgaver                       |
| `Progress`  | Fremdriftsindikator for prosjekter og mål          |
| `Avatar`    | Profilbilde/initialer for teammedlemmer            |
| `Separator` | Visuell skillelinje mellom prosjekter              |
| `Heading`   | Titler                                             |