Assistants de préférences

Sept petits paquets par framework, chacun responsable d'une tâche utilisateur de bout en bout, plus une barre qui en assemble cinq. Vous avez déjà rencontré theme-picker; voici locale-picker, text-size-picker et motion-picker (préférences), share-picker et search-picker (actions), date-time-picker (une valeur de formulaire) et picker-bar.

Ce que vous allez construire

Un panneau de réglages où les utilisateurs choisissent leur langue et la taille du texte — des choix qui s'appliquent à tout le document, persistent entre les visites et vous coûtent deux composants et quelques lignes de CSS. Environ 15 minutes.

Avant de commencer

Terminez d'abord le tutoriel du framework pour votre pile. Les exemples ci-dessous sont en Svelte ; les assistants React, Vue, Angular, HTML, Nunjucks et Blazor correspondent contrat par contrat.

La forme qu'ils partagent

  • Un bouton-icône sans tête qui ouvre une liste WAI-ARIA APG (ou, pour date-time-picker, une boîte de dialogue de sélection de date) — pas de <select>natif, pour que chaque framework obtienne le même comportement clavier et les mêmes crochets de style.
  • Un crochet de classe par paquet (.locale-picker, .text-size-picker, .share-picker, .date-time-picker), zéro CSS livré.
  • Sûr pour le SSR : les écritures dans le DOM n'ont lieu que dans le cycle de vie mount/effect.
  • Propre pour l'i18n : chaque chaîne visible par l'utilisateur est une prop.
  • Disponible pour Svelte (canonique), React, Vue, Angular, élément personnalisé HTML, Nunjucks et Blazor — le même contrat dans chacun.

theme-picker, locale-picker et text-size-picker possèdent chacun une préférence utilisateur de bout en bout (sélection, application au DOM, localStorage persistance facultative via la prop storageKey ). share-picker et date-time-picker sont de nature différente : share-picker possède une action — il n'applique rien au document et ne persiste rien — et date-time-picker possède une valeur de formulaire, car une date dans un formulaire est une donnée, pas une préférence.

locale-picker — langue et direction

Laisse l'utilisateur choisir une locale, puis écrit lang et dir sur la racine du document pour que votre bibliothèque i18n, votre CSS et les technologies d'assistance la suivent toutes. Le RTL est détecté automatiquement pour l'arabe, l'hébreu et d'autres écritures de droite à gauche. Il se concentre uniquement sur la signalisation — votre bibliothèque i18n gère la traduction.

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

Les options sont étiquetées à partir d'une table intégrée de noms de locales (remplaçable via localeLabels), chaque option porte son propre attribut lang pour que les lecteurs d'écran prononcent « Français » en français, et les codes à tiret bas comme en_US sont convertis sans perte vers BCP 47 et inversement en-US. Branchez onChange (ou la valeur liée) sur i18next, Paraglide, vue-i18n ou ce que vous utilisez.

text-size-picker — taille contrôlée par le lecteur

Un gain d'accessibilité qui prend quelques minutes : l'assistant définit data-text-size="{slug}" sur la racine du document et votre CSS associe chaque valeur à une taille.

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

Dimensionnez votre mise en page en rem et toute l'application s'adapte au choix de l'utilisateur. Les étiquettes sont formées à partir des slugs avec une majuscule initiale (« X Large ») ou remplacées avec sizeLabels.

Vérifiez votre travail : choisissez « Large » dans le sélecteur et le texte de la page grandit. Rechargez — il est toujours grand : le choix a été conservé dans localStorage. Passez votre sélecteur de langue à l'arabe et dir="rtl" est défini sur le document automatiquement.

share-picker — envoyer cette page

Un bouton à un seul glyphe qui ouvre la feuille de partage native de l'appareil quand elle existe, et sinon se rabat sur une liste de destinations que vous fournissez, plus une action de copie de l'URL. Il ne livre aucune URL de réseau social — quels réseaux appartiennent à votre produit est votre décision, pas celle du 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 vaut par défaut location.hrefdonc le cas courant n'a besoin d'aucun câblage. Forcez un chemin avec strategy="list" ou strategy="native" quand vous avez besoin d'un comportement cohérent entre plateformes pour des tests ou des captures d'écran.

date-time-picker — une date, une heure, ou les deux

Un champ de texte dans lequel vous pouvez taper, plus un bouton-icône qui ouvre une boîte de dialogue de sélection de date WAI-ARIA APG. Correct pour la locale par construction — les noms des mois, les noms des jours, le premier jour de la semaine et l'horloge 12 ou 24 heures viennent tous de Intl — et la valeur est toujours une chaîne ISO triable et sans fuseau horaire : "2026-03-15" pour mode="date", "09:30" pour mode="time", "2026-03-15T09:30" pour 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",
  }}
/>

La valeur ISO est identique à ce qu'envoie <input type="date"> donc vous pouvez échanger le contrôle natif sans toucher à votre backend.

motion-picker — respecter la réduction des animations

L'assistant définit data-motion="{slug}" sur la racine du document. Sa première valeur vient du réglage prefers-reduced-motion du système d'exploitation du visiteur — toujours, sans indicateur d'adhésion, car le mouvement est un vrai besoin d'accessibilité (WCAG 2.3.3). Votre CSS et vos scripts décident de ce qui est supprimé.

<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 — une boîte de recherche qui ne gêne pas

Un bouton-icône qui ouvre un champ de recherche. Appuyer sur Entrée, ou sur le bouton ⏎ à côté du champ, navigue par une simple requête GET vers /?<text> (le texte est rogné et encodé en URI ; une requête vide ne mène nulle part). Ses trois noms accessibles sont obligatoires, car l'assistant ne livre pas d'anglais.

<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 change le chemin (/search?foo), et navigate garde la navigation dans l'application ; sans lui, l'assistant appelle location.assign.

picker-bar — toute la ligne d'en-tête d'un coup

picker-bar rend la recherche, le thème, la locale, la taille du texte et le partage sur une ligne, avec l'ensemble complet des thèmes de référence et l'échelle de texte à sept pas déjà câblés. Chaque nom accessible arrive dans un seul objet labels obligatoire.

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

Atteignez n'importe quel sélecteur enveloppé avec searchProps, themeProps, localeProps, textSizeProps et shareProps.

Les infobulles sont intégrées

Chaque sélecteur affiche son étiquette dans une infobulle quand le pointeur repose sur son bouton ou que le bouton a le focus clavier. L'infobulle reste pendant que le pointeur se déplace dessus, Échap la ferme où que se trouve le focus, et elle ne s'affiche jamais tant que la fenêtre du sélecteur est ouverte. Elle porte le même texte que le nom accessible du bouton, donc rien à configurer ni à traduire deux fois ; les thèmes de référence la stylent.

Un panneau de réglages en un seul endroit

Les quatre assistants de préférences se composent naturellement :

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

Vérifié : les extraits de locale-picker, text-size-picker, share-picker, date-time-picker et du panneau combiné ont été compilés individuellement contre les paquets 0.1.1 publiés, via svelte/server (2026-08-29) ; les extraits de motion-picker, search-picker et picker-bar ont été rendus contre les composants des paquets eux-mêmes le 2026-10-06.

Où les trouver

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

Chaque paquet a sa propre spec, des tests associés aux clauses de la spec, des exemples exécutables (y compris des motifs de cookies pour le SSR) et un pipeline de build + publication. Les paquets Svelte sont canoniques ; en cas de doute, leurs contrats l'emportent.

Étapes suivantes

Des questions en chemin ? La page d'aide regorge de réponses — et si une étape peut être plus claire, dites-le-nous et nous améliorerons volontiers le tutoriel.