Gå til innholdet

Versjonslogg

Versjonsnumrene følger semantisk versjonering. Det offentlige API-et er større enn funksjonene pakken eksporterer, så disse regnes som brytende endringer og krever en ny hovedversjon:

  • et klassenavn, et data-*-attributt eller en lovlig verdi som forsvinner eller endrer betydning,
  • et hvilket som helst annet attributt en byggefunksjon skriver ut, aria-* medregnet, som forsvinner eller endrer betydning. Det byggefunksjonene sender ut står i konsumentens markup, og kan styles og testes mot. Det en web component setter i nettleseren er ikke API på samme måte: det er oppførsel, og rettes som oppførsel,
  • et part-navn i en komponent med shadow DOM,
  • et tokennavn eller en --fs-*-variabel,
  • en funksjon eller en type i fs,
  • en oppføring i exports i package.json.

Nye komponenter, nye valgfrie attributter, nye attributter en byggefunksjon legger til, og rettelser som ikke endrer markup kommer i et nytt undertall. Før 1.0 kan et undertall også ha brytende endringer, og da står de under en egen overskrift «Brytende».

0.36.0 (2026-10-25)

Brytende

  • Forslagslista står i det øverste laget. Den var position: absolute i <fs-suggestion>, og en forelder med overflow, som kroppen i en dialog, klippet den. Nå er lista popover="manual", og komponenten åpner og lukker vinduet etter hidden, som fortsatt er det som sier om lista er åpen. Komponenten legger den rett under feltet, like bred som det. --fs-suggestion-layer er tatt ut, siden det øverste laget ligger over alt uten z-index. Slik oppdaterer du: ta bort --fs-suggestion-layer fra egen CSS. fs.suggestion() skriver popover selv; skriver du markupen for hånd, gjør komponenten det.
  • En rettelse fra diagnoseMarkup er en liste av endringer. Fix har edits, hver med start, end og text, i tekstens rekkefølge og uten overlapp, i stedet for ett start, end og text. Det samme i Kotlin, Fix(title, edits, preferred) med Edit. Grunnen er «Gi feltet id «epost»», som skriver id på feltet og for på ledeteksten under ett. Slik oppdaterer du: bruk fix.edits bakfra, så posisjonene i de første holder. Språkserveren og editorene gjør det alt.
  • Fanene i <fs-tabs> er lenker til panelene, ikke knapper. Raden er en <ul class="fs-tabs__list"> med <li><a href="#panel">, og hvert .fs-tabs__panel har den id-en lenken peker på. Uten JavaScript er raden en innholdsfortegnelse, og panelene står etter hverandre under den. Med JavaScript skriver komponenten rollene, aria-selected, aria-controls fra href, tabbestoppene og hidden="until-found" på panelene som ikke er valgt, så søk i siden og en lenke til noe inni et panel velger fanen. Lenkene har ingen id, og komponenten skriver aldri en, så en oppdatering fra serveren kjenner dem igjen og valget står. Hvert valg skrives i adressen, som #sak-vedlegg, uten ny historikkoppføring, og adressen velger fane når siden lastes. En role="tab" på noe som ikke er en lenke til et panel, er ikke lenger en fane, og komponenten sier fra i konsollen. Slik oppdaterer du: bytt <div class="fs-tabs__list"> med knapper til en <ul> med lenker, gi hvert panel en id og la lenken peke på den.
  • fs.tabs() og Fs.tabs tar keys i stedet for count, og selected er borte. Nøklene kommer fra dataene, én per fane, og gir panelene ${id}-${nøkkel} og overskriftene ${id}-${nøkkel}-heading. En nøkkel som er tom, har mellomrom eller står to ganger, kaster, likt i TypeScript og Kotlin. Den første fanen er utgangspunktet; et annet valg kommer fra adressen, fra hidden="until-found" på de andre panelene, eller fra appen gjennom selected og select() på elementet. Slik oppdaterer du: fs.tabs({ id, count: 3, selected: 1, label }) blir fs.tabs({ id, keys: ["soknaden", "vedlegg", "meldinger"], label }).
  • fs.tabs() og Fs.tabs skriver bare lenker, id-er og klasser. list har klassen og aria-label, tabs har href, panels har id og klassen, og det nye headings har id til overskriften i hvert panel. Ingen roller, aria-*, tabindex eller hidden: uten JavaScript skal raden være en liste med lenker, og en <section> med navn ville blitt et landemerke. Komponenten skriver resten når den registreres. Slik oppdaterer du: spre faner.tabs[i] på en <a> i en <li>, og faner.headings[i] på overskriften i panelet.
  • En tom label kaster i fs.tabs(), i React-inngangen og i Fs.tabs. Raden må ha et navn som sier hva fanene velger mellom; uten det sier skjermleseren bare «faneliste». label sjekkes før keys, med samme melding i TypeScript og Kotlin. Slik oppdaterer du: gi raden et navn, som label: "Deler av saken".
  • fs.tabs() i @fristil/designsystem/react gir hele tilstanden. Den tar selected (standard 0) og skriver rollene, aria-selected, aria-controls, tabIndex, hidden og aria-labelledby fra panelet til lenken, som får id ${id}-${nøkkel}-tab. item gir role="presentation" til hvert <li>. Da har komponenten ingenting å skrive når den registreres, og React hydrerer uten avvik. hidden er boolsk, fordi React 19 skriver hidden="until-found" som hidden=""; søk i siden åpner derfor ikke et skjult panel i en React-app. Har markupen aria-selected fra serveren, gjelder den når siden lastes, og adressen velger ingen fane. Slik oppdaterer du: importer fs fra @fristil/designsystem/react og spre faner.item på <li>.
  • fs.suggestion() og Fs.suggestion tar keys i stedet for count. Alternativene får id fra nøkkelen, ${id}-option-${nøkkel}, ikke fra plassen i lista, så en oppdatering fra serveren kjenner dem igjen. Antall nøkler avgjør om tommeldingen har hidden, som count gjorde. Nøklene sjekkes som i fs.tabs(). Slik oppdaterer du: fs.suggestion({ id, count: kommuner.length }) blir fs.suggestion({ id, keys: kommuner.map((k) => k.nr) }).

Nytt

  • Forslagslista har et navn, fra ledeteksten. fs.suggestion() gir ledeteksten id ${id}-label og lista aria-labelledby mot den. Kommer markupen uten, gjør komponenten det samme, og står ledeteksten rundt feltet, får lista teksten som aria-label, uten å skrive id på ledeteksten.
  • fs.suggestion({ pending: true }) skriver aria-busy="true" på lista, og stilarket skjuler «Ingen treff» mens det står.
  • Ti regler i kjernen om det malen må skrive selv, for at komponentene skal virke uten JavaScript og overleve en oppdatering fra serveren, i fristil sjekk, språkserveren, VS Code og IntelliJ: kontroll-uten-kobling (ledeteksten har verken for mot feltet eller står rundt det, med rettelsen «Gi feltet id «navn»» fra feltets name), panel-uten-id i <fs-popover>, fanelenke-uten-panel, fane-uten-lenke (role="tab" på noe som ikke er en lenke), fanerad-uten-navn (med rettelse), tekst-i-ledetekst (hjelpetekst eller feilmelding inni <label>), verdi-uten-felt (data-value uten .fs-suggestion__value, og omvendt), feilmelding-motsier (hidden på feilmeldingen ved siden av aria-invalid="true"), utgatt-attributt (invalid, required-marker og control-id på <fs-field>, med rettelse og beskjed om hva som gjelder i stedet) og id-fra-komponenten (en id komponenten lagde selv, kopiert inn fra DevTools; bare i markupmodus). Reglene tier der malen avgjør markupen, også ved en bundet id som :id, th:id eller asp-for. Hver bygde side i dokumentasjonen gir null funn, også hver forhåndsvisning lest som markup, og en vaktpost i bygget krever det.
  • Manifestet sier hvilke deler som må ha id fra serveren, stableId på delen, og hvilke attributter som er tatt ut av et element, removed med beskjeden. Begge er tillegg; schemaVersion er fortsatt 1.
  • fs.progress({ valueText }) skriver aria-valuetext, som skjermleseren leser i stedet for prosenten: «Steg 3 av 7» sier mer, og max leses sjelden opp.
  • Snippetene har id-en som tabbestopp, og for og href følger med. Og live-templates.xml i pakken har de samme oppskriftene som live templates for IntelliJ, med id-en som variabel.

Rettet

  • Hvert alternativ i forslagslista har aria-selected fra starten. Komponenten skrev det først når lista ble lukket, så alternativene serveren sendte, sto uten fram til da, mens resten av lista hadde det.
  • <fs-popover> skriver aldri id på panelet. Den lagde en når malen ikke hadde skrevet en, og Datastar og idiomorph byttet da ut panelet ved neste oppdatering, med fokus og det brukeren gjorde i det. Nå beholdes panelet. Uten id kan knappen ikke kobles til panelet med aria-controls, og komponenten sier fra i konsollen. Byttes panelet ut mens det står åpent fordi id-en fra serveren endret seg, sier komponenten også fra, som <fs-field> gjør. Oppskriften har id på panelet og merker ingen forskjell.
  • server-controlled satt etter et klikk lot ikke serveren flytte fanen med bare hidden. aria-selected komponenten selv hadde skrevet, ble regnet som serverens ord. Nå gjør komponenten det samme som når serveren svarer på et klikk, og attributtet kan også fjernes igjen mens siden lever.
  • Søk i siden velger fanen også i faner i faner. Komponenten venter til nettleseren er ferdig med å avdekke treffet før den skriver raden; før stoppet Chromium og WebKit før det ytre panelet.
  • Serverens aria-selected vinner over adressen når siden lastes. En React-app rendrer på serveren uten å se fragmentet, og valgte komponenten fra adressen før React hydrerte, meldte React avvik. Peker adressen på en annen fane i raden, rettes den til den valgte.
  • hidden skrives tilbake i formen serveren brukte, hidden="" eller hidden="until-found", og i én form per rad.
  • Før registrering viser tabs.css panelet adressen peker på, eller panelet med det inni, i stedet for alltid det første, så innholdet ikke hopper når komponenten registreres. Serverens hidden går foran, og under server-controlled vises det første. Regelen står bak @media (scripting: enabled): uten JavaScript vises alle panelene.

0.35.0 (2026-10-22)

Brytende

  • <fs-field> og <fs-suggestion> skriver aldri id på feltet. Datastar og idiomorph kjenner igjen et element på id, og et felt med en id serveren ikke sendte, ble byttet ut ved neste oppdatering, med fokus og det brukeren hadde skrevet. Nå er det malen som skriver for på ledeteksten og id på feltet, som HTML uansett krever for at feltet skal ha et navn før skriptet har kjørt, eller som legger ledeteksten rundt feltet. Komponenten skriver for fra feltets id når ledeteksten ikke står rundt feltet. Et felt uten id med ledeteksten ved siden av gir en advarsel i konsollen. Hjelpeteksten, feilmeldingen, lista og alternativene får fortsatt id av komponenten når de mangler: byttes de ut, kobler komponenten dem på nytt, og det markerte alternativet gjenfinnes på teksten. Slik oppdaterer du: skriv for og id i malen, eller legg <label> rundt feltet.
  • invalid, required-marker og control-id er tatt ut av <fs-field>. Feltet er ugyldig når feilmeldingen synes, altså har tekst og står uten hidden og uten display: none (som data-show i Datastar setter), eller når serveren har skrevet aria-invalid på feltet. Komponenten rører aldri hidden på feilmeldingen; før skjulte den en feilmelding serveren hadde rendret synlig, fordi verten manglet invalid. Påkrevd-merket står som data-required på ledeteksten, og vises da også uten JavaScript. control-id fikk komponenten til å skrive en id serveren ikke sendte. Slik oppdaterer du: <fs-field invalid> blir en synlig .fs-error-text (i Datastar data-attr:hidden="!$ugyldig" på feilmeldingen framfor data-attr:invalid på verten), required-marker="x" blir data-required="x" på <label>, og control-id blir id på feltet. Egenskapene invalid, requiredMarker og controlId er borte, og React-typene i react-jsx er oppdatert.
  • <fs-suggestion> viser alltid teksten i feltet. Før skrev den data-value inn i det synlige feltet, så brukeren som valgte Bergen, så «4601», og skjemaet sendte koden under feltets name. Nå får et skjult .fs-suggestion__value koden når serveren har skrevet det, og suggestion-select har både value og label. Tilbakestilles skjemaet, følger koden teksten: serverens når teksten er serverens, ellers det valgte, ellers alternativet med den teksten, ellers ingen. Slik oppdaterer du: skal skjemaet sende koden, legg inn <input class="fs-suggestion__value" type="hidden" name="…">, eller les evt.detail.value.
  • .fs-suggestion__field er borte, og statuselementet har verken aria-live eller data-ignore-morph. Feltet, lista, tommeldingen og statuselementet står rett i <fs-suggestion>, som selv gir lista plassen under feltet. role="status" gir aria-live="polite" av seg selv, og tømmer en oppdatering statuselementet, skriver komponenten antallet på nytt (ikke under prefiltered, der det nye antallet kommer rett etter), så Datastars data-ignore-morph trengs ikke, og det samme gjelder med htmx. Tommeldingen trenger ikke hidden: stilarket skjuler den sammen med lista. fs.suggestion() gir ikke lenger field. Slik oppdaterer du: ta bort <div class="fs-suggestion__field"> og {...forslag.field} rundt feltet, og aria-live, data-ignore-morph og hidden på de to elementene.

Lagt til

  • <fs-tabs> og <fs-popover> sier fra når en patch byttet ut elementene fordi serveren ikke skrev id. Datastar og idiomorph morfer ikke et element med id inn i et uten: det gamle fjernes og serverens settes inn. Lager komponenten id-ene selv, som den gjør for bar markup, byttes fanene og panelet ut ved neste patch som sender området på nytt, og fanevalget og fokus er borte. Nå kommer det én advarsel i konsollen i det øyeblikket det skjer, med beskjed om å skrive id i malen. Sider som aldri patcher, eller patcher smalt, får ingen advarsel. Reprodusert mot Datastar v1.0.4 i fs-tabs-morph.browser.test.ts, som kjører den ekte bundlen.
  • <fs-field> og <fs-suggestion> sier fra når en oppdatering byttet ut feltet fordi id-en fra serveren endret seg mellom to rendringer, som når malen lager id-en av et tilfeldig tall eller en teller, og det brukeren hadde skrevet, gikk tapt.
  • <fs-field> og <fs-suggestion> sier fra når for på ledeteksten pekte på en annen id enn feltets. Komponenten skriver den om, men uten JavaScript hadde feltet da ingen ledetekst.
  • fs.suggestion() gir det skjulte feltet for koden som value, og .fs-suggestion__value er en ny del i manifestet og i begge editorene. Komponenten sender input og change på feltet når den skriver eller tømmer koden, så data-bind i Datastar ser den; de bobler ikke, så en data-on:input på verten utløses ikke av dem.
  • <fs-suggestion> går til første og siste alternativ med PgUp og PgDn. Home og End røres ikke; de flytter markøren i teksten.
  • idOrFallback sier også at en ny id per rendring bytter ut elementene ved neste patch.

Rettet

  • <fs-suggestion> mistet markeringen når serveren sendte lista på nytt. Alternativene får id av komponenten, og en oppdatering bytter dem ut med nye id-er. Markeringen gjenfinnes nå på teksten.
  • <fs-suggestion> valgte bare på mousedown. Hjelpemidler som aktiverer et element, sender click, og den valgte da ingenting. Nå skjer valget på click, og mousedown holder bare fokus i feltet.
  • <fs-suggestion> åpnet lista og endret verdien i et felt med readonly. Nå åpner et felt med readonly eller disabled ikke lista.
  • Et alternativ med aria-disabled="true" kunne velges. Nå vises det, men hoppes over av piltastene og kan ikke velges med mus.
  • Enter under en IME-komposisjon valgte det markerte alternativet. Nå hører tastene til komposisjonen.
  • <fs-popover> lukket vinduet etter en patch fra en mal uten popover. Morfingen tok open på verten og popover på panelet i samme omgang. Komponenten lukket panelet, gjenåpnet det i neste mikrotask, og først etterpå kom toggle med «closed» fra lukkingen, som en egen oppgave. Den ble tatt som brukerens lukking. Nå gjelder toggle bare når den stemmer med panelets faktiske tilstand.

Endret

  • «Markup og oppførsel» sier hvilke id-er som overlever en oppdatering. Felt og forslagsfelt får for og id fra malen, fanene og sprettoppvinduet trenger id fra serveren når hele området sendes på nytt, og id-er i en løkke lages av en nøkkel i dataene, aldri av indeksen eller en teller. Regelbøkene i agent/ og sidene for felt, forslagsfelt og faner sier det samme. Før sto det at outer morfer og at malen ikke trenger gjøre noe, og det holdt bare for markup fra byggefunksjonene.

0.34.0 (2026-10-18)

Brytende

  • hidden på <fs-error-summary> er et flagg i JSX-typene. hidden kom fra HTMLAttributes som boolean, så hidden={false} kompilerte, mens React 18 skriver det som hidden="false" på en web component, som er sant: boksen ble aldri vist. Typen er nå true | undefined, som open på <fs-dialog>, slik regelboka for React sier om hvert boolske attributt på en web component. fs.errorSummary().host sendte alt true eller ingenting. Det er brytende for den som skrev hidden={!harFeil} eller hidden={false}: det kompilerte, og i React 19 virket det, siden React 19 setter egenskapen hidden i stedet for attributtet. Skriv hidden={harFeil ? undefined : true}, eller bruk fs.errorSummary({ count }).host, som sender det riktig.
  • fristil sjekk meldte grønt for en tom fil og en tom side. Bare tom standard inn ga feilkode. En tom fil, og en adresse som svarte 200 med tom HTML, ga «Markupen stemmer» og 0, så en CI-linje mot en side som rendret tomt ble grønn. Nå gir en adresse med tom side feilkode, og det gjør også filer som alle er tomme. Én tom fil blant flere, som en tom partial i et glob, er fortsatt ikke en feil. En CI-linje som var grønn mot en tom side, blir rød med denne versjonen.

Lagt til

  • setAttributes og typen ReactSet fra /react. fs.setAttributes derfra er den samme funksjonen, for sett med className, htmlFor og tabIndex som tall. Hovedinngangen eksporterer rememberManaged, som /react bruker for å gi et omdøpt sett de samme forvaltede navnene; den er ikke ment for apper.
  • Regelen del-flere-ganger i fristil sjekk, editoren og diagnoseMarkup. En del med det nye feltet unique i manifestet som står mer enn én gang, gir en advarsel: et <fs-field> med to kontroller, to hjelpetekster i ett felt, en fs-table med to <caption>. unique står på delene der en til er en feil og ikke vanlig innhold, og leses av Part i Kotlin og Part i TypeScript. Regelen leser parts på elementene og klassene i manifestet, så en ny del med unique sjekkes uten en egen regel. Et treff inni en nøstet vert av samme slag hører til den, det som står i en <template> teller ikke, og står det malsyntaks i verten, sier regelen ingenting.
  • tbody i fs-table kan stå flere ganger i manifestet, som HTML tillater.
  • Fristil.manifest.builders i Kotlin-pakken. Hver byggefunksjon med valgene (name, required, type som values, flag, text, number eller textList, og description) og formen på svaret (attributes, text, values, list eller group). Tabellen generatoren bak Fs bruker er fortsatt utelatt. TypeScript-typen Manifest har fått builders med samme form.
  • Fristil.buildTheme(oppskrift) og Fristil.inspectTheme(css) i Kotlin-pakken. Temaet kunne før bare bygges med fristil tema og Gradle-oppgaven fristilTema. Oppskriften er JSON-teksten fristil tema leser, med norske eller engelske nøkler, og svarene Theme og ThemeReport har de samme feltene som CoreTheme og CoreThemeReport i TypeScript. Fargekontrakten ligger nå i jar-en, som manifestet.
  • Manifestet har en tokens-seksjon. Avstandsskalaen, brekkpunktene og containerbreddene fra tokens.ts, fargefamiliene, rollene og lagene med hva hver er til, hvert --fs-color-*-navn, og komponentvariablene per komponent med standardverdien stilarket faller tilbake på. Fargene står som akser og navn, ikke som verdier, siden et tema kan ha andre verdier enn Fristils egne. ManifestTokens i Kotlin leser det samme. schemaVersion er fortsatt 1, og tokens i Kotlin er null i et manifest fra før denne versjonen. Beskrivelsene av familiene, rollene og lagene eksporteres også fra @fristil/designsystem/tokens, som FAMILY_DESCRIPTIONS, ROLE_DESCRIPTIONS og LAYER_DESCRIPTIONS.
  • Manifestet sier hva hver klasse tar som innhold, og hvilke deler den består av. Hver klasse i classes har content, med samme ord som delene til elementene: text, free, parts eller none. Klassene med en fast struktur, som fs-table, fs-list og fs-toggle-group, har parts med selector, description, content, multiple og optional, og en klasse som bare er en del av en annen komponents markup, som fs-card__title, peker på forelderen med partOf. schemaVersion er fortsatt 1. I Kotlin har ManifestClass fått content, partOf og parts, og content er null i et manifest fra før denne versjonen, så IntelliJ kan lese manifestet til en eldre pakke.

