Helper delle preferenze

Sette piccoli pacchetti per framework, ognuno responsabile di un compito dell'utente da cima a fondo, più una barra che ne riunisce cinque. Hai già incontrato theme-picker; ecco locale-picker, text-size-picker e motion-picker (preferenze), share-picker e search-picker (azioni), date-time-picker (un valore di modulo) e picker-bar.

Cosa costruirai

Un pannello di impostazioni in cui gli utenti scelgono lingua e dimensione del testo — scelte che si applicano all'intero documento, persistono tra le visite e ti costano due componenti e qualche riga di CSS. Circa 15 minuti.

Prima di iniziare

Completa prima il tutorial del framework per il tuo stack. Gli esempi qui sotto sono Svelte; gli helper per React, Vue, Angular, HTML, Nunjucks e Blazor corrispondono contratto per contratto.

La forma che condividono

  • Un pulsante a icona headless che apre una listbox WAI-ARIA APG (o, per date-time-picker, una finestra di selezione della data) — nessun <select>nativo, così ogni framework ottiene lo stesso comportamento da tastiera e gli stessi hook di stile.
  • Un hook di classe per pacchetto (.locale-picker, .text-size-picker, .share-picker, .date-time-picker), zero CSS distribuito.
  • Sicuro per SSR: le scritture nel DOM avvengono solo nel ciclo di vita mount/effect.
  • Pulito per l'i18n: ogni stringa visibile all'utente è una prop.
  • Disponibile per Svelte (canonico), React, Vue, Angular, elemento personalizzato HTML, Nunjucks e Blazor — lo stesso contratto in ciascuno.

theme-picker, locale-picker e text-size-picker gestiscono ciascuno una preferenza dell'utente da cima a fondo (selezione, applicazione al DOM, localStorage persistenza facoltativa tramite la prop storageKey ). share-picker e date-time-picker sono diversi per natura: share-picker gestisce un' azione — non applica nulla al documento e non rende persistente nulla — e date-time-picker gestisce un valore di modulo, perché una data in un modulo è un dato, non una preferenza.

locale-picker — lingua e direzione

Permette all'utente di scegliere una lingua-regione, poi scrive lang e dir sulla radice del documento, così la tua libreria i18n, il tuo CSS e le tecnologie assistive la seguono tutti. L'RTL è rilevato automaticamente per arabo, ebraico e altre scritture da destra a sinistra. Si limita a segnalare — la traduzione la gestisce la tua libreria i18n.

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

Le opzioni sono etichettate da una tabella integrata di nomi di lingua (sostituibile tramite localeLabels), ogni opzione porta il proprio attributo lang così gli screen reader pronunciano "Français" in francese, e i codici con trattino basso come en_US vengono convertiti da e verso BCP 47 senza perdite en-US. Collega onChange (o il valore associato) a i18next, Paraglide, vue-i18n o a ciò che usi.

text-size-picker — dimensione controllata dal lettore

Un guadagno di accessibilità che richiede minuti: l'helper imposta data-text-size="{slug}" sulla radice del documento e il tuo CSS associa ogni valore a una dimensione.

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

Dimensiona il layout in rem e l'intera app si ridimensiona con la scelta dell'utente. Le etichette sono in maiuscolo iniziale a partire dagli slug ("X Large") o sostituite con sizeLabels.

Controlla il lavoro: scegli "Large" dal selettore e il testo della pagina cresce. Ricarica — è ancora grande: la scelta è stata salvata in localStorage. Passa il selettore della lingua all'arabo e dir="rtl" viene impostato automaticamente sul documento.

share-picker — invia questa pagina

Un pulsante a glifo singolo che apre il foglio di condivisione nativo del dispositivo dove esiste, e altrimenti ripiega su un elenco di destinazioni che fornisci, più un'azione per copiare l'URL. Non include URL di social network — quali reti appartengano al tuo prodotto è una tua scelta, non del design system.

<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 ha come valore predefinito location.hrefquindi il caso comune non richiede collegamenti. Forza un percorso con strategy="list" oppure strategy="native" quando serve un comportamento coerente tra le piattaforme per test o schermate.

date-time-picker — una data, un'ora o entrambe

Un campo di testo in cui puoi scrivere, più un pulsante a icona che apre una finestra di selezione della data WAI-ARIA APG. Corretto per la lingua per costruzione — nomi dei mesi, nomi dei giorni, primo giorno della settimana e orologio a 12 o 24 ore provengono tutti da Intl — e il valore è sempre una stringa ISO ordinabile e senza fuso orario: "2026-03-15" per mode="date", "09:30" per mode="time", "2026-03-15T09:30" per 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",
  }}
/>

Il valore ISO è identico a quello che invia <input type="date"> così puoi sostituire il controllo nativo senza toccare il tuo backend.

motion-picker — rispetta la riduzione del movimento

L'helper imposta data-motion="{slug}" sulla radice del documento. Il suo primo valore viene dall'impostazione prefers-reduced-motion del sistema operativo del visitatore — sempre, senza opt-in, perché il movimento è un'esigenza reale di accessibilità (WCAG 2.3.3). Il tuo CSS e i tuoi script decidono cosa sopprimere.

<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 — una casella di ricerca che non si mette in mezzo

Un pulsante a icona che apre un campo di ricerca. Premere Invio, o il pulsante ⏎ accanto al campo, naviga con una semplice richiesta GET verso /?<text> (il testo viene ripulito dagli spazi e codificato URI; una ricerca vuota non porta da nessuna parte). I suoi tre nomi accessibili sono obbligatori, perché l'helper non include inglese.

<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 cambia il percorso (/search?foo) e navigate mantiene la navigazione dentro l'app; senza di esso l'helper chiama location.assign.

picker-bar — l'intera riga dell'intestazione in una volta

picker-bar rende ricerca, tema, lingua, dimensione del testo e condivisione in una riga, con il set completo di temi di riferimento e la scala del testo a sette gradini già collegati. Ogni nome accessibile arriva in un unico oggetto labels obbligatorio.

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

Raggiungi qualsiasi selettore avvolto con searchProps, themeProps, localeProps, textSizeProps e shareProps.

I tooltip sono integrati

Ogni selettore mostra la propria etichetta in un tooltip quando il puntatore si ferma sul suo pulsante o il pulsante ha il focus da tastiera. Il tooltip resta mentre il puntatore si sposta su di esso, Esc lo chiude ovunque sia il focus, e non compare mai mentre il popup del selettore è aperto. Porta lo stesso testo del nome accessibile del pulsante, quindi non c'è nulla da configurare né da tradurre due volte; i temi di riferimento lo stilizzano.

Un pannello di impostazioni in un unico posto

I quattro helper delle preferenze si compongono in modo naturale:

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

Verificato: gli snippet di locale-picker, text-size-picker, share-picker, date-time-picker e del pannello combinato sono stati compilati singolarmente rispetto ai 0.1.1 pacchetti pubblicati tramite svelte/server (2026-08-29); gli snippet di motion-picker, search-picker e picker-bar sono stati renderizzati con i componenti dei pacchetti stessi il 2026-10-06.

Dove trovarli

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

Ogni pacchetto ha il proprio spec, test mappati sulle clausole dello spec, esempi eseguibili (inclusi i pattern con cookie per SSR) e una pipeline di build + pubblicazione. I pacchetti Svelte sono canonici; nel dubbio, vincono i loro contratti.

Prossimi passi

Domande lungo la strada? La pagina di aiuto è piena di risposte — e se un passo può essere più chiaro, faccelo sapere e miglioreremo volentieri il tutorial.