کمکی‌های ترجیحات

هفت بسته کوچک برای هر چارچوب، هر کدام صاحب یک کار کاربر از ابتدا تا انتها، به‌علاوه نواری که پنج‌تای آن‌ها را گرد هم می‌آورد. پیش‌تر با theme-pickerآشنا شده‌اید؛ این‌ها locale-picker، text-size-picker و motion-picker (ترجیحات)، share-picker و search-picker (کنش‌ها)، date-time-picker (یک مقدار فرم) و picker-bar هستند.

چه چیزی می‌سازید

یک پنل تنظیمات که کاربران زبان و اندازه متن خود را در آن برمی‌گزینند — انتخاب‌هایی که به کل سند اعمال می‌شوند، میان بازدیدها می‌مانند و برایتان دو مؤلفه و چند خط CSS خرج برمی‌دارند. حدود ۱۵ دقیقه.

پیش از شروع

نخست آموزش چارچوب را برای پشته خود به پایان برسانید. نمونه‌های زیر 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 اندازه‌گذاری کنید و کل برنامه با انتخاب کاربر مقیاس می‌گیرد. برچسب‌ها از slug‌ها با حرف اول بزرگ ساخته می‌شوند («X Large») یا با sizeLabels.

کارتان را بررسی کنید: «Large» را از انتخابگر برگزینید، و متن صفحه بزرگ می‌شود. دوباره بارگذاری کنید — هنوز بزرگ است: انتخاب در localStorageماندگار شد. انتخابگر زبان را روی عربی بگذارید و dir="rtl" به‌طور خودکار روی سند تنظیم می‌شود.

share-picker — این صفحه را بفرستید

دکمه‌ای تک‌نماد که برگه اشتراک‌گذاری بومی دستگاه را در جایی که باشد باز می‌کند، و در غیر این صورت به فهرستی از مقصدهایی که شما می‌دهید بازمی‌گردد، به‌علاوه کنش کپی نشانی. هیچ نشانی شبکه اجتماعی ارسال نمی‌کند — اینکه کدام شبکه‌ها به محصول شما تعلق دارند تصمیم شماست، نه تصمیم سامانه طراحی.

<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 را باز می‌کند. از بنیاد با محلی سازگار — نام ماه‌ها، نام روزهای هفته، نخستین روز هفته و ساعت ۱۲ یا ۲۴ ساعته همه از 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 (۲۰۲۶-۰۸-۲۹)؛ قطعه‌های motion-picker، search-picker و picker-bar در ۲۰۲۶-۱۰-۰۶ در برابر مؤلفه‌های خود بسته‌ها رندر شدند.

از کجا بگیرید

# 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، نمونه‌های اجراشدنی (از جمله الگوهای کوکی SSR) و یک خط لوله ساخت و انتشار دارد. بسته‌های Svelte مرجع‌اند؛ در صورت تردید قراردادهای آن‌ها برنده است.

گام‌های بعدی

در مسیر پرسشی دارید؟ صفحه راهنما پر از پاسخ است — و اگر گامی می‌تواند روشن‌تر باشد، به ما بگویید و ما با کمال میل آموزش را بهبود می‌دهیم.