انتخاب‌گر تمThemeSelector

دو کارتِ کوچک که هر تم را با یک پیش‌نمایشِ مینیاتوری از یک پنجره‌ی مرورگر نشان می‌دهند؛ روی هر کدام کلیک کنید یا با جهت‌نما بروید تا حلقه‌ی انتخاب رویش بنشیند و برچسبش روشن شود.

محمدجواد

ساخته شده توسط محمدجواد

سازنده فارسی‌فای، بچه دولوپر

وابستگی‌ها

بدون وابستگی

نوع تعامل

یک radiogroup با دو گزینه است: کلیک گزینه را انتخاب می‌کند و جهت‌نماها (و Home/End) با tabindex گردشی بین گزینه‌ها می‌چرخند و همان‌جا انتخاب می‌کنند. هاور روی گزینه‌ی انتخاب‌نشده یک حلقه‌ی ظریف نشان می‌دهد و گزینه‌ی فعال همیشه حلقه دارد و برچسبش پررنگ می‌شود. با فوکوسِ صفحه‌کلید، حلقه‌ی سومِ برند روشن می‌شود. با `value` می‌شود کنترل‌شده راندش — `null` یعنی هیچ‌چیز انتخاب نیست — و `disabled` کل گروه را کم‌رنگ و بی‌اثر می‌کند. پیش‌نمایش‌ها با container-query مقیاس می‌شوند و در هر اندازه‌ی کارت سالم می‌مانند.

پراپ‌ها

گزینه‌هایی که می‌تونی به کامپوننت بدی؛ ستاره‌دارها اجباری‌اند.

themes
ThemeOption[]پیش‌فرض: DEFAULT_THEMES

گزینه‌های تم؛ هر گزینه `value`، برچسب و پالتِ شش‌رنگِ نقاشیِ پیش‌نمایش (sidebar/panel/border/strong/muted/tile) دارد. می‌تونید پالتِ خودتون رو بديد.

value
string | null

مقدارِ کنترل‌شده؛ `undefined` یعنی کامپوننت خودش state دارد و `null` یعنی کنترل‌شده با هیچ‌چیزِ انتخاب‌شده (مثلِ قبل از آن‌که تم روی کلاینت معلوم شود).

defaultValue
stringپیش‌فرض: themes[0].value

گزینه‌ی انتخاب‌شده‌ی اول در حالتِ کنترل‌نشده؛ اگر ندهید گزینه‌ی نخست انتخاب است.

onChange
(value: string) => void

با هر انتخابِ تازه صدا زده می‌شود؛ در حالتِ کنترل‌شده تنها راهِ باخبرشدن از انتخاب کاربر است.

labels
booleanپیش‌فرض: true

نمایش برچسبِ فارسی («تیره»/«روشن») زیرِ هر کارت؛ برچسب با انتخاب روشن و خاموش می‌شود.

disabled
booleanپیش‌فرض: false

کل گروه را کم‌رنگ و بی‌اعتنا به اشاره‌گر و صفحه‌کلید می‌کند (`aria-disabled` هم می‌گیرد).

نصب

۱. این آدرس رو یک‌بار توی components.json بذار تا shadcn مخزن فارسی‌فای رو بشناسه.

components.jsonjson
{
  "registries": {
    "@farsify": "https://farsify.ir/r/{name}.json"
  }
}

۲. بعد با دستورِ زیر کامپوننت رو نصب کن.

npx shadcn@latest add @farsify/theme-selector

پیش‌نیاز: Tailwind CSS v4 و صفحهٔ راست‌به‌چپ dir="rtl".

نمونه

theme-selector.tsxtsx
"use client";

import { useState } from "react";
import { ThemeSelector } from "@/components/farsify/theme-selector";

export function ThemePicker() {
  const [theme, setTheme] = useState<string | null>("dark");

  return <ThemeSelector value={theme} onChange={setTheme} />;
}

// نام‌کنترل‌شده — گزینه‌ی نخست انتخاب است؛ برچسب‌ها هم اختیاری‌اند
<ThemeSelector defaultValue="light" labels={false} />

// پالتِ خودت — هر گزینه شش رنگِ نقاشیِ پیش‌نمایش را می‌گیرد
<ThemeSelector
  themes={[
    {
      value: "midnight",
      label: "نیمه‌شب",
      colors: {
        sidebar: "#1C1C1E",
        panel: "#111112",
        border: "transparent",
        strong: "#4A4A50",
        muted: "#2C2C30",
        tile: "#232326",
      },
    },
    {
      value: "paper",
      label: "کاغذ",
      colors: {
        sidebar: "#FAFAFC",
        panel: "#FFFFFF",
        border: "rgba(0, 0, 0, 0.06)",
        strong: "#D6D6DE",
        muted: "#EBEBF0",
        tile: "#F2F2F6",
      },
    },
  ]}
  itemClassName="w-56"
/>

// پیش از آن‌که تمِ کاربر معلوم شود — هیچ گزینه‌ای انتخاب نیست و همه با tab در دسترس‌اند
<ThemeSelector value={null} />

نکته‌ها

  • علاوه بر کلیک و جهت‌نما، `Home` گزینه‌ی نخست و `End` گزینه‌ی آخر را انتخاب می‌کند — رفتارِ استانداردِ radiogroup که مرجع هم دارد.
  • اگر `value={null}` بدهید هیچ گزینه‌ای انتخاب نیست و همه با tab در دسترس می‌مانند؛ برای حالتِ «هنوز تمِ کاربر معلوم نیست» ساخته شده.
  • پیش‌نمایش‌ها با واحدِ `cqw` روی container-query کشیده شده‌اند؛ هر چه کارت بزرگ‌تر شود نقاشی هم بی‌کشیدگی بزرگ می‌شود — برای کارت‌های بزرگ‌تر فقط `itemClassName="w-56"` بدهید.
  • جهت‌نماها فیزیکی‌اند (`ArrowRight` گزینه‌ی بعدی)، مثلِ مرجع؛ کارت‌ها اما در چیدمانِ RTL راست‌به‌چپ می‌نشینند و نقاشیِ داخلی با `dir="ltr"` عینِ مرجع می‌ماند.
  • رنگ‌های داخلِ هر `ThemeOption` داده‌ی نقاشی‌اند و با hex می‌مانند (تصمیمِ صاحب‌پروژه)؛ قابِ خودِ کامپوننت (سطح کارت، جوهر برچسب، حلقه‌ی فوکوس) از توکن‌های سایت می‌خواند.
  • با `prefers-reduced-motion` گذارِ حلقه‌ها و برچسب‌ها خاموش می‌شود؛ انتخاب همان‌جا بی‌حرکت جابه‌جا می‌شود.

مجوز

کد بعد از نصب مالِ خودته؛ هر تغییری خواستی بده و توی پروژه‌ی خودت نگهش دار. برای پروژه‌های شخصی و تجاری آزاده، حتی اگه کدت بسته باشه. فقط اسم و لینک فارسی‌فای رو یه جا توی پروژه نگه دار.