Помощники настроек

Семь небольших пакетов на каждый фреймворк, каждый владеет одной пользовательской задачей от начала до конца, плюс панель, которая собирает пять из них. Вы уже знакомы с theme-picker; вот locale-picker, text-size-picker и motion-picker (настройки), share-picker и search-picker (действия), date-time-picker (значение формы) и picker-bar.

Что вы построите

Панель настроек, где пользователи выбирают язык и размер текста — выбор, который применяется ко всему документу, сохраняется между посещениями и обходится вам в два компонента и несколько строк CSS. Около 15 минут.

Прежде чем начать

Сначала завершите руководство по фреймворку для вашего стека. Примеры ниже на Svelte; помощники для React, Vue, Angular, HTML, Nunjucks и Blazor соответствуют им контракт за контрактом.

Общая форма

  • Безголовая кнопка-значок, открывающая список WAI-ARIA APG (или, для date-time-picker, диалог выбора даты) — без встроенного <select>, чтобы каждый фреймворк получал одинаковое поведение клавиатуры и крючки стилей.
  • Один крючок класса на пакет (.locale-picker, .text-size-picker, .share-picker, .date-time-picker), ноль поставляемого CSS.
  • Безопасно для SSR: записи в DOM происходят только в жизненном цикле mount/effect.
  • Чисто для i18n: каждая видимая пользователю строка — это prop.
  • Доступно для Svelte (эталонный), React, Vue, Angular, пользовательского элемента HTML, Nunjucks и Blazor — один и тот же контракт в каждом.

theme-picker, locale-picker и text-size-picker владеют каждый одной пользовательской настройкой от начала до конца (выбор, применение к DOM, необязательное localStorage сохранение через prop storageKey ). share-picker и date-time-picker иные по природе: share-picker владеет действием — он ничего не применяет к документу и ничего не сохраняет — а date-time-picker владеет значением формы, ведь дата в форме — это данные, а не настройка.

locale-picker — язык и направление

Позволяет пользователю выбрать локаль, затем записывает lang и dir в корень документа, чтобы ваша библиотека i18n, ваш CSS и вспомогательные технологии следовали за ней. RTL определяется автоматически для арабского, иврита и других письменностей справа налево. Он занимается только сигнализацией — перевод выполняет ваша библиотека 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"
/>

Варианты подписываются из встроенной таблицы названий локалей (переопределяется через localeLabels), каждый вариант несёт собственный атрибут lang , чтобы программы чтения с экрана произносили «Français» по-французски, а коды с подчёркиванием вроде en_US без потерь преобразуются в BCP 47 и обратно en-US. Подключите onChange (или связанное значение) к i18next, Paraglide, vue-i18n или тому, что вы используете.

text-size-picker — размер, которым управляет читатель

Выигрыш в доступности, занимающий минуты: помощник устанавливает data-text-size="{slug}" в корне документа, а ваш CSS сопоставляет каждое значение с размером.

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

Задавайте размеры макета в rem и всё приложение масштабируется вместе с выбором пользователя. Подписи формируются из слагов с заглавной буквы («X Large») или переопределяются через sizeLabels.

Проверьте свою работу: выберите «Large» в селекторе, и текст страницы увеличится. Перезагрузите — он по-прежнему крупный: выбор сохранён в localStorage. Переключите селектор языка на арабский, и dir="rtl" автоматически устанавливается на документе.

share-picker — отправить эту страницу

Кнопка с одним значком, открывающая нативное окно «Поделиться» устройства, где оно есть, а иначе переходящая к списку адресатов, который предоставляете вы, плюс действие копирования URL. Он не поставляет никаких URL социальных сетей — какие сети подходят вашему продукту, решаете вы, а не система дизайна.

<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 по умолчанию location.hrefпоэтому обычному случаю не нужна никакая проводка. Принудительно задайте путь с помощью strategy="list" или strategy="native" когда нужно единообразное поведение на разных платформах для тестов или снимков экрана.

date-time-picker — дата, время или и то и другое

Текстовое поле, в которое можно печатать, плюс кнопка-значок, открывающая диалог выбора даты WAI-ARIA APG. Корректен для локали по построению — названия месяцев, названия дней, первый день недели и 12- или 24-часовой формат берутся из Intl — а значение всегда сортируемая строка ISO без часового пояса: "2026-03-15" для mode="date", "09:30" для mode="time", "2026-03-15T09:30" для 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",
  }}
/>

Значение ISO идентично тому, что отправляет <input type="date"> поэтому вы можете подменять нативный элемент управления, не трогая бэкенд.

motion-picker — уважайте уменьшение движения

Помощник устанавливает data-motion="{slug}" в корне документа. Его первое значение берётся из настройки prefers-reduced-motion операционной системы посетителя — всегда, без флага согласия, потому что движение — это реальная потребность доступности (WCAG 2.3.3). Ваш CSS и скрипты решают, что подавлять.

<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 — окно поиска, которое не мешает

Кнопка-значок, открывающая поле поиска. Нажатие Return или кнопки ⏎ рядом с полем переходит простым запросом GET на /?<text> (текст обрезается и кодируется как URI; пустой запрос никуда не ведёт). Его три доступных имени обязательны, потому что помощник не поставляет английский.

<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 меняет путь (/search?foo), а navigate удерживает навигацию внутри приложения; без него помощник вызывает location.assign.

picker-bar — вся строка заголовка сразу

picker-bar отображает поиск, тему, локаль, размер текста и «Поделиться» в одной строке, с полным набором эталонных тем и семиступенчатой шкалой текста, уже подключёнными. Каждое доступное имя приходит в одном обязательном labels объекте.

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

Доберитесь до любого обёрнутого селектора через searchProps, themeProps, localeProps, textSizeProps и shareProps.

Подсказки встроены

Каждый селектор показывает свою метку в подсказке, когда указатель находится на его кнопке или на кнопке фокус клавиатуры. Подсказка остаётся, пока указатель перемещается на неё, Escape закрывает её, где бы ни был фокус, и она никогда не показывается, пока открыто всплывающее окно селектора. Она несёт тот же текст, что и доступное имя кнопки, так что ничего не нужно настраивать или переводить дважды; эталонные темы оформляют её.

Панель настроек в одном месте

Четыре помощника настроек естественно складываются вместе:

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

Проверено: фрагменты locale-picker, text-size-picker, share-picker, date-time-picker и объединённой панели были по отдельности скомпилированы против опубликованных 0.1.1 пакетов, через svelte/server (2026-08-29); фрагменты motion-picker, search-picker и picker-bar были отрисованы против собственных компонентов пакетов 2026-10-06.

Где их взять

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

У каждого пакета есть собственный spec, тесты, сопоставленные с пунктами spec, исполняемые примеры (включая шаблоны cookie для SSR) и конвейер сборки и публикации. Пакеты Svelte — эталонные; в случае сомнений их контракты главнее.

Дальнейшие шаги

Есть вопросы по ходу дела? Страница справки полна ответов — а если какой-то шаг можно сделать яснее, сообщите нам и мы с радостью улучшим руководство.