Toast
Elementet er beholderen, ikke meldingen. Du legger det inn én gang i appen og kaller show() når noe skal meldes.
Beholderen er en role="status"-region, ikke role="alert". En melding som dukker opp i hjørnet skal ikke avbryte det skjermleseren holder på med. Er beskjeden så viktig at den må avbryte, hører den hjemme i en Alert i selve siden.
// main.ts, kjøres én gang når appen starterimport { defineFsToast } from "@fristil/designsystem/toast"import "@fristil/designsystem/tokens.css"import "@fristil/designsystem/toast.css"
defineFsToast()Slik tar du den i bruk
Section titled “Slik tar du den i bruk”Beholderen legges inn én gang, og meldingene kommer fra koden. Det er forskjellen fra de andre komponentene: her kaller du en metode framfor å sette et attributt.
<!-- Stilarkene siden trenger. Uten byggesteg er det `<link>`, ikke `import`: en nettleser slår ikke opp et pakkenavn. --><link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@fristil/designsystem@0.27.1/src/tokens/tokens.css"><link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@fristil/designsystem@0.27.1/src/components/frittstaende/toast/toast.css"><link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@fristil/designsystem@0.27.1/src/components/css/button/button.css"><!-- Eller alt i én fil: https://cdn.jsdelivr.net/npm/@fristil/designsystem@0.27.1/dist/fristil.css --><fs-toast id="meldinger" role="status" aria-live="polite" aria-atomic="false" aria-label="Meldinger" data-ignore-morph></fs-toast>
<button class="fs-button" id="lagre" type="button">Lagre søknaden</button>
<!-- Én gang for hele appen, i sidemalen. Ikke én gang per komponent. --><script type="module"> import { defineFsToast } from "https://cdn.jsdelivr.net/npm/@fristil/designsystem@0.27.1/dist/components/frittstaende/toast/fs-toast.js"
defineFsToast()</script>
<script type="module"> const meldinger = document.getElementById("meldinger")
document.getElementById("lagre").addEventListener("click", async () => { try { await lagre() meldinger.show("Søknaden er lagret", { color: "success" }) } catch { meldinger.show("Klarte ikke å lagre. Prøv igjen.", { color: "danger", duration: 0 }) } })</script>En feilmelding står til brukeren lukker den, derfor duration: 0.
// src/fristil.d.ts, gir <fs-toast> typer i JSXimport "@fristil/designsystem/react-jsx"// main.tsx, kjøres én gang når appen starterimport { defineFsToast } from "@fristil/designsystem/toast"import "@fristil/designsystem/toast.css"import "@fristil/designsystem/button.css"
defineFsToast()Legg beholderen i rotkomponenten, og del referansen med en context:
import { createContext, useContext, useRef } from "react"import { fs } from "@fristil/designsystem/react"import { type FsToast, showToast } from "@fristil/designsystem/toast"import "@fristil/designsystem/tokens.css"
const varsler = fs.toast({ label: "Meldinger" })
const Meldinger = createContext<(tekst: string, farge?: "success" | "danger") => void>( () => {},)
export function MeldingerProvider({ children }: { children: React.ReactNode }) { const kø = useRef<FsToast>(null)
const meld = (tekst: string, farge?: "success" | "danger") => { if (!kø.current) return showToast(kø.current, tekst, { color: farge, duration: farge === "danger" ? 0 : undefined }) }
return ( <Meldinger.Provider value={meld}> {children} <fs-toast ref={kø} {...varsler.host} /> </Meldinger.Provider> )}
export const useMeldinger = () => useContext(Meldinger)const meld = useMeldinger()
<button {...fs.button()} onClick={() => meld("Søknaden er lagret", "success")}> Lagre søknaden</button>Meldingene lever i DOM-en, ikke i Reacts tilstand. Det er med vilje: de skal ikke forsvinne fordi en komponent ble rendret på nytt. showToast() venter på at defineFsToast() har kjørt, så et kall før registreringen blir gjort i det den er der, i stedet for å feile.
---import { fs } from "@fristil/designsystem"import "@fristil/designsystem/tokens.css"import "@fristil/designsystem/toast.css"
// Legg beholderen i sideoppsettet, så finnes den på hver side
const varsler = fs.toast({ label: "Meldinger" })
---
<slot />
<fs-toast id="meldinger" {...varsler.host}></fs-toast>
<script> import { defineFsToast } from "@fristil/designsystem/toast" defineFsToast()
// Gjør køen tilgjengelig for øyene på siden window.meld = (tekst, valg) => document.getElementById("meldinger").show(tekst, valg)</script>Beholderen er tom fra serveren, og fylles bare av kode som kjører i nettleseren. Den koster derfor ingenting på en side som ikke bruker den.
<!-- Datastar selv. Uten den gjør `data-`-attributtene under ingenting. --><script type="module" src="https://cdn.jsdelivr.net/gh/starfederation/datastar@v1.0.4/bundles/datastar.js"></script>
<!-- Stilarkene siden trenger. Uten byggesteg er det `<link>`, ikke `import`: en nettleser slår ikke opp et pakkenavn. --><link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@fristil/designsystem@0.27.1/src/tokens/tokens.css"><link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@fristil/designsystem@0.27.1/src/components/frittstaende/toast/toast.css"><link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@fristil/designsystem@0.27.1/src/components/css/button/button.css"><!-- Eller alt i én fil: https://cdn.jsdelivr.net/npm/@fristil/designsystem@0.27.1/dist/fristil.css -->
<fs-toast id="meldinger" role="status" aria-live="polite" aria-atomic="false" aria-label="Meldinger" data-ignore-morph></fs-toast>
<button class="fs-button" type="button" data-on:click="document.getElementById('meldinger').show('Søknaden er lagret', { color: 'success' })"> Lagre søknaden</button>
<!-- Én gang for hele appen, i sidemalen. Ikke én gang per komponent. --><script type="module"> import { defineFsToast } from "https://cdn.jsdelivr.net/npm/@fristil/designsystem@0.27.1/dist/components/frittstaende/toast/fs-toast.js"
defineFsToast()</script>Meldingene er ikke en tilstand som skal speiles i et signal. De er noe som skjer, og derfor kaller data-on:click metoden direkte.
Markupen
Section titled “Markupen”En toast har tre deler, og fs.toast() gir ett attributtsett til hver. Det er den samme varsler som står i React- og Astro-oppskriftene over:
const varsler = fs.toast({ label: "Meldinger" })| Sett | Hvilken del | Hvem skriver den |
|---|---|---|
varsler.host | Beholderen, altså <fs-toast> | Du, én gang i appen |
varsler.toast | Selve meldingen | Komponenten, når du kaller show() |
varsler.close | Lukkeknappen i meldingen | Komponenten |
Derfor sprer du host og ikke hele objektet. Beholderen er det eneste elementet du skriver:
<fs-toast {...varsler.host} />Sprer du hele varsler, setter du tre attributter som heter host, toast og close, og ingen av dem er noe nettleseren kjenner. Beholderen står da uten role="status" til defineFsToast() har kjørt, for komponenten fyller inn rollen når den mangler. Fram til da leser ingen skjermleser opp det som kommer i den, og data-ignore-morph kommer aldri, siden det er det ene komponenten ikke setter selv.
De to andre settene er navn, ikke markup. Komponenten kjenner dem og bruker dem på det den lager selv. Du trenger dem bare når noen andre skriver meldingen: en Datastar-app der serveren patcher en melding rett inn i regionen, må skrive fs-toast og fs-toast__close selv, og da skal de hete det samme som komponentens egne.
Har du ikke noe byggesteg, skriver du det samme settet ut som vanlige attributter, slik oppskriftene «Ren HTML» og «Datastar» gjør:
<fs-toast id="meldinger" role="status" aria-live="polite" aria-atomic="false" aria-label="Meldinger" data-ignore-morph></fs-toast>data-ignore-morph er den viktigste av dem, og den eneste komponenten ikke setter selv.
data-ignore-morph er ikke valgfri. Komponenten lager og fjerner sine egne meldinger inne i regionen, og uten attributtet river en oppdatering fra serveren dem bort, fordi serverens utgave av regionen er tom. Se Markup og oppførsel.
Attributter og metoder
Section titled “Attributter og metoder”| Navn | Hva det er |
|---|---|
duration | Standard levetid i millisekunder. 0 lar meldingene bli stående. |
label | Tekst som sier hva regionen er. Blir aria-label. Standard: «Varsler» |
show(tekst, valg) | Viser en melding og returnerer elementet |
dismiss(melding) | Fjerner én melding, og sender toast-dismiss. Hadde meldingen fokus, går fokus til meldingen ved siden av |
clear() | Fjerner alle, uten å sende noe |
showToast(element, tekst, valg) fra @fristil/designsystem/toast gjør det samme som show(), men venter først på at elementet er registrert. Bruk den der kallet kan komme før defineFsToast(), som i en modul som lastes ved siden av registreringen.
Valgene til show():
| Valg | Standard | Effekt |
|---|---|---|
color | neutral | success, warning eller danger gir en farget kant |
duration | fra elementet | Millisekunder før meldingen forsvinner |
closeLabel | Lukk melding | Tekst på lukkeknappen |
Meldinger som forsvinner
Section titled “Meldinger som forsvinner”En melding som forsvinner av seg selv er en tilgjengelighetsfelle: den som leser sakte, bruker forstørrelse eller skjermleser rekker ikke å lese den. Komponenten gjør to ting med det:
- Lukkeknappen er alltid der.
- Tiden stopper mens musa er over meldingen eller fokus er inne i den, og går først når begge er ute.
Skal beskjeden kunne leses igjen senere, hører den ikke hjemme her. En kvittering skal stå i siden.
Klasser du kan style
Section titled “Klasser du kan style”| Klasse | Hva det er |
|---|---|
.fs-toast | Én melding. Fargen kommer fra data-color. |
.fs-toast__close | Lukkeknappen |
| Variabel | Standard |
|---|---|
--fs-toast-space | var(--fs-spacing-4) |
--fs-toast-width | min(24rem, calc(100vw - var(--fs-spacing-8))) |
--fs-toast-padding | var(--fs-spacing-3) var(--fs-spacing-4) |
--fs-toast-radius | var(--fs-spacing-1) |
--fs-toast-layer | 60 |
Beholderen slipper klikk gjennom når den er tom, så den ikke stenger for knapper under seg.
Se Tilpasning for hvordan variablene og laget virker.
Tilgjengelighet
Section titled “Tilgjengelighet”- Én ting om gangen. Fem meldinger oppå hverandre leses opp etter hverandre, og ingen av dem får oppmerksomhet.
- Ikke legg viktige handlinger i en melding som forsvinner. Angre-knappen bør stå i siden.
- Feil som stopper brukeren hører i en Alert eller en Error Summary, ikke i hjørnet.
- Teksten skal si hva som skjedde, ikke bare «Ferdig».