Endret

  • Pakken sender med kildene kartene peker på. dist har kildekart og deklarasjonskart, og de for fs, /react, /register, /dom, /diagnostics og kommandolinja pekte på fjorten filer i src som ikke var med i tarballen, så «gå til definisjon» og et stakkspor derfra landet i en fil som ikke fantes. src/*.ts, src/diagnostics, src/jsx og src/vocabulary følger nå med, som src/components alt gjorde, og byggesteget krever at hvert medsendt kart peker på en fil i pakken.
  • Regelboka for maler sier at Kotlin-pakken ligger på Maven Central. agent/maler.md sa at io.github.markusaugust:fristil og Gradle-pluginen måtte bygges fra kildekoden. Begge ligger på Central fra 0.33.0, med samme versjon som npm-pakken, men ikke hver npm-versjon går dit. Regelboka ber derfor agenten bruke den nyeste versjonen som finnes på Central, og aldri skrive en den ikke har sett der.

Rettet

  • <fs-error-summary> tar fokus også i React. React gir fokus tilbake til elementet som hadde det før en commit, og boksen flyttet fokus inne i den samme commiten. Fokus endte derfor på innsendingsknappen i Chromium og Firefox, og på feltet i alle tre motorene når skjemaet ble sendt med Enter, både når boksen ble satt inn og når hidden ble tatt bort. Boksen tar nå fokus én gang til etter commiten, hvis det gikk tilbake dit det var. Har et skript flyttet fokus til noe annet, står det. En React-app som viser boksen mens brukeren står i et felt, for eksempel ved blur, merker at fokus nå flyttes dit, slik det alltid har gjort uten React.
  • Et merke, en etikett, en knapp eller en lenke i et kort strekkes ikke lenger til hele bredden. .fs-card er en flekskolonne, så <span class="fs-badge">Innvilget</span> ble et bånd tvers over kortet, slik eksempelet på komponentsiden viste. Avsnitt, lister og bilder strekkes som før.
  • fristil lsp leter ikke over arbeidsområdet for en fil utenfor det. Letingen etter manifestet stoppet ved rota av arbeidsområdet fila lå under, men for en fil utenfor det, som en kladdefil, og for hver fil når editoren ikke ga noe arbeidsområde, som Neovim uten treff på root_markers, fantes ingen rot å stoppe ved, så den gikk helt til rota av filsystemet og tok en pakke i hjemmemappa som prosjektets. Nå letes det ikke fra en slik fil: den sjekkes mot manifestet ved rota av arbeidsområdet, om det har et, ellers mot pakkens, slik Editoren-siden og IntelliJ-pluginen alt sa.
  • Tagnavnene er typet fra /register og /react-jsx alene. Typesikker bruk-siden sa at hendelsestypene følger med hver inngang til web-komponentene, men HTMLElementTagNameMap ble bare utvidet i komponentfilene, og /register og /react-jsx tar dem ikke med i typene. Da var document.querySelector("fs-dialog") et Element, og lytteren på det fikk Event uten detail, så mønsteret på siden var en typefeil. Kartet over tagnavnene står nå i events.ts, som hver inngang alt tar med, så querySelector("fs-dialog") gir FsDialog | null fra alle tretten inngangene, også hver komponentinngang for de andre komponentene. Bygget kompilerer en fil per inngang, med bare den importen, så det ikke kan gli tilbake.
  • fs.setAttributes fra /react skriver class og for, og rydder. Funksjonen var hovedinngangens, mens settene fra /react har className og htmlFor, så den skrev attributtene classname og htmlfor, og fant ikke byggefunksjonen bak det omdøpte settet: aria-invalid ble stående etter at feltet var rettet, uten advarsel. Nå oversettes navnene tilbake før de settes, tabIndex blir tabindex, og settet rydder det samme som fra hovedinngangen.
  • engines slipper ikke lenger inn Node 21 og 22.0–22.2. Kjernen lastes med process.getBuiltinModule, som kom i Node 20.16 og 22.3, men >=20.16 tok med versjonene mellom, og der sa feilmeldingen at Node 22.1 var eldre enn 20.16. Kravet er nå ^20.16.0 || >=22.3.0, og meldingen nevner begge grensene. Oppføringen under 0.32.0 som sier «20.16 eller nyere», gjelder med dette unntaket.
  • Regelboka for React og Kodeagenter-siden sa at react-jsx stopper en variant som ikke finnes. react-jsx typer attributtene på web-komponentene, og ingen av dem har en variant. Det er fs.button({ variant }) som gjør en ukjent variant til en kompileringsfeil, med eller uten react-jsx, og en håndskrevet className eller data-variant ser ingen av dem, og heller ikke fristil sjekk, som leser class, ikke className. Begge tekstene sier nå hva som fanger hva, og at det håndskrevne skal erstattes av byggefunksjonene.
  • defineFs* kaster ikke når klassen alt er registrert under et eget navn. defineFsToast("app-toast") fulgt av defineFs() ga NotSupportedError på fs-toast, siden en konstruktør bare kan stå i registeret én gang, og <fs-session-timeout> og <fs-connection-status> ble aldri registrert. Nå registreres en underklasse under det andre navnet, så begge navnene virker og flere kall er ufarlige, slik dokumentasjonen sier. tagName er dessuten typet som string. Før var typen standardnavnet, så TypeScript avviste defineFsToast("app-toast") før det kom så langt.
  • <fs-toast> flytter fokus riktig også i en skyggerot. Komponenten leste document.activeElement, som peker på skyggeverten når regionen står i en skyggerot, slik hver forhåndsvisning i dokumentasjonen gjør. Da så det ut som om ingen melding hadde fokus: lukket brukeren meldingen med Enter, falt fokus til body i stedet for til meldingen ved siden av eller dit det kom fra, og det samme skjedde ved clear(). Fokus leses nå fra komponentens egen rot. <fs-session-timeout> husker hvor fokus sto før varselet enten feltet står i siden eller i samme skyggerot som komponenten, så det får fokus tilbake også når nettleseren ikke setter det dit selv.
  • README sier hva et eget tagnavn gir. Den lovet at du bestemmer «hva det skal hete», men stilarkene til <fs-field>, <fs-tabs>, <fs-popover>, <fs-dialog> og <fs-suggestion> er skrevet for fs-*-navnene, så en komponent under et annet navn mistet display-regelen på verten, og <fs-dialog> også plasseringen av en dialog som står åpen før den er modal. README sier nå at oppførselen følger med navnet, men ikke stilen, og hvilke fire komponenter som setter klassen sin selv.
  • popover-toggle er beskrevet slik den sendes. Beskrivelsen i events.ts og web-types.json sa «Panelet åpnet eller lukket seg», men hendelsen kommer bare når brukeren åpner eller lukker panelet, og fra show(), hide() og toggle(). Settes open som egenskap eller attributt, som React 19 og Datastars data-attr:open gjør, kommer ingen hendelse, ellers hadde Datastar-oppskriften fått sitt eget signal i retur. Popover-siden skiller nå meny.open = false fra hide().
  • Regelboka for maler lovet at <fs-tabs> husker fanevalget etter enhver patch. Den nevnte innerHTML, htmx og Turbo, men valget er knyttet til selve knappen og overlever bare en morfing som beholder elementene, som Datastars outer, htmx med idiomorph og Turbo 8 med morfing. Bytter patchen ut nodene, følger komponenten markupen. Regelboka sier nå hvilke patcher som beholder valget, og at serveren ellers må skrive det selv eller bruke server-controlled.
  • Regelbøkene sa at markupen i en ramme-komponent står der uansett. «Kom i gang» og «Rammeverk» ble rettet i 0.33.0, men ikke teksten i generatoren, så alle seks regelbøkene manglet unntaket: Tabs viser bare det første panelet og Suggestion skjuler lista til de er registrert.
  • React-oppskriften på Dialog-siden og mønsteret «Bekreft en handling» tømmer returnValue før showModal(). «Ren HTML»- og Astro-fanene fikk tømmingen i 0.33.0, men React-fanen, mønstersiden og demoen åpnet dialogen med den gamle verdien stående. Ble dialogen åpnet igjen etter en «Slett» og lukket uten knapp, fikk close-lytteren «slett» på nytt og slettet igjen.
  • fristil sjekk-tema og inspectTheme hoppet over en nøstet blokk som begynner med et element og et kolon. html:not([data-theme="light"]) { … } eller body:has(…) { … } inni en @media, et @layer eller en annen blokk ble lest som deklarasjonen html, og den slukte alt fram til neste semikolon, blokkene etter den med. Et tema med et brudd der fikk «Temaet holder hvert løfte». Nå leses blokka som en regel, slik nettleseren gjør.
  • fristil sjekk-tema og inspectTheme leste @media not (prefers-color-scheme: dark) som mørkt tema. Blokka gjelder i lyst tema, og Fristils egne lyse verdier ga brudd. not snur nå mediespørringen, også skrevet med en medietype, som not all and (…) eller not screen and (…), og blokka kontrolleres bare som lys. Med en annen medietype, som not print and (…), gjelder blokka skjermen i både lyst og mørkt tema, og den kontrolleres som en blokk uten betingelse.
  • fristil sjekk-tema og inspectTheme meldte systemfargene som feil. --fs-color-disabled-surface, --fs-color-disabled-text og --fs-color-overlay er ingen celle, men ble lest som familien disabled med roller som manglet, og som et token systemet ikke har. En overstyring skrevet på hver temagrense, slik dokumentasjonen ber om, ga feilkode, og det gjorde Fristils egen tokens.css også, med tolv problemer. Nå hopper sjekken over dem: de lover ingen kontrast.
  • Et tema uten farger fra fristil tema ga feilkode i fristil sjekk-tema. Et tema med bare skrift og form fikk «Fant ingen —fs-color-*-verdier», og inspectTheme meldte det som et problem, enda dokumentasjonen sier at et tema laget med tema holder kravene. Nå svarer sjekken at det ikke er noen kontrast å kontrollere, uten feilkode. En fil uten noen --fs-*-verdier gir fortsatt feilkode.
  • En fs-progress med value viste ikke fyllet i høykontrastmodus. Den fylte delen leste currentColor, og modusen tvang bakgrunnen på pseudoelementet tilbake til Canvas, så stolpen så tom ut uansett hvor langt den hadde kommet. Fyllet tegnes nå i Highlight rett på ::-webkit-progress-value og ::-moz-progress-bar.
  • Klassen på et avsnitt i fs-card og fs-alert vinner over komponenten. .fs-card p og .fs-alert p slo klassene på avsnittet på spesifisitet. I et kort virket ikke --fs-paragraph-line-height, en fs-card__title som var en <p> fikk artikkelens linjehøyde, og fs-help-text og fs-error-text mistet marginen i toppen, også i en melding. Reglene teller nå bare elementet, så et avsnitt uten klasse ser ut som før.
  • En besøkt fs-link skifter farge under musa. :hover sto før :visited med samme spesifisitet, så besøktfargen vant og hover-fargen kom aldri. Rekkefølgen er snudd.
  • Ringen rundt fs-avatar vises over bildet. Den var en box-shadow innover, og en slik skygge males under innholdet, så et bilde dekket den helt og ringen fantes bare med initialer. Den er nå en outline innover, også i fs-avatar-stack, der skyggen utenfor fortsatt skiller bildene, og hver avatar i stabelen males for seg, så ringen til den bak ikke skinner gjennom bildet til den foran. En slik outline slår nettleserens egen fokusmarkering, så en fs-avatar som er en knapp får --fs-focus-ring på :focus-visible, og i en stabel løftes den med fokus over naboene så fokusringen vises hel.
  • --fs-skeleton-radius virker på data-variant="text". Tekstlinjene hadde en fast radius og leste ikke variabelen. Standarden der er fortsatt var(--fs-spacing-0-5), og tabellen på siden sier det.
  • Et langt valg i fs-select med data-picker="styled" ble ikke klippet. Regelen som klipper det, treffer <selectedcontent>, og kommentaren sa at nettleseren lager elementet selv. Det gjør den ikke, så med markupen på siden ble et langt valg brutt over flere linjer, og feltet ble høyere. Komponentsiden sier nå at <button><selectedcontent></selectedcontent></button> må stå først i <select>-en for at valget skal klippes.
  • Siden om fs-textarea sa at rows virker som før. Minstehøyden er litt under tre rader, så rows="1" og rows="2" gir det samme feltet. Siden sier nå hvor grensen går, og at --fs-textarea-min-height: 0 lar rows bestemme alene.
  • fristil tema oppskrift.json --ut tema.css skrev ingen fil. Uten likhetstegn ble --ut og tema.css lest som to filnavn etter oppskriften og ignorert. Temaet gikk til utdata, og kjøringen avsluttet med 0. På samme måte forsvant --aksent #f00 bak fargen i fila. Nå stopper tema og ber om likhetstegn, slik overta og agent gjør, og mer enn én oppskriftsfil gir også feilkode. overta og agent stopper dessuten på et ukjent flagg med likhetstegn, som --utt=src/ui eller --rammverk=react, i stedet for å bruke standarden.
  • fristil tema leser oppskriften som JSON.parse. Sto en nøkkel to ganger, vant den første, mens JSON.parse lar den siste vinne. Tall som 0700 og 1. og et rått kontrolltegn i en streng ble godtatt, og en fil med svært dyp nesting stoppet den kjørbare fila med «stack overflow». Nå vinner den siste verdien, slike tall og strenger gir «er ikke gyldig JSON», og det gjør også mer enn 128 objekter og lister inni hverandre.
  • Tall i temaet og manifestet skrives som String(tall) i JavaScript. 1e-7 ble 0.0000001 og 1e21 ble 1000000000000000000000, og et uendelig tall ble inf. Nå får de eksponent, som 1e-7 og 1e+21, og Infinity heter Infinity. I manifestet blir et uendelig tall null, som i JSON.stringify. Vanlige tall skrives som før.
  • Meldingen for aria-controls som peker på en id som ikke finnes, slutter ikke lenger med et mellomrom. Det sto igjen etter «styrer.», i fristil sjekk, i editoren og som to mellomrom før regelnavnet i assertFristil.
  • En BOM først i en fil eller side gir ikke lenger en kolonne for mye. fristil sjekk talte U+FEFF som et tegn på linje 1, så kolonnen var én mer enn editoren viser. For en adresse svarte npx riktig, fordi Node fjerner BOM-en, mens den kjørbare fila og java -jar ga én kolonne mer. Nå fjernes den for filer, standard inn og adresser i alle verter.
  • Språkserveren og fristil sjekk regner \r som linjeskift. Bare \n delte linjer, så et dokument med \r-linjeskift ble én lang linje, og en editor som sender bufferen slik den er, som Neovim med fileformat=mac, fikk funnene og hurtigrettelsene på feil sted. Nå er \n, \r\n og \r alene linjeskift, som LSP krever. Linje og kolonne i diagnosePage, assertFristil og fristil sjekk følger samme regel. Hurtigrettelsen «Sett inn en ledetekst» gir også <label> sin egen linje med samme innrykk når linjeskiftene er \r.
  • Eksempelet på siden om Kotlin viste feil regelnavn. Funnet for en for som peker på en id som ikke finnes, sto med [for-peker-feil], men regelen heter id-finnes-ikke. for-peker-feil gjelder en for som peker på et element som ikke kan ha ledetekst. Den som kopierte navnet til <!-- fristil-ignore-next … -->, fikk funnet likevel.
  • Oppskriften for å bytte skilletegn i fs-breadcrumbs tok ikke med alternativteksten. Siden viste content: "›", og en regel utenfor laget slår stilarket, så den som fulgte rådet fikk tilbake feilen 0.33.0 rettet: skjermleseren sa «›» mellom hvert steg. Snutten har nå content: "›" / "" bak samme @supports som stilarket, og siden sier hvorfor. En test kjører snutten fra siden mot stilarket.
  • Oppskriften for å bytte pila i fs-accordion lot rotasjonen stå. Siden sa at content og border var nok, men pseudoelementet har fast størrelse og transform, og vinkelen byttes når panelet er åpent, så et «+» sto på skrå og ble et «×» i åpen tilstand. Et synlig tegn uten alternativ tekst ble dessuten en del av navnet på knappen, det skjermleseren sier når <summary> får fokus. Snutten nullstiller nå begge og gir tegnet tom alternativ tekst.
  • JSDoc-eksempelet for alert spredte alert.title. alert.title er klassenavnet, en streng, og <p {...alert.title}> gir tegnene som attributter og ikke klassen. Eksempelet skriver class={alert.title}, som accordion og card.
  • Fieldset-siden sa at en <legend> lenger ned ikke kobles til gruppen. Nettleserne bruker den første <legend>-en som navn og tegnet ledetekst uansett hvor den står. Regelen om at den skal stå først gjelder fortsatt, men begrunnelsen er at HTML krever det, og at markupen ellers er ugyldig.
  • Select-siden sa at listen tegnes i siden fra Chromium 148. Det eneste stilarket spør om, er @supports (appearance: base-select), og den slår til i eldre Chromium også; 148 er versjonen Fristil er testet i. Tabellen sier nå hva som avgjør, og hvilke versjoner som er testet.
  • Skip Link-siden begrunnet tabindex="-1" med at neste Tab fortsatte i menyen. Nettleseren fortsetter fra målet etter hoppet også uten attributtet. Det som mangler uten det, er fokuset og skjermleserens opplesning, og det sier siden og mønstersiden «Sideskjelett» nå. Error Summary-siden og JSDoc-en til fs-error-summary sa det samme om en vanlig ankerlenke, at neste tastetrykk fortsatte i oppsummeringen, og sier nå at nettleseren bare gir fokus til et mål som kan få det. Siden og stilarket sa også at fokus satt fra kode aldri regnes som synlig fokus. Det gjelder bare etter et museklikk, og det er det tilfellet :focus er valgt for.
  • Lagringseksempelet på Switch-siden rullet ikke tilbake ved et feilsvar, og tok fokuset fra bryteren. fetch avviser bare ved nettverksfeil, så et 500-svar ble meldt som «Varsler er på.» med bryteren stående i ny stilling. Eksemplet slo dessuten av bryteren mens den lagret, og disabled tar fokuset fra en bryter som har det uten å gi det tilbake, så et nytt mellomromstrykk gjorde ingenting. «Ren HTML» og React sjekker nå svar.ok og sperrer et nytt trykk uten disabled, Datastar-fanen og eksemplet med fs.setAttributes slår ikke lenger av bryteren, og JSDoc-en til disabled i switchControl advarer mot bruken. Det korte eksemplet på Connection Status-siden og i JSDoc-en til FsConnectionStatus sjekker også svar.ok. En test kjører «Ren HTML»-eksemplet fra siden mot en server som svarer 500.
  • fs-list beholder punktene og tallene sammen med Tailwinds Preflight. Standardvarianten sa ingenting om list-style og lente seg på nettleseren, mens Preflight setter list-style: none på hver <ul> og <ol>. Med oppsettet Tailwind-siden anbefaler, @import "tailwindcss" og base før fristil, mistet lista punktene og nummereringen, og det gjorde lista i fs-error-summary også. Klassen setter nå list-style-type: revert, som går tilbake til nettleserens eget stilark, så listene ser ut som uten Tailwind. Variantene plain og divided er fortsatt uten punkter. Vil du ha et annet punkt på alle .fs-list, sett det selv, i CSS uten lag eller i et lag etter fristil: en list-style på ul i et lag foran, som base, når ikke lenger fram til klassen. Testen bygger oppsettet med Tailwinds egen preflight.css, ikke en håndskrevet utgave med bare knappereglene.
  • Lagrekkefølgen for Tailwind tar med fristil-tema. Et generert tema erklærer @layer fristil, fristil-tema;, og en @layer-setning flytter ikke lag som alt er erklært. Med den anbefalte linja @layer theme, base, fristil, components, utilities; havnet temaet derfor sist, etter utilities, og slo Tailwind-klassene og @layer base på de samme elementene, enda Tilpasning- og Eget tema-sidene sa at din egen CSS fortsatt vinner. Linja i dokumentasjonen og i kommentaren i tailwind.css er nå @layer theme, base, fristil, fristil-tema, components, utilities;, og Eget tema-siden sier at laget må stå der når Tailwind brukes, og at linja må stå i <head> når temaet lastes som en <link> før den bundlede CSS-en, for da har temaet alt erklært lagene sine. Temaet fristil tema skriver, er uendret.
  • Tailwind-siden og Design tokens-siden sa at skalaen følger med når du setter --fs-spacing-1. p-4 er calc(var(--fs-spacing-1) * 4), men --fs-spacing-4 og de andre trinnene er egne verdier, så eksempelet --fs-spacing-1: 0.3rem ga p-4 1,2rem mens komponentene sto på 1rem. Sidene og kommentaren i tailwind.css sier nå at trinnene må skrives om hver for seg, og eksempelet gjør det. Tabellen over skalaen oppgir også px som Tailwind-motstykket til --fs-spacing-px, der det sto «ingen».

0.33.0 (2026-10-12)

Brytende

  • Kjernen bærer ingen data. fristil-kjerne.wasm hadde manifestet og fargekontrakten bygget inn. Nå gir verten dem. For diagnoseMarkup, diagnosePage, buildTheme og fristil fra npm er ingenting endret: pakken gir kjernen sitt eget manifest og sin egen fargekontrakt. Den som laster kjernen selv, merker det:

    • loadCore() gir en kjerne uten noe. Kall loadManifest() før sjekken og loadContract() før temaet, ellers kaster de med en forklaring. resetManifest() er fjernet, siden det ikke finnes noe innebygd å gå tilbake til. version().manifest er null før et manifest er gitt.
    • En vert i et annet språk kaller load_manifest_raw og den nye load_contract_raw først. diagnose_markup_raw, diagnose_page_raw og inspect_theme_raw svarer nå 0, eller 1 med {"error": …}, som build_theme_raw og diagnose_styled_raw alt gjorde. reset_manifest er fjernet.
    • Kommandolinja leser manifestet, fargekontrakten, regelbøkene og komponentene fra @fristil/designsystem: fra mappa FRISTIL_PACKAGE peker på, eller fra node_modules i arbeidsmappa eller over den. npm-pakken og fristil.jar setter variabelen selv. fristil.wasm i wasmtime og den kjørbare fila fra cargo install trenger den, eller pakken installert, og stopper med en forklaring uten. fristil agent og fristil overta svarer dermed for versjonen prosjektet har installert.

    Modulen er 93 kB mindre.

  • En temagrense har nå en flate. Et element med data-theme="light" eller data-theme="dark" får temaets bakgrunn og tekstfarge. Før fikk det mørke tokens, men beholdt sidens hvite bakgrunn, så brødteksten i en mørk seksjon på en lys side ble 1,23:1. Regelen har spesifisitet null i @layer fristil, så en komponent med data-theme på seg, og din egen stil uten lag eller i et senere lag, beholder sin egen bakgrunn. Stil i et lag foran fristil taper. <html> og fs-toast får ingenting. En innebygd komponent med data-theme på rotelementet står nå på temaets flate i stedet for å være gjennomsiktig over verten.

  • --fs-focus-ring, --fs-color-disabled-surface og --fs-color-disabled-text følger en temagrense. De sto bare på :root, der en var() regnes ut én gang for hele siden, så en mørk seksjon på en lys side fikk den lyse fokusringen, 2,4:1 mot flaten. De deklareres nå i hver temablokk. Har du overstyrt en av dem på :root, gjelder ikke overstyringen lenger inne i et data-theme. Skriv den også på [data-theme="light"] og [data-theme="dark"].

  • fristil sjekk-tema kontrollerer en blokk som ikke er bundet til lyst tema, som en bar :root, i mørkt tema også. Fristils mørke blokk overstyrer bare tokenene den selv har, så en verdi som ingen mørk blokk tar, står også når systemet er i mørkt. Sjekken så den bare som lys: --fs-color-neutral-text: #0d4e8c på :root ga «holder hvert løfte», men har 2,22:1 i mørkt. Funnene merkes «i mørkt tema, ingen mørk blokk overstyrer». En blokk med color-scheme: light, en selektor med light som eget ord og @media print gjelder bare lyst og kontrolleres som før. Et tema som før var grønt kan nå gi funn, og da var det brutt i mørkt hele tiden.

  • fristil sjekk melder nye funn.

    • Et flagg med en tekstmal i verdien, som data-optional="{{ .Optional }}" eller open="<%= open %>". Malen skriver verdien som tekst, og et boolsk attributt er på uansett verdi, også «false» og en tom streng. Regelen er boolsk-med-verdi. Astro, Svelte, JSX, Razor og JTE tar selv bort et usant boolsk attributt, og meldes ikke. Står attributtet alt i en betingelse i malen, som {{ if .Open }} open="{{ .Open }}" {{ end }}, meldes det heller ikke.
    • En fs-link eller fs-button med både aria-disabled="true" og href. Stilen slår av musa, men Enter følger lenken. Regelen er ny: deaktivert-med-href.
  • fs.link({ disabled: true }) gir role="link" og tabindex="0". En deaktivert lenke skal skrives uten href, ellers følger Enter den, og uten href er en <a> ingen lenke for skjermleseren og står ikke i tabrekkefølgen. De to gir begge tilbake, og disabled: false fjerner dem. fs.link() forvalter dermed role og tabindex: setter du en av dem selv på en lenke, som role="menuitem" i en meny, fjerner fs.setAttributes(lenke, fs.link()) den. Sett den etter. Komponentsidene for Link og Button viste en deaktivert lenke med href, og det gjorde også rådet i byggefunksjonen. Har du fulgt det, ta bort href mens lenken er av.

  • hidden skjuler nå alle komponentene. Forfatterstil slår nettleserens [hidden] { display: none }, så <div class="fs-alert" hidden> og 41 andre klasser, åtte av elementene og barn uten klasse, som option i en stylet fs-select, et ledd i fs-breadcrumbs og en rad i fillista, sto synlige. En regel i @layer fristil med !important skjuler dem nå, slik nettleseren selv gjør. Den kan ikke overstyres med en regel uten !important, og heller ikke med !important uten lag. hidden="until-found" får stå.

  • .fs-tooltip__bubble er display: none når den er skjult, ikke visibility: hidden. En skjult boble tok plass og ga sidelengs rulling ved høyre kant. Teksten er fortsatt knappens beskrivelse gjennom aria-describedby.

Lagt til

  • .fs-icon og fs.icon(), for et ikon fra appens eget sett. Klassen går på en <svg> og gjør den like stor som teksten rundt. fs.icon() gir aria-hidden="true", og fs.icon({ label }) gir role="img" og aria-label for et ikon som betyr noe alene. Fristil sender ingen ikoner. Stilarket er @fristil/designsystem/icon.css.

  • Regelen ikon-uten-navn. En <button> eller <a href> med bare et ikon i seg og ikke noe navn er en feil i fristil sjekk, i editorene og i diagnoseMarkup. Navnet kan komme fra tekst, også med fs-sr-only, fra aria-label, aria-labelledby eller title, eller fra alt på et bilde. Det som er skjult med aria-hidden="true" eller hidden, teller ikke. Står det noe på knappen eller et barn som ikke er vanlig HTML, som :aria-label, v-bind, x-text eller {...props}, kan navnet komme derfra, og regelen tier.

  • Typen Manifest fra @fristil/designsystem/diagnostics, formen på manifest.json slik skjemaet beskriver den, for den som leser manifestet selv. Kotlin-pakken har det samme som Fristil.manifest, og leser et prosjekts eget manifest med Manifest.parse: elementene med attributter, deler og hendelser, og klassene med attributtene og de lovlige verdiene.

  • Delene og beskrivelsene står i manifestet. Hvert element har nå description og parts: delene web-komponenten finner i markupen, med selektoren den leter med, hva delen skal inneholde, og om den kan stå flere ganger eller mangle. <fs-field> har ledetekst, kontroll, hjelpetekst og feilmelding, og <fs-dialog> har dialogen, tittelen og kroppen. Hvert attributt på et element og hvert valg i en byggefunksjon har description, og et attributt på en klasse har beskrivelsen til valget med samme navn. Et verktøy eller et annet språk kan dermed lese hva hvert element leter etter, uten å lese kildekoden. Feltene er nye og valgfrie i skjemaet, så schemaVersion er fortsatt 1. Fragmentet fra fristil overta gir klassene nye navn også i selektorene og beskrivelsene, så kopien beskrives med sine egne klasser. Selektorene eksporteres også, fra hovedinngangen og fra inngangen til hver av de sju web-komponentene som finner deler, som FS_FIELD_PARTS fra @fristil/designsystem/field.

  • @fristil/designsystem/fargekontrakt.json i exports, så en side som laster kjernen i nettleseren kan gi den fargekontrakten med loadContract(), som den gir manifestet fra ./manifest.json.

  • Hendelsene har typer i addEventListener. HTMLElementEventMap og DocumentEventMap utvides med hver hendelse web-komponentene sender, med detail slik komponenten sender den: dialog-toggle gir event.detail.open som boolean og returnValue som string. Typene følger med hver inngang til web-komponentene, /register, /react og /react-jsx medregnet, så as CustomEvent<…> trengs ikke lenger. Typen står også som FsEventMap i hovedinngangen. Den genereres fra den samme metadataen som manifestet og web-types.json, og har ingen avhengighet av React.

  • "./package.json" i exports. Et verktøy som leser pakkens versjon med require.resolve("@fristil/designsystem/package.json") eller import … with { type: "json" }, fikk ERR_PACKAGE_PATH_NOT_EXPORTED. Nå finner de den.

  • --fs-progress-border styrer kanten rundt sporet i fs-progress.

  • fs.popover() skriver type="button" på knappen. En knapp uten type sender skjemaet den står i.

  • session-activity og activity-interval på fs-session-timeout. Klokka nullstilles av aktivitet i nettleseren, men serverøkten visste ikke om den. En bruker som skrev i et langt skjema uten et eneste kall til serveren, fikk aldri varselet, og innsendingen feilet fordi serverøkten var ute. Hendelsen kommer i starten av hvert intervall med aktivitet og på slutten hvis brukeren har gjort noe siden, høyst én gang per activity-interval sekunder (standard 60, minst 1), og aldri mens varselet står eller etter at økten er ute. fs.sessionTimeout() tar activityInterval. Komponentsiden viser også hvordan flere faner holder klokkene i takt.

  • --fs-accordion-padding-inline gir overskriften og innholdet i fs-accordion samme luft til sidene. Før gjaldt --fs-accordion-padding bare overskriften.

  • Hendelsene står i manifestet, i web-types.json og i regelbøkene. Elementene som sender hendelser, har nå events i manifestet, med feltene i detail og typen deres, som dialog-toggle med open og returnValue. web-types.json har dem som js.events, og regelbøkene har en kolonne for dem. Før sto de bare på komponentsidene, og noen ikke der heller.

  • manifest i Fristil.diagnoseMarkup og Fristil.diagnosePage i Kotlin. Teksten i et manifest.json, som det fristilManifest skriver, gir sjekk mot komponentene prosjektet har overtatt. Uten valget gjelder manifestet i pakken, som før. Et manifest kjernen ikke kan lese, kaster IllegalArgumentException med grunnen.

Endret

  • Pil opp og ned bytter ikke lenger fane i en vannrett fs-tabs. De stjal siderullingen og avvek fra WAI-ARIA. De virker når raden har aria-orientation="vertical".

Rettet

  • fs.setAttributes virker på en <svg>. Klassen ble satt med className, som er skrivebeskyttet på et svg-element, og kallet kastet «Cannot set property className». Den settes nå med setAttribute.
  • <fs-field> kobler bare sine egne deler. Sto et felt inni et annet, som et søkefelt i et sprettoppvindu inni feltet, fant det ytre feltet det innerste feltets kontroll først og skrev for, id og aria-describedby på den. Nå ser hvert felt bare kontrollen, ledeteksten, hjelpeteksten og feilmeldingen som hører til det selv, slik de andre komponentene alt avgrenset delene sine. Et felt med mer enn én kontroll gir en advarsel i konsollen, siden bare den første får koblingen.
  • En overskrift i kroppen til <fs-dialog> blir ikke lenger tittel. Uten aria-labelledby og aria-label tok komponenten den første overskriften i dialogen, også en som sto i fs-dialog__body, og ga den klassen fs-dialog__title og stilen som følger med. Nå er tittelen den første overskriften utenfor kroppen, og en dialog som står uten navn, gir en advarsel i konsollen i stedet for å låne innholdets overskrift.
  • En ubestemt fs-progress var en tom ramme i høykontrastmodus. Stripen som sveiper over, er en gradient, og modusen fjerner gradienter. Den tegnes nå i Highlight på Canvas med forced-color-adjust: none, så brukerens systemfarger fortsatt gjelder.
  • Kotlin skriver tall i attributter slik JavaScript gjør. jsNumber ga 1000000000000000000000 for 1e+21, 0.0000001 for 1e-7 og den eksakte binærverdien for heltall over 2^53, som 123456789012345683968 for 123456789012345680000, og byggefunksjonene i Kotlin kunne dermed skrive et annet attributt enn fs i TypeScript. Nå brukes de færreste sifrene som gir det samme tallet tilbake, og JavaScripts regler for når eksponent brukes. Etterprøvd mot JavaScript på 224 614 tall uten avvik, hver toerpotens medregnet.
  • fristilManifest skriver bare build/fristil/manifest.json når manifests har fragmenter, og sletter fila når det siste fragmentet er fjernet. Språkserveren foretrekker den fila framfor pakken i node_modules, så en kopi uten fragmenter ble stående med versjonen fra da den ble skrevet, også etter at pakken var oppgradert.
  • Fokusringen på det valgte alternativet i fs-toggle-group var 1,23:1. Ringen står oppå den valgte flaten, og har nå accent-content som farge der.
  • Sporet i fs-progress var 1,12:1 mot siden. Stolpen har nå en kant i neutral-border, så det synes hvor langt den går. Kanten er tegnet innover og tar ikke av høyden.
  • fs-dialog meldte forrige knapps verdi når dialogen ble lukket uten knapp. Escape og close() uten argument lar returnValue stå fra forrige lukking. En dialog serveren åpnet på nytt og så lukket, sendte derfor dialog-toggle med for eksempel returnValue: "slett" fra gangen før, og en app som skiller «Avbryt» fra «Slett» på verdien kunne slette noe brukeren aldri ba om. Komponenten tømmer nå verdien hver gang den åpner dialogen. Skriver noe open på verten etter at brukeren har trykket en knapp, men før close er kommet, åpner komponenten heller ikke dialogen igjen. Før gjorde den det, og verdien ble borte for en lytter på close.
  • fs-dialog åpnet seg ikke igjen etter at verten var flyttet. En morfer som flytter verten kobler den fra og til, og komponenten tok den gamle returnValue for en lukking brukeren gjorde før skriptet kom, og fjernet open serveren nettopp hadde satt. Den sjekken gjøres nå bare første gang en dialog sees, også når den flyttes inn i en ny vert.
  • fs-dialog meldte open: true for en dialog som var åpen hele tiden. En morfer som flytter verten eller dialogen, tar dialogen ut av topplaget, og komponenten åpner den igjen. dialog-toggle kommer nå bare når tilstanden faktisk endrer seg.
  • data-color="neutral" på fs-dialog ga mer luft enn ingen farge. Verdien er standardverdien og skal se ut som ingen verdi, men reglene for luften traff bare en dialog uten attributtet. Nå gjelder de alt som ikke er en av de fem fargene, også en tom verdi fra en mal.
  • Knappen i fs-popover sendte skjemaet den sto i. Komponenten setter nå type="button" på en <button> uten type, eller med en tom eller ukjent verdi, også etter en patch som river det bort. En knapp som sier type="submit" selv, får stå. Eksemplene på komponentsiden og snippeten i editoren har det med.
  • Escape i fs-popover tok for mye.
    • Et trykk i et annet felt flyttet fokus fra feltet til knappen. Nå lukkes vinduet, og fokus blir stående.
    • Et trykk i en modal dialog åpnet oppå vinduet lukket vinduet også. Nå lukker det bare dialogen.
    • Et trykk noe annet alt hadde brukt, som en forslagsliste i vinduet som lukket seg, lukket vinduet også. Nå står vinduet.
    • Med to vinduer inni hverandre lukkes nå det innerste først.
  • fristil sjekk --css fulgte ikke @import fra en pakke. @import "@fristil/designsystem/fristil.css" er et navn i pakkens exports, og fila ligger i dist/. Sjekken lette etter den som en fil i node_modules under arbeidsmappa, fant ingenting og meldte hver klasse som ustylet. Nå slår den opp i exports, også mønstre som ./* og en pakke importert ved navn alene, i nærmeste node_modules over stilarket. En relativ sti slås aldri opp som en pakke.
  • fristil sjekk-tema leste dark inne i :not(). .kort:not([data-theme="dark"]) ble kontrollert som et mørkt tema. :root:not([data-theme="dark"]) gjelder også i systemets mørke modus, og kontrolleres nå mot begge temaer.
  • fristil sjekk meldte Bootstraps fs-1 til fs-6 som ukjente klasser. En klasse som er fs- og bare sifre, er ikke Fristils.
  • fristil sjekk kjente ikke writingsuggestions, autocorrect og headingoffset. De er globale attributter i HTML.
  • fs-suggestion nådde ikke et felt React styrer. Valget ble satt rett på noden, React så ingen endring, onChange kom aldri, og ved neste rendring skrev React den gamle teksten tilbake. Komponenten bruker nå setteren fra elementets prototype.
  • fs-session-timeout kunne hoppe over en opplesning. Et tikk som kom sent, kunne gå fra 61 til 59 sekunder, og «1 minutt» ble aldri lest opp. Opplesningen kommer nå når en terskel krysses.
  • fs-toast mistet fokus. Ble den siste meldingen lukket, eller ble alle fjernet med clear(), mens en melding hadde fokus, falt fokus til body. Det går nå tilbake dit det kom fra. label skrev også over en aria-label forfatteren hadde satt, og når label ble fjernet, sto det gamle navnet igjen.
  • fs-tabs og fs-error-summary holdt fast faner og lenker en morfing hadde byttet ut. Begge har nå én lytter på verten.
  • Dokumentasjonen lovet for mye. «Kom i gang» og «Rammeverk» sa at markupen i en ramme-komponent vises uansett, men Tabs og Suggestion skjuler innhold til de er registrert. Datastar-oppskriften for Error Summary flyttet ikke fokus ved andre feilede innsending, og skjema-mønsteret skrev oppsummeringen med role="alert" på nytt ved hvert tastetrykk, så skjermleseren ble avbrutt. Toast og Connection Status sier nå at de er inerte mens en modal dialog står åpen.
  • Avslått vant ikke over en tilstand. Et ugyldig eller vellykket felt som var slått av, så rødt eller grønt ut, ikke avslått. Det gjaldt input, select, textarea, filopplasting, avkryssingsboks og radioknapp.
  • Gjeldende side i fs-pagination ble uleselig under musa i høykontrast. Hover-regelen slo høykontrastregelen, og bakgrunn og tekst fikk samme farge.
  • Skilletegnet i fs-breadcrumbs ble lest opp. Generert innhold leses opp, så skjermleseren sa «skråstrek» mellom hvert steg. Det har nå en tom alternativ tekst der nettleseren støtter det.
  • Boblen i fs-tooltip forsvant før musa nådde den. Gapet mellom knappen og boblen har nå en usynlig bro i knappens bredde mens boblen vises. Flytter du boblen under knappen, må broen også flyttes; snutten på komponentsiden har det med. Komponentsiden sier at boblen ikke kan lukkes med Escape, som WCAG 1.4.13 krever, og når Popover passer bedre.
  • Pila i fs-accordion pekte sidelengs i høyre-til-venstre, og pila i fs-select sto 6 piksler fra kanten der i stedet for 12.
  • En overskrift i summary i fs-accordion fikk nettleserens marg og størrelse. Dokumentasjonen viser <summary><h3>, og raden ble høyere og teksten større enn uten overskrift.
  • Flaggene på klassene manglet i editoren og i regelbøkene. data-interactive på fs-card, data-selectable på fs-tag, data-hoverable på fs-table og data-optional på fs-label og fs-legend sto i manifestet, men ikke i forklaringen i VS Code eller i tabellene en kodeagent leser. Klasseattributtene til web components, som data-color på .fs-dialog, sto heller ikke i regelbøkene.
  • fristil sjekk ga feil på <fs-error-summary data-autofocus="true">. Komponenten flytter fokus for alt annet enn "false", og "true" er nå lovlig.
  • Editoren sa at required-marker="text" skriver «må fylles ut». Den skriver «(påkrevd)».
  • Regelbøkene kalte data-variant="outline" oppfunnet. Det er standardvarianten til fs-card og fs-tag. Eksempelet er nå outlined. fs-theme-control sto ikke i lista som kalles «hele lista».
  • Språkserveren fant bare pakken ved rota av arbeidsområdet. I et monorepo ble en side i apps/web sjekket mot det innebygde manifestet, enda apps/web/node_modules hadde pakken. Den leter nå fra mappa til fila og oppover, men ikke over rota av arbeidsområdet.
  • Dokumentasjonen sa at feilmeldingene bare finnes i VS Code. Pluginen for IntelliJ har dem, i filer og i HTML i en merket streng, og regelbøkene nevner nå den og Fristil for Kotlin. Kotlin-siden sa at hver tråd får sin egen instans av kjernen. Det er en pool.

0.32.1 (2026-10-09)

Rettet

  • fs-divider var usynlig i flex og grid. Nettleseren gir <hr> auto-marger til sidene, og i en flex-kolonne vinner de over align-items: stretch. Streken har ikke noe innhold å være bred av, så den ble 0 piksler bred og forsvant. Det gjaldt også eksempelet på komponentsiden og demosiden for sideskjelettet. Klassen nullstiller nå margene til sidene og strekker seg selv, så streken fyller bredden også i en flex- eller grid-forelder som sentrerer barna eller legger dem til start. Har du gjort streken smalere selv, står den nå til start; sett margin-inline: auto for å sentrere den.

Endret

  • fs-divider med data-variant="subtle" er litt tydeligere. Den brukte --fs-color-neutral-surface, som lå så nær lerretet at streken nesten forsvant. Nå bruker den --fs-color-neutral-raised, som ligger mellom flatefargen og border-subtle i både lyst og mørkt tema.

0.32.0 (2026-10-08)

Brytende

  • Sjekken er Rust-kjernen. diagnoseMarkup og diagnosePage i @fristil/designsystem/diagnostics kjører nå den samme WebAssembly-modulen som VS Code, IntelliJ og Fristil for Kotlin, i stedet for en egen utgave i TypeScript. Svarene er de samme: begge utgavene ble kjørt side om side på over 4000 sider uten ett avvik før TypeScript-utgaven ble slettet. Modulen følger pakken og lastes første gang en av funksjonene kalles, i Node, Bun og Deno.

    Disse forsvinner fra @fristil/designsystem/diagnostics:

    BorteI stedet
    diagnose(text, elements, classes)diagnoseMarkup(text). Et eget ordforråd gis som manifest med loadCore(modul).loadManifest(json).
    checkReferences(text)diagnosePage(text), som gir koblingsfunnene sammen med resten. Regelnavnet i rule skiller dem.
    pageSource(text)Ingen. diagnosePage leser siden slik den gjorde.
    elements, classes@fristil/designsystem/manifest.json, med elementene, klassene og de lovlige verdiene.
    closest, tagEnd, withoutHiddenIngen. De var hjelpere for sjekken.
    typene Attribute, Element, Elements, ClassAttribute, ClassInfo, ClassesSkjemaet i @fristil/designsystem/manifest.json (manifest.schema.json).

    diagnosePage(text, elementer, klasser) tar ikke lenger egne lister.

    Ingenting endres i fs, i komponentene eller i CSS-en.

  • Temaet er Rust-kjernen. buildTheme i @fristil/designsystem/tema og inspectTheme og checkTheme i @fristil/designsystem/tema-sjekk kjører den samme WebAssembly-modulen som sjekken. Svarene er de samme: de to utgavene ble kjørt side om side på over 6000 oppskrifter og temaer, med ugyldige farger, farlige verdier og ødelagt CSS, uten ett avvik, heller ikke i kontrastforholdene. buildTheme laster modulen fra pakken i Node, Bun og Deno. I nettleseren lastes den med loadCore fra @fristil/designsystem/diagnostics, og modulen er @fristil/designsystem/kjerne.wasm.

    Fargekontrakten er data, i src/tokens/fargekontrakt.json, og regningen finnes bare i kjernen. Disse forsvinner:

    BorteI stedet
    buildMatrix(merker, utseende) fra matrisebuildTheme(merker), som gir light, dark og violations. Fristils egne farger er lightCells og darkCells i matrise.
    buildFamily, promisesFor, checkPromises og typen Violation fra kontraktbuildTheme for et tema, inspectTheme for å kontrollere ett. Violation står i tema.
    parseBlocks og typen ParsedBlock fra tema-sjekkinspectTheme, som leser blokkene.
    typene Matrix og RoleCss fra matriseMatrixToken gir hvert navn.

    ROLES, NEUTRAL_LAYERS, REQUIREMENT, FAMILIES, FRISTIL_BRANDS, roleToCss og tokenName er der som før, og @fristil/designsystem/farge er uendret.

  • Pakken krever Node 20.16 eller nyere (engines i package.json), eller Bun eller Deno. Kjernen lastes med process.getBuiltinModule, som kom i Node 20.16 og 22.3. En eldre Node får beskjed om at versjonen er for gammel. Node 18 og 20 har ikke lenger støtte fra Node-prosjektet.

  • fristil overta gir kopien nytt navn. fs-button blir app-button i klassene, taggene, customElements.define og selektorene, så kopien er din og kan stå ved siden av originalen. Variablene fra temaet, --fs-*, beholder navnet. Bytt til det nye navnet i markupen der kopien skal brukes. Kopier du har tatt fra før, røres ikke.

Nytt

  • Kommandolinja uten Node. Den samme kommandolinja kjører som java -jar fristil.jar (Java 17 eller nyere), i wasmtime som fristil.wasm, og som en kjørbar fil med cargo install --git https://github.com/MarkusAugust/fristil fristil. Jar-en og modulen ligger ved hver utgivelse på GitHub, med attestasjon av hvilken commit de ble bygd av, og svarer det samme som npx, byte for byte.

  • fristil overta skriver et fragment av manifestet ved siden av kopien, fristil-manifest.json, og fristil sjekk --manifest=<fil> sjekker markupen for kopien mot det. Bare kopiens egne navn sjekkes, ikke resten av prosjektets klasser.

  • Sjekken ser stilarkene. fristil sjekk --css=<fil>, og css som valg til diagnoseMarkup og diagnosePage, sier fra om en klasse ingen av stilarkene styler (ustylet-klasse) og om en verdi uten regel (ustylet-verdi). Stilarkene leses av en CSS-leser i kjernen, med nesting, :is(), :where(), @layer og @import. Pakkens egen kontroll av at CSS-en og manifestet sier det samme, bruker den også.

  • diagnoseRendered(page) sjekker siden slik nettleseren har rendret den, med Playwright: DOM-en etter at web-komponentene og skriptene har kjørt, og stilarkene den faktisk lastet, også fra et CDN.

  • fristil lsp, språkserveren. Funnene og hurtigrettelsene i hver editor som snakker LSP: Neovim, Helix, VS Code og andre, med UTF-16-posisjoner. Den sjekker mot manifestet i build/fristil/ eller i node_modules, med det innebygde som reserve, og leser det på nytt når det endres. Den startes med npx, java -jar, wasmtime eller den kjørbare fila.

  • fristil manifest skriver manifestet sjekken bruker, med fragmentene lagt til, for en editor eller et annet verktøy.

  • Gradle-pluginen io.github.markusaugust.fristil, med fristilSjekk (kjøres av check), fristilTema og fristilManifest, som skriver build/fristil/manifest.json. Den kjører den samme kommandolinja i Gradle-prosessen.

  • Kommandolinja er skrevet i Rust. npx @fristil/designsystem kjører den som en WASI-modul med Node sin egen node:wasi, og den samme koden kan bygges som en kjørbar fil. Kommandoene, flaggene, meldingene og feilkodene er de samme: alle 130 kjøringene i pakkens egen test av kommandolinja svarer likt, byte for byte. Én melding er ny: er en oppskrift til fristil tema ikke gyldig JSON, er forklaringen kjernens egen og ikke Nodes.

  • Funnene har regelnavn, linje og kolonne. rule er navnet på regelen, som ukjent-klasse, og line og column er der funnet begynner, fra 1.

  • Et funn kan undertrykkes med en kommentar over taggen: <!-- fristil-ignore-next ukjent-klasse -->. Uten regelnavn gjelder den alle reglene for den neste taggen. Det virker i fristil sjekk, i editorene og i testene.

  • loadCore(modul) i @fristil/designsystem/diagnostics laster kjernen fra en modul du har selv, som i nettleseren eller i en utvidelse som pakker den med seg. Den gir også loadManifest(json), resetManifest() og version().

  • @fristil/designsystem/manifest.json beskriver hele ordforrådet og hver byggefunksjon som data, med skjema. Det er det Fristil for Kotlin genereres fra, og det kjernen sjekker mot.

  • data-color på fs-dialog sjekkes. Sjekken kjente ikke fargene fs.dialog({ color }) gir, fordi ordlista bare leste byggefunksjoner som svarer med ett sett. data-color="red" sa derfor ingenting. Nå meldes en ukjent farge, med de fem lovlige og neutral som standard.

  • Flaggene på klassene står i manifestet: data-optional på fs-label og fs-legend, data-interactive på fs-card, data-selectable på fs-tag og data-hoverable på fs-table. Sjekken sier fra om data-optional="false", som slår flagget på, slik den gjør for boolske attributter på elementene.

  • Kommentarer i malspråkene hoppes over. Markup i {{-- --}} i Blade, @* *@ i Razor, {{/* */}} i Go, {# #} i Jinja, Twig og Nunjucks, og <%-- --%> i JSP ble sjekket som om den ble rendret, og en tagg i en kommentar ga funn.

  • fristil sjekk-tema og inspectTheme leser CSS med en ekte tokenizer, den fra Servo som Firefox bruker. En klamme eller et semikolon i en streng, i url(…) eller i en kommentar delte en blokk eller en deklarasjon, og en farge kunne forsvinne fra kontrollen uten et ord. En blokk som ikke er lukket, leses nå til fila slutter, slik nettleseren gjør, i stedet for å hoppes over, og meldingen om klammene står fortsatt.

  • En stor side med mange funn sjekkes raskere. Linja og kolonnen til hvert funn ble regnet fra starten av teksten. 6000 funn på en side med 3000 linjer tok en kvart sekund, og tar nå 25 millisekunder.

  • Hver side i dokumentasjonen finnes som Markdown. /components/button/ har sin på /components/button.md, og hver side lenker til sin med <link rel="alternate" type="text/markdown">. Lenkene i /llms.txt går dit. Filene lages av de bygde sidene, så «Ren HTML»-fanen med <link>-ene, importlinjene og versjonsnummeret står der slik siden viser dem, og fanene står etter hverandre.

0.31.0 (2026-10-06)

Nytt

  • fristil sjekk sjekker hele sider. En adresse, som fristil sjekk http://localhost:8080/skjema, hentes og sjekkes som en hel side, og --rendret gjør det samme for filer og standard inn. På en hel side kreves det i tillegg at hver id i for, aria-describedby, aria-labelledby og aria-controls finnes, at for peker på et felt, at ingen id står to ganger, at hvert felt med en fs--klasse har en ledetekst, at hver synlige hjelpetekst og feilmelding som ikke er et statusfelt er koblet, og at hver lenke i <fs-error-summary> går til en id som finnes. En adresse som svarer med noe annet enn HTML, gir feilkode. Bar markup inni <fs-field> gir ingen funn. En mal sjekkes som før uten flagget, siden en id der kan stå i en annen fil.
  • diagnosePage(html) og checkReferences(html) i @fristil/designsystem/diagnostics gir den samme sjekken til tester skrevet i JavaScript. pageSource(html) gir siden slik sjekken leser den, uten kommentarer, innholdet i <script>, <style>, <textarea> og <template>, og markup i attributtverdier, med posisjonene uendret.

0.30.0 (2026-10-05)

Nytt

  • Kolonner som kan sorteres i .fs-table. .fs-table__sort er knappen i overskriften, og aria-sort på <th> er tilstanden. Pila tegnes fra aria-sort, med begge pilene dempet når kolonnen ikke er sortert, og knappen fyller cellen, så overskriftsraden har samme høyde og innrykk som uten knapp. fs.tableSort({ direction }) gir header og button, og tableSortDirections er de lovlige retningene. Sorteringen er appens.

0.29.0 (2026-10-05)

Brytende

  • <fs-session-timeout> lager ikke lenger dialogen selv. Den som rendrer skriver dialogen og teksten inne i elementet, på sitt eget språk, og komponenten tar tiden: den åpner og lukker dialogen, fyller inn tallet og leser opp nedtellingen. Før var tittelen, teksten og knappene norsk skrevet rett i koden, og kunne ikke oversettes. Komponenten finner delene på <dialog>, .fs-session-timeout__count, [role=status] og knappene i et <form method="dialog">, og setter open tilbake når en morfing tar det fra en dialog som står i topplaget, slik <fs-dialog> gjør. Uten det sto siden fast etter en patch fra Datastar uten data-ignore-morph. En knapp med value="logout" logger ut, og alt annet, Escape medregnet, forlenger. Hendelsene, warn-at, expires-at, extend() og reset() er de samme, men session-logout og session-extend fra en knapp kommer nå med close-hendelsen, altså litt etter klikket og ikke i det. Det du må gjøre: skriv dialogen inn i elementet, slik komponentsiden viser. Et tomt <fs-session-timeout> sier fra i konsollen i det varselet skulle kommet, og fristil sjekk melder det.

  • fs.sessionTimeout() gir ett attributtsett per del, som fs.dialog(): host, dialog, title, text, count, live, actions, extend og logout. titleId er påkrevd, siden en id som lages av seg selv ikke overlever hydrering.

  • Komponenten ligger i ramme/ og ikke i frittstaende/. Navnene i exports er de samme, men stien til fila er ny. Det du må gjøre: lenker du rett til fila på en CDN, bytt frittstaende/session-timeout med ramme/session-timeout i adressen.

  • Skrifttokenene heter --fs-font-size-* og --fs-font-weight-*. De fjorten navnene var de eneste uten fs-, og de står på :root i en side Fristil ikke alltid eier. Et annet designsystem på samme side deklarerte de samme navnene, og da avgjorde rekkefølgen på stilarkene hvilken verdi begge systemenes komponenter fikk. Ved 0.22.0 fikk de beholde navnene, siden de alt fulgte CSS-egenskapen de setter; det holder ikke når navnet deles med andre. Det gjelder de ni størrelsene, de fire vektene og --font-family-base fra temageneratoren, som nå heter --fs-font-family-base. Tailwind-klassene, som text-fs-m, heter det samme. Det finnes ingen aliaser, siden et alias fortsatt måtte stått på :root under det gamle navnet. Det du må gjøre: bytt --font-size- med --fs-font-size-, --font-weight- med --fs-font-weight- og --font-family-base med --fs-font-family-base i egen CSS, og lag temaet ditt på nytt med fristil tema hvis du har ett. Et tema laget med en eldre utgave setter skriften under de gamle navnene, og den delen av temaet virker ikke lenger uten at noe sier fra. Fargene virker som før.

Nytt

  • data-fs-boundary er en grense mot vertssiden. Bygges en komponent inn i en side du ikke eier, slår vertens CSS uten lag @layer fristil, og en button { background: none } tar bakgrunnen fra .fs-button. Under et element med attributtet ser nettleseren bort fra CSS uten lag, så Fristils lag gjelder igjen, mens skrift og annet som arves fortsatt kommer fra verten og --fs-*-variablene virker. Regelen ligger i fristil.css, og som @fristil/designsystem/boundary.css. Den har spesifisiteten (0,2,0), og egen CSS inne i grensen må ligge i et lag. Se «Vertens CSS stopper ved grensen» under Tilpasning.

  • --fs-select-padding og --fs-select-width. Luften i nedtrekkslista kan settes som i feltet. I standardutseendet står plassen til pila fast, og auto gir et kompakt felt så bredt som innholdet.

  • --fs-input-width, av samme grunn.

  • Boblen i .fs-tooltip har box-sizing: border-box, så --fs-tooltip-width er hele boblen også i en side uten en CSS-reset.

  • close-label på <fs-toast> er standard for hva lukkeknappen i hver melding heter for skjermleseren. fs.toast({ closeLabel }) skriver det, og closeLabel i show() overstyrer det for én melding.

  • Antall treff i <fs-suggestion> kan oversettes, med count-none, count-one og count-other, og count-zero, count-two, count-few og count-many for språk som har dem. {n} står for antallet, og Intl.PluralRules velger form etter lang på siden. Uten attributtene er teksten norsk som før.

  • Markeringen på .fs-label og .fs-legend leses fra variabler: --fs-label-required-symbol, --fs-label-required-text og --fs-label-optional-text, med « *», « (påkrevd)» og « (valgfri)» som reserve.

  • Opplesningen av nedtellingen følger sidens språk. Varigheten skrives med Intl etter lang, så den er «1 minutt og 30 sekunder» på en norsk side og «1 minute and 30 seconds» på en engelsk.

  • @fristil/designsystem/default-texts har hver standardtekst pakken skriver inn i siden. En vaktpost i bygget feller en tekst som står rett i en komponent i stedet for der, en tekst i CSS som ikke leses fra en variabel, og en tekst som mangler i tabellen på siden om oversettelse.

0.28.0 (2026-10-04)

Brytende

  • .fs-toast er nå klassen på regionen, og meldingen heter .fs-toast__message. Regionen ble stylet med elementnavnet, så en <fs-toast> registrert under et annet navn mistet plasseringen i hjørnet. Komponenten setter klassen på seg selv, og fs.toast().host skriver den. Elementnavnet fs-toast har fortsatt de samme reglene, så en region uten klassen står der den sto. Det du må gjøre: har du egen CSS mot .fs-toast, eller en server som skriver meldinger rett inn i regionen, bytt til .fs-toast__message. fs.toast().toast heter fs.toast().message, og TOAST_MESSAGE_CLASS er ny ved siden av TOAST_CLASS. --fs-toast-*-variablene er de samme.

  • Varianten default på hjelpeteksten heter strong. Standardverdien er muted, og overalt ellers betyr default «gir ikke attributt». Det du må gjøre: bytt data-variant="default" med data-variant="strong", og fs.helpText({ variant: "default" }) med "strong".

  • fs.suggestion() leser ikke lenger open for tommeldingen. Den skriver hidden på «Ingen treff» når det finnes treff, eller når pending er satt. At lista er lukket, tar stilarket seg av: .fs-suggestion__empty vises ikke etter en liste med hidden. Før måtte en app som filtrerer selv (prefiltered) sende open, en tilstand bare komponenten kjenner, så meldingen kunne aldri vises. Det du må gjøre: tommeldingen må stå etter lista, som søsken, og et asynkront søk sender pending: true mens svaret er underveis.

  • Forslagslista åpnes ikke når den er tom og feltet er tomt. Fokus åpnet en liste uten alternativer, med aria-expanded="true" over ingenting. Et felt som venter på sitt første søk viser dermed ikke «Ingen treff» før brukeren har skrevet noe.

  • fs.setAttributes rydder bare det byggefunksjonen selv forvalter. Før hadde den én felles liste, og fs.setAttributes(knapp, fs.button()) fjernet disabled, hidden, open, aria-describedby, multiple og accept fra elementet, selv om knappefunksjonen aldri har skrevet noen av dem. En deaktivert knapp ble aktiv, og et åpent <details> lukket seg. Nå fjernes et attributt bare når settet kommer fra en byggefunksjon som skriver det: fs.switch() kan ta bort disabled, fs.button() rører det ikke. Regelen går begge veier: har byggefunksjonen et valg for et attributt, eier den det. fs.spinner() har label, så et kall uten label fjerner role og aria-label, også når du skrev dem for hånd. Det gjorde den ikke før. id og for fjernes aldri. Det du må gjøre: send valgene på nytt i hvert kall, og skal to sett på samme element, send dem hver for seg, fs.setAttributes(felt, fs.input(), kobling.control), ikke spredd sammen i ett objekt. Et spredd objekt settes riktig, men rydder bare data-*, og funksjonen sier fra i konsollen første gang.

Lagt til

  • pending i fs.suggestion(). Holder «Ingen treff» skjult mens svaret på et søk er underveis.
  • Regelboka for bundlere forklarer fs.setAttributes(). Ingen av regelbøkene nevnte funksjonen, så en kodeagent som skrev markup uten spredning fikk ikke vite om den, eller om at to sett sendes hver for seg.

Rettet

  • En dialog styrt av React-tilstand lot seg ikke lukke i React 19. React skriver el.open = undefined når en prop går fra true til borte, og setteren vekslet attributtet i stedet for å fjerne det. Dialogen åpnet seg igjen etter både knappen og Escape, og sprettoppvinduet ble stående åpent. Det samme gjaldt invalid, disabled og optional på <fs-field> og prefiltered på <fs-suggestion>.
  • Fanene lot seg ikke bytte under server-controlled. Klikket ble angret i neste mikrotask, fordi komponenten leste serverens gamle valg fra minnet. Klikket står nå til noen andre rører aria-selected eller hidden, og da bestemmer serveren som før.
  • optional og required-marker på <fs-field> lot seg ikke slå av. Komponenten leste sin egen data-optional og data-required tilbake fra ledeteksten. Markeringer serveren skrev der, står fortsatt.
  • <fs-field> fjernet en data-state="invalid" malen hadde skrevet. Den fjerner nå bare den den selv satte.
  • Feiloppsummeringen tok fokus når en synlig liste ble tømt og fylt igjen. Brukeren ble rykket ut av feltet han rettet. Fokus flyttes nå bare når boksen går fra skjult til synlig.
  • <fs-connection-status> ble hengende etter å ha vært ute av dokumentet. Kvitteringen sto for godt, og linja sa offline etter at nettet var tilbake.
  • Økttidsvarselet sluttet å være modalt når elementet ble flyttet, og opplesningen sa «2 minutter» med 1:30 igjen. Den sier nå «ett minutt og 30 sekunder».
  • fs.setAttributes rydder de boolske valgene. data-interactive på kortet, data-hoverable på tabellen, data-selectable på etiketten, data-autofocus på feiloppsummeringen og placement på sprettoppvinduet ble stående etter at valget var tatt bort.
  • fristil tema --merke1, --merke2 og --merke3 ble lest som filnavn. Flaggleseren godtok ikke sifre.
  • fristil overta button --ut src/ui ignorerte flagget og skrev til standardmappa. Den stopper nå og ber om likhetstegn, slik agent gjør.
  • Advarselen fra fs.dialog() uten id ba om id. Valget heter titleId, og det står nå i meldingen.
  • Eksempellinja i fristil --hjelp hadde en linjefortsettelse som ble til fem mellomrom midt i kommandoen.
  • En deaktivert knapp med variant så aktiv ut. secondary, ghost og danger hadde samme bakgrunn, tekst og kant med og uten disabled, og fikk hoverfarge under musa. Regelen for deaktivert sto over variantene og tapte på rekkefølgen.
  • Musa tok bort feilrammen og rammen på en valgt avkryssingsboks, radio og bryter. Hover var løftet over tilstandene med :not(:disabled).
  • Hjelpeboblen sto en hel boblebredde til venstre i RTL.
  • Søkefeltet mistet forstørrelsesglasset når det var ugyldig, gyldig eller deaktivert og input.css ble lastet etter search.css. Tilstandene setter nå background-color og ikke kortformen.
  • «(valgfri)» hadde kontrast 4,37:1 i lyst tema. Teksten brukte fargen for deaktivert tekst. Den bruker nå --fs-color-neutral-text-subtle.
  • «(påkrevd)» kunne komme ut som «pÃ¥krevd». Tegnet sto rått i stilarket, og en side uten <meta charset> fra en tjener som ikke sender tegnsettet, leste det feil. Det står nå som en escape.
  • Markup med både data-required og data-optional viste «(valgfri)». Byggefunksjonen velger påkrevd, og stilarket gjør nå det samme.
  • Regelbøkene for kodeagenter sa tre ting som ikke stemte. At registrering på en server gir «customElements is not defined» (den gjør ingenting, uten et ord), at siden «ser tom ut» uten registrering (det gjelder bare de tre frittstående), og at tokens.css må lastes før komponentens stilark (rekkefølgen betyr ikke noe). React-boka viser nå fs.field() med useId() for et felt, ikke <fs-field>, som gir hydreringsfeil med server-rendring. Formatet fra fristil sjekk står med alvoret, fil:linje:kolonne: feil: melding.
  • Lenkene til dokumentasjonen peker på fristil.sobernetics.no. homepage i pakken, regelbøkene, web-types.json og lenkene diagnostikken skriver ut brukte fristil.netlify.app.
  • Byttet en patch ut et panel i <fs-tabs>, sto fanen og pekte på en id som ikke fantes. Det nye panelet fikk verken id, rolle eller hidden. Det får nå den samme id-en fanen alt peker på. <fs-suggestion> gjør det samme for feltet og lista: et nytt felt får id-en ledetekstens for peker på.
  • Fanene tok piltaster med Alt, Ctrl og Cmd. Alt+venstrepil er «tilbake» i nettleseren, og ble stoppet.
  • Escape i et sprettoppvindu inne i en modal dialog lukket begge. Tastetrykket stopper nå i vinduet.
  • Feiloppsummeringen meldte en prosentkodet lenke som brutt. href="#f%C3%B8dselsdato" peker på id="fødselsdato", og oppslaget dekoder nå fragmentet slik nettleseren gjør.
  • Økttidsvarselet frøs når warn-at ble hevet mens dialogen sto.
  • Tilkoblingslinja viste den gamle teksten når offline-text eller online-text ble byttet mens den sto.
  • Skillelinja og skjelettet var usynlige i høykontrastmodus. Begge var bare en bakgrunn. Linja bruker nå en systemfarge, og skjelettet får en kant.
  • Et kort som er en <button> hadde nettleserens knappeskrift, 13 piksler. Kortet arver nå hele skriften.
  • <span aria-disabled="true"> i sidenavigeringen hadde ikke boksen til lenkene, så rekka hoppet når «Neste» ble byttet inn på siste side.

0.27.1 (2026-10-03)

Rettet

  • Størrelsen på fristil.css i regelbøkene. Tre av dem sa «rundt 75 kB, under 10 kB komprimert» om en fil på 90 kB og 11 kB. Tallet var hardkodet i generatoren; det regnes nå ut av de samme exports-oppføringene fila bygges av. bygg:css skrev også KiB merket som kB, og telte UTF-16-kodeenheter framfor byte.

0.27.0 (2026-10-02)

Lagt til

  • fs.progress() og progress.css. En framdriftsstolpe på et ekte <progress>, så rollen og opplesningen av tallet kommer fra nettleseren. data-color gir success, warning og danger, og uten value blir stolpen ubestemt. max har standardverdien 100 og skrives alltid ut: HTMLs egen standard er 1, og en <progress value="40"> uten max viser en full stolpe uten at noe sier fra.

0.26.1 (2026-10-02)

Rettet

  • Beskrivelsene i klassekatalogen stemmer med komponentsidene. classes.ts og web-types.json leser frontmatteren fra hver komponentside, og fire av dem sa «én klasse» om en komponent med to. Hoverboksen i VS Code og JetBrains-IDE-ene siterte tallet videre.

0.26.0 (2026-10-02)

Lagt til

  • .fs-theme-control lar brukeren velge tema uten JavaScript. Klassen står på en radioknapp, og value sier hvilket tema den velger: auto, light eller dark. Selektoren er en ekstra linje på de to temablokkene i tokens.css, og den samme linja i temaer buildTheme() skriver. «Følg systemet» har ingen temablokk, siden en verdi uten blokk nettopp skal falle tilbake på mediespørringen. light og dark vinner over data-theme på <html>, slik at serveren kan sende et lagret valg mens et klikk slår igjennom før svaret er tilbake. auto gjør det ikke: har serveren skrevet data-theme, står serverens verdi, og det som lagrer valget må også fjerne attributtet.

  • Hvert av de tre valgene setter color-scheme selv. light gir light, dark gir dark, og auto gir light dark så lenge <html> ikke bærer data-theme="light" eller data-theme="dark". Det siste er ikke et tema, men beskjeden til nettleseren om at siden fungerer i begge, så den kan tegne sine egne flater etter systemet.

    Bruker du velgeren, skal du ikke skrive color-scheme på <html> selv: en regel utenfor et lag slår @layer fristil, og nettleserens egne flater blir låst til systemet mens brukeren har valgt noe annet. For den som ikke bruker velgeren, er ingenting endret, og bar :root har fortsatt ingen color-scheme.

    Overstyrer du et token per tema, må dine egne [data-theme]-blokker ha velgerens selektor ved siden av. CSS kan ikke skrive et attributt, så velgeren setter ingen data-theme.

0.25.0 (2026-10-01)

Rettet

  • Språkvask i regelbøkene og dokumentasjonen. Et «de» som skulle vært «dem», og en forklaring og et eksempel som var lånt fra en konkret feilrapport framfor å stå på egne bein. Teksten beskriver nå mekanismen: en innebygd komponent i en vertsside.

0.24.0 (2026-10-01)

Lagt til

  • JetBrains-IDE-ene foreslår nå fs--klassene. web-types.json hadde bare de ni web-komponentene, så du fikk forslag på <fs-field> men ingenting på fs-button, enda klassene er den eneste delen en CSS-komponent har. Nå står alle 72 under contributions.css.classes, med komponentnavn og lenke til dokumentasjonssiden.

    VS Code-utvidelsen har hatt dette hele tiden, i kode framfor i data, så dette retter en skjevhet mellom de to editorene. Lista bygges av classesData(), den samme funksjonen som skriver classes.ts, så editoren kan ikke foreslå en klasse diagnostikken avviser.

0.23.0 (2026-10-01)

Brytende

  • data-theme="light" deklarerer nå alle de lyse tokenene, ikke bare color-scheme. Før sto blokken med én linje mens [data-theme="dark"] hadde alle 90, så lyst tema virket bare på <html>: et barn kan ikke overstyre en variabel det arver uten å deklarere den på nytt. Nå er attributtet en temagrense begge veier, på et hvilket som helst element, og et tema kan ligge inne i et annet.

    Overstyrer du en farge per tema, må du legge til en [data-theme="light"]- blokk. En deklarasjon på elementet selv vinner over en verdi det arver, uansett hvilket lag arven kom fra, så uten blokken står Fristils egen farge inne i en lys temagrense mens din gjelder ellers på siden. Oppskriften i «Tilpasning» viser alle fire blokkene.

  • Temageneratoren skriver nå også [data-theme="light"]. buildTheme() skrev :root, mediespørringen og [data-theme="dark"], men ikke den lyse blokken, så et generert tema hadde nøyaktig den samme asymmetrien: inne i en lys temagrense sto Fristils egen farge, mens temaets farge gjaldt ellers på siden. Har du et generert tema, må fila skrives om igjen med fristil tema for å få blokken.

  • color-scheme settes ikke lenger på :root. Den står bare på [data-theme="light"] og [data-theme="dark"], altså der noen har valgt et tema. Egenskapen arves nedover, så en verdi på roten gjaldt hele dokumentet, også der pakken bare er en gjest: en komponent som lastet fristil.css inn i en vertsside ga hele verten mørke rullefelt og skjemakontroller i mørk modus, langt utenfor komponenten selv.

    Vil du at nettleserens flater skal følge systemet på hele siden, skriver du color-scheme: light dark på <html> selv. Det er den samme avtalen som for lagrekkefølgen.

Rettet

  • Regelbøkene for kodeagenter og dokumentasjonen sa ulike ord om det samme. Regelbøkene under agent/ skrev «byggerne» og «egendefinert element» der dokumentasjonen skrev «byggefunksjon» og «web component». En agent som får begge i konteksten møtte to navn på én ting. sjekk-ordbruk.ts vokter nå begge ordene, og leser også README.md i pakken, som den ikke gjorde før.

0.22.0 (2026-09-29)

Brytende

  • Fargene er en matrise, ikke to lag. En farge er et punkt i en matrise av familie (accent, visited, brand1-3, neutral, danger, warning, success) og rolle (surface, border-subtle, border, border-strong, fill, content, text-subtle, text, text-strong). Navnet er --fs-color-<familie>-<rolle>.

    --palette-* og --semantic-* er borte. Det samme er --size-*, som nå heter --fs-spacing-*. Hvert fargenavn, hver avstand, hver linjeavstand, hvert ikon, skyggen og fokusringen heter nå --fs-<slag>-<navn>, som hos Tailwind, daisyUI og Digdir, og slaget i navnet er det som lar Tailwind-temaet bli generert framfor skrevet av for hånd. De tretten --font-size-* og --font-weight-* beholder navnene sine, siden de alt følger CSS-egenskapen de setter.

  • Kontrasten er garantert av konstruksjonen. Kuløren er konsumentens, lysheten er rollens. Lyshetene er regnet fram mot et sveip rundt hele fargesirkelen på den høyeste metningen sRGB kan vise, så løftene holder uansett merkefarge. Justeringspasset som flyttet fargen din etterpå er borte, og det samme er metningstaket maxChroma: uten et pass å mate finnes det ingenting å dempe for.

  • Generatoren er 352 linjer mot 744. buildScale, buildNeutralScale, adjustForContrast, SCALE_STEPS, NEUTRAL_STEPS, MAX_CHROMA, CHROMA_FLOOR, CHROMA_CEILING og typen AdjustResult er fjernet fra @fristil/designsystem/farge, som nå bare har fargeregningen. Fra @fristil/designsystem/tema er typen ThemeAdjustment og feltet Theme.adjustments borte. En familie du utelater arver Fristils egen framfor å felle kjøringen, så fristil tema --aksent=#7c3aed er nok. Flagget het --interaktiv, og heter nå --aksent, som familien. To navn på den samme fargen var nettopp det matrisen skulle bli kvitt.

  • To nye roller og ett nytt lag kom av migreringen. border-strong, fordi koden brukte tre kantnivåer og matrisen hadde to: en feltramme som endrer seg ved hover, og en tabellstrek som er tydeligere enn en radskiller. text-strong, fordi brødtekst satt til text ble #4a4d51 der den før var #1a1a1a, altså 8,5:1 mot hvitt der den hadde 17,4:1. Kravet holdt, lesbarheten ikke. Brødteksten står nå på text-strong, #27292b, som er 14,6:1. Og raised, som er et lag den nøytrale familien har ved siden av rollene, siden tekst må holde mot et kort som ligger over et kort.

Lagt til

  • fristil sjekk-tema <fil…> kontrollerer et tema noen har skrevet selv, og sier hvilken celle som ryker og hvorfor. Den teller verdiene konsumenten selv skrev: både filtallet og løftetallet er de samme for én linje som for et helt tema, siden standardverdiene fyller hullene.

  • Tre nye inngangspunkter: ./kontrakt med rollene og løftene, ./matrise som bygger et tokensett av merkefarger, og ./tema-sjekk.

Rettet

  • Gjeldende side i pagineringen forsvant under musa. .fs-pagination a:hover slo [aria-current="page"] på spesifisitet, så sidetallet brukeren sto på fikk en lys flate mens teksten ble stående i accent-content: 1,09:1 i lyst tema og 1,25:1 i mørkt. Gjeldende side er nå unntatt fra regelen og har sin egen hover. Feilen har vært der siden pagineringen kom, og ingen test så den, fordi :hover ikke kan leses av getComputedStyle.

    hover.browser.test.ts fører nå musa over tolv av de fjorten hover-reglene som bytter bakgrunn, i begge temaer, og krever 4,5:1. De to siste ligger bak et pseudoelement og bak en @supports Firefox ikke har, og kontrolleres på tokenparet. Lista er bundet til stilarkene, så en ny slik regel uten et tilfelle feller testen, og hvert tilfelle påstår at flaten under musa er en annen enn flaten uten.

  • Siste steg i brødsmulestien ble blått under musa. Samme årsak: :hover slo [aria-current="page"]. Gjeldende steg er ikke et sted å gå, og står nå urørt.

  • Tre steder i dokumentasjonen sto «byggefunksjon» der ordet var et verb, som «generatoren byggefunksjon skalaene». Setningene ga ingen mening.

  • «Tilpasning» sa «fire veier» over en tabell med tre rader.

  • «Eget tema» sa at skriften settes i @layer fristil. Den står i fristil-tema.

0.21.0 (2026-09-28)

Lagt til

  • Folketelling over attributter bare morferen leser. fs.browser.test.ts kaller hver byggefunksjon og krever at data-ignore-morph kommer ut fra nøyaktig connectionStatus, sessionTimeout, suggestion og toast, og at data-preserve-attr ikke kommer ut fra noen. Vakten feller både et nytt sted attributtet dukker opp og et sted det forsvinner fra, siden begge endrer hva malen må skrive. Hjelperen i morfing.browser.test.ts hopper over attributter som står i en fredningsliste, slik den ekte løkka gjør. Står et navn der, fjerner løkka ingenting for nettopp det attributtet, og testen etterprøver ingenting. Noen av fixturene bygges av byggefunksjonene, og der feller folketellingen. Andre er skrevet for hånd, som en Go-mal ville gjort, og der feller en ny påstand i hjelperen om at lista er tom.

Rettet

  • <fs-field> leter ikke lenger etter data-role på hjelpetekst og feilmelding. Komponenten leste [data-role='help'] og [data-role='error'] ved siden av klassene fs-help-text og fs-error-text. Kroken sto bare i en querySelector i kilden, uten tester og uten omtale noe sted, så ingen kunne bygge på den med vilje. data-role er uprefikset og eies i praksis av andre, og en app som brukte det til noe annet inne i et felt fikk sitt eget avsnitt adoptert som feilmelding, uten at noe sa fra. Komponentsiden sier nå hva komponenten faktisk krever, og en test holder fjerningen på plass.

    Fjerningen er ingen brytende endring etter reglene øverst. Ingen byggefunksjon skrev attributtet og ingen komponent satte det, så ingenting i konsumentens markup endrer betydning: den virker som før, den blir bare ikke koblet.

  • Dokumentasjonen påsto mer enn den kunne vise. «Holder markupen der, holder den overalt» var en påstand om alle morfere, mens vi kjører løkka til én. Kravet står nå slik det er: det en komponent har satt, skal overleve at attributter blir fjernet fra et element som består. Og data-preserve-attr og data-ignore-morph er beskrevet som morferens egne attributtnavn, siden et data-*-attributt bare betyr noe for koden som leser det.

  • «To komponenter eier sitt eget innhold» var tre. <fs-toast> sto ikke i lista i «Markup og oppførsel», og at fs.suggestion() setter data-ignore-morph på statusområdet sto bare på komponentsiden, ikke der regelen forklares.

  • overta sa ingenting om tilgjengelighetsansvaret. Siden om tilpasning sier nå at en overtatt og endret komponent ikke dekkes av en tilgjengelighetserklæring som viser til designsystemet.

0.20.0 (2026-09-28)

Lagt til

  • fristil agent og regelbøkene i agent/. Seks regelbøker skrevet for kodeagenter som Claude Code og GitHub Copilot, én per miljø: html, maler, bundles, react, astro og datastar. Hver er komplett for sitt miljø og nevner ingen andre, siden en agent som leser om className i et prosjekt med Go-maler er en agent som skriver className i en Go-mal. Vue, Svelte, Solid og Lit deler bundles, og --rammeverk=svelte peker dit.

    Hver fil har de fire feilene en agent gjør oftest øverst: glemt stilark, oppfunne komponenter og varianter, hardkodede farger og bommet registrering. Så følger alle komponentene med klasse, element, attributter og lovlige verdier, tokenlagene, de tre reglene for web components, og sjekken som retteløkke. Kommandoen leser miljøet av package.json, og --rammeverk=<navn> overstyrer.

    Kommandoen skriver aldri en fil. AGENTS.md, CLAUDE.md og .github/copilot-instructions.md er konsumentens egne, og et verktøy som skriver i dem må gjette stier, flette med innhold det ikke har skrevet, og holde en kopi i takt med pakken. Regelboka blir derfor stående i pakken, på node_modules/@fristil/designsystem/agent/<navn>.md, og følger versjonen. Mappa er med i tarballen.

    De samme filene ligger også på dokumentasjonssiden, på /llms.txt og /agent/<navn>.md. Den viktigste agenten har ingen node_modules: blir noen bedt om å lage et skjema med Fristil i en tom mappe, skrives markupen før npm install har kjørt.

    Filene genereres av scripts/generate-agent.ts, og scripts/sjekk-agent.ts feller hvis de er utdaterte, hvis en komponent mangler i en av dem, hvis et markupeksempel ikke tåler diagnoseMarkup, hvis en importsti ikke finnes i exports, hvis et tokennavn ikke finnes, eller hvis en html-blokk importerer et pakkenavn der nettleseren trenger en URL.

Rettet

  • Versjonen i dokumentasjonens CDN-adresser kom fra 74 hardkodede steder. prepare-version skrev dem om ved hver utgivelse, men et omskrivingssteg må finne den gamle versjonen for å bytte den, så en adresse som hadde glidd ble stående og pekte på en eldre pakke enn teksten rundt beskrev. Kilden har nå plassholderen @fristil/designsystem@VERSJON, og remark-versjon.mjs setter inn versjonen fra package.json når siden bygges. Omskrivingssteget er fjernet, og fire vaktposter passer på: tre på at kilden bruker plassholderen, og sjekk-bygget-versjon.ts på at den faktisk ble byttet ut i det bygde resultatet.

  • To kodeeksempler i dokumentasjonen importerte et pakkenavn i nettleseren. Datastar-sporet på Rammeverk og dialogen på Bekreft en handling hadde import … from "@fristil/designsystem/field" i en <script type="module">. Ingen av sidene har et byggesteg, og en nettleser slår ikke opp et pakkenavn uten byggesteg eller importmap, så koden feilet stille for den som limte den inn. Begge bruker nå hele URL-en.

    Regelen fantes i sjekk-oppskrifter.ts, men den leser bare komponentsidene og velger regel ut fra navnet på fanen, så de to sto med feilen i flere runder. sjekk-nettleserimport.ts dekker nå alle sidene uavhengig av fane: en html-blokk er markup for nettleseren uansett hvor den står.

0.19.0 (2026-09-28)

Lagt til

  • maxChroma i temageneratoren, og --maks-metning på kommandolinja. Taket på metningen i de kulørte skalaene har stått fast på 0,16 i OKLCH. De fleste merkefarger ligger under det, men neon gjør ikke: #39ff14 ligger på 0,286 og mistet 44 prosent mot taket, #ff2d6f på 0,240 og mistet 33. Taket kan nå settes, i buildTheme({ maxChroma }), med --maks-metning på kommandolinja, eller som maksMetning i en oppskriftsfil. Standarden er den samme som før, så et tema som ikke oppgir noe kommer ut likt.

    Kontrastkravet berøres ikke. Taket styrer metning, mens kontrasten kommer av de faste lyshetene i skalaen og av passet som måler hvert par etterpå og flytter lysheten. Mer metning gir flere slike justeringer, ikke en svakere garanti, og et merke med hevet tak kjøres nå gjennom hele listen av par i theme.browser.test.ts. Den nøytrale skalaen har sitt eget, mye lavere tak, og er uendret.

    Oppskriftsfila sier nå fra om en ukjent toppnøkkel, slik dokumentasjonen alltid har lovet. En fil som har stått med en skrivefeil og virket, stopper derfor nå. $schema er fortsatt lovlig: editorer skriver den inn av seg selv, og den sier ingenting om temaet. En fil som er gyldig JSON, men ikke et objekt, får også en forklaring framfor et stakkspor fra Node.

    buildTheme avviser et tak utenfor 0,01 til 0,33. Et negativt tak speilet fargen og gjorde knallgrønt til magenta, NaN ga en feilmelding om en heksadesimal farge som aldri var oppgitt, og 0 ga et helt grått tema, alle tre i stillhet.

0.18.0 (2026-09-27)

Lagt til

  • fristil sjekk <fil…> og @fristil/designsystem/diagnostics. Sjekken editorutvidelsen kjører mens du skriver, elementer som ikke finnes, attributter elementet ikke har, verdier utenfor lista, klasser som fs-buton og <fs-field> uten kontroll, ligger nå i pakken. Kommandolinjen leser filer eller standard inn og skriver hvert funn som fil:linje:kolonne: melding, med feilkode ved funn. diagnoseMarkup(html) er den samme sjekken som funksjon, for en test i appen som kjører den over HTML-en serveren faktisk sender, der det ikke finnes noen malfil å sjekke. Editorutvidelsen importerer nå diagnostikken fra pakken, og listene den sjekker mot genereres inn i pakken som elements.ts og classes.ts.

  • <fs-popover>, <fs-suggestion> og <fs-dialog> kobler fra bar struktur, som fanene og feiloppsummeringen. En knapp og et panel med klassen fs-popover er nok: komponenten setter popover="manual", lager id-en og skriver aria-controls på knappen, ellers den første knappen utenfor panelet som ikke peker på noe annet. En <label>, et <input>, en .fs-suggestion__list med <li> og et statuselement er nok for forslagsfeltet: komponenten setter rollene, lager id-ene, skriver for og aria-controls, og holder lista lukket til brukeren rører feltet. Dialogen får klassen, og navnet sitt fra den første overskriften når den ikke alt har et fra aria-labelledby eller aria-label, med en id komponenten lager om den mangler; peker aria-labelledby på en overskrift, er det den som får tittelklassen. Bare det som mangler skrives, suggestion.css holder en bar liste skjult til komponenten er registrert, og advarslene sier hva som faktisk mangler. derivedParts og uniqueId i /host-element er hjelperne bak.

  • <fs-tabs> og <fs-error-summary> kobler fra bar struktur. En .fs-tabs__list med knapper og ett .fs-tabs__panel per knapp er nok: komponenten setter rollene, lager id-ene, kobler aria-controls og aria-labelledby, og skjuler panelene som ikke er valgt. Valget leses fra aria-selected, ellers fra hvilket panel som ikke er hidden. Feiloppsummeringen får klassen, role="alert" og tabindex="-1", og overskriften klassen sin. Det serveren har skrevet står, og det komponenten fylte inn settes tilbake etter en patch, med de samme id-ene. tabs.css viser bare det første synlige panelet til komponenten er registrert, så innholdet ikke hopper. Kotlin-malen i spilldemoen skrev sju attributter per fane for hånd. fs.tabs() og fs.errorSummary() er som før, for markup som lages med JavaScript.

    To ting endrer seg for markup som alt fantes. Under server-controlled setter fanene fortsatt ikke brukerens valg tilbake, men skriver nå ut det markupen sier: aria-selected, tabbestoppet og hidden i takt, så en patch som bare rørte panelene flytter også markeringen. Det komponenten selv skrev teller ikke som markupens svar. Og advarselen om manglende tabindex på feiloppsummeringen er borte, siden komponenten setter den.

  • @fristil/designsystem/register: defineFs() registrerer alle ni web-komponentene i ett kall, også fra dist/register.js på en CDN. Spilldemoen sto med sju defineFs*-kall i en useEffect, altså etter første tegning, og den første meldingen fra hendelsesstrømmen kom i gapet: «reportSuccess is not a function». Dokumentasjonen sier nå: registrer ved import, aldri i en effekt.

  • De frittstående komponentene har funksjoner som venter på registreringen: showToast(element, tekst, valg), reportFailure(element), reportSuccess(element), extendSession(element) og resetSession(element). De venter på customElements.whenDefined for elementets tagnavn og kaller så metoden, så et kall som kommer for tidlig blir gjort i stedet for å feile. Et element ingen registrerer gir en advarsel i konsollen etter tre sekunder. whenUpgraded(element) i /host-element er hjelperen bak dem.

  • @fristil/designsystem/fristil.css: alle stilarkene i én fil. For den som lenker fra CDN eller node_modules uten byggesteg. De enkelte stilarkene henter delene sine med @import, som nettleseren først ser når fila er lastet, og på mobilnett sto feltene uten ramme til den andre runden kom; spilldemoen forhåndslastet fem filer for hånd for å komme rundt det. Fila bygges av CSS-oppføringene i exports, med tokens.css først, uten @import og uten kommentarer. Et byggesteg bruker de enkelte stilarkene som før. Tailwind-temaet er ikke med, siden det ikke er et stilark for en side.

  • Dialogen kan ha en farget topp. fs.dialog({ color }) gir data-color på dialogen og en ny header-del som pakker overskriften, med en valgfri subtitle under. Fargene er varslerens, info, success, warning og danger, og brand for merkefargen. Med toppen i markupen flyttes luften fra dialogen til delene, så båndet går helt ut i kantene; uten den står dialogen som før. To dialoger i spilldemoen skrev om padding på tre klasser for å få til dette, og det brakk ved en oppgradering. fs.dialog.colors og fs.dialog.isColor lister og sjekker verdiene.

0.17.0 (2026-09-27)

Brytende

  • idEllerReserve heter idOrFallback. Hjelperen i @fristil/designsystem/shared var den siste eksporterte identifikatoren på norsk, og identifikatorer i pakken skal være engelske. Ingen alias: pakken har ingen konsumenter ennå, og en brytende endring gjøres én gang. Parameteren oppgittId i byggefunksjonene heter givenId; den er intern.

0.16.0 (2026-09-27)

Lagt til

  • reset() på <fs-session-timeout> nullstiller klokka uten å sende session-extend, for en app som alt har forlenget økten selv, som når en autolagring gikk gjennom.
  • --fs-toast-layer styrer z-index på varselregionen, med 60 som reserve.
  • isToastColor eksporteres ved siden av toastColors.
  • fs.popover() tar placement. fs.popover.isPlacement sjekker en verdi, også i /react, og popoverPlacements fra hovedinngangen lister dem, med typen PopoverPlacement. Før måtte attributtet skrives for hånd uten typesjekk.
  • --fs-suggestion-layer styrer z-index på forslagslista, med 30 som reserve.

Rettet

  • Escape i <fs-session-timeout> forlenger, og dialogen kommer ikke tilbake. Komponenten lyttet ikke på close, så neste tikk så en lukket dialog etter varselgrensen og åpnet den igjen, ett sekund etter Escape, hver gang, med en ny session-warn. Escape regnes nå som «jeg er her» og gjør det samme som knappen.
  • Skjermleseren får vite hvor lenge det er igjen i det varselet åpnes. Tallet i avsnittet er aria-hidden, og live-området ble fylt først ved neste terskel, så med standardverdiene hørte skjermleseren «Vi logger deg ut om for å beskytte opplysningene dine», og første tall kom tre minutter senere.
  • <fs-session-timeout> står stille etter utløpet og etter «Logg ut nå» til extend() eller reset() kalles. Før startet syklusen på nytt av seg selv, med ny dialog og ny session-expired hvert expires-at-sekund, og en app som brukte mer enn ett sekund på utloggingen fikk dialogen tilbake.
  • warn-at og expires-at sjekkes. Et tall som ikke er et tall, eller et varsel som ikke kommer før utløpet, gir beskjed i konsollen framfor stillhet. warnAt og expiresAt har fått settere, rulling i en boks teller som aktivitet, og hver forekomst har sin egen overskrift-id.
  • Pausen i <fs-toast> holder til både musa og fokus har forlatt meldingen. Den var to uavhengige par, og musa som gikk ut startet klokka igjen mens fokus sto i meldingen, som så forsvant under brukeren.
  • dismiss() flytter fokus til meldingen ved siden av når meldingen som lukkes hadde fokus. Før falt fokus til body, og neste Tab startet øverst på siden.
  • Varselregionen er aria-atomic="false", både fra fs.toast() og fra komponenten. status er atomisk som standard, og hele stabelen ble lest opp på nytt for hver ny melding.
  • Regionen heter «Varsler» begge steder. Komponenten sa «Meldinger» når attributtet manglet, fs.toast() sa «Varsler». Et tomt duration gir standardverdien framfor null, og en ukjent color gir ingen kant.
  • <fs-connection-status> melder connection-lost én gang per utfall. Nettlesere fyrer gjerne flere offline på rad, og hver ga en ny hendelse og en ny opplesning av den samme linja. Linja settes nå inn tom og fylles i neste tegning, slik at live-området finnes før innholdet kommer. offlineText og onlineText har fått settere.
  • <fs-suggestion> lukker lista når fokus forlater komponenten. Tab gikk videre til neste felt, og lista ble stående over det med aria-expanded="true" på et felt som ikke lenger hadde fokus. Lytteren på document er borte: et trykk i lista holder fokus i feltet, fokus på noe inne i komponenten lar lista stå, og alt utenfor lukker den. Dermed virker feltet også inne i en skyggerot, der klikket i feltet før ble regnet som et klikk utenfor og lukket lista i samme klikk som åpnet den.
  • «Ingen treff» følger lista. Meldingen er søsken til lista og ble stående synlig under et lukket felt etter Escape. fs.suggestion() skriver den også skjult på et lukket felt uten alternativer, som er det anbefalte oppsettet for asynkront søk; før sto meldingen der alt ved sidelasting.
  • Markeringen ryddes i det alternativet filtreres bort, også med server-controlled. Før pekte aria-activedescendant på et skjult alternativ, og skjermleseren leste det opp.
  • Alternativene kjennes igjen på role="option", ikke på klassen. En mal med egen styling fikk verken filtrering eller piltaster, uten et ord.
  • <fs-suggestion> sier fra når [role="status"] mangler. Antall treff leses opp der, og uten elementet sto komponenten stille.
  • fs.suggestion() skriver aria-selected og aria-activedescendant under samme vilkår, altså bare når lista er åpen. Før fikk en lukket liste det ene uten det andre.
  • Det markerte alternativet vises i høykontrastmodus, med systemfargene.
  • <fs-popover> står på riktig side i dir="rtl". Posisjonen er fysisk, men stilarket brukte den som inset-inline-start, som er høyre kant i en side som leses fra høyre. start og end i placement følger nå leseretningen.
  • top-start og top-end klemmes inn i vinduet. Et panel ved toppen av siden lå helt utenfor skjermen, uten å kunne rulles fram. Er det ikke plass på den siden plasseringen ber om, legges panelet på den andre.
  • Verten følger med når nettleseren selv lukker et popover="auto". Håndskrevet markup med bare popover er auto, og etter en lett avvisning sto verten med open og knappen med aria-expanded="true" over et lukket panel. Komponenten lytter nå på toggle.
  • Klikk utenfor et sprettoppvindu i en skyggerot leser composedPath(), så et klikk i panelet ikke regnes som utenfor.
  • <fs-dialog server-controlled> lukker når patchen tar open fra en modal dialog. Reparasjonen var slått av, men ingen kalte close(), og dialogen sto igjen i topplaget med display: none mens resten av siden var inert.
  • <fs-tabs> hopper over deaktiverte faner. Piltastene, Home og End valgte en fane med disabled, focus() på en deaktivert knapp gjør ingenting, og raden sto uten en eneste fane som kunne få fokus. Tastaturbrukeren var låst ute. select() avviser dem også.
  • Faner i faner er hver sin rad. Komponenten fant faner og paneler i hele undertreet, så et klikk på en indre fane skjulte det ytre panelet den sto i.
  • Panelet finnes gjennom aria-controls. Koblingen står i markupen og leses derfra, med rekkefølgen som reserve bare for markup uten aria-controls. Panelene kan dermed stå i en annen rekkefølge enn fanene, og utenfor verten. Peker aria-controls på en id som ikke finnes, sier komponenten fra i konsollen. Peker to faner på det samme panelet, vises det når en av dem er valgt.
  • Tabbestoppet flyttes når en patch deaktiverer fanen brukeren valgte, til den neste fanen som kan få fokus. Valget står. Har brukeren ikke valgt noe, er markupen serverens, og komponenten rører den ikke.
  • Venstre og høyre pil bytter retning i dir="rtl".
  • selected på <fs-tabs> kan settes. Getteren sto uten setter, og faner.selected = 1, som dokumentasjonen viste, kastet.
  • fs.tabs() med negativt count gir en tom rad framfor å kaste.
  • <fs-error-summary> prøver igjen når fokus ikke landet. Sto boksen i et skjult panel eller en lukket dialog i det den kom, feilet focus() i stillhet mens flagget «har flyttet fokus» ble satt, og boksen fikk aldri fokus da forelderen ble synlig og lista byttet ut. Gjenforsøket skjer bare når ingen står i et felt, så live-validering som patcher lista ikke river fokus ut av feltet brukeren retter i.
  • En lenke til en <label> uten kontroll følger for dit den peker, som til en gruppe.
  • <fs-error-summary> sier fra når tabindex mangler. Uten det gjør focus() ingenting, og hele grunnen til komponenten forsvant i stillhet.
  • En lenke til en <label> som omslutter kontrollen sender fokus til kontrollen, ikke til ledeteksten.
  • Rullingen til feltet følger sidens scroll-behavior framfor å tvinge smooth, så prefers-reduced-motion gjelder av seg selv.
  • <fs-field> eier bare det den selv skrev. Komponenten leser aria-invalid, aria-describedby og disabled fra kontrollen, fordi serveren kan ha skrevet feltet med fs.field(). Skillet mellom serverens ord og komponentens eget ekko fantes bare for aria-invalid, og de to andre hadde samme feil: aria-describedby pekte fortsatt på feilmeldingen etter at feltet var gyldig, og på en hjelpetekst en patch hadde fjernet, og felt.disabled = false slo ikke av et disabled komponenten selv hadde satt. Minnet er nå nøklet på kontrollen og overlever at feltet flyttes, slik React gjør ved en omstrukturering, og at kontrollen forsvinner og kommer tilbake i en patch. Id-er serveren selv la i aria-describedby blir med videre.
  • Ingen aria-disabled på kontrollen. Komponenten skrev det ved siden av disabled, og strøk samtidig et aria-disabled konsumenten selv hadde skrevet. computeFieldAttributes er kontrakten, og den skriver det bare på ledeteksten; et ekte disabled er alt synlig for hjelpemidlene.
  • Serverens aria-invalid står ordrett når det er serveren som sier feltet er ugyldig. false er gyldig og vanlig i håndskrevet HTML, og ble strøket ved hver patch. grammar og spelling betyr ugyldig, men ble lest som «ikke true» og strøket på samme måte. Tom streng leses som false, slik ARIA sier.
  • requiredMarker som egenskap betyr det samme som attributtet. Setteren skriver "none" bokstavelig, siden required-marker="none" overstyrer en data-required serveren skrev på ledeteksten. Før fjernet setteren attributtet, og markeringen kom tilbake fra ledeteksten. Getteren leser nå markupen, ledeteksten medregnet, framfor bare sitt eget attributt.

0.15.0 (2026-09-26)

  • web-types.json følger med pakken. Det er JetBrains sitt format for fullføring og forklaring i HTML, med de ni <fs-*>-elementene og attributtene deres. IDE-en finner den selv fra node_modules, gjennom feltet web-types i package.json. Etterprøvd i IntelliJ IDEA Ultimate 2026.2: fullføring av elementene, attributtene og verdiene, og forklaring med lenke. Fila genereres fra editor/metadata.ts i repoet, der VS Code-utvidelsen også lages.
  • observedAttributes på komponentklassene er nå skrivebeskyttede tupler (as const) i stedet for string[]. Verdiene er de samme. En klasse som arver fra en komponent og skriver over observedAttributes med string[] får en typefeil, og må bruke as const den også.

0.14.0 (2026-09-24)

Brytende

  • Verten heter host i alle byggefunksjonene. Den delen serveren skriver, og komponenten fester seg på, het tre ting: host i fs.dialog() og fs.popover(), region i fs.toast() og container i fs.errorSummary(). Tre ord for den samme tingen, og to av dem sa ikke hva den var. De heter nå host, som er ordet arkitekturen bruker om den ellers.

    fs.connectionStatus() og fs.sessionTimeout() er urørt. De har bare én del, og returnerer attributtene flatt, slik fs.button() gjør. Nøkkelen finnes der verten selv bærer attributter. fs.field(), fs.tabs() og fs.suggestion() har mange deler og ingen host, fordi verten deres ikke har noe å bære.

    - <fs-toast {...varsler.region} />
    + <fs-toast {...varsler.host} />
    - <fs-error-summary {...feil.container}>
    + <fs-error-summary {...feil.host}>
    

Lagt til

  • setAttributes tar null. document.querySelector() gir Element | null, så hvert eneste kallsted i en strict-app måtte skrive en vakt eller et utropstegn rundt et oppslag som nesten alltid treffer. Den gjør nå ingenting på null, som element?.classList. Parameteren er utvidet, så ingen kallsteder brytes.

Rettet

  • Panelet i <fs-popover> nullstiller lista. Panelet er ofte en <ul> med handlinger, og .fs-popover hadde ingen list-style. Markupen i dokumentasjonen skrev data-variant="plain" for å bøte på det, men det attributtet finnes bare på .fs-list og traff ingen regel, så panelet sto med nettleserens kuler. Nullstillingen hører i pakken og ikke hos konsumenten.

  • ref virker på de egendefinerte elementene i JSX. Typene bygget på HTMLAttributes, som ikke har ref; den ligger i RefAttributes. Derfor var <fs-toast ref={kø} /> en typefeil, altså nøyaktig mønsteret dokumentasjonen anbefaler for å kalle .show() og .hide() fra React. Alle vertene bygger nå på en felles Host-type som har begge.

  • warn-at og expires-at godtar det byggefunksjonen sender. De sto som number i JSX-typene, mens fs.sessionTimeout() sender strenger, slik HTML-attributter er. Det er den samme feilen som tabIndex: "0", motsatt vei, og den viste seg bare i en ekte React-app.

  • fs.connectionStatus() og fs.sessionTimeout() reklamerte med en død klasse. Begge sender ut fs-connection-status og fs-session-timeout, men stilarkene stylet bare elementnavnet, så klassen gjorde ingenting. Nå treffer regelen begge, og en konsument som setter klassen på noe annet enn elementet får den samme oppførselen.

0.13.0 (2026-09-24)

Brytende

  • .fs-dialog er en kolonne når .fs-dialog__body er et direkte barn. Det er rettelsen under, men den endrer hvordan innholdet legger seg for markup som alt finnes, og det er den strukturen dokumentasjonen viser. Marger mellom avsnitt slutter å falle sammen, og tekst og knapper som står rett i dialogen ved siden av kroppen blir egne rader i full bredde. En dialog uten .fs-dialog__body er urørt.

  • display kan ikke lenger settes alene på .fs-dialog. En <dialog> uten open skjules av nettleserens eget stilark, og lagene sorterer bare innenfor ett opphav, så en hvilken som helst forfatterregel med display slår den skjulingen. Setter du din egen, må du ta med dialog.fs-dialog:not([open]):not(:is(:popover-open)) { display: none }, minst like spesifikk som din egen regel. Det står på komponentsiden og på siden om tilpasning. Dette er det eneste unntaket fra løftet om at en enkel selektor slår hva som helst i pakken.

Rettet

  • Dialogen hopper heller ikke sidelengs. 0.12.1 ga den plassen til en modal før den var det, og det loddrette hoppet var borte. Et vannrett kom i stedet: nettleserens eget stilark setter max-width: calc(100% - 6px - 2em) på dialog:modal, og det hadde vi ikke. På en skjerm som er 390 piksler bred gikk dialogen derfor fra 358 til 352 piksler i det showModal() kjørte, og flyttet seg tre piksler mot høyre.

    .fs-dialog[open]:not(:modal) inne i <fs-dialog> bruker nå inset: 0 med margin: auto, det samme maksmålet på bredden og overflow: auto, altså de samme verdiene spesifikasjonen gir en modal, i logisk form. Maksmålet på høyden står med vilje bare på .fs-dialog, siden det er det strengeste av de to og dermed gjelder i begge tilstandene. Testen leser boksen før og etter showModal(), med to slags innhold, og krever at den står stille.

  • .fs-dialog__body ruller nå faktisk. Klassen har hatt overflow-y: auto siden komponenten kom, uten at den kunne gjøre noe: dialogen var en blokk, så kroppens høyde var innholdsbestemt og ble aldri klippet. Det som rullet var dialogen selv, og bare når den var modal, så overskriften og knapperaden forsvant ut av syne sammen med teksten. .fs-dialog:has(> .fs-dialog__body) er nå en kolonne, og da krymper kroppen og ruller mens tittelen og knappene blir stående. Det er også det dokumentasjonen har sagt hele tiden.

    Regelen tar samtidig tilbake det den tok: en <dialog> uten open er skjult av nettleserens eget stilark, og en forfatterregel med display slår den uansett lag og spesifisitet. dialog.fs-dialog:not([open]):not(:popover-open) setter display: none igjen, ellers ville en lukket dialog stått som et kort oppå innholdet rundt, fra sidelasting og etter hver lukking. Unntaket for popover står der spesifikasjonens eget stilark har det, siden en popover aldri setter open.

    Det gjør display til den ene egenskapen en konsument ikke kan sette fritt på .fs-dialog. Gjør du det, må du ta den skjulte tilstanden tilbake selv. Det står nå på komponentsiden.

    Vilkåret i selektoren er en del av oppførselen, og står nå i tabellen over klassene. Kolonnen virker bare når kroppen er et direkte barn, og den endrer samtidig hvordan alt annet legger seg: marger mellom avsnitt slutter å falle sammen, og tekst og knapper som står rett i dialogen blir egne rader i full bredde. En dialog uten kropp er derfor urørt.

0.12.1 (2026-09-24)

Rettet

  • Dialogen hopper ikke lenger på plass. En dialog serveren vil vise sendes med open, ellers finnes ikke innholdet uten JavaScript, og nettleseren legger den da i den vanlige flyten. Først når komponenten kaller showModal() flyttes den til topplaget og midtstilles, og mellom de to øyeblikkene hopper den. På en treg forbindelse er hoppet godt synlig: testet i Chromium sto dialogen på topp 0 og landet på topp 128, og det ble meldt fra en telefon.

    .fs-dialog[open]:not(:modal) inne i <fs-dialog> plasseres nå som en modal allerede før den er det, med flaten bak malt av en skygge, siden ::backdrop bare finnes i topplaget. Laget kan settes med --fs-dialog-layer, som er 40 i utgangspunktet.

    Regelen gjelder bare inne i verten. En .fs-dialog som brukes uten komponenten, og med vilje ikke er modal, står der den står.

0.12.0 (2026-09-24)

Brytende

  • ThemeInput er en union. Merkefargene kan utelates, men da skal ingen av dem stå: enten alle fire, eller ingen. Vilkåret står i typen og ikke bare som en feilmelding i kjøretid, så buildTheme({ interactive, danger }) er en typefeil. En konsument som leste input.interactive fra en variabel av typen ThemeInput, må nå skille de to tilfellene. Meldingen i kjøretid står ved siden av, for oppskriften kan komme fra en JSON-fil eller et skript uten typer.

Lagt til

  • Temageneratoren kan også sette skrift og form. Den bygget fargene, og bare dem, og to systemer med den samme paletten ser fortsatt ulike ut når skriften og hjørnene er ulike. buildTheme() tar nå to valgfrie blokker, typography og shape. Seks av verdiene finnes også som flagg: --skrift, --knapp-hjorner, --felt-hjorner, --flate-hjorner, --knapp-ramme og --knapp-vekt. Vektene og linjeavstandene settes fra fil. Utelates begge blokkene, er temaet nøyaktig det det var før.

    Hjørnene er delt i tre framfor ett felles tall, fordi ett tall er feil: et annet norsk designsystem har helt runde knapper mens feltene har nesten rette hjørner, og med én verdi blir feltene kapsler. buttonRadius treffer knapp, paginering og hopplenke, fieldRadius felt, tekstområde og nedtrekksliste, og surfaceRadius de elleve flatene, meldingen og hjelpeboblen medregnet. Avkryssingsboksen, merket, etiketten, valggruppa, avataren og skjelettet står utenfor, fordi hjørnet der ikke er et stilvalg, men selve formen.

    Skriften skrives som en ekte regel på :root og ikke bare som et token. Fristil arver skrift med vilje, så et token alene ville ikke endret én eneste bokstav.

  • Den genererte fila ligger i sitt eget lag, fristil-tema, erklært etter fristil med @layer fristil, fristil-tema;. Den lå i @layer fristil først, og da avgjorde rekkefølgen stilarkene ble lastet i. Den rekkefølgen har konsumenten ikke alltid i hånda: både Astro og TanStack Start legger sin bundlede CSS inn rett før </head>, altså etter en <link> appen selv har skrevet, og temaet tapte da mot pakkens egne verdier. Begge lagene ligger fortsatt foran usortert CSS, så konsumentens egne regler vinner som før.

  • Et tema kan la fargene stå. Merkefargene er nå valgfrie, og utelates alle fire, lages et tema som bare setter skrift og form. Det er ikke en kuriositet: bruker organisasjonen allerede Fristils palett, er det nettopp skriften og hjørnene som skiller, og å kjøre fargene gjennom generatoren ville flyttet dem bort fra der de skal være. #1362ae kommer ut som #1e6ab7, siden skalaene regnes om i OKLCH fra merkefargen. Enten alle fire fargene, eller ingen: to farger kaster, siden resten av temaet da ville blitt bygget av standardfarger uten at noen ba om det. Se «Brytende» over for hva det gjorde med typen.

  • Verdier i et tema kan ikke bryte ut av regelen de skrives inn i. Oppskriften er en JSON-fil som kan komme fra et annet repo eller fra et byggesteg, og «4px; } html { display: none } :root { --x: 1 lukket både erklæringen og :root-blokka, og fikk en vilkårlig regel inn i @layer fristil. Tre veier til var åpne, og alle er etterprøvd i nettleser: 1px ( åpnet en parentes som slukte resten av stilarket, Arial\ lot baksnabelen spise semikolonet, og Arial</style><script>… kjørte et skript i en side der CSS-en står inline. Verdier med ;, {, }, <, >, \, /* eller styretegn avvises nå, parenteser og anførselstegn må gå opp, og meldingen navngir nøkkelen i oppskriften framfor variabelen vi skriver.

  • Et ukjent flagg eller en ukjent nøkkel stopper kjøringen framfor å bli ignorert. --knapp-hjørner med ø er den naturlige norske stavemåten, mens flagget heter hjorner, og temaet kom før ut uten hjørnet og uten et ord. Det samme gjaldt en skrivefeil inne i oppskriftsfila, altså den som kan komme fra et annet repo.

  • Vekt og linjeavstand er tokens. --font-weight-regular, --font-weight-medium, --font-weight-semibold, --font-weight-bold, --semantic-line-height-default, --semantic-line-height-heading, --semantic-line-height-article og --semantic-line-height-compact. Verdiene er nøyaktig dem komponentene hadde skrevet ut fra før, så ingenting ser annerledes ut.

  • Trettisju stilark leser nå vekt og linjeavstand fra tokenene framfor å skrive tallet. Verdiene er de samme, så ingenting ser annerledes ut, men uten dette ville typography.weights og typography.lineHeights i et tema truffet tre komponenter og ikke resten. Knappen, overskriften og avsnittet leser dem gjennom sin egen komponentvariabel, de andre leser tokenet direkte: en egen variabel per komponent er verdt det der man vil kunne skille dem, og støy der man ikke vil.

  • Seks nye komponentvariabler der form sto skrevet ut i stilarket: --fs-button-border-width, --fs-button-font-weight, --fs-button-line-height, --fs-heading-font-weight, --fs-heading-line-height og --fs-paragraph-line-height. Regelen er at form og størrelse leses fra en komponentvariabel med tokenverdien som reserve, og disse tre komponentene brøt den.

0.11.0 (2026-09-23)

Brytende

  • server-filtered heter prefiltered. Navnet var misvisende på to måter. Det handler ikke om servere: en React-app som rendrer bare treffene har filtrert like fullt, uten at noen server er involvert. Og det handler ikke om hvem som filtrerte, men om hva de filtrerte på. Ingen konsumenter ennå, så navnet er byttet framfor å dokumenteres rundt.

  • id er påkrevd i fs.field(), og i de andre byggerne som tar en. Den var valgfri, og funksjonen laget en når den manglet. Det var en felle: id-en er tilfeldig, så to kjøringer gir to ulike, og rendres det samme feltet på en server og så i nettleseren, peker for og aria-describedby på noe annet enn det som står der. React melder avvik ved hydreringen, og advarselen sier selv at avviket ikke blir rettet opp. Det er sett i en ekte app.

    Å kaste når document ikke finnes ble vurdert og forkastet: det er å slutte fra miljøet til hva utvikleren mente. document === undefined betyr bare «dette er ikke en nettleser», og en Astro-side rendres på serveren og hydrerer ingenting, så der er en laget id helt i orden.

    Typen alene er heller ikke nok, for en konsument uten TypeScript ser ingen type, og ren HTML med <script type="module"> er en førsteklasses måte å bruke Fristil på. Utelates id-en likevel, lager byggeren en og sier fra i konsollen, én gang, med navnet på byggeren i meldingen. Uten den reserven fikk en JavaScript-konsument aria-describedby="undefined-help" og verken for eller id, altså verre enn den ustabile id-en kravet skulle bli kvitt.

    Reserven gjelder fs.field(), fs.suggestion(), fs.tabs(), fs.popover() og fs.dialog(). Forslagsfeltet er grunnen til at den ikke kunne bo i feltet alene: uten id ble list.id og options[n].id til undefined-list og undefined-option-0, mens meldingen sa «fs.field()» og sendte utvikleren til feil sted.

    Den tomme strengen teller som ingen id. fs.field({ id: "" }) ga for="" og help.id="-help", altså det samme problemet uten at noe sa fra.

    I React kommer id-en fra useId(). Ellers er feltets eget navn som regel det opplagte valget. Vet du sikkert at markupen rendres én gang, kall createFieldId() selv. Den er nå også eksportert fra @fristil/designsystem/react, siden det er inngangen React-dokumentasjonen ber deg bruke.

    <fs-field> er upåvirket når komponenten er det eneste som kobler feltet: den lager id-er i nettleseren, etter at HTML-en står der. Bruker du React med server-rendring, skal du bruke fs.field(), så koblingen står ferdig i markupen serveren sendte.

  • createFieldId eksporteres også fra @fristil/designsystem/react. Den lå bare i hovedinngangen og i ./field-core, mens dokumentasjonen peker på den i en seksjon som ber leseren importere fra /react.

Rettet

  • Forslagsfeltet sluttet å melde antall treff når noen andre filtrerte. filter() returnerte med en gang, så en app med attributtet mistet opplesningen i [role="status"]. Det sto ikke noe sted, og den som ikke ser skjermen merket det.

    Beskjeden kommer nå av at lista har endret seg, og ikke av tastetrykket. Med prefiltered rendrer appen lista, og mellom tastetrykket og det nye svaret kan det gå et halvt sekund over nettverket. Bare en endring i antallet leses opp; det første antallet er utgangspunktet og ikke en nyhet.

    Tommeldingen er derimot en del av det lista viser, og den eier du med prefiltered. fs.suggestion({ count }) skriver hidden på den når du har treff, så rendrer du feltet for hvert søk, er det gjort. Komponenten kan ikke se forskjell på «søket ga ingenting» og «svaret er ikke kommet ennå», og meldte derfor «Ingen treff» ved fokus på et tomt felt og i hvert opphold i et asynkront søk.

  • prefiltered kunne ikke settes fra React. Getteren hadde ingen setter, og React 19 skriver egenskapen framfor attributtet når et egendefinert element har en med det navnet. Skrivingen kastet «Cannot set property», attributtet landet aldri, og komponenten skjulte det React nettopp hadde rendret. Det gamle navnet server-filtered kunne ikke være et egenskapsnavn, så feilen kom med omdøpingen.

  • prefiltered gjorde ingenting før neste tastetrykk. Attributtet sto i observedAttributes, men tilbakekallet svarte bare på server-controlled. Slås det av mens siden lever, filtrerer komponenten nå med en gang.

Lagt til

  • sjekk-dokumentasjon.ts avviser et eksempel som kaller fs.field() uten id. Ingenting annet i rekka leser kodeblokkene i dokumentasjonen, så et utdatert eksempel kunne stått grønt gjennom hele bun run sjekk. Den leser koden: i mdx kodegjerdene, i Astro frontmatteret og skriptene. I brødtekst teller bare et kall som har en argumentliste, siden fs.field() uten argumenter er navnet på en funksjon og står slik i dusinvis av setninger. Det skillet måtte til: fs.suggestion({ count: treff.length }) sto i en setning under et eksempel og slapp gjennom.

0.10.0 (2026-09-23)

Brytende

  • data-preserve-attr er borte fra hele pakken. fs.tabs(), fs.popover(), fs.suggestion() og fs.dialog() skriver det ikke lenger, og typene deres har ikke feltet. Skriver du markupen for hånd, kan du slette attributtet.

    Lista krevde at malen skrev av navnene på hvert attributt komponenten kom til å røre, ni strenger fordelt på fire komponenter, uten at noen kompilator så på dem. Endret Fristil hva en komponent satte, gikk malen stille i stykker. Komponentene setter nå tilbake det brukeren gjorde, og ingen mal trenger å kjenne attributtene.

Lagt til

  • server-controlled på verten gir serveren tilstanden tilbake. Det fredningen ga, og som reparasjonen måtte erstatte, er at den som skrev markupen kunne bestemme hvem som eier tilstanden. «Gå videre til steg 2» er en ekte ting en server vil kunne gjøre. Står attributtet der, reparerer komponenten ingenting, og hver patch bestemmer. Ett attributt å huske i stedet for ni, og standardvalget er det som er riktig nesten alltid. Gjelder <fs-tabs>, <fs-popover>, <fs-suggestion> og <fs-dialog>, og er deklarert for JSX på alle fire.

  • setAttr, setFlag, setText, addClass, SERVER_CONTROLLED og isServerControlled er nye eksporter fra @fristil/designsystem/host-element. De fire første skriver bare når noe faktisk endrer seg, og er de eneste lovlige veiene til et attributt, et boolsk flagg, tekst og en klasse i en komponent som observerer sine egne. En overtatt komponent bruker dem.

  • scripts/sjekk-skriving.ts håndhever den regelen. Den kjører som del av build, både i pakken og i rota, og leser kilden, fordi regelen ikke lar seg etterprøve ved å kjøre noe: bryter en komponent den, kaller observatøren seg selv, mikrooppgavekøen tømmes aldri, og en testkjøring henger framfor å feile. Ingen stakksporing, ingen påstand, bare en kjøring som må drepes for hånd.

    Den ser etter seks skrivemåter, ikke bare setAttribute: el.hidden = x, el.tabIndex = n, el.htmlFor = s, el.textContent = s, classList.add() og el.style.cssText = s. Alle gir en mutasjonspost når ingenting endrer seg, og fem av dem var i bruk. Verten er ikke et unntak: komponentene observerer seg selv, så this.classList.add() henger en kjøring like godt som en skriving på et barn.

  • sjekk:server kjøres nå også fra rota. Den sto bare i pakkens egen build, så den kjørte ved publisering og ikke i CI, mens CLAUDE.md sa at den var en vaktpost.

  • stabilitet.browser.test.ts teller mutasjoner i hver av de fem komponentene etter at brukeren har gjort noe, og krever null. Den fanger en komponent som skriver litt for mye.

Endret

  • Komponentene setter tilbake det brukeren gjorde. <fs-tabs> setter fanevalget tilbake, <fs-popover> at vinduet er åpent og hvor det står, <fs-suggestion> om lista er utvidet og hva som er markert, og <fs-dialog> open på selve <dialog> når den fortsatt står i topplaget. Alle fire observerer nå de attributtene de selv setter, og hver skriving sammenligner først.

    Sprettoppvinduet reparerer én vei: har noen bedt om at vinduet er åpent, blir det stående, men sender serveren open, åpnes det, for det er noe serveren faktisk sa. En app lukker det med egenskapen, altså meny.open = false, hide() eller toggle(); et attributt fjernet utenfra er ikke til å skille fra en morfing, og der er server-controlled svaret. Dialogen skiller på samme måte mellom open på verten, som er serverens beskjed, og open på <dialog>, som nettleseren setter.

    Komponentene husker identiteter og ikke posisjoner. <fs-tabs> husker selve knappen brukeren valgte: en indeks er ingen identitet, og en id virker ikke i håndskrevet markup, som ofte ikke har noen. En attributtmorfing beholder nodene, så referansen overlever den. <fs-suggestion> husker både id-en og teksten på det markerte alternativet, siden id-ene fra fs.suggestion() er posisjonelle og en ny liste gjenbruker dem. Er det borte, glemmer komponenten det framfor å gjette, og fanene rydder opp etter seg slik at raden fortsatt svarer.

0.9.0 (2026-09-23)

Brytende

  • FIELD_PRESERVED_ATTRIBUTES er fjernet. Lista fantes for at en mal skulle kunne skrive av navnene på alt <fs-field> setter, inn i data-preserve-attr, slik at en morfing lot dem stå. Den er ikke lenger nødvendig: komponenten ser at attributtene er borte, og setter dem tilbake. Bruker du den i dag, kan du ta bort både lista og data-preserve-attr på feltet.

Rettet

  • Sprettoppvinduet åpnet seg igjen når en patch både fjernet open og overlot tilstanden. En morfing setter ett attributt om gangen, og open kommer før server-controlled i dokumentrekkefølgen, så komponenten reparerte mens serveren var midt i å si at den overtar. Reparasjonen venter nå til hele patchen har landet, og sjekker vilkåret på nytt der.

  • Forslagsfeltet satte ikke tilbake aria-activedescendant alene. Rev en patch bare pekeren, mens markeringen sto igjen, så ingenting galt ut i markupen, men skjermleseren hadde mistet lesepunktet sitt. Begge sidene av koblingen sjekkes nå.

  • En fanerad sluttet å svare når en patch fjernet den valgte fanen. <fs-tabs> glemte valget, men lot markupen stå i utakt: ingen fane markert, alle paneler skjult, og et klikk gjorde ingenting, fordi select(0) sammenlignet mot en selected som svarer 0 også når ingenting er markert. Komponenten velger nå den første fanen i det tilfellet, og melder fra med tab-select, siden det er komponentens eget valg og ikke brukerens. Sendte serveren med vilje en rad uten markering, blir den overkjørt, men bare når brukeren hadde valgt noe fra før.

  • Et felt kunne ikke bli gyldig igjen. <fs-field> leser aria-invalid fra kontrollen, fordi serveren kan ha skrevet feltet med fs.field() og da står svaret allerede der. Uten et skille mellom serverens attributt og komponentens eget leste den tilbake sitt eget svar fra forrige runde, så felt.invalid = false fjernet flagget på verten mens den røde rammen og feilmeldingen ble stående.

    Komponenten noterer nå verdien den selv skrev, og hvilken kontroll den ble skrevet på. Står det noe annet der neste gang, har noen andre rørt attributtet, og det er serverens ord som gjelder. Avlesningen er dermed alltid utledet av en endring som faktisk har skjedd, så det samme dokumentet gir alltid det samme svaret. Skrev serveren aria-invalid selv, står det derfor: felt.invalid = false fjerner flagget på verten, men stryker ikke det serveren sa. Bruk den ene av de to kildene, ikke begge.

Endret

  • <fs-field> kobler også en ledetekst som står utenfor elementet. En <label for> som peker på kontrollen navngir feltet like godt som en inni, og komponenten skriver nå fs-label, data-required og aria-disabled på den. Bytter en patch ut kontrollen med en uten id, husker komponenten id-en, så ledetekstens for fortsetter å peke på et element som finnes. Én forskjell er verdt å vite: en ledetekst utenfor ligger ikke i det komponenten observerer, så river en patch klassen av den, kommer den ikke tilbake før neste gang feltet synkroniserer.

  • <fs-field> reparerer sin egen kobling. Bevaringslista var en kontrakt vi ikke kunne kontrollere: en Go- eller Kotlin-mal måtte skrive av ni attributtnavn fra dokumentasjonen, og endret Fristil hva komponenten satte, gikk malen stille i stykker. Komponenten observerer nå de attributtene den selv utleder, og setter dem tilbake i det en patch river dem bort. Id-ene den har laget huskes, så en skjermleser midt i en opplesning ikke følger en peker som skifter under den.

    Skillet er mellom det komponenten har regnet ut og det brukeren har gjort. Det første kan regnes ut på nytt, og repareres. Det andre, som open på et sprettoppvindu eller hvilken fane som er valgt, finnes det ingen kilde til, og en reparasjon ville dessuten kjempet mot en server som med vilje endret noe. Det fredes fortsatt med data-preserve-attr, skrevet av byggefunksjonen.

Lagt til

  • Komponentene sier fra når markupen de fikk ikke henger sammen. Alle disse ga før stillhet, og feilen viste seg først når noen leste siden med skjermleser:

    • <fs-field> uten en kontroll, og uten en ledetekst. Feltet regnes som navngitt av en <label> inni, en <label for> utenfor, eller aria-label og aria-labelledby på kontrollen, som i et søkefelt med bare et ikon;
    • <fs-tabs> uten noe med role="tab", og med færre paneler enn faner;
    • <fs-dialog> uten en <dialog> som direkte barn;
    • <fs-suggestion> uten en combobox, og uten en listboks;
    • <fs-popover> uten et panel, uten en id på panelet, og uten en knapp som peker på det;
    • <fs-error-summary> med punkter som ikke lenker til feltene, og med en lenke som peker på en id som ikke finnes. Den siste meldes når boksen synkroniserer, ikke først når noen klikker: en lenke som ikke fører noe sted er like ødelagt om ingen prøver den.

    Advarselen kommer én gang per element og melding, og først når siden har falt til ro. Det siste er grunnen til at den kan stoles på: HTML som strømmer fra en server leveres i pakker, og et brudd mellom ledeteksten og feltet er helt vanlig, så komponenten ser ofte halvferdig markup i det den kobles til.

  • warnAboutMarkup er en ny eksport fra @fristil/designsystem/host-element. En overtatt komponent bruker den, så den må være tilgjengelig.

  • En vaktpost på at hver komponent kan overtas. sjekk-cli.ts kjører overta på hver komponent verktøyet selv lister opp, og krever at ingen fil i kopien peker ut av mappa, og at hver @fristil/…-henvisning står i exports. Faren har stått skrevet ned lenge: en komponent som henter noe fra en ny mappe uten et inngangspunkt gir en kopi som peker i løse lufta, og feilen viser seg først når konsumenten bygger. Nå sier den fra her.

0.8.3 (2026-09-23)

Rettet

  • <fs-dialog> åpnet igjen en dialog brukeren nettopp hadde lukket. Serveren skriver open begge steder, så innholdet finnes uten JavaScript, og <form method="dialog"> lukker boksen uten JavaScript også. Skjer det før komponenten har fått kjøre, finnes det ingen lytter, og første synkronisering så en vert som sa «åpen» og en lukket dialog. Da spratt dialogen opp igjen. På mobil skjedde det hver gang, fordi vinduet der er langt nok til å rekke et trykk.

    returnValue skiller de to tilfellene: nettleseren setter den til verdien på knappen som lukket dialogen, så en dialog som aldri har vært åpnet har den tom. En mal som bare skriver open på verten åpnes derfor som før. Funnet i spilldemoen, på 375 piksler.

    Det holder bare når knappen har en value. Lukkes dialogen med en knapp uten verdi, etterlater nettleseren ingenting å se etter. Det står nå på dialogsiden, og eksemplene der har alltid hatt en.

0.8.2 (2026-09-23)

Rettet

  • <fs-field> mistet koblingen mellom ledetekst og felt ved første patch. Bytter en morfing ut selve kontrollen, finnes det ingen node å frede, og den nye kommer uten id. Ledeteksten står igjen med sin for. Komponenten fant da ingen id, fant opp en ny, skrev den bare på kontrollen, og for pekte etter det på et element som ikke fantes. Feltet var altså uten ledetekst for en skjermleser, og det holdt seg til siden ble lastet på nytt.

    Id-en leses nå også fra ledetekstens for, og for skrives hver gang, ikke bare når den mangler: de to er den samme opplysningen og kan ikke få si hver sin ting. Funnet i spilldemoen, i appen som sender HTML-biter fra en Kotlin-server.

0.8.1 (2026-09-23)

Endret

  • fs.dialog({ open: true }) skriver open på <dialog> også. Det sto bare på verten, og da var innholdet borte for den som ikke har JavaScript: en <dialog> uten open er skjult. Nå vises det, plassert over innholdet under seg slik nettleserens egen stil gjør det, og komponenten gjør den om til en ekte modal med showModal() når den får kjøre. I React var det dessuten det eneste som stemte: komponenten setter open før React hydrerer, og sto det ikke i serverens HTML, meldte React avvik ved hvert eneste oppslag. Funnet i demoappen i TanStack Start.

    <fs-dialog> tar attributtet bort med removeAttribute framfor close() før den kaller showModal(). close() sender en ekte close-hendelse, og den ville nå kommet ved hver eneste lasting av en åpen dialog. En app som lytter på close rett på <dialog> fikk altså en lukking før brukeren hadde sett dialogen. dialog-toggle så den ikke, siden komponenten stopper på :modal.

    Komponenten ser nå på :modal og ikke på open når den lukker. Byggefunksjonen sender open på <dialog>, så i React er det React som eier attributtet, og React oppdaterer barn før forelder: lukker appen dialogen, er attributtet borte før komponenten får vite det. close() gjør ingenting uten attributtet, så dialogen ble stående i topplaget, usynlig, med resten av siden inert.

    Styrer du dialogen selv fra nettleseren, skal du ikke sende open inn i fs.dialog(). showModal() kaster InvalidStateError på en dialog som alt står åpen, så en app som gjorde begge deler virket før og kaster nå. Det står i typen, i JSDoc-en og på siden.

    Har du JavaScript, ser du dessuten dialogen et øyeblikk som en boks der den står i flyten, før komponenten flytter den til midten som en modal. Før var det ingenting å se før modalen kom.

Rettet

  • fs.field() sa ikke fra om at en id som lages av seg selv er tilfeldig. Rendres feltet både på serveren og i nettleseren, blir det to ulike, og for og aria-describedby peker på noe annet enn det som står der. React melder avvik ved hydreringen. Det står nå i typen, på feltsiden og i React-fanen, med useId() som svaret.

0.8.0 (2026-09-23)

Brytende

  • Boolske attributter på en vert er true, ikke "". fs.dialog().host og fs.popover().host sendte ut open: "". React 19 setter egenskaper framfor attributter på egendefinerte elementer, og el.open = "" er usant, så setteren i komponenten fjernet attributtet igjen: dialogen og panelet åpnet seg ikke, og om det skjedde kom an på om elementet var oppgradert ennå. data-* er noe annet og beholder den tomme strengen, for dem sender React videre som attributter i begge versjoner. Flag i jsx/react.ts er dermed true | undefined igjen, slik dokumentasjonen har sagt hele tiden.

  • Avslaget på fokus i feiloppsummeringen heter data-autofocus="false". Det het autofocus="false". autofocus er en boolsk egenskap på HTMLElement, så React 19 satte el.autofocus = "false", som er sant, mens attributtet aldri kom i markupen. Avslaget virket altså ikke i React 19, og feilen var taus. Samme feilklasse som open over.

  • ReactAttributes<T> gir number for tabIndex. Typen er eksportert, altså offentlig API, og den sa før string. Rettelsen under er grunnen: verdien var feil, og typen beskrev feilen. Kode som tok imot den gamle typen som en streng, for eksempel const t: string = fs.tabs(...).tabs[0] .tabIndex, kompilerer ikke lenger. Selve attributtet i DOM-en er uendret.

Rettet

  • tabIndex kom ut som en streng fra React-inngangen. NAVN døpte om tabindex, men lot verdien stå. I HTML er den en streng, i React er tabIndex et tall, så fs.tabs() ga tabIndex: "0" og TypeScript avviste den i enhver React-app. Den virket i nettleseren, siden React gjør om verdien selv, så feilen viste seg bare som en typefeil hos konsumenten. Funnet i en ekte React-app, ikke her.

  • fs.fieldset lovet en tilstand som ikke fantes. states har alltid oppgitt success, men fieldset.css hadde bare en regel for invalid. Attributtet ble skrevet, og ingenting skjedde.

  • JSX-deklarasjonene godtok ikke det byggerne sender ut. Et boolsk attributt er "" fra byggeren, mens Flag i jsx/react.ts bare tillot true og undefined. <fs-popover {...fs.popover({ open: true }).host}> type-sjekket derfor ikke, altså to deler av det samme API-et som var uenige om det samme attributtet.

Lagt til

  • En vaktpost på at hver lovlig verdi finnes i CSS-en. pakke-css.browser.test.ts går gjennom listene byggerne reklamerer med, variants, colors, sizes, states, pickers, markers og types, kaller byggeren med hver av dem, og rendrer elementet to ganger, med og uten attributtet. Noe i den beregnede stilen må være forskjellig, pseudoelementene medregnet. Et tekstsøk ville passert på en tom regel og på en som blir overstyrt lenger nede. Farger regelen et barn framfor elementet selv, som feltsettet gjør med .fs-legend, kreves det i stedet at en regel som gjelder her erklærer noe. Uten det ville en tom blokk passert, og det var nettopp en tom blokk som slapp gjennom første utgave av prøven.

    Verdier som ikke sender ut noe attributt hoppes over: standardverdien, og verdier som ikke er ment å se annerledes ut. To egne prøver passer på at hoppelista ikke blir en bakdør. Den ene krever at opsjonsnavnet er et byggefunksjonen kjenner, siden feil navn ellers tar en hel liste ut av prøven i stillhet. Den andre krever at hver liste en byggefunksjon har, står i tabellen.

    Hvilke regler som gjelder hentes fra CSSOM, ikke fra teksten: en regel i en @supports motoren ikke har, eller i en @media som ikke slår til, gjør ingenting. Den stylede nedtrekkslista står i begge, og skal ikke endre noe i Firefox.

    dom.browser.test.ts sjekket at en verdi kan settes og fjernes, ikke at den betyr noe. Fieldset-feilen over hadde ligget der siden komponenten kom.

  • En vaktpost på at ingen byggefunksjon sender ut et navn React staver annerledes. react.browser.test.ts kalte før ti byggefunksjoner for hånd og så bare etter class og for. Den går nå gjennom hver byggefunksjon i /react, med hver lovlige verdi funksjonen selv oppgir, og avviser hvert navn i Reacts egen tabell, også readonly, maxlength og colspan, som ingen byggefunksjon sender ut ennå. Den krever også at hver byggefunksjon i fs finnes i /react.

  • En vaktpost på at byggerne passer i JSX-deklarasjonene. src/jsx/typer.browser.test.ts tilordner det byggerne sender ut til JSX.IntrinsicElements, og det er typecheck:tests som er prøven. Begge deler er offentlig API, og de kan gå fra hverandre uten at noe sier fra.

0.7.0 (2026-09-22)

Brytende

  • fs.dialog() tar nå et valgobjekt og gir ett attributtsett per element. Den ga før { class: "fs-dialog" } til selve <dialog>. Nå heter kallet fs.dialog({ titleId, open }) og gir host, dialog, title, body og footer, på samme form som fs.popover(). Klassenavnene ligger fortsatt på funksjonen: fs.dialog.title og de andre.

  • Dialogen har flyttet fra css/ til ramme/, siden den nå har en komponent. @fristil/designsystem/dialog peker derfor på <fs-dialog> og ikke lenger på byggefunksjonen; den hentes fra hovedinngangen, som for de andre sammensatte komponentene. @fristil/designsystem/dialog.css er uendret.

Lagt til

  • <fs-dialog> lar en server åpne en dialog. Å åpne en <dialog> er et kall og ikke et attributt: bare showModal() flytter fokus inn, holder fokus inne i dialogen, lukker på Escape og gjør resten av siden utilgjengelig. <dialog open> er bare en boks på siden. En app som skriver HTML på serveren, i Datastar, htmx, en Go-mal eller en Razor-visning, kunne derfor ikke åpne en dialog i det hele tatt uten å skrive sitt eget skript ved siden av. Nå sier serveren open på <fs-dialog>, og komponenten gjør kallet.

    Lukker brukeren dialogen, fjernes open fra verten igjen, så markupen sier det samme som skjermen. De to open-ene er ikke det samme: det på verten er serverens beskjed og er ikke fredet, for ellers kunne serveren aldri åpnet dialogen igjen etter første lukking. Det nettleseren setter på selve <dialog> når showModal() kalles, er derimot fredet, ellers river morfingen det bort og lukker dialogen i samme øyeblikk som den åpnet den. morfing.browser.test.ts kjører hele runden.

    Komponenten melder fra med hendelsen dialog-toggle, som bærer open og returnValue, altså hvilken knapp som lukket dialogen. Den er composed, så den kommer ut av en skyggerot.

    Komponenten gjør ingenting når den står løsrevet fra siden. En morfer bygger serverens utgave i et løsrevet tre før den sammenlignes, og et egendefinert element tas i bruk der også; uten den sperren kastet showModal() ved hver patch som rørte dialogen.

Rettet

  • color-scheme i tokens.css. Uten den tegner nettleseren sine egne flater lyst uansett hva tokenene sier: nedtrekkslista til en <select>, rullefelt og kalenderpanelet i et datofelt. En side i mørkt tema fikk da en hvit liste midt i seg. :root sier nå light dark, så systemvalget avgjør, og data-theme setter den til light eller dark slik at nettleserens flater følger med når appen tvinger fram et tema.

    Dette ble funnet i en demoapp, ikke i enhetstestene. Det er bare nettleserens eget utseende som røper det.

  • <fs-field> sier selv at det er en blokk. Et egendefinert element er display: inline til noen sier noe annet, og feltet var det eneste som pakker inn andre elementer uten å si det. Barna er blokker, så det så riktig ut helt til noen ga elementet en avstand eller la det i et rutenett. <fs-suggestion> har alltid sagt det samme om seg selv.

0.6.1 (2026-09-22)

Lagt til

  • --semantic-focus-ring samler fokusringen på ett sted. Den sto skrevet ut med bredde og farge i tjue regler fordelt på atten stilark. Selektorene er forskjellige i hver komponent, og outline-offset skal være ulik, så det som faktisk gjentok seg var verdien. Nå skriver hver komponent outline: var(--semantic-focus-ring), og en konsument som vil ha en annen fokusring endrer ett token framfor atten filer.

    De tre stedene som med vilje har en annen farge, feiloppsummeringen og hopplenken, skriver outline-color etter kortformen og arver bredden. Da følger også de med hvis bredden endres.

    En ny vaktpost i pakke-css.browser.test.ts avviser en outline med solid skrevet ut i et komponentstilark.

0.6.0 (2026-09-22)

Brytende

  • fs.popover() sender ikke lenger ut slot="trigger". Attributtet var et levn fra den gangen komponenten hadde shadow DOM. Uten en skyggerot gjør slot ingenting i HTML, så det var en merkelapp som så ut som noe annet enn det var, og dokumentasjonen ba leseren skrive noe nettleseren ignorerte.

    <fs-popover> kjenner nå igjen delene på koblingen som må være der uansett: panelet er det som har popover, og knappen er den som peker på panelet med aria-controls. Begge deler skriver fs.popover() fra før, så markup laget med byggefunksjonen virker uendret, og et slot-attributt som blir stående fra en server som ikke er oppdatert, gjør ingen skade.

    Skriver du markupen for hånd, må knappen ha aria-controls med panelets id, og panelet må ha popover. Det er de samme to tingene tilgjengeligheten krever.

    Typen PopoverAttributes har mistet slot fra trigger.

0.5.3 (2026-09-22)

Rettet

  • Pakken lar seg importere på en server. class X extends HTMLElement blir evaluert i det modulen lastes, og HTMLElement finnes bare i nettleseren. Ni av inngangspunktene stoppet derfor med «HTMLElement is not defined» på en server uten DOM, hovedinngangen @fristil/designsystem inkludert. Det gjorde import { fs } from "@fristil/designsystem" umulig i en Node- eller Bun-server, altså nøyaktig den linja «Kom i gang» ber leseren skrive, i nettopp den situasjonen systemet er bygd for.

    Web-komponentene arver nå fra HostElement i det nye inngangspunktet @fristil/designsystem/host-element, som faller tilbake på en tom klasse når HTMLElement ikke finnes. defineFs* gjør ingenting når det ikke finnes noen customElements å registrere i, så et kall fra en modul som kjøres begge steder er trygt.

    Ingen nettlesertest kunne se dette, for der finnes HTMLElement. En ny vaktpost, scripts/sjekk-server-import.ts, kjører i Bun uten DOM og importerer hver JavaScript-oppføring i exports fra dist.

0.5.2 (2026-09-22)

Lagt til

  • <select> kan tegne nedtrekkslista inne i siden. fs.select({ picker: "styled" }) gir data-picker="styled", og da bruker feltet appearance: base-select: lista blir et vanlig element i siden i stedet for et vindu fra operativsystemet, og får Fristils farger, avstander og skygge. Elementet er fortsatt en helt vanlig <select>, så innsending, tastatur og skjermleser er uendret.

    Det må slås på, og det er med vilje. Chromium 148 og WebKit 26.4 har appearance: base-select; Firefox 150 har det ikke og viser nettleserens egen liste som før. Begge deler er testet i hver sin motor. I høykontrastmodus faller alle tre tilbake til nettleserens egen liste, siden systemfargene er de eneste som er garantert lesbare der.

    Nye variabler: --fs-select-option-padding og --fs-select-viewport-margin. --fs-select-radius gjelder nå også lista.

Rettet

  • FIELD_PRESERVED_ATTRIBUTES dekker nå hjelpeteksten og feilmeldingen. Skriver malen ingen id-er, lager <fs-field> dem selv, og aria-describedby på kontrollen peker på dem. id sto ikke i noen liste, så en morfing fjernet den, og koblingen pekte på noe som ikke fantes: verken hjelpeteksten eller feilmeldingen ble lest opp. Lista har nå en help-nøkkel, error har fått id og ledeteksten har fått for.

  • <fs-error-summary> tar fokus hver gang boksen vises på nytt. Den tok det bare første gang. Mønsteret i en Datastar-app er at serveren sender boksen skjult og bare slår hidden av og på, mens lista står med de samme lenkene, og da nullstilte ingenting flagget. Andre gang det samme skjemaet feilet, ble brukeren stående i feltet uten å få vite hvorfor innsendingen ikke gikk gjennom. Flagget nullstilles nå når boksen skjules.

  • <fs-suggestion> leser hvilket alternativ som er markert fra markupen. Komponenten holdt en egen teller ved siden av aria-selected. Satte serveren markeringen selv med fs.suggestion({ activeIndex }), hoppet første piltast til toppen av lista i stedet for til alternativet etter, og markeringen serveren nettopp sendte var borte.

  • Pil opp åpner lista, og går til det siste alternativet. Den markerte før et alternativ uten å åpne lista, så aria-activedescendant pekte inn i noe feltet samtidig meldte som lukket, og skjermleseren leste opp et alternativ som ikke sto på skjermen. Uten noe markert fra før gikk den dessuten til det nest siste, ikke det siste.

  • fs.suggestion().empty bevarer hidden gjennom en oppdatering. Komponenten skjuler «Ingen treff» når noe passer, men elementet hadde ingen data-preserve-attr, så teksten dukket opp igjen ved hver patch, også midt i en liste med treff. Byggefunksjonen sender nå hidden når serveren har noe å velge mellom, og navnet står i lista.

    Begge ble funnet av en ny vaktpost, morfing.browser.test.ts, som kjører Datastars egen attributtsynkronisering mot ekte markup etter at komponenten har gjort jobben sin. Den dekker felt, forslagsfelt, faner og sprettoppvindu.

  • .fs-select snur riktig i språk som skrives fra høyre mot venstre. Feltet holdt av plass til pila si med en padding på fire verdier, altså over, høyre, under og venstre. De to fysiske sidene snur ikke med språket, så i RTL lå plassen på feil side og teksten la seg oppå pila. Plassen settes nå logisk, og pila, som er to gradienter og derfor ikke har noen logisk variant, speilvendes med :dir(rtl). Nedtrekkslista var ikke dekket av retningstesten i det hele tatt, og er det nå. En ny vaktpost i pakke-css.browser.test.ts avviser fysiske kortformer med fire verdier i alle stilark.

  • fs.setAttributes fjerner nå data-size og data-picker igjen. Den rydder bare i en lukket liste av attributtnavn, og de to sto utenfor. En avatar eller en overskrift som gikk tilbake til standardstørrelsen beholdt altså data-size fra forrige kall, og fikk aldri standardutseendet sitt igjen. En ny vaktpost kaller hver byggefunksjon med hver lovlige verdi den selv oppgir, regner et attributt som valgfritt når det ikke er med i kallet uten argumenter, og krever at hvert av dem lar seg fjerne.

0.5.1 (2026-09-21)

Rettet

  • React-inngangen døper nå også om autocomplete til autoComplete. fs.suggestion() sender det ut på kontrollen, og React advarte «Invalid DOM property autocomplete» i konsollen for hvert forslagsfelt. En ny vaktpost kaller hver byggefunksjon, teller opp hvert attributtnavn de sender ut, og krever at ingen av dem er et navn React staver annerledes uten at react.ts døper det om.

  • <fs-session-timeout> bygger dialogen sin først når den skal vises. Den lagde den i connectedCallback, altså før React rakk å hydrere, og hydreringen feilet med «server rendered HTML didn’t match the client». Nå gjør den det samme som <fs-toast> og <fs-connection-status>: ingenting står i DOM-en før det trengs.

Endret

  • fs.toast() er dokumentert. Byggefunksjonen fantes, men sto ikke på komponentsiden. Den gir tre attributtsett, og det er region du sprer på <fs-toast>, ikke hele objektet.

0.5.0 (2026-09-21)

Brytende

  • Serveren skriver markupen, komponentene fester bare oppførsel. Seks komponenter satte tidligere klasser, roller og tilgjengelighetskoblinger på elementer serveren hadde sendt. Det virker ikke i en app med server-rendret HTML: Datastars morfing fjerner hvert attributt som ikke står i HTML-en serveren nettopp sendte, og React kan kaste bort noder et egendefinert element har lagt inn i et tre React eier. Testet med ekte Datastar mistet et <fs-field> både aria-describedby, klassen på ledeteksten og den skjulte feilmeldingen etter én patch, og fikk dem ikke tilbake.

    Markupen skrives nå av nye byggere i fs: fs.errorSummary(), fs.popover(), fs.tabs(), fs.suggestion() og fs.toast(). Byggerne setter også data-preserve-attr for de attributtene komponenten endrer underveis, så du slipper å vite om det. Se «Server først» i dokumentasjonen.

  • <fs-calendar> og <fs-date-field> er fjernet. Nettleserens eget panel i <input type="date"> gjør jobben, og fs.field() sammen med fs.input({ type: "date" }) gir et komplett datofelt. Mønstersiden «Dato i et skjema» viser oppsettet, med de to forskjellene vi har testet mellom nettleserne. exports-oppføringene ./calendar, ./date-field og ./date-field.css er borte, og det samme er --fs-calendar-*- og --fs-date-field-*-variablene og alle part-navnene i kalenderen.

  • <fs-suggestion> er flyttet fra frittstående til ramme. Den lagde tidligere hele feltet selv, så det fantes verken ledetekst eller inndatafelt før skriptet hadde kjørt, og ingenting ble med i innsendingen. Nå skriver serveren feltet og lista med fs.suggestion().

  • <fs-tabs> har ingen selected- eller label-attributter lenger. Hvilken fane som er valgt står i markupen serveren sendte. <fs-error-summary> har ikke lenger heading: overskriften skrives av serveren.

  • Ingen komponent bruker shadow DOM. ::part() er dermed ikke lenger en del av det offentlige API-et.

  • <fs-field> leser tilstanden fra markupen, ikke fra egne attributter. Skrev serveren feltet med fs.field(), står aria-invalid og data-required allerede på elementene. Komponenten regnet tidligere ut sitt eget svar fra attributtene på verten, fant ingenting der, og fjernet det serveren nettopp hadde skrevet. De to halvdelene av API-et kranglet altså med hverandre når de ble brukt sammen. Den setter heller ikke lenger aria-hidden på feilmeldingen: hidden tar den allerede ut av tilgjengelighetstreet, og attributtet ga hydreringsfeil i React.

Lagt til

  • <fs-session-timeout> varsler før en innlogget økt går ut, med en nedtelling som leses opp ved terskler i stedet for hvert sekund, og fokus som går tilbake dit brukeren var.

  • <fs-connection-status> sier fra når forbindelsen til serveren er borte. navigator.onLine sier bare at maskinen har et nettverk, så reportFailure() og reportSuccess() lar appen melde fra selv.

  • Siden «Server først» forklarer hvorfor markupen kommer fra serveren, med testene bak, og hva det betyr i TanStack Start, React Server Components og Datastar.

  • Mønstersiden «Dato i et skjema» viser hvordan du ber om en dato uten en datovelger fra oss, med de to forskjellene vi har testet mellom nettleserne: WebKit krever skilletegnene, og Chromium tar imot feil rekkefølge som gyldig.

  • FIELD_PRESERVED_ATTRIBUTES er lista serveren skal skrive som data-preserve-attr når den ikke kan kalle fs.field() selv.

Rettet

  • <fs-toast duration="0"> lot ikke meldingene bli stående. Attributtet ble lest som «ugyldig» og falt tilbake til seks sekunder, så bare show(…, { duration: 0 }) virket.

  • <fs-error-summary> fikk ikke fokus når serveren bare tok bort hidden. Komponenten så etter endringer i barna, ikke i attributtene, så en oppsummering som nettopp ble synlig ble stående uten fokus.

  • <fs-error-summary> finner nå feltet i sin egen rot. Oppslaget gikk mot document, som ikke ser inn i en skyggerot, så lenken ble en vanlig ankerlenke uten fokusflytting når skjemaet lå inne i en annen komponent.

  • FIELD_PRESERVED_ATTRIBUTES dekker alle tre elementene. Den dekket bare kontrollen, men komponenten legger også fs-label og markeringene på ledeteksten og skjuler feilmeldingen. Serveren må derfor skrive én liste per element, og konstanten har nå label, control og error.

  • <fs-popover> lot seg ikke åpne etter at den var flyttet i DOM-en. Referansen til knappen ble stående, så lytteren ble aldri festet på nytt.

  • fs.popover({ open: true }) sier det nå på verten. Før satte den bare aria-expanded på knappen, så markupen meldte at panelet var åpent mens komponenten mente det var lukket.

  • <fs-suggestion> mistet musevalg etter at serveren sendte en ny liste. Alternativene fikk bare lytteren sin når selve feltet var nytt.

Endret

  • React-inngangen døper også om tabindex til tabIndex, og dekker de nye sammensatte byggerne. fs.errorSummary(), fs.popover(), fs.tabs(), fs.suggestion() og fs.toast() finnes nå også i @fristil/designsystem/react, der hvert attributtsett er døpt om for seg. Pakken har fortsatt ingen avhengighet til React: dette er ren omdøping av tre objektnøkler.

  • lit er ikke lenger en avhengighet. Etter omleggingen rendrer ingen komponent sitt eget innhold, så Lit ble brukt til nesten ingenting. Komponentene er vanlige HTMLElement-klasser, og pakken har nå ingen avhengigheter i det hele tatt.

0.4.0 (2026-09-21)

Endret

  • lit er en vanlig avhengighet, ikke en valgfri peerDependency. Du trenger bare npm install @fristil/designsystem, og ikke lenger vite at Lit finnes. Bygget ditt er upåvirket: ingenting registreres ved import alene, så Lit kommer først med når du kaller en defineFs*.

0.3.0 (2026-09-21)

Rettet

  • sjekk-eksport.ts leser svaret fra npm pack --json i begge formene npm bruker. npm 11 svarer med en liste, npm 12 med et objekt der pakkenavnet er nøkkelen, og arbeidsflyten som publiserer henter alltid nyeste npm. Utgivelsen stoppet derfor på «{} is not iterable», med en feil som pekte på vår egen kode framfor på npm. Skriptet sier nå fra med npm sin egen feilmelding når pakkingen feiler.

  • bin peker på dist/cli.js uten ./ foran. npm rettet det selv ved publisering og advarte om at navnet var «invalid and removed», som leste som om kommandolinja forsvant. Den var med hele tiden.

0.2.0 (2026-09-21)

Lagt til

  • fristil overta <komponent> kopierer kildekoden til én komponent inn i prosjektet ditt, og skriver om henvisningene ut av mappa. Til bruk når tilpasning gjennom CSS ikke strekker til. Kopien er din, og oppdateringer av pakken rører den ikke.

  • fristil --hjelp (og --help, -h, help) skriver ut hva kommandoen kan. Uten argumenter gjorde den før et forsøk på å lage et tema, og klaget over manglende farger uten å nevne at overta fantes.

  • Pakken kan publiseres på npm. Den har lisens (MIT), en README som blir forsiden på npm, og feltene repository, homepage, bugs og keywords. publishConfig sier public, siden en pakke med navnerom ellers blir privat, og prepublishOnly bygger, så en publisering fra et rent klon ikke kan komme ut uten dist.

  • bun run sjekk:pakke kjører publint mot pakken, som en del av bygget. Den leser exports slik nettlesere, byggverktøy og TypeScript faktisk gjør det.

Rettet

  • types står først i hver oppføring i exports. Betingelsene leses i rekkefølge, og import sto først, så TypeScript fant typene bare fordi .d.ts-fila lå ved siden av .js-fila. Stiene er de samme som før.

  • Kommandolinja svarer med en forklaring i stedet for et stakkspor fra Node når kommandoen er ukjent, eller når temafila mangler eller ikke er JSON.

  • Lista over valgte filer i file-upload brekker nå til flere linjer på smal skjerm. Raden var bredere enn en telefon når filnavnet var langt, og knappen «Fjern » ble klippet av.

  • <fs-error-summary> oppdaterer overskriften når heading endrer seg. Den ble bare skrevet første gang, så en oppsummering som talte ned fra to feil til én ble stående på to.

0.1.0 (2026-09-21)

Første versjon.

Lagt til

  • Tokens for farge, størrelse, typografi, radius og skygge, med semantiske navn (--semantic-*) over en råskala. Lyst og mørkt tema, og en egen fil for Tailwind 4.
  • 34 CSS-komponenter som bare trenger en klasse og data-*-attributter: accordion, alert, avatar, badge, breadcrumbs, button, card, checkbox, dialog, divider, error-text, fieldset, file-upload, heading, help-text, input, label, link, list, pagination, paragraph, radio, search, select, skeleton, skip-link, spinner, sr-only, switch, table, tag, textarea, toggle-group og tooltip.
  • Fire rammekomponenter som kobler sammen markupen du selv skriver: <fs-field>, <fs-error-summary>, <fs-popover> og <fs-tabs>.
  • Fire frittstående komponenter som eier markupen og interaksjonen: <fs-calendar>, <fs-date-field>, <fs-suggestion> og <fs-toast>.
  • fs-API-et: én funksjon per komponent som tar et valgobjekt og gir attributter tilbake, med lovlige verdier og vakt hengt på funksjonen. En egen inngang for React (@fristil/designsystem/react) med className og htmlFor, og JSX-deklarasjoner for de egendefinerte elementene.
  • fristil-kommandoen som bygger et tema fra egne farger, sjekker kontrasten mot WCAG og skriver ut CSS-en.
  • Tilpasning utenfra: all CSS ligger i @layer fristil, form og størrelse leses fra --fs-*-variabler med tokenverdien som reserve, og komponenten med shadow DOM eksponerer delene sine med ::part().