Voorkeurenhelpers

Zeven kleine pakketten per framework, elk bezit één gebruikerstaak van begin tot eind, plus een balk die er vijf samenvoegt. Je hebt theme-pickeral ontmoet; hier zijn locale-picker, text-size-picker en motion-picker (voorkeuren), share-picker en search-picker (acties), date-time-picker (een formulierwaarde) en picker-bar.

Wat je gaat bouwen

Een instellingenpaneel waar gebruikers hun taal en tekstgrootte kiezen — keuzes die voor het hele document gelden, tussen bezoeken bewaard blijven en je twee componenten en een paar regels CSS kosten. Ongeveer 15 minuten.

Voordat je begint

Rond eerst de framework-handleiding voor jouw stack af. De voorbeelden hieronder zijn Svelte; de helpers voor React, Vue, Angular, HTML, Nunjucks en Blazor komen contract voor contract overeen.

De vorm die ze delen

  • Een headless pictogramknop die een WAI-ARIA APG-listbox opent (of, voor date-time-picker, een datumkiezerdialoog) — geen native <select>, zodat elk framework hetzelfde toetsenbordgedrag en dezelfde stijlhaken krijgt.
  • Eén klassehaak per pakket (.locale-picker, .text-size-picker, .share-picker, .date-time-picker), nul CSS meegeleverd.
  • SSR-veilig: DOM-schrijfacties gebeuren alleen in de mount-/effect-levenscyclus.
  • i18n-schoon: elke voor de gebruiker zichtbare tekenreeks is een prop.
  • Beschikbaar voor Svelte (canoniek), React, Vue, Angular, HTML-custom element, Nunjucks en Blazor — in elk hetzelfde contract.

theme-picker, locale-picker en text-size-picker bezitten elk één gebruikers voorkeur van begin tot eind (selectie, toepassing op de DOM, optionele localStorage persistentie via de storageKey -prop). share-picker en date-time-picker zijn van een andere soort: share-picker bezit een actie — hij past niets toe op het document en bewaart niets — en date-time-picker bezit een formulierwaarde, omdat een datum in een formulier gegevens zijn, geen voorkeur.

locale-picker — taal en richting

Laat de gebruiker een landinstelling kiezen en schrijft vervolgens lang en dir naar de documentroot, zodat je i18n-bibliotheek, je CSS en hulptechnologieën allemaal volgen. RTL wordt automatisch gedetecteerd voor Arabisch, Hebreeuws en andere schriften van rechts naar links. Hij beperkt zich tot signaleren — je i18n-bibliotheek regelt de vertaling.

<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"
/>

Opties krijgen hun label uit een ingebouwde tabel met landinstellingsnamen (te overschrijven via localeLabels), elke optie draagt zijn eigen lang -attribuut zodat schermlezers "Français" in het Frans uitspreken, en codes met onderstreping zoals en_US worden zonder verlies heen en weer omgezet naar BCP 47 en-US. Koppel onChange (of de gebonden waarde) aan i18next, Paraglide, vue-i18n of wat je ook gebruikt.

text-size-picker — grootte onder controle van de lezer

Een toegankelijkheidswinst van enkele minuten: de helper zet data-text-size="{slug}" op de documentroot en je CSS koppelt elke waarde aan een grootte.

<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%; }

Maak je layout op in rem en de hele app schaalt mee met de keuze van de gebruiker. Labels worden uit de slugs met hoofdletters gevormd ("X Large") of overschreven met sizeLabels.

Controleer je werk: kies "Large" in de kiezer en de paginatekst wordt groter. Laad opnieuw — hij is nog steeds groot: de keuze is bewaard in localStorage. Zet je taalkiezer op Arabisch en dir="rtl" wordt automatisch op het document gezet.

share-picker — verstuur deze pagina

Een knop met één glyph die het native deelvenster van het apparaat opent waar dat bestaat, en anders terugvalt op een lijst met bestemmingen die jij aanlevert, plus een actie om de URL te kopiëren. Hij levert geen URL's van sociale netwerken mee — welke netwerken bij jouw product horen, is jouw keuze, niet die van het designsysteem.

