Gå til innholdet

Tabs

Serveren skriver rollene og skjuler panelene som ikke er valgt. Komponenten tar piltastene.

Det er hele grunnen til at den finnes. I en fanerad skal Tab hoppe forbi hele raden og inn i panelet, mens piltastene flytter mellom fanene. Et sett knapper uten den koblingen gir én tabbestopp per fane, og skjermleseren sier «knapp» der den skulle sagt «fane, 2 av 3, valgt».

// main.ts, kjøres én gang når appen starter
import { defineFsTabs } from "@fristil/designsystem/tabs"
import "@fristil/designsystem/tabs.css"
defineFsTabs()

fs.tabs() gir ett attributtsett per element. Id-ene og koblingen mellom fane og panel regnes ut av byggefunksjonen, så de kan ikke komme i utakt.

const faner = fs.tabs({ id: "sak", count: 3, selected: 0, label: "Deler av saken" })
<fs-tabs>
<div class="fs-tabs__list" role="tablist" aria-label="Deler av saken">
<button id="sak-tab-0" role="tab" type="button" aria-selected="true"
aria-controls="sak-panel-0" tabindex="0">Søknaden</button>
<button id="sak-tab-1" role="tab" type="button" aria-selected="false"
aria-controls="sak-panel-1" tabindex="-1">Vedlegg</button>
</div>
<div id="sak-panel-0" class="fs-tabs__panel" role="tabpanel"
aria-labelledby="sak-tab-0" tabindex="0">
<p class="fs-paragraph">Søknaden ble sendt 4. mars 2026.</p>
</div>
<div id="sak-panel-1" class="fs-tabs__panel" role="tabpanel"
aria-labelledby="sak-tab-1" tabindex="0" hidden>
<p class="fs-paragraph">Tre vedlegg er lastet opp.</p>
</div>
</fs-tabs>

hidden på panelene kommer fra serveren, ikke fra komponenten. Gjorde komponenten det, ville alle panelene vises til skriptet hadde kjørt, og innholdet hoppe når de skjulte seg selv.

Sender serveren området på nytt mens brukeren holder på, river oppdateringen fanevalget bort, siden ingenting av det sto i HTML-en den sendte. Komponenten ser det og setter valget tilbake, så malen din trenger ingenting ekstra. Skal serveren kunne flytte fanen selv, som i «gå videre til steg 2», settes server-controlled<fs-tabs>. Se Markup og oppførsel.

Komponenten har ikke noe selected-attributt. Svaret står i markupen, som aria-selected på fanen, og leses derfra. To steder å holde i synk er ett for mye.

const faner = document.querySelector("fs-tabs")
faner.selected // 0
faner.select(1) // velger «Vedlegg» og sender `tab-select`
<fs-tabs id="saksfaner">
<div class="fs-tabs__list" role="tablist" aria-label="Deler av saken">
<button id="sak-tab-0" role="tab" type="button" aria-selected="true"
aria-controls="sak-panel-0" tabindex="0">Søknaden</button>
<button id="sak-tab-1" role="tab" type="button" aria-selected="false"
aria-controls="sak-panel-1" tabindex="-1">Vedlegg</button>
</div>
<div id="sak-panel-0" class="fs-tabs__panel" role="tabpanel"
aria-labelledby="sak-tab-0" tabindex="0">Søknaden</div>
<div id="sak-panel-1" class="fs-tabs__panel" role="tabpanel"
aria-labelledby="sak-tab-1" tabindex="0" hidden>Vedlegg</div>
</fs-tabs>
<script type="module">
import { defineFsTabs } from "@fristil/designsystem/tabs"
defineFsTabs()
document.getElementById("saksfaner").addEventListener("tab-select", (evt) => {
console.log("valgte fane", evt.detail.index)
})
</script>

Fanene er role="tab" i en role="tablist", og panelene er role="tabpanel" pekt til med aria-labelledby. Alt dette står i markupen fra serveren, så semantikken er riktig før skriptet har kjørt.

TastGjør
PiltasteneFlytter mellom fanene, og går rundt fra siste til første
Home og EndFørste og siste fane
TabUt av raden og inn i det valgte panelet

Bare den valgte fanen har tabindex="0". Panelet har tabindex="0" slik at det får fokus også når det ikke har noe fokuserbart i seg, ellers hopper Tab rett forbi innholdet som nettopp ble vist.

KlasseHva det er
.fs-tabs__listRaden med knapper. Serveren gir den role="tablist"
.fs-tabs__panelEtt panel. Den valgte er uten hidden

Knappene har ingen egen klasse, de styles som .fs-tabs__list button.

VariabelReserve
--fs-tabs-gapvar(--size-1)
--fs-tabs-paddingvar(--size-2) var(--size-4)
--fs-tabs-panel-spacevar(--size-4)
.fs-tabs__list {
--fs-tabs-gap: 0;
}