Preference helpers

Seiven wee packages per framework, ilkane ownin ae uiser job frae stairt tae feenish, plus a bar that assembles fiv o them. Ye've met theme-picker; here are locale-picker, text-size-picker an motion-picker (preferences), share-picker an search-picker (actions), date-time-picker (a form value), an picker-bar.

Whit ye'll big

A settins panel whaur uisers pick their leid an text size — choices that apply tae the hail document, persist athort visits an cost ye twa components an a few lines o CSS. Aboot 15 minutes.

Afore ye stairt

Feenish the framework tutorial for yer stack. The samples ablow are Svelte; the React, Vue, Angular, HTML, Nunjucks, an Blazor helpers match contract-for-contract.

The shape they share

  • A headless icon button that opens a WAI-ARIA APG listbox (or, for date-time-picker, a date-picker dialog) — nae native <select>, sae ilka framework gits identical keyboard behaviour an stylin hooks.
  • Ae class hook per package (.locale-picker, .text-size-picker, .share-picker, .date-time-picker), zero CSS shippit.
  • SSR-safe: DOM writes happen anely in the mount/effect lifecycle.
  • i18n-clean: ilka uiser-facin string is a prop.
  • Available for Svelte (canonical), React, Vue, Angular, HTML custom element, Nunjucks, an Blazor — the same contract in ilkane.

theme-picker, locale-picker, an text-size-picker ilkane own ae uiser preference frae stairt tae feenish (selection, DOM application, optional localStorage persistence storageKey bi a prop). share-picker an date-time-picker are different in kind: share-picker owns an action — it applies naething tae the document an persists naething — an date-time-picker owns a form value, since a date in a form is data raither than a preference.

locale-picker — leid an direction

Lets the uiser pick a locale, then writes lang an dir tae the document root sae yer i18n library, yer CSS, an assistive technology aw follae. RTL is detectit automatically for Arabic, Hebrew, an ither richt-tae-left scripts. It focuses purely on signallin — yer i18n library handles the translation.

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

Options are labelled frae a biggit-in table o locale names (owerridable bi localeLabels), ilka option cairries its ain lang attribute sae screen readers pronounce “Français” in French, an underscore codes like en_US round-trip losslessly tae BCP 47 en-US. Wire onChange (or the bund value) intae i18next, Paraglide, vue-i18n, or whitever ye uise.

text-size-picker — reader-controllit sizin

An accessibility win that taks minutes: the helper sets data-text-size="{slug}" on the document root an yer CSS maps ilka value tae sizin.

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

Size yer layoot in rem an the hail app scales wi the uiser's choice. Labels are title-casit frae the slugs (“X Large”) or owerriddin wi sizeLabels.

Check yer wirk: pick “Large” frae the picker, an the page text grows. Reload — it's still lairge: the choice persistit tae localStorage. Switch yer leid picker tae Arabic an dir="rtl" lands on the document automatically.

share-picker — send this page

A single-glyph button that opens the device's native share sheet whaur ane exists, an itherwise faws back tae a list o destinations ye supply, plus a copy-the-URL action. It ships nae social-network URLs — whit networks belang in yer product is yer cry, no the design system's.

<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 defauld tae location.hrefsae the common case needs nae wirin. Force a path wi strategy="list" or strategy="native" whan ye need consistent behaviour athort platforms for testin or screenshots.

date-time-picker — a date, a time, or baith

A text field ye can type intae, plus an icon button that opens a WAI-ARIA APG date-picker dialog. Locale-correct bi construction — month names, weekday names, first day o week, an 12- vs 24-hour clock aw come frae Intl — an the value is aye a sortable, timezone-free ISO string: "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",
  }}
/>

The ISO value is identical tae whit <input type="date"> posts, sae ye can swap the native control in or oot wioot touchin yer backend.

motion-picker — respect reduced motion

The helper sets data-motion="{slug}" on the document root. Its first value comes frae the visitor's operatin-system prefers-reduced-motion settin — aye, wi nae opt-in flag, because motion is a real accessibility need (WCAG 2.3.3). Yer CSS an scripts decide whit gits suppressed.

<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 — a seek box that bides oot the road

An icon button that opens a seek field. Pressin Return, or the ⏎ button beside the field, navigates wi a plain GET request tae /?<text> (the text trimmed an URI-encodit; an empty query gangs naewhaur). Its three accessible names are required, because the helper ships nae English.

<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 changes the path (/search?foo), an navigate keeps the navigation in-app; wioot it the helper calls location.assign.

picker-bar — the hail header row at ance

picker-bar renders seek, theme, locale, text-size an share in ae row, wi the full set o reference themes an the seiven-step text scale pre-wired. Ilka accessible name comes in ae required 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}
/>

Reach onie wrappit picker wi searchProps, themeProps, localeProps, textSizeProps an shareProps.

Tooltips come biggit in

Ilka picker shaws its label in a tooltip whan the pointer rests on its button or the button haes keyboard focus. The tooltip bides whan the pointer muives ontae it, Escape dismisses it whaurever focus is, an it never shaws while the picker's popup is open. It cairries the same text as the button's accessible name, sae there's naething tae configure an naething tae translate twice; the reference themes style it.

A settins panel in ae place

The fower preference helpers compose naturally:

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

Verified: the locale-picker, text-size-picker, share-picker, date-time-picker an combined-panel snippets were compilit individually against the publishit 0.1.1 packages through svelte/server (2026-08-29); the motion-picker, search-picker an picker-bar snippets were renderit against the packages' ain components on 2026-10-06.

Whaur tae git them

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

Ilka package haes its ain spec, tests mappit tae the spec clauses, runnable examples (includin SSR cookie patterns), an a build + publish pipeline. The Svelte packages are canonical; whan in doubt, their contracts win.

Neist steps

Questions alang the wey? The help page is fu o answers — an if a step could be clearer, tell us an we'll gladly improve the tutorial.