Typesikker bruk
Klasser og dataattributter gjør komponentene rammeverksuavhengige, men alt er strenger:
<button class="fs-button" data-variant="secundary">Lagre utkast</button>Knappen får primærstilen. Ingen feilmelding, bare en variant som stille aldri ble brukt. Derfor finnes fs.
Navnerommet fs
Section titled “Navnerommet fs”import { fs } from "@fristil/designsystem"
<button {...fs.button({ variant: "secondary" })}>Lagre utkast</button><input {...fs.input({ type: "email", state: "invalid" })} /><span {...fs.badge({ color: "success" })}>Innvilget</span><a {...fs.link()} href="/kvittering">Se kvittering</a>Skriv fs. i editoren, så ser du hele systemet. Hver komponent er en funksjon som tar et valgobjekt og returnerer attributtene du sprer inn i elementet. Formen er den samme for alle.
Returverdiene er vanlige objekter. Derfor virker samme API i React, Astro, Datastar og ren HTML.
I vanlig DOM
Section titled “I vanlig DOM”I JSX sprer du objektet rett inn i elementet. Uten JSX gjør fs.setAttributes jobben:
const felt = document.querySelector<HTMLInputElement>("#epost")
fs.setAttributes(felt, fs.input({ type: "email", state: "invalid" }))Kall den på nytt for å endre tilstand. Attributter fra forrige kall som ikke er med i det nye settet blir fjernet. Ellers ville feltet blitt stående rødt etter at feilen var rettet. Den rydder bare i systemets egne attributter og fs--klasser; data-testid, name og dine egne klasser står urørt.
Standardverdier
Section titled “Standardverdier”fs.button()// { class: "fs-button" }
fs.button({ variant: "primary" })// { class: "fs-button" }, primær er standard i CSS-en
fs.button({ variant: "danger" })// { class: "fs-button", "data-variant": "danger" }Du sier alltid hvilken variant du vil ha; funksjonen vet når attributtet kan utelates.
Lovlige verdier og vakter
Section titled “Lovlige verdier og vakter”fs.button.variants// readonly ["primary", "secondary", "ghost", "danger"]
fs.button.isVariant("ghost") // truefs.button.isVariant("gost") // falseSamme mønster overalt: fs.badge.colors og fs.badge.isColor, fs.input.types og fs.input.isType.
Kommer verdien fra et API, en URL eller localStorage, er den en string TypeScript ikke kan garantere noe om. Vakten gir en gyldig verdi eller en trygg reserve:
const svar = await hentKnappeoppsett()const variant = fs.button.isVariant(svar.variant) ? svar.variant : "primary"
fs.button({ variant })Etter vakten vet TypeScript at verdien er en ButtonVariant.
fs.input
Section titled “fs.input”Skrevet for hånd må type og data-variant stemme overens, ellers skjer ingenting når brukeren trykker på ikonet.
fs.input({ type: "date" })// { class: "fs-input", type: "date", "data-variant": "date" }
fs.input({ type: "email" })// { class: "fs-input", type: "email" }, e-post har ikke noe ikonÉn verdi styrer begge. Det samme gjelder tilstand: state: "invalid" gir både data-state og aria-invalid.
Ett begrep, én betydning
Section titled “Ett begrep, én betydning”| Valg | Hva det betyr | Hvor |
|---|---|---|
variant | Visuell vekt eller betydning | button, helpText, errorText |
color | Hva en status betyr | badge |
state | Valideringstilstand | input, textarea, select |
type | HTML-typen på feltet | input |
state er én type på tvers av de tre feltkomponentene, så en verdi kan sendes mellom dem uten konvertering:
const tilstand = fs.isState(fraServer) ? fraServer : "default"
fs.input({ state: tilstand })fs.textarea({ state: tilstand })fs.select({ state: tilstand })fs.field
Section titled “fs.field”Fem koblinger må stemme samtidig. fs.field regner dem ut:
const felt = fs.field({ id: "epost", required: "symbol", help: true, error: true, invalid: !erGyldig,})
<label {...felt.label}>E-postadresse</label><input {...fs.input({ type: "email" })} {...felt.control} /><p {...fs.helpText()} {...felt.help}>Vi sender kvittering hit.</p><p {...fs.errorText()} {...felt.error}>Skriv en gyldig adresse.</p>Uten JSX:
fs.setAttributes(ledetekst, felt.label)fs.setAttributes(input, { ...fs.input({ type: "email" }), ...felt.control })fs.setAttributes(hjelpetekst, { ...fs.helpText(), ...felt.help })fs.setAttributes(feilmelding, { ...fs.errorText(), ...felt.error })Du får for, id, aria-describedby, aria-invalid og data-state ferdig utregnet.
Legg merke til at fs.input bare får type. Tilstanden ligger i felt.control, og de to overlapper med vilje ikke. Sendte du state til begge, ville aria-invalid blitt skrevet ut to ganger i maler som gjengir attributtene bokstavelig, som Astro og ren HTML. felt.state finnes fortsatt hvis du vil drive noe annet med den.
Feilmeldingen tas med i aria-describedby bare når feltet er ugyldig. Ellers ville skjermleseren pekt på et skjult element. felt.error inneholder hidden når den ikke skal vises.
Gjenbrukbar knapp i React
Section titled “Gjenbrukbar knapp i React”import { fs } from "@fristil/designsystem/react"import type { ButtonVariant } from "@fristil/designsystem"import type { ComponentProps } from "react"
type Props = ComponentProps<"button"> & { variant?: ButtonVariant}
export function Knapp({ variant, ...rest }: Props) { return <button {...fs.button({ variant })} {...rest} />}Bytter teamet rammeverk, er det disse tolv linjene som skrives om, ikke designsystemet.
ComponentProps<"button"> tar med ref, som siden React 19 er en vanlig prop og følger med gjennom {...rest} uten forwardRef. ComponentPropsWithRef og ComponentPropsWithoutRef trengs ikke lenger.
Web-komponentene
Section titled “Web-komponentene”TypeScript kjenner ikke <fs-field>, <fs-session-timeout> og <fs-connection-status> uten videre. Importer typene én gang i en .d.ts-fil:
import "@fristil/designsystem/react-jsx"Da får attributtene autofullføring og sjekk:
<fs-field required-marker="text" /> // ok<fs-field required-marker="tekst" /> // feil: "none" | "symbol" | "text"<fs-session-timeout warnAt={1500} /> // feil: attributtet heter warn-at<fs-connection-status offline="Nede" /> // feil: ukjent attributtRen HTML, Astro og Datastar trenger ikke dette. Der er <fs-field> bare en HTML-tagg.
Hva typene ikke fanger
Section titled “Hva typene ikke fanger”Attributtene kan spres på feil element. <div {...fs.button({ variant: "danger" })} /> kompilerer, for returverdien er bare et objekt med HTML-attributter.
Returtypen på type er bredere enn verdien du sendte inn. fs.input({ type: "date" }).type er hele unionen, ikke "date".
Uten TypeScript
Section titled “Uten TypeScript”fs er valgfritt. Uten TypeScript eller byggesteg er dette like gyldig:
<button class="fs-button" data-variant="secondary">Lagre utkast</button>Resultatet er identisk. Forskjellen er hvor tidlig du får vite at du skrev secundary.
Vil du bare ha én komponent:
import { button } from "@fristil/designsystem/button"Subpath-ene returnerer class og for, som hovedinngangen. React-varianten finnes bare som samlet fs.