Asistentes de preferencias
Siete paquetes pequeños por framework, cada uno dueño de una tarea del usuario de principio a fin, más una barra que reúne cinco de ellos. Ya conociste theme-picker; aquí están locale-picker, text-size-picker y motion-picker (preferencias), share-picker y search-picker (acciones), date-time-picker (un valor de formulario) y picker-bar.
Qué vas a construir
Un panel de ajustes donde los usuarios eligen su idioma y el tamaño del texto: elecciones que se aplican a todo el documento, persisten entre visitas y te cuestan dos componentes y unas líneas de CSS. Unos 15 minutos.
Antes de empezar
Termina primero el tutorial del framework de tu stack. Los ejemplos de abajo son de Svelte; los asistentes de React, Vue, Angular, HTML, Nunjucks y Blazor coinciden contrato por contrato.
La forma que comparten
- Un botón de icono sin cabeza que abre un cuadro de lista WAI-ARIA APG (o, en date-time-picker, un diálogo selector de fecha): sin
<select>nativo, para que cada framework obtenga el mismo comportamiento de teclado y los mismos ganchos de estilo. - Un gancho de clase por paquete (
.locale-picker,.text-size-picker,.share-picker,.date-time-picker), cero CSS distribuido. - Seguro para SSR: las escrituras en el DOM ocurren solo en el ciclo de vida mount/effect.
- Limpio para i18n: toda cadena visible para el usuario es una prop.
- Disponible para Svelte (canónico), React, Vue, Angular, elemento personalizado HTML, Nunjucks y Blazor, con el mismo contrato en cada uno.
theme-picker, locale-picker y text-size-picker son dueños cada uno de una
preferencia del usuario de principio a fin (selección, aplicación al DOM,
localStorage persistencia opcional mediante la prop storageKey
). share-picker y date-time-picker son distintos por naturaleza: share-picker es dueño de una acción (no aplica nada al documento y no persiste nada) y date-time-picker es dueño de un
valor de formulario, porque una fecha en un formulario es un dato, no una preferencia.
locale-picker: idioma y dirección
Deja que el usuario elija una configuración regional y luego escribe lang y
dir en la raíz del documento para que tu biblioteca i18n, tu CSS y las tecnologías de apoyo la sigan. RTL se detecta automáticamente para árabe, hebreo y otras escrituras de derecha a izquierda. Se centra solo en señalar: tu biblioteca i18n se encarga de la traducción.
<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"
/>
Las opciones se etiquetan a partir de una tabla integrada de nombres de configuración regional (se puede anular mediante localeLabels), cada opción lleva su propio atributo
lang para que los lectores de pantalla pronuncien «Français» en francés, y los códigos con guion bajo como en_US
se convierten sin pérdida a BCP 47 y de vuelta en-US. Conecta
onChange (o el valor enlazado) a i18next, Paraglide, vue-i18n o lo que uses.
text-size-picker: tamaño controlado por el lector
Una victoria de accesibilidad que lleva minutos: el asistente establece
data-text-size="{slug}" en la raíz del documento y tu CSS asigna cada valor a un tamaño.
<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 tu diseño en rem y toda la aplicación se escala con la elección del usuario. Las etiquetas se forman a partir de los slugs con mayúscula inicial («X Large») o se anulan con sizeLabels.
Comprueba tu trabajo: elige «Large» en el selector y el texto de la página crece. Recarga: sigue grande, porque la elección se persistió en
localStorage. Cambia tu selector de idioma a árabe y
dir="rtl" se establece en el documento automáticamente.
share-picker: envía esta página
Un botón de un solo glifo que abre la hoja de compartir nativa del dispositivo donde existe y, si no, recurre a una lista de destinos que tú proporcionas, además de una acción para copiar la URL. No incluye URL de redes sociales: qué redes pertenecen a tu producto es decisión tuya, no del sistema de diseño.
<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 tiene como valor predeterminado location.hrefasí que el caso común no necesita cableado. Fuerza una ruta con strategy="list" o
strategy="native" cuando necesites un comportamiento coherente entre plataformas para pruebas o capturas de pantalla.
date-time-picker: una fecha, una hora o ambas
Un campo de texto en el que puedes escribir, más un botón de icono que abre un diálogo selector de fecha WAI-ARIA APG. Correcto para la configuración regional por construcción: los nombres de los meses, los nombres de los días, el primer día de la semana y el reloj de 12 o 24 horas provienen todos de Intl y el valor es siempre una cadena ISO ordenable y sin zona horaria: "2026-03-15" para
mode="date", "09:30" para
mode="time", "2026-03-15T09:30" para
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",
}}
/>
El valor ISO es idéntico a lo que envía <input type="date">
así que puedes cambiar el control nativo por este o viceversa sin tocar tu backend.
motion-picker: respeta el movimiento reducido
El asistente establece data-motion="{slug}" en la raíz del documento. Su primer valor proviene de la configuración
prefers-reduced-motion del sistema operativo del visitante: siempre, sin una marca de adhesión voluntaria, porque el movimiento es una necesidad real de accesibilidad (WCAG 2.3.3). Tu CSS y tus scripts deciden qué se suprime.
<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: un cuadro de búsqueda que no estorba
Un botón de icono que abre un campo de búsqueda. Pulsar Intro, o el botón ⏎ junto al campo, navega con una simple solicitud GET a
/?<text> (el texto se recorta y se codifica como URI; una consulta vacía no lleva a ninguna parte). Sus tres nombres accesibles son obligatorios, porque el asistente no incluye inglés.
<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 la ruta (/search?foo), y
navigate mantiene la navegación dentro de la aplicación; sin él, el asistente llama a location.assign.
picker-bar: toda la fila del encabezado de una vez
picker-bar renderiza búsqueda, tema, configuración regional, tamaño de texto y compartir en una fila, con el conjunto completo de temas de referencia y la escala de texto de siete pasos ya cableados. Cada nombre accesible llega en un único objeto
labels obligatorio.
<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}
/>
Alcanza cualquier selector envuelto con searchProps,
themeProps, localeProps,
textSizeProps y shareProps.
Las descripciones emergentes vienen integradas
Cada selector muestra su etiqueta en una descripción emergente cuando el puntero reposa sobre su botón o el botón tiene foco de teclado. La descripción permanece mientras el puntero se mueve hacia ella, Escape la cierra esté donde esté el foco, y nunca se muestra mientras la ventana emergente del selector está abierta. Lleva el mismo texto que el nombre accesible del botón, así que no hay nada que configurar ni traducir dos veces; los temas de referencia le dan estilo.
Un panel de ajustes en un solo lugar
Los cuatro asistentes de preferencias se componen de forma natural:
<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>
Verificado: los fragmentos de locale-picker, text-size-picker, share-picker, date-time-picker y del panel combinado se compilaron individualmente contra los paquetes 0.1.1 publicados, mediante
svelte/server (2026-08-29); los fragmentos de motion-picker, search-picker y picker-bar se renderizaron contra los componentes de los propios paquetes el 2026-10-06.
Dónde conseguirlos
# 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-
Cada paquete tiene su propia spec, pruebas asignadas a las cláusulas de la spec, ejemplos ejecutables (incluidos patrones de cookies para SSR) y una canalización de compilación y publicación. Los paquetes de Svelte son los canónicos; en caso de duda, prevalecen sus contratos.
Próximos pasos
- Catálogo de componentes : la referencia completa de los 571 componentes.
- Ayuda : solución de problemas y preguntas frecuentes.
- Por qué Lily y cómo apoyar el proyecto.
¿Dudas por el camino? La página de ayuda está llena de respuestas, y si algún paso puede ser más claro, cuéntanoslo y con gusto mejoraremos el tutorial.