Preferansehjelpere

Syv små pakker per rammeverk, hver eier én brukeroppgave fra ende til annen, pluss en linje som setter sammen fem av dem. Du har allerede møtt theme-picker; her er locale-picker, text-size-picker og motion-picker (preferanser), share-picker og search-picker (handlinger), date-time-picker (en skjemaverdi) og picker-bar.

Hva du skal bygge

Et innstillingspanel der brukerne velger språk og tekststørrelse — valg som gjelder hele dokumentet, består mellom besøk og koster deg to komponenter og noen få linjer CSS. Omtrent 15 minutter.

Før du begynner

Fullfør rammeverksveiledningen for stacken din. Eksemplene nedenfor er Svelte; hjelperne for React, Vue, Angular, HTML, Nunjucks og Blazor samsvarer kontrakt for kontrakt.

Formen de deler

  • En hodeløs ikonknapp som åpner en WAI-ARIA APG-listeboks (eller, for date-time-picker, en datovelgerdialog) — ingen native <select>, slik at hvert rammeverk får identisk tastaturoppførsel og stilkroker.
  • Én klassekrok per pakke (.locale-picker, .text-size-picker, .share-picker, .date-time-picker), null CSS leveres.
  • SSR-sikker: DOM-skriving skjer bare i mount/effect-livssyklusen.
  • i18n-ren: hver tekststreng brukeren ser er en egenskap.
  • Tilgjengelig for Svelte (kanonisk), React, Vue, Angular, HTML-egendefinert element, Nunjucks og Blazor — samme kontrakt i hver.

theme-picker, locale-picker og text-size-picker eier hver én bruker- preferanse fra ende til annen (valg, DOM-anvendelse, valgfri localStorage persistens storageKey via en egenskap). share-picker og date-time-picker er annerledes av art: share-picker eier en handling — den bruker ingenting på dokumentet og lagrer ingenting — og date-time-picker eier en skjemaverdi, siden en dato i et skjema er data, ikke en preferanse.

locale-picker — språk og retning

Lar brukeren velge et lokale, og skriver deretter lang og dir til dokumentroten slik at i18n-biblioteket ditt, CSS-en din og hjelpeteknologi alle følger etter. RTL oppdages automatisk for arabisk, hebraisk og andre høyre-til-venstre-skrifter. Den fokuserer rent på signalisering — i18n-biblioteket ditt håndterer oversettelsen.

<script>
  import LocalePicker from "@lilydesignsystem/svelte-locale-picker";

  let locale = $state("en-US");
</script>

<LocalePicker
  label="Language"
  locales={["en-US", "fr", "ar", "he", "ja"]}
  bind:value={locale}
  storageKey="my-app-locale"
/>

Alternativene får etiketter fra en innebygd tabell over lokalenavn (kan overstyres via localeLabels), hvert alternativ bærer sitt eget lang -attributt slik at skjermlesere uttaler «Français» på fransk, og understrekkoder som en_US går tapsfritt tur-retur til BCP 47 en-US. Koble onChange (eller den bundne verdien) til i18next, Paraglide, vue-i18n eller hva du enn bruker.

text-size-picker — leserstyrt størrelse

En tilgjengelighetsseier som tar minutter: hjelperen setter data-text-size="{slug}" på dokumentroten, og CSS-en din tilordner hver verdi til størrelse.

<script>
  import TextSizePicker from "@lilydesignsystem/svelte-text-size-picker";
</script>

<TextSizePicker
  label="Text size"
  sizes={["small", "medium", "large", "x-large"]}
  storageKey="my-app-text-size"
/>
:root[data-text-size="small"]   { font-size: 87.5%; }
:root[data-text-size="medium"]  { font-size: 100%; }
:root[data-text-size="large"]   { font-size: 112.5%; }
:root[data-text-size="x-large"] { font-size: 125%; }

Dimensjoner oppsettet ditt i rem og hele appen skalerer med brukerens valg. Etiketter får stor forbokstav fra slugene («X Large») eller overstyres med sizeLabels.

Sjekk arbeidet ditt: velg «Large» fra velgeren, og sideteksten vokser. Last på nytt — den er fortsatt stor: valget ble lagret i localStorage. Bytt språkvelgeren til arabisk, og dir="rtl" havner på dokumentet automatisk.

share-picker — send denne siden

En knapp med ett enkelt symbol som åpner enhetens native delingsark der det finnes, og ellers faller tilbake til en liste over destinasjoner du oppgir, pluss en kopier-URL-handling. Den leverer ingen URL-er til sosiale nettverk — hvilke nettverk som hører hjemme i produktet ditt er din avgjørelse, ikke designsystemets.

<script>
  import SharePicker from "@lilydesignsystem/svelte-share-picker";

  const targets = [
    { id: "mastodon", label: "Mastodon",
      href: (url, title) => `https://mastodon.social/share?text=${encodeURIComponent(title)}%20${encodeURIComponent(url)}` },
    { id: "email", label: "Email",
      href: (url, title) => `mailto:?subject=${encodeURIComponent(title)}&body=${encodeURIComponent(url)}`, newTab: false },
  ];
</script>

<SharePicker
  label="Share this page"
  title="An article worth reading"
  {targets}
  copyLabel="Copy link"
  copiedLabel="Link copied"
/>

url er som standard location.hrefså det vanlige tilfellet trenger ingen kobling. Tving en bane med strategy="list" eller strategy="native" når du trenger konsekvent oppførsel på tvers av plattformer for testing eller skjermbilder.

date-time-picker — en dato, et klokkeslett eller begge deler

Et tekstfelt du kan skrive i, pluss en ikonknapp som åpner en WAI-ARIA APG-datovelgerdialog. Lokalekorrekt fra grunnen av — månedsnavn, ukedagsnavn, første dag i uken og 12- eller 24-timers klokke kommer alle fra Intl — og verdien er alltid en sorterbar, tidssonefri ISO-streng: "2026-03-15" for mode="date", "09:30" for mode="time", "2026-03-15T09:30" for mode="datetime".

<script>
  import DateTimePicker from "@lilydesignsystem/svelte-date-time-picker";

  let appointment = $state("");
</script>

<DateTimePicker
  inputId="appointment"
  name="appointment"
  label="Choose an appointment date"
  locale="en-GB"
  bind:value={appointment}
  labels={{
    previousYear: "Previous year",
    previousMonth: "Previous month",
    nextMonth: "Next month",
    nextYear: "Next year",
    confirm: "OK",
    cancel: "Cancel",
  }}
/>

ISO-verdien er identisk med det <input type="date"> sender, så du kan bytte den native kontrollen inn eller ut uten å røre backend.

motion-picker — respekter redusert bevegelse

Hjelperen setter data-motion="{slug}" på dokumentroten. Den første verdien kommer fra besøkerens operativsystem- prefers-reduced-motion innstilling — alltid, uten avmerkingsflagg, fordi bevegelse er et reelt tilgjengelighetsbehov (WCAG 2.3.3). CSS-en og skriptene dine avgjør hva som undertrykkes.

<script>
  import MotionPicker from "@lilydesignsystem/svelte-motion-picker";
</script>

<MotionPicker
  label="Motion"
  motions={["no-preference", "reduce"]}
  storageKey="my-app-motion"
/>
:root[data-motion="reduce"] *,
:root[data-motion="reduce"] *::before,
:root[data-motion="reduce"] *::after {
  animation: none !important;
  transition: none !important;
  scroll-behavior: auto !important;
}

search-picker — en søkeboks som holder seg unna

En ikonknapp som åpner et søkefelt. Å trykke Return, eller ⏎-knappen ved siden av feltet, navigerer med en vanlig GET-forespørsel til /?<text> (teksten trimmes og URI-kodes; et tomt søk går ingen steder). De tre tilgjengelige navnene er påkrevd, fordi hjelperen ikke leverer engelsk.

<script>
  import SearchPicker from "@lilydesignsystem/svelte-search-picker";
  import { goto } from "$app/navigation";
</script>

<SearchPicker
  label="Search this site"
  inputLabel="Search terms"
  submitLabel="Search"
  placeholder="Search…"
  action="/search"
  navigate={(href) => goto(href)}
/>

action endrer banen (/search?foo), og navigate holder navigasjonen i appen; uten den kaller hjelperen location.assign.

picker-bar — hele topptekstraden på en gang

picker-bar gjengir søk, tema, lokale, tekststørrelse og deling i én rad, med hele settet med referansetemaer og den syv-trinns tekstskalaen ferdig koblet. Hvert tilgjengelige navn kommer i ett påkrevd labels objekt.

<script>
  import PickerBar from "@lilydesignsystem/svelte-picker-bar";
</script>

<PickerBar
  labels={{
    search: "Search", searchInput: "Search terms", searchSubmit: "Search",
    theme: "Theme", locale: "Language", textSize: "Text size", share: "Share",
  }}
  themesUrl="/themes/"
  locales={["en", "fr", "es"]}
  shareTargets={targets}
/>

Nå en hvilken som helst innpakket velger med searchProps, themeProps, localeProps, textSizeProps og shareProps.

Verktøytips er innebygd

Hver velger viser etiketten sin i et verktøytips når pekeren hviler på knappen eller knappen har tastaturfokus. Verktøytipset blir stående mens pekeren beveger seg over på det, Escape avviser det uansett hvor fokus er, og det vises aldri mens velgerens popup er åpen. Det har samme tekst som knappens tilgjengelige navn, så det er ingenting å konfigurere og ingenting å oversette to ganger; referansetemaene stiler det.

Et innstillingspanel på ett sted

De fire preferansehjelperne setter seg naturlig sammen:

<fieldset class="fieldset">
  <legend>Preferences</legend>
  <ThemePicker    label="Theme"     themesUrl="/themes/" themes={themes} storageKey="pref-theme" />
  <LocalePicker   label="Language"  locales={locales}    storageKey="pref-locale" />
  <TextSizePicker label="Text size" sizes={sizes}        storageKey="pref-text-size" />
  <MotionPicker   label="Motion"    motions={motions}    storageKey="pref-motion" />
</fieldset>

Verifisert: snuttene for locale-picker, text-size-picker, share-picker, date-time-picker og det kombinerte panelet ble kompilert hver for seg mot de publiserte 0.1.1 pakkene via svelte/server (2026-08-29); snuttene for motion-picker, search-picker og picker-bar ble gjengitt mot pakkenes egne komponenter 2026-10-06.

Hvor du får tak i dem

# one directory per package in the monorepo, e.g.
git clone https://github.com/LilyDesignSystem/lily-design-system
cd lily-design-system/lily-design-system-svelte-theme-picker
# or lily-design-system-react-theme-picker, -vue- / -angular- / -html- / -web-components- / -nunjucks- / -blazor-

Hver pakke har sin egen spec, tester knyttet til spec-klausulene, kjørbare eksempler (inkludert SSR-informasjonskapselmønstre) og en bygge- og publiseringsløype. Svelte-pakkene er kanoniske; ved tvil vinner kontraktene deres.

Neste steg

Spørsmål underveis? hjelpesiden er full av svar — og hvis et steg kan bli tydeligere, fortell oss så forbedrer vi gjerne veiledningen.