<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 staat standaard op location.href, dus het gewone geval vraagt geen bedrading. Forceer een pad met strategy="list" of strategy="native" wanneer je consistent gedrag over platforms nodig hebt voor tests of schermafbeeldingen.

date-time-picker — een datum, een tijd of allebei

Een tekstveld waarin je kunt typen, plus een pictogramknop die een WAI-ARIA APG-datumkiezerdialoog opent. Van nature taalcorrect — maandnamen, dagnamen, eerste dag van de week en 12- of 24-uursklok komen allemaal uit Intl — en de waarde is altijd een sorteerbare ISO-string zonder tijdzone: "2026-03-15" voor mode="date", "09:30" voor mode="time", "2026-03-15T09:30" voor 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",
  }}
/>

De ISO-waarde is identiek aan wat <input type="date"> verstuurt, dus je kunt het native bedieningselement in- of uitwisselen zonder je backend aan te raken.

motion-picker — respecteer verminderde beweging

De helper zet data-motion="{slug}" op de documentroot. De eerste waarde komt uit de prefers-reduced-motion -instelling van het besturingssysteem van de bezoeker — altijd, zonder opt-in-vlag, omdat beweging een echte toegankelijkheidsbehoefte is (WCAG 2.3.3). Jouw CSS en scripts bepalen wat er wordt onderdrukt.

<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 — een zoekvak dat uit de weg blijft

Een pictogramknop die een zoekveld opent. Op Return drukken, of op de ⏎-knop naast het veld, navigeert met een gewoon GET-verzoek naar /?<text> (de tekst wordt bijgesneden en URI-gecodeerd; een lege zoekopdracht gaat nergens heen). De drie toegankelijke namen zijn verplicht, omdat de helper geen Engels meelevert.

<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 wijzigt het pad (/search?foo), en navigate houdt de navigatie binnen de app; zonder roept de helper location.assign.

picker-bar — de hele kopregel in één keer

picker-bar rendert zoeken, thema, taal, tekstgrootte en delen in één rij, met de volledige set referentiethema's en de zeventrapsschaal voor tekst vooraf aangesloten. Elke toegankelijke naam komt in één verplicht labels -object.

<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}
/>

Bereik elke ingepakte kiezer met searchProps, themeProps, localeProps, textSizeProps en shareProps.

Tooltips zijn ingebouwd

Elke kiezer toont zijn label in een tooltip wanneer de aanwijzer op zijn knop rust of de knop toetsenbordfocus heeft. De tooltip blijft staan terwijl de aanwijzer erheen beweegt, Escape sluit hem waar de focus ook is, en hij verschijnt nooit terwijl de pop-up van de kiezer open is. Hij draagt dezelfde tekst als de toegankelijke naam van de knop, dus er is niets te configureren en niets dubbel te vertalen; de referentiethema's geven hem vorm.

Een instellingenpaneel op één plek

De vier voorkeurenhelpers combineren vanzelf:

<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>

Geverifieerd: de fragmenten voor locale-picker, text-size-picker, share-picker, date-time-picker en het gecombineerde paneel zijn afzonderlijk gecompileerd tegen de gepubliceerde 0.1.1 -pakketten, via svelte/server (2026-08-29); de fragmenten voor motion-picker, search-picker en picker-bar zijn op 2026-10-06 gerenderd tegen de eigen componenten van de pakketten.

Waar je ze vindt

# 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-

Elk pakket heeft zijn eigen spec, tests gekoppeld aan de specclausules, uitvoerbare voorbeelden (inclusief SSR-cookiepatronen) en een build- en publicatiepijplijn. De Svelte-pakketten zijn canoniek; bij twijfel winnen hun contracten.

Volgende stappen

Vragen onderweg? De helppagina staat vol antwoorden — en als een stap duidelijker kan, laat het ons weten en we verbeteren de handleiding graag.