ตัวช่วยการตั้งค่า

แพ็กเกจเล็ก ๆ เจ็ดตัวต่อเฟรมเวิร์ก แต่ละตัวดูแลงานของผู้ใช้หนึ่งอย่างตั้งแต่ต้นจนจบ บวกกับแถบที่รวมห้าตัวเข้าด้วยกัน คุณเคยรู้จัก 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 ตรงกันทีละสัญญา

รูปแบบที่ใช้ร่วมกัน

  • ปุ่มไอคอนแบบ headless ที่เปิด listbox ตาม WAI-ARIA APG (หรือสำหรับ date-time-picker คือกล่องโต้ตอบเลือกวันที่) — ไม่มี <select>แบบเนทีฟ ทุกเฟรมเวิร์กจึงได้พฤติกรรมแป้นพิมพ์และ style hook เหมือนกัน
  • หนึ่ง class hook ต่อแพ็กเกจ (.locale-picker, .text-size-picker, .share-picker, .date-time-picker) ไม่มี CSS ส่งมาด้วย
  • ปลอดภัยสำหรับ SSR: การเขียน DOM เกิดขึ้นเฉพาะในวงจรชีวิต mount/effect
  • เป็นมิตรต่อ i18n: ทุกข้อความที่ผู้ใช้เห็นเป็น prop
  • ใช้ได้กับ Svelte (ตัวหลัก), React, Vue, Angular, HTML custom element, Nunjucks และ Blazor — สัญญาเดียวกันในทุกตัว

theme-picker, locale-picker และ text-size-picker แต่ละตัวดูแล การตั้งค่า ของผู้ใช้หนึ่งอย่างตั้งแต่ต้นจนจบ (การเลือก, การนำไปใช้กับ DOM, localStorage การเก็บค่าแบบไม่บังคับผ่าน storageKey prop) ส่วน 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 — ส่งหน้านี้

ปุ่มสัญลักษณ์เดียวที่เปิดชีตแชร์แบบเนทีฟของอุปกรณ์ถ้ามี และถ้าไม่มีก็จะสลับไปใช้รายการปลายทางที่คุณกำหนด พร้อมการกระทำคัดลอก 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-

แต่ละแพ็กเกจมีสเปกของตัวเอง การทดสอบที่โยงกับข้อกำหนดของสเปก ตัวอย่างที่รันได้ (รวมถึงรูปแบบคุกกี้สำหรับ SSR) และไปป์ไลน์สำหรับบิลด์และเผยแพร่ แพ็กเกจ Svelte เป็นตัวหลัก เมื่อสงสัย ให้ถือสัญญาของมันเป็นหลัก

ขั้นต่อไป

มีคำถามระหว่างทางไหม? หน้าช่วยเหลือ เต็มไปด้วยคำตอบ — และถ้าขั้นตอนไหนทำให้ชัดเจนกว่านี้ได้ บอกเรา แล้วเรายินดีปรับปรุงบทเรียน