@kashi-ui/mcp 1.1.0 → 1.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/docs.json CHANGED
@@ -1 +1 @@
1
- {"version":"1.1.0","generated":"2026-11-08","docs":[{"slug":"components/accordion","url":"https://kashi.sepantapouya.com/docs/components/accordion/","title":"آکاردئون","description":"بخش‌های جمع‌شونده برای محتوای طولانی، مثل پرسش‌های متداول؛ روی details بومی.","group":"کامپوننت‌ها","components":["Accordion","AccordionItem"],"markdown":"```tsx\nimport { Accordion, AccordionItem } from \"@kashi-ui/react\";\n```\n\n## نمونه\n\nبا `type=\"single\"` باز شدن هر بخش بقیه را می‌بندد.\n\n```tsx\n<Accordion type=\"single\">\n <AccordionItem title=\"کاشی رایگان است؟\" defaultOpen>بله. …</AccordionItem>\n <AccordionItem title=\"با Next.js کار می‌کند؟\">بله؛ …</AccordionItem>\n <AccordionItem title=\"می‌توانم فونت خودم را استفاده کنم؟\">بله، …</AccordionItem>\n</Accordion>\n```\n\n| ویژگی | نوع | پیش‌فرض | توضیح |\n|---|---|---|---|\n| type | \"single\" \\| \"multiple\" | \"multiple\" | فقط یک بخش باز یا چند بخش (Accordion) |\n| flush | boolean | false | بدون حاشیه (Accordion) |\n| title | ReactNode | — | عنوان بخش (AccordionItem) |\n| defaultOpen | boolean | false | باز در ابتدا (AccordionItem) |\n\n## دسترس‌پذیری\n\nروی `<details>` و `<summary>` بومی ساخته شده: بدون JavaScript کار می‌کند، با <kbd>Enter</kbd> و <kbd>Space</kbd> باز و بسته می‌شود و صفحه‌خوان وضعیت باز یا بسته را اعلام می‌کند. جستجوی صفحه (<kbd>Ctrl</kbd>+<kbd>F</kbd>) محتوای بخش‌های بسته را هم پیدا و باز می‌کند.\n\n## راهنمای استفاده\n\nکِی استفاده کنیم:\n\n- پرسش‌های متداول و راهنماهایی که کاربر فقط بخشی از آن‌ها را می‌خواند.\n- تنظیمات پیشرفته‌ای که بیشتر کاربران لازم ندارند.\n\nکِی استفاده نکنیم:\n\n- محتوایی که همه باید ببینند، مثل شرایط مهم خرید؛ پنهانش نکنید.\n- جابه‌جایی بین چند نمای هم‌سطح یک محتوا. (زبانه‌ها: /docs/components/tabs/)\n- مراحل پشت‌سرهم یک فرایند. (استپر: /docs/components/stepper/)\n\nدرست و نادرست:\n\n- ✓ درست: عنوان هر بخش خودش پرسش یا موضوع را کامل بگوید: «هزینهٔ ارسال چقدر است؟»\n- ✗ نادرست: عنوان‌های مبهم مثل «بیشتر» یا «اطلاعات» که باید باز شوند تا معلوم شود چه دارند.\n- ✓ درست: همهٔ بخش‌ها بسته شروع شوند، مگر یکی که بیشتر کاربران دنبالش هستند.\n- ✗ نادرست: همه را باز نگه دارید؛ آن وقت آکاردئون فقط شلوغی اضافه است."},{"slug":"components/alert","url":"https://kashi.sepantapouya.com/docs/components/alert/","title":"هشدار","description":"پیامی مهم دربارهٔ وضعیت سیستم یا نتیجهٔ یک عمل، با آیکون شاخص و عمل‌های اختیاری.","group":"کامپوننت‌ها","components":["Alert"],"markdown":"```tsx\nimport { Alert } from \"@kashi-ui/react\";\n```\n\n## رنگ‌ها\n\nسطح هشدار خنثی است و رنگ فقط در آیکون شاخص دیده می‌شود؛ این کار صفحه را آرام نگه می‌دارد.\n\n```tsx\n<Alert tone=\"brand\" title=\"نسخهٔ جدید منتشر شد\">کاشی ۰.۲ با هفت کامپوننت تازه در دسترس است.</Alert>\n<Alert tone=\"success\" title=\"تغییرات ذخیره شد\" />\n<Alert tone=\"warning\" title=\"اشتراک شما ۳ روز دیگر تمام می‌شود\">برای جلوگیری از قطع سرویس، اشتراک را تمدید کنید.</Alert>\n<Alert tone=\"error\" title=\"پرداخت ناموفق بود\">موجودی کارت کافی نیست. کارت دیگری را امتحان کنید.</Alert>\n```\n\n## عمل‌ها و بستن\n\n```tsx\n<Alert\n tone=\"info\"\n title=\"به‌روزرسانی در دسترس است\"\n actions={<>\n <Button variant=\"link\">به‌روزرسانی</Button>\n <Button variant=\"link\">بعداً</Button>\n </>}\n onClose={() => setOpen(false)}\n>\n نسخهٔ جدید بهبودهای امنیتی دارد.\n</Alert>\n```\n\n## پس‌زمینهٔ رنگی\n\nبرای پیام‌هایی که باید بیشتر دیده شوند، از `tinted` استفاده کنید.\n\n```tsx\n<Alert tone=\"warning\" tinted title=\"حالت نگهداری\">سرویس امشب از ساعت ۲ تا ۴ در دسترس نیست.</Alert>\n```\n\n## نوار بالای صفحه و فشرده\n\n```tsx\n<Alert tone=\"brand\" layout=\"banner\" title=\"نسخهٔ ۰.۸ منتشر شد\" actions={…} onClose={…}>…</Alert>\n<Alert tone=\"warning\" size=\"sm\" title=\"این ایمیل قبلاً ثبت شده است.\" />\n```\n\n**ویژگی‌های جدید**\n\n| ویژگی | نوع | پیش‌فرض | توضیح |\n|---|---|---|---|\n| layout | \"card\" \\| \"banner\" | \"card\" | نوار تمام‌عرض بالای صفحه |\n| size | \"sm\" \\| \"md\" | \"md\" | نسخهٔ فشرده برای داخل فرم |\n| classNames | { root, icon, title, description, actions } | — | کلاس دلخواه برای هر بخش |\n\n## ویژگی‌ها\n\n| ویژگی | نوع | پیش‌فرض | توضیح |\n|---|---|---|---|\n| tone | \"neutral\" \\| \"brand\" \\| \"info\" \\| \"success\" \\| \"warning\" \\| \"error\" | \"neutral\" | رنگ و آیکون پیش‌فرض |\n| title | ReactNode | — | عنوان |\n| children | ReactNode | — | توضیح |\n| icon | ReactNode \\| false | — | آیکون سفارشی؛ false برای حذف |\n| actions | ReactNode | — | دکمه‌ها یا لینک‌های زیر متن |\n| onClose | () => void | — | با دادن این تابع دکمهٔ بستن نمایش داده می‌شود |\n| closeLabel | string | \"بستن\" | نام دسترس‌پذیر دکمهٔ بستن |\n| tinted | boolean | false | پس‌زمینهٔ رنگی کم‌رنگ |\n| role | string | خودکار | پیش‌فرض alert برای خطا و هشدار، status برای بقیه |\n\n## متغیرهای CSS\n\n| متغیر CSS | پیش‌فرض | توضیح |\n|---|---|---|\n| --k-alert-radius | var(--k-radius-xl) | گردی گوشه‌ها |\n| --k-alert-padding | 1rem × density | فاصلهٔ داخلی |\n| --k-alert-bg | var(--k-bg-secondary) | پس‌زمینه |\n\n## دسترس‌پذیری\n\n- هشدارهای `error` و `warning` نقش `alert` دارند و صفحه‌خوان آن‌ها را فوراً اعلام می‌کند؛ بقیه با نقش `status` بدون قطع کاربر خوانده می‌شوند.\n- فقط هشدارهایی را که پس از بارگذاری صفحه اضافه می‌شوند `alert` کنید؛ هشدار ثابت صفحه را با `role=\"note\"` نمایش دهید.\n- آیکون تزئینی است و از صفحه‌خوان پنهان است؛ عنوان باید معنا را کامل برساند.\n\n## راهنمای استفاده\n\nکِی استفاده کنیم:\n\n- پیامی مهم دربارهٔ همین بخش صفحه: نتیجهٔ یک عمل، هشدار پیش از تصمیم، یا محدودیتی که کاربر باید بداند.\n- خطایی که کل فرم را می‌گیرد، مثلاً قطع شدن اتصال هنگام ثبت.\n\nکِی استفاده نکنیم:\n\n- پیامی که به کل سایت مربوط است (نسخهٔ جدید، اختلال سرویس). (بنر: /docs/components/banner/)\n- تأیید کوتاه پس از یک عمل («ذخیره شد»). (توست: /docs/components/toast/)\n- خطای یک فیلد؛ آن را کنار همان فیلد با `error` نشان دهید. (ورودی: /docs/components/input/)\n\nدرست و نادرست:\n\n- ✓ درست: عنوان بگوید چه شده و متن بگوید حالا چه کنید.\n- ✗ نادرست: پیام کلی که نه علت را می‌گوید نه راه را.\n- ✓ درست: رنگ با معنا: `error` برای شکست، `warning` برای هشدار، `success` برای نتیجهٔ خوب.\n- ✗ نادرست: رنگ قرمز برای جلب توجه به پیامی که خطا نیست؛ کاربر یاد می‌گیرد قرمز را جدی نگیرد.\n\nمتن:\n\n- عنوان کوتاه و بدون علامت تعجب؛ متن یک یا دو جمله.\n- اگر عملی ممکن است (تلاش دوباره، تماس با پشتیبانی)، با `actions` دکمه‌اش را بگذارید."},{"slug":"components/avatar","url":"https://kashi.sepantapouya.com/docs/components/avatar/","title":"آواتار","description":"نمایش تصویر یا حروف اول یک شخص، با وضعیت حضور و گروه‌بندی.","group":"کامپوننت‌ها","components":["Avatar","AvatarGroup"],"markdown":"```tsx\nimport { Avatar, AvatarGroup } from \"@kashi-ui/react\";\n```\n\n## تصویر و حروف اول\n\nاگر تصویر نباشد یا بارگذاری نشود، حروف اول نام نمایش داده می‌شود. بین حروف فارسی نیم‌فاصله قرار می‌گیرد تا به هم نچسبند. در اندازه‌های `xs` و `sm` فقط حرف اول نمایش داده می‌شود تا خوانا بماند.\n\n```tsx\n<Avatar name=\"سپنتا پویا\" src=\"/images/sepanta-pouya.jpg\" size=\"lg\" />\n<Avatar name=\"سارا احمدی\" size=\"lg\" />\n<Avatar name=\"علی رضایی\" size=\"lg\" brand />\n<Avatar name=\"کاشی\" size=\"lg\" square />\n```\n\n## اندازه‌ها\n\n```tsx\n<Avatar name=\"سارا احمدی\" size=\"xs\" />\n<Avatar name=\"سارا احمدی\" size=\"sm\" />\n<Avatar name=\"سارا احمدی\" />\n<Avatar name=\"سارا احمدی\" size=\"lg\" />\n<Avatar name=\"سارا احمدی\" size=\"xl\" />\n<Avatar name=\"سارا احمدی\" size=\"2xl\" />\n```\n\n## وضعیت\n\n```tsx\n<Avatar name=\"سارا احمدی\" status=\"online\" size=\"lg\" />\n<Avatar name=\"علی رضایی\" status=\"busy\" size=\"lg\" />\n<Avatar name=\"مریم کریمی\" status=\"away\" size=\"lg\" />\n<Avatar name=\"رضا نوری\" status=\"offline\" size=\"lg\" />\n```\n\n## گروه\n\n```tsx\n<AvatarGroup max={4} aria-label=\"اعضای تیم\">\n <Avatar name=\"سپنتا پویا\" src=\"/images/sepanta-pouya.jpg\" />\n <Avatar name=\"سارا احمدی\" brand />\n <Avatar name=\"علی رضایی\" />\n <Avatar name=\"مریم کریمی\" brand />\n <Avatar name=\"رضا نوری\" />\n <Avatar name=\"نگار صادقی\" />\n</AvatarGroup>\n```\n\n## رنگ خودکار\n\nبا `color=\"auto\"` هر نام همیشه یک رنگ ثابت می‌گیرد (در هر دستگاه یکسان)؛ فهرست اعضا خواناتر و رنگارنگ‌تر می‌شود.\n\n```tsx\n{members.map((m) => <Avatar key={m.id} name={m.name} color=\"auto\" size=\"lg\" />)}\n```\n\n**ویژگی‌های جدید**\n\n| ویژگی | نوع | پیش‌فرض | توضیح |\n|---|---|---|---|\n| color | \"auto\" \\| \"neutral\" \\| \"brand\" \\| \"info\" \\| \"success\" \\| \"warning\" \\| \"error\" | \"neutral\" | رنگ حروف اول؛ همهٔ رنگ‌ها کنتراست AA دارند |\n\n## ویژگی‌ها\n\n| ویژگی | نوع | پیش‌فرض | توضیح |\n|---|---|---|---|\n| name | string | — | نام شخص (الزامی)؛ برای نام دسترس‌پذیر و حروف اول |\n| src | string | — | آدرس تصویر |\n| size | \"xs\" \\| \"sm\" \\| \"md\" \\| \"lg\" \\| \"xl\" \\| \"2xl\" | \"md\" | ۲۴ تا ۶۴ پیکسل |\n| status | \"online\" \\| \"offline\" \\| \"busy\" \\| \"away\" | — | نشانگر وضعیت حضور |\n| statusLabels | Partial<Record<AvatarStatus, string>> | فارسی | متن وضعیت برای صفحه‌خوان |\n| square | boolean | false | شکل مربعی برای سازمان‌ها و پروژه‌ها |\n| brand | boolean | false | رنگ برند برای حالت حروف اول |\n\n**ویژگی‌های AvatarGroup**\n\n| ویژگی | نوع | پیش‌فرض | توضیح |\n|---|---|---|---|\n| max | number | — | حداکثر تعداد نمایش؛ بقیه به شکل «+۲» |\n| size | AvatarSize | \"md\" | اندازهٔ آواتار باقی‌مانده |\n| moreLabel | (count: number) => string | \"۲ نفر دیگر\" | متن صفحه‌خوان برای باقی‌مانده |\n\n## متغیرهای CSS\n\n| متغیر CSS | پیش‌فرض | توضیح |\n|---|---|---|\n| --k-avatar-size | 2.5rem | اندازه |\n| --k-avatar-radius | var(--k-radius-full) | گردی گوشه‌ها |\n| --k-avatar-bg | var(--k-bg-tertiary) | پس‌زمینهٔ حروف اول |\n| --k-avatar-fg | var(--k-text-secondary) | رنگ حروف اول |\n\n## دسترس‌پذیری\n\n- آواتار نقش `img` دارد و نام شخص به‌همراه وضعیت («سارا احمدی، آنلاین») اعلام می‌شود. تصویر داخلی تزئینی است.\n- وضعیت فقط با رنگ نمایش داده نمی‌شود؛ متن آن برای صفحه‌خوان در دسترس است.\n\n## راهنمای استفاده\n\nکِی استفاده کنیم:\n\n- نشان دادن شخص در کامنت، فهرست اعضا، منوی کاربر و تخصیص کار.\n- گروهی از افراد با `AvatarGroup`، مثل اعضای یک پروژه.\n\nکِی استفاده نکنیم:\n\n- لوگوی شرکت یا محصول؛ تصویر معمولی یا کارت بگذارید.\n- تنها نشانهٔ هویت در جایی که نام لازم است؛ آواتار را همیشه با نام کنارش بیاورید، مگر فضا خیلی کم باشد.\n\nدرست و نادرست:\n\n- ✓ درست: `name` را همیشه بدهید تا وقتی عکس بارگذاری نشد حروف اول نشان داده شود و صفحه‌خوان نام را بخواند.\n- ✗ نادرست: فقط `src` بدون نام؛ با خرابی عکس، دایرهٔ خالی و بی‌نام می‌ماند.\n- ✓ درست: در گروه شلوغ `max` بدهید تا «+۴ نفر دیگر» نمایش داده شود.\n- ✗ نادرست: بیست آواتار در یک ردیف که از کادر بیرون می‌زند."},{"slug":"components/badge","url":"https://kashi.sepantapouya.com/docs/components/badge/","title":"بج","description":"برچسب کوچک برای وضعیت، دسته‌بندی یا شمارش.","group":"کامپوننت‌ها","components":["Badge"],"markdown":"```tsx\nimport { Badge } from \"@kashi-ui/react\";\n```\n\n## رنگ‌ها\n\nرنگ را بر اساس معنا انتخاب کنید، نه سلیقه؛ کاربر رنگ‌ها را در کل محصول یکسان تفسیر می‌کند.\n\n```tsx\n<Badge>پیش‌نویس</Badge>\n<Badge tone=\"brand\">جدید</Badge>\n<Badge tone=\"success\" dot>فعال</Badge>\n<Badge tone=\"warning\">در انتظار</Badge>\n<Badge tone=\"error\">رد شده</Badge>\n<Badge tone=\"info\">بتا</Badge>\n```\n\n## اندازه و شکل\n\n```tsx\n<Badge tone=\"brand\" size=\"sm\">کوچک</Badge>\n<Badge tone=\"brand\">متوسط</Badge>\n<Badge tone=\"brand\" size=\"lg\">بزرگ</Badge>\n<Badge tone=\"success\" square>مربعی</Badge>\n```\n\n## سبک‌ها و برچسب‌های حذف‌شدنی\n\n```tsx\n<Badge tone=\"success\" variant=\"solid\">پرداخت‌شده</Badge>\n<Badge tone=\"brand\" variant=\"outline\">بتا</Badge>\n<Badge variant=\"outline\" icon={<UserIcon />}>۱۲ عضو</Badge>\n\n// فیلترهای فعال\n{tags.map((t) => <Badge key={t} tone=\"brand\" onRemove={() => remove(t)}>{t}</Badge>)}\n```\n\n**ویژگی‌های جدید**\n\n| ویژگی | نوع | پیش‌فرض | توضیح |\n|---|---|---|---|\n| variant | \"soft\" \\| \"solid\" \\| \"outline\" | \"soft\" | سبک |\n| icon | ReactNode | — | آیکون ابتدای متن |\n| onRemove / removeLabel | () => void / string | «حذف {متن}» | دکمهٔ حذف با نام برای صفحه‌خوان |\n\n## ویژگی‌ها\n\n| ویژگی | نوع | پیش‌فرض | توضیح |\n|---|---|---|---|\n| tone | \"neutral\" \\| \"brand\" \\| \"error\" \\| \"warning\" \\| \"success\" \\| \"info\" | \"neutral\" | رنگ معنایی |\n| size | \"sm\" \\| \"md\" \\| \"lg\" | \"md\" | اندازه |\n| dot | boolean | false | نقطهٔ رنگی ابتدای متن |\n| square | boolean | false | گوشهٔ کم به‌جای گرد |\n\n## متغیرهای CSS\n\n| متغیر CSS | پیش‌فرض | توضیح |\n|---|---|---|\n| --k-badge-radius | var(--k-radius-full) | گردی گوشه‌ها |\n| --k-badge-font-weight | 500 | وزن متن |\n\n## دسترس‌پذیری\n\n- همهٔ ترکیب‌های رنگ متن و پس‌زمینه کنتراست حداقل ۴٫۵ به ۱ دارند.\n- رنگ نباید تنها حامل معنا باشد؛ متن نشان باید به‌تنهایی وضعیت را برساند.\n- اگر عدد نشان (مثلاً تعداد پیام‌ها) تغییر می‌کند، تغییر را با یک ناحیهٔ `aria-live` اعلام کنید.\n\n## راهنمای استفاده\n\nکِی استفاده کنیم:\n\n- وضعیت یک مورد در جدول یا کارت: «پرداخت شد»، «در انتظار»، «لغو شده».\n- شمارش کوچک کنار یک عنوان یا زبانه.\n\nکِی استفاده نکنیم:\n\n- فیلتری که کاربر روشن و خاموش می‌کند. (چیپس: /docs/components/chips/)\n- متن طولانی یا جمله؛ بج یک یا دو کلمه است.\n- دکمه؛ بج کلیک‌خور به نظر نمی‌رسد. (دکمه: /docs/components/button/)\n\nدرست و نادرست:\n\n- ✓ درست: یک رنگ برای یک معنا در کل محصول.\n- ✗ نادرست: رنگ‌های تزئینی که معنا ندارند و کاربر نمی‌فهمد کدام مهم است.\n- ✓ درست: متن کوتاه: «ارسال شد».\n- ✗ نادرست: جمله: «سفارش شما با موفقیت ارسال گردید»."},{"slug":"components/banner","url":"https://kashi.sepantapouya.com/docs/components/banner/","title":"بنر","description":"پیام سطح صفحه مثل نسخهٔ جدید، پایان اشتراک یا اختلال سرویس، با دکمهٔ عمل و بستن.","group":"کامپوننت‌ها","components":["Banner"],"markdown":"```tsx\nimport { Banner } from \"@kashi-ui/react\";\n```\n\nبرای پیامی که به کل صفحه یا حساب مربوط است. پیام مربوط به یک بخش یا فرم را با `Alert` و پیام گذرا را با `Toast` نشان دهید.\n\n## نمونه\n\n```tsx\n<Banner tone=\"brand\" icon={<InfoIcon />} onClose={dismiss}\n actions={<Button size=\"sm\" variant=\"primary\">مشاهدهٔ تغییرات</Button>}>\n <strong>نسخهٔ ۰٫۱۰ منتشر شد.</strong> هفت کامپوننت تازه…\n</Banner>\n\n<Banner tone=\"warning\" icon={<WarningIcon />} actions={<Button size=\"sm\">تمدید اشتراک</Button>}>\n <strong>اشتراک شما ۳ روز دیگر تمام می‌شود.</strong> …\n</Banner>\n```\n\nبرای چسباندن به بالای صفحه، `flush` را بدهید تا حاشیه و گوشهٔ گرد حذف شود.\n\n| ویژگی | نوع | پیش‌فرض | توضیح |\n|---|---|---|---|\n| tone | \"neutral\" \\| \"brand\" \\| \"warning\" \\| \"error\" | \"neutral\" | رنگ |\n| icon | ReactNode | — | آیکون شاخص |\n| actions | ReactNode | — | دکمه یا لینک‌ها |\n| onClose | () => void | — | با آن دکمهٔ بستن نمایش داده می‌شود |\n| flush | boolean | false | بدون حاشیه، برای بالای صفحه |\n| label / closeLabel | string | «اطلاعیه» / «بستن اطلاعیه» | نام ناحیه و دکمه |\n\n## دسترس‌پذیری\n\nناحیهٔ `role=\"region\"` با نام «اطلاعیه»، تا کاربر صفحه‌خوان بتواند به آن پرش کند. نوار به‌عمد `aria-live` ندارد، چون هنگام بارگذاری صفحه نمایش داده می‌شود؛ برای پیام‌هایی که بعداً ظاهر می‌شوند از `Toast` استفاده کنید.\n\n## راهنمای استفاده\n\nکِی استفاده کنیم:\n\n- پیام سطح سایت یا حساب: اختلال سرویس، پایان نزدیک اشتراک، نسخهٔ تازه.\n- پیامی که تا کاربر کاری نکند یا آن را نبندد می‌ماند.\n\nکِی استفاده نکنیم:\n\n- پیام مربوط به یک بخش یا فرم. (هشدار: /docs/components/alert/)\n- تبلیغ و پیشنهاد فروش در هر صفحه؛ کاربر عادت می‌کند بنرها را نخواند.\n- تأیید یک عمل. (توست: /docs/components/toast/)\n\nدرست و نادرست:\n\n- ✓ درست: یک بنر در هر زمان، بالای صفحه، با یک دکمهٔ عمل روشن: «تمدید اشتراک».\n- ✗ نادرست: دو سه بنر روی هم که نیمی از صفحه را می‌گیرند.\n- ✓ درست: اگر پیام دانستنی است نه اجباری، `onClose` بدهید و بسته شدنش را نگه دارید تا برنگردد (در وب کامپوننت با ویژگی `remember`).\n- ✗ نادرست: بنری که با هر بار باز کردن صفحه دوباره برمی‌گردد.\n\n## وب کامپوننت\n\nهمین کامپوننت بدون React، برای HTML، وردپرس، Vue، Svelte و Angular: `<kashi-banner>` در `@kashi-ui/elements`. متن خود عنصر است؛ عنصرهای `slot=\"actions\"` کنار متن و `slot=\"icon\"` پیش از آن می‌آیند. با `remember` بستن اطلاعیه در همان مرورگر یادش می‌ماند.\n\nنصب بدون ابزار ساخت (CDN):\n\n```html\n<link rel=\"stylesheet\" href=\"https://cdn.jsdelivr.net/npm/@kashi-ui/css/dist/kashi.css\">\n<script type=\"module\" src=\"https://cdn.jsdelivr.net/npm/@kashi-ui/elements/dist/cdn/kashi-elements.js\"></script>\n```\n\nیا با npm، فقط همین بخش:\n\n```bash\nnpm i @kashi-ui/elements @kashi-ui/css\n```\n\n```js\nimport \"@kashi-ui/css\";\nimport \"@kashi-ui/elements/basic.js\";\n```\n\n```html\n<kashi-banner tone=\"brand\" dismissible remember=\"v25\">\n نسخهٔ <strong>۰.۲۵</strong> منتشر شد: همهٔ کامپوننت‌ها حالا بدون React هم هستند.\n <a slot=\"actions\" class=\"k-btn k-btn--secondary k-btn--sm\" href=\"/docs/web-components/\">ببینید</a>\n</kashi-banner>\n```\n\nویژگی‌ها:\n\n| نام | مقدار | پیش‌فرض | توضیح |\n|---|---|---|---|\n| tone | brand \\| warning \\| error | neutral | رنگ اطلاعیه |\n| dismissible | بولی | — | دکمهٔ بستن |\n| remember | کلید | — | پس از بستن، دیگر نمایش داده نشود (localStorage) |\n| flush | بولی | — | بدون حاشیه و گوشه، برای بالای صفحه |\n| label / close-label | متن | اطلاعیه / بستن اطلاعیه | نام‌ها برای صفحه‌خوان |\n\nرویدادها و JavaScript:\n\n| نام | توضیح |\n|---|---|\n| kashi-close | پیش از بستن؛ با event.preventDefault() بسته نمی‌شود |\n| element.close() | بستن با JavaScript |"},{"slug":"components/breadcrumb","url":"https://kashi.sepantapouya.com/docs/components/breadcrumb/","title":"بردکرامب","description":"نشان می‌دهد کاربر کجای ساختار سایت است.","group":"کامپوننت‌ها","components":["Breadcrumb"],"markdown":"```tsx\nimport { Breadcrumb } from \"@kashi-ui/react\";\n```\n\n## نمونه\n\nخانه فقط با آیکون نمایش داده می‌شود و برچسبش برای صفحه‌خوان می‌ماند.\n\n```tsx\n<Breadcrumb items={[\n { label: \"خانه\", href: \"/\", icon: <HomeIcon />, hideLabel: true },\n { label: \"تنظیمات\", href: \"/settings\" },\n { label: \"امنیت\" },\n]} />\n```\n\n## مسیر بلند\n\nبا `maxItems` وسط مسیر به «…» جمع می‌شود؛ با زدن آن، مسیر کامل باز می‌شود. نام‌های بلند هم کوتاه می‌شوند و متن کامل در `title` است.\n\n```tsx\n<Breadcrumb maxItems={3} items={[\n { label: \"خانه\", href: \"/\", icon: <HomeIcon />, hideLabel: true },\n { label: \"پروژه‌ها\", href: \"/projects\" },\n { label: \"فروشگاه آنلاین\", href: \"/projects/shop\" },\n { label: \"تنظیمات\", href: \"/projects/shop/settings\" },\n { label: \"امنیت و ورود دو مرحله‌ای\" },\n]} />\n```\n\n## جداکنندهٔ «/»\n\n```tsx\n<Breadcrumb separator=\"slash\" items={[…]} />\n```\n\n| ویژگی | نوع | پیش‌فرض | توضیح |\n|---|---|---|---|\n| items | { label, href?, icon?, hideLabel? }[] | — | آخرین مورد صفحهٔ فعلی است |\n| maxItems | number | — | جمع کردن وسط مسیرهای بلند |\n| separator | \"chevron\" \\| \"slash\" | \"chevron\" | شکل جداکننده |\n| label | string | \"مسیر\" | نام nav برای صفحه‌خوان |\n| expandLabel | string | \"نمایش مسیر کامل\" | نام دکمهٔ «…» |\n\n## متغیرهای CSS\n\n`--k-breadcrumb-max-item` (پیش‌فرض `14rem`): بیشترین عرض هر مورد پیش از کوتاه شدن.\n\n## دسترس‌پذیری\n\nداخل `<nav aria-label=\"مسیر\">` با فهرست `<ol>`؛ صفحهٔ فعلی `aria-current=\"page\"` دارد و لینک نیست. جداکننده‌ها تزئینی‌اند و در راست‌به‌چپ آینه می‌شوند. «…» یک دکمهٔ واقعی با نام «نمایش مسیر کامل» است.\n\n## راهنمای استفاده\n\nکِی استفاده کنیم:\n\n- سایت یا پنلی با چند سطح، مثل فروشگاه (خانه ← پوشاک ← کفش) یا تنظیمات تودرتو.\n- صفحه‌هایی که کاربر از جستجو یا پیوند مستقیم به آن‌ها می‌رسد و باید بداند کجاست.\n\nکِی استفاده نکنیم:\n\n- سایت یک‌سطحی یا صفحهٔ اصلی.\n- مراحل یک فرایند؛ بردکرامب مکان را می‌گوید، نه پیشرفت را. (استپر: /docs/components/stepper/)\n\nدرست و نادرست:\n\n- ✓ درست: آخرین مورد صفحهٔ فعلی است و پیوند ندارد.\n- ✗ نادرست: پیوند صفحهٔ فعلی به خودش.\n- ✓ درست: در مسیرهای بلند `maxItems` بدهید تا وسطشان «…» شود.\n- ✗ نادرست: مسیری که در موبایل به چند خط می‌شکند."},{"slug":"components/button-group","url":"https://kashi.sepantapouya.com/docs/components/button-group/","title":"گروه دکمه","description":"دکمه‌های چسبیده به هم برای عمل‌های مرتبط، مثل «روز / هفته / ماه» یا «قبلی / امروز / بعدی».","group":"کامپوننت‌ها","components":["ButtonGroup","Button"],"markdown":"```tsx\nimport { ButtonGroup, Button } from \"@kashi-ui/react\";\n```\n\n## نمونه\n\n```tsx\n<ButtonGroup aria-label=\"بازهٔ تقویم\">\n <Button aria-pressed={view === \"day\"} onClick={() => setView(\"day\")}>روز</Button>\n <Button aria-pressed={view === \"week\"} onClick={() => setView(\"week\")}>هفته</Button>\n <Button aria-pressed={view === \"month\"} onClick={() => setView(\"month\")}>ماه</Button>\n</ButtonGroup>\n\n<ButtonGroup aria-label=\"پیمایش\">\n <Button iconOnly aria-label=\"قبلی\">…</Button>\n <Button>امروز</Button>\n <Button iconOnly aria-label=\"بعدی\">…</Button>\n</ButtonGroup>\n```\n\nبرای انتخاب یک گزینه از چند گزینه، `SegmentedControl` معمولاً مناسب‌تر است؛ `ButtonGroup` برای عمل‌هاست. دکمهٔ روشن با `aria-pressed` یا `aria-current` برجسته می‌شود.\n\n| ویژگی | نوع | پیش‌فرض | توضیح |\n|---|---|---|---|\n| aria-label | string | — | نام گروه برای صفحه‌خوان |\n| children | Button[] | — | دکمه‌ها؛ همهٔ گونه‌ها و اندازه‌های Button |\n\n## دسترس‌پذیری\n\nظرف `role=\"group\"` با نام دارد. هر دکمه جدا در ترتیب Tab است و حلقهٔ فوکوس روی دکمهٔ کناری نمی‌رود.\n\n## راهنمای استفاده\n\nکِی استفاده کنیم:\n\n- چند عمل هم‌خانواده کنار هم: «قبلی / امروز / بعدی» یا ابزارهای ویرایشگر.\n- دکمهٔ اصلی با فهرست گزینه‌های کناری (Split button).\n\nکِی استفاده نکنیم:\n\n- انتخاب یک حالت از چند حالت، مثل «روز / هفته / ماه». (سگمنت: /docs/components/segmented-control/)\n- عمل‌های بی‌ربط که فقط کنار هم افتاده‌اند؛ آن‌ها را جدا بگذارید.\n\nدرست و نادرست:\n\n- ✓ درست: همهٔ دکمه‌های گروه یک گونه و یک اندازه.\n- ✗ نادرست: یک دکمهٔ اصلی و یک دکمهٔ فرعی چسبیده به هم؛ سلسله‌مراتب گم می‌شود."},{"slug":"components/button","url":"https://kashi.sepantapouya.com/docs/components/button/","title":"دکمه","description":"برای اجرای یک عمل. شش گونه برای سلسله‌مراتب بصری، چهار اندازه و حالت بارگذاری دسترس‌پذیر.","group":"کامپوننت‌ها","components":["Button"],"markdown":"```tsx\nimport { Button } from \"@kashi-ui/react\";\n```\n\n## گونه‌ها\n\nدر هر بخش از صفحه فقط یک دکمهٔ `primary` بگذارید. بقیهٔ عمل‌ها با `secondary` یا `tertiary` نمایش داده شوند.\n\n```tsx\n<Button variant=\"primary\">ذخیره</Button>\n<Button variant=\"secondary\">پیش‌نمایش</Button>\n<Button variant=\"tertiary\">انصراف</Button>\n<Button variant=\"link\">اطلاعات بیشتر</Button>\n<Button variant=\"danger\">حذف</Button>\n<Button variant=\"danger-secondary\">بایگانی</Button>\n```\n\n## اندازه‌ها\n\n```tsx\n<Button variant=\"primary\" size=\"sm\">کوچک</Button>\n<Button variant=\"primary\">متوسط</Button>\n<Button variant=\"primary\" size=\"lg\">بزرگ</Button>\n<Button variant=\"primary\" size=\"xl\">خیلی بزرگ</Button>\n```\n\n## با آیکون\n\n`iconStart` در RTL سمت راست و `iconEnd` سمت چپ قرار می‌گیرد. آیکون‌های جهت‌دار مثل فلش را با `data-k-mirror` علامت بزنید تا در RTL آینه شوند.\n\n```tsx\n<Button variant=\"primary\" iconStart={<PlusIcon />}>پروژهٔ جدید</Button>\n<Button iconEnd={<ArrowIcon />}>ادامه</Button>\n<Button iconOnly aria-label=\"جستجو\"><SearchIcon /></Button>\n<Button variant=\"danger-secondary\" iconOnly aria-label=\"حذف\"><TrashIcon /></Button>\n```\n\n## حالت‌ها\n\n```tsx\n<Button variant=\"primary\" loading>پرداخت</Button>\n<Button disabled>غیرفعال</Button>\n```\n\n## شکل، اندازه و لینک\n\nبا `href` دکمه یک لینک واقعی (`<a>`) می‌شود؛ با `as` می‌توانید کامپوننت لینک روتر خود را بدهید، مثل `as={Link}` در Next.js.\n\n```tsx\n<Button variant=\"primary\" shape=\"pill\">کاملاً گرد</Button>\n<Button shape=\"square\">گوشهٔ کم</Button>\n<Button size=\"xs\">خیلی کوچک</Button>\n<Button href=\"/docs\" variant=\"link\">دکمهٔ لینکی</Button>\n<Button as={Link} href=\"/pricing\">با Link در Next.js</Button>\n```\n\n**ویژگی‌های جدید**\n\n| ویژگی | نوع | پیش‌فرض | توضیح |\n|---|---|---|---|\n| shape | \"default\" \\| \"pill\" \\| \"square\" | \"default\" | شکل گوشه‌ها |\n| size | \"xs\" \\| \"sm\" \\| \"md\" \\| \"lg\" \\| \"xl\" | \"md\" | اندازهٔ xs (۲۸ پیکسل) اضافه شد |\n| href / target / rel | string | — | رندر به‌صورت لینک؛ با disabled، href حذف و aria-disabled گذاشته می‌شود |\n| as | ElementType | — | عنصر یا کامپوننت دلخواه |\n\n## ویژگی‌ها\n\n| ویژگی | نوع | پیش‌فرض | توضیح |\n|---|---|---|---|\n| variant | \"primary\" \\| \"secondary\" \\| \"tertiary\" \\| \"link\" \\| \"danger\" \\| \"danger-secondary\" | \"secondary\" | سبک بصری و اهمیت عمل |\n| size | \"sm\" \\| \"md\" \\| \"lg\" \\| \"xl\" | \"md\" | اندازه؛ ارتفاع‌ها ۳۶، ۴۰، ۴۴ و ۴۸ پیکسل |\n| loading | boolean | false | نمایش اسپینر؛ دکمه فوکوس‌پذیر می‌ماند ولی کلیک نمی‌گیرد |\n| loadingLabel | string | \"در حال بارگذاری\" | متنی که صفحه‌خوان هنگام بارگذاری اعلام می‌کند |\n| iconStart | ReactNode | — | آیکون ابتدای متن |\n| iconEnd | ReactNode | — | آیکون انتهای متن |\n| iconOnly | boolean | false | دکمهٔ مربعی فقط‌آیکون؛ aria-label الزامی است |\n| block | boolean | false | تمام‌عرض |\n\nسایر ویژگی‌های `<button>` مثل `onClick`، `type` و `disabled` هم پشتیبانی می‌شوند. مقدار پیش‌فرض `type` برابر `\"button\"` است تا فرم‌ها ناخواسته ارسال نشوند.\n\n## متغیرهای CSS\n\n| متغیر CSS | پیش‌فرض | توضیح |\n|---|---|---|\n| --k-btn-height-{sm,md,lg,xl} | 2.25rem تا 3rem × density | ارتفاع هر اندازه |\n| --k-btn-padding-{sm,md,lg,xl} | 0.75rem تا 1.25rem × density | فاصلهٔ افقی داخلی |\n| --k-btn-radius | var(--k-radius-md) | گردی گوشه‌ها |\n| --k-btn-font-weight | 600 | وزن متن |\n| --k-btn-gap | var(--k-space-1_5) | فاصلهٔ آیکون و متن |\n\n## دسترس‌پذیری\n\n- در حالت `loading` از `aria-disabled` به‌جای `disabled` استفاده می‌شود تا فوکوس کیبورد از دست نرود، و متن «در حال بارگذاری» با `role=\"status\"` اعلام می‌شود.\n- دکمهٔ `iconOnly` بدون `aria-label` در حالت توسعه هشدار می‌دهد.\n- حداقل ناحیهٔ لمس ۲۴×۲۴ پیکسل است و حلقهٔ فوکوس در حالت کنتراست بالای ویندوز هم دیده می‌شود.\n- برای رفتن به صفحهٔ دیگر از لینک (`<a class=\"k-btn\">`) استفاده کنید، نه دکمه.\n\n## راهنمای استفاده\n\nکِی استفاده کنیم:\n\n- اجرای یک عمل: ثبت، ذخیره، پرداخت، حذف، باز کردن گفتگو.\n- مهم‌ترین عمل صفحه با `variant=\"primary\"`؛ عمل‌های دیگر `secondary` یا `tertiary`.\n\nکِی استفاده نکنیم:\n\n- رفتن به صفحهٔ دیگر بدون انجام کاری؛ پیوند بگذارید، یا دکمه با `href` که خودش `<a>` می‌سازد.\n- انتخاب یک حالت از چند حالت. (سگمنت: /docs/components/segmented-control/)\n- روشن و خاموش کردن یک تنظیم. (سوییچ: /docs/components/switch/)\n\nدرست و نادرست:\n\n- ✓ درست: یک دکمهٔ اصلی در هر بخش؛ بقیه فرعی.\n- ✗ نادرست: چند دکمهٔ اصلی کنار هم؛ کاربر نمی‌داند کار اصلی کدام است.\n- ✓ درست: برچسب فعل است و می‌گوید چه می‌شود.\n- ✗ نادرست: «تأیید» و «بله» که بدون خواندن پرسش معنا ندارند.\n- ✓ درست: هنگام ارسال `loading` بدهید تا دوبار کلیک نشود و صفحه‌خوان «در حال بارگذاری» بخواند.\n- ✗ نادرست: دکمه را غیرفعال و بی‌توضیح رها کنید؛ کاربر نمی‌فهمد چرا کار نمی‌کند.\n\nمتن:\n\n- دو تا چهار کلمه، با فعل: «افزودن به سبد»، «ارسال دوبارهٔ کد».\n- دکمهٔ فقط‌آیکون `aria-label` می‌خواهد که کار را بگوید: «حذف سفارش ۴۸۱۲۷»."},{"slug":"components/card","url":"https://kashi.sepantapouya.com/docs/components/card/","title":"کارت","description":"ظرفی برای گروه‌بندی محتوای مرتبط، با سربرگ، بدنه و پانویس.","group":"کامپوننت‌ها","components":["Card","CardHeader","CardTitle","CardDescription","CardBody","CardFooter"],"markdown":"```tsx\nimport { Card, CardHeader, CardTitle, CardDescription, CardBody, CardFooter } from \"@kashi-ui/react\";\n```\n\n## پایه\n\n```tsx\n<Card>\n <CardHeader>\n <CardTitle>پلن حرفه‌ای</CardTitle>\n <CardDescription>برای تیم‌های در حال رشد</CardDescription>\n </CardHeader>\n <CardBody>۴۹۰ هزار تومان در ماه، تا ۲۰ کاربر.</CardBody>\n <CardFooter>\n <Button variant=\"tertiary\">مقایسه</Button>\n <Button variant=\"primary\">انتخاب پلن</Button>\n </CardFooter>\n</Card>\n```\n\n## کارت قابل‌کلیک\n\nکل سطح کارت قابل‌کلیک است، ولی فقط یک لینک با کلاس `k-card__link` دارد؛ کاربر کیبورد با یک Tab از آن عبور می‌کند.\n\n```tsx\n<Card interactive>\n <CardHeader>\n <Badge tone=\"brand\">راهنما</Badge>\n <CardTitle>\n <a className=\"k-card__link\" href=\"/docs/accessibility/\">دسترس‌پذیری در کاشی</a>\n </CardTitle>\n <CardDescription>آنچه خودکار تضمین می‌شود و آنچه بر عهدهٔ شماست.</CardDescription>\n </CardHeader>\n</Card>\n```\n\n## سبک‌ها، تصویر و منوی سربرگ\n\n```tsx\n<Card variant=\"elevated\" padding=\"sm\">\n <CardMedia src=\"/cover.jpg\" alt=\"\" ratio=\"16 / 9\" />\n <CardHeader actions={<DropdownMenu trigger={…}>…</DropdownMenu>}>\n <CardTitle>سبک برجسته</CardTitle>\n <CardDescription>با تصویر و منوی سربرگ</CardDescription>\n </CardHeader>\n</Card>\n\n<Card variant=\"filled\">…</Card>\n```\n\n**ویژگی‌های جدید**\n\n| ویژگی | نوع | پیش‌فرض | توضیح |\n|---|---|---|---|\n| variant | \"outlined\" \\| \"elevated\" \\| \"filled\" | \"outlined\" | سبک (Card) |\n| padding | \"none\" \\| \"sm\" \\| \"md\" \\| \"lg\" | \"md\" | فاصلهٔ داخلی همهٔ بخش‌ها (Card) |\n| actions | ReactNode | — | دکمه یا منو در انتهای سربرگ (CardHeader) |\n| ratio | string | \"16 / 9\" | نسبت ابعاد تصویر (CardMedia) |\n\n## ویژگی‌ها\n\n| ویژگی | نوع | پیش‌فرض | توضیح |\n|---|---|---|---|\n| as | ElementType | \"article\" | عنصر HTML؛ برای کارت‌های غیرمستقل div یا section |\n| flat | boolean | false | بدون سایه |\n| interactive | boolean | false | حالت hover و فوکوس برای کارت قابل‌کلیک |\n\n`CardTitle` پیش‌فرض `h3` است. با `as` سطح عنوان را با ساختار صفحه هماهنگ کنید؛ برای مثال `<CardTitle as=\"h2\">`.\n\n## متغیرهای CSS\n\n| متغیر CSS | پیش‌فرض | توضیح |\n|---|---|---|\n| --k-card-padding | 1.5rem × density | فاصلهٔ داخلی بخش‌ها |\n| --k-card-radius | var(--k-radius-xl) | گردی گوشه‌ها |\n| --k-card-bg | var(--k-bg-elevated) | پس‌زمینه |\n| --k-card-border | var(--k-border-secondary) | رنگ حاشیه |\n| --k-card-shadow | var(--k-shadow-xs) | سایه |\n\n## دسترس‌پذیری\n\n- از کارت‌هایی که کل آن‌ها یک لینک بزرگ است (`<a>` دور همهٔ محتوا) پرهیز کنید؛ صفحه‌خوان همهٔ متن را به‌عنوان نام لینک می‌خواند.\n- در کارت قابل‌کلیک، دکمه‌های داخلی دیگر را بالای لایهٔ لینک (`position: relative; z-index: 1`) قرار دهید.\n\n## راهنمای استفاده\n\nکِی استفاده کنیم:\n\n- گروه کردن محتوای یک موضوع: یک محصول، یک سفارش، یک بخش تنظیمات.\n- شبکه‌ای از موردهای هم‌شکل که هر کدام به صفحهٔ خودش می‌رود (`interactive`).\n\nکِی استفاده نکنیم:\n\n- کارت داخل کارت؛ سطح‌ها را با فاصله و جداکننده نشان دهید. (جداکننده: /docs/components/divider/)\n- داده‌های ردیفی که مقایسه می‌شوند. (جدول: /docs/components/table/)\n\nدرست و نادرست:\n\n- ✓ درست: کارت قابل‌کلیک با `interactive` و یک پیوند اصلی که کل کارت را می‌گیرد.\n- ✗ نادرست: کارت کلیک‌خور که داخلش چند دکمه هم هست؛ کاربر نمی‌داند کجا را بزند."},{"slug":"components/carousel","url":"https://kashi.sepantapouya.com/docs/components/carousel/","title":"کاروسل","description":"نمایش چند کارت یا تصویر در یک ردیف قابل‌پیمایش، مثل محصولات پیشنهادی.","group":"کامپوننت‌ها","components":["Carousel"],"markdown":"```tsx\nimport { Carousel } from \"@kashi-ui/react\";\n```\n\n## نمونه\n\n```tsx\n<Carousel label=\"محصولات پیشنهادی\" perView={2}>\n {products.map((p) => <ProductCard key={p.id} {...p} />)}\n</Carousel>\n```\n\nروی scroll-snap بومی مرورگر ساخته شده: با کشیدن انگشت، چرخ موس، دکمه‌ها و کلیدهای جهت کار می‌کند و در RTL خودکار درست است. چرخش خودکار ندارد، چون خواندن را برای همه، به‌خصوص کاربران صفحه‌خوان و کم‌بینا، سخت می‌کند.\n\n| ویژگی | نوع | پیش‌فرض | توضیح |\n|---|---|---|---|\n| children | ReactNode | — | هر فرزند یک اسلاید |\n| label | string | — | نام کاروسل (الزامی) |\n| perView | number | 1 | تعداد اسلاید در هر نما؛ یا متغیر --k-carousel-per-view |\n| dots | boolean | true | نقطه‌های پیمایش |\n| labels | { prev?, next?, slide? } | — | متن دکمه‌ها و نام اسلایدها |\n\n## دسترس‌پذیری\n\nالگوی Carousel در WAI-ARIA: ظرف با `aria-roledescription=\"اسلایدر\"` و هر اسلاید با نام «۲ از ۵». ریل اسلایدها فوکوس‌پذیر است و ←→ بین آن‌ها حرکت می‌کند؛ اسلاید فعال با `aria-live` اعلام می‌شود.\n\n## راهنمای استفاده\n\nکِی استفاده کنیم:\n\n- چند کارت هم‌شکل که همه‌شان لازم نیست دیده شوند: محصولات پیشنهادی، مقاله‌های مرتبط.\n- گالری عکس یک محصول.\n\nکِی استفاده نکنیم:\n\n- محتوای مهمی که همه باید ببینند؛ اسلاید دوم و سوم را کمتر کسی می‌بیند.\n- بنر اصلی صفحه که خودکار می‌چرخد؛ کاشی چرخش خودکار ندارد چون خواندن را سخت می‌کند.\n- مقایسهٔ چند مورد کنار هم. (جدول: /docs/components/table/)\n\nدرست و نادرست:\n\n- ✓ درست: بخشی از کارت بعدی دیده شود تا معلوم باشد ردیف ادامه دارد.\n- ✗ نادرست: یک اسلاید تمام‌عرض بدون نشانهٔ ادامه، که فقط با نقطه‌های ریز می‌شود فهمید.\n\n## وب کامپوننت\n\nهمین کامپوننت بدون React، برای HTML، وردپرس، Vue، Svelte و Angular: `<kashi-carousel>` در `@kashi-ui/elements`.\n\nنصب بدون ابزار ساخت (CDN):\n\n```html\n<link rel=\"stylesheet\" href=\"https://cdn.jsdelivr.net/npm/@kashi-ui/css/dist/kashi.css\">\n<script type=\"module\" src=\"https://cdn.jsdelivr.net/npm/@kashi-ui/elements/dist/cdn/kashi-elements.js\"></script>\n```\n\nیا با npm، فقط همین بخش:\n\n```bash\nnpm i @kashi-ui/elements @kashi-ui/css\n```\n\n```js\nimport \"@kashi-ui/css\";\nimport \"@kashi-ui/elements/nav.js\";\n```\n\n```html\n<kashi-carousel label=\"محصولات\" per-view=\"2\">\n <div class=\"k-card\" style=\"padding: 2rem\">محصول ۱</div>\n <div class=\"k-card\" style=\"padding: 2rem\">محصول ۲</div>\n <div class=\"k-card\" style=\"padding: 2rem\">محصول ۳</div>\n <div class=\"k-card\" style=\"padding: 2rem\">محصول ۴</div>\n</kashi-carousel>\n```\n\nویژگی‌ها:\n\n| نام | مقدار | پیش‌فرض | توضیح |\n|---|---|---|---|\n| label | متن | — | نام اسلایدر |\n| per-view | عدد | 1 | تعداد اسلاید در هر نما |\n| dots | false | — | بدون نقطه‌ها |\n\nرویدادها و JavaScript:\n\n| نام | توضیح |\n|---|---|\n| element.go(n) | رفتن به اسلاید n (از صفر) |\n| kashi-change | detail: { index } |"},{"slug":"components/charts","url":"https://kashi.sepantapouya.com/docs/components/charts/","title":"نمودار","description":"نمودار ستونی، انباشته، خطی، دایره‌ای و دونات، میله‌ای افقی، سهم از کل و خط روند؛ با ارقام فارسی و جدول هم‌ارز برای صفحه‌خوان.","group":"کامپوننت‌ها","components":["BarChart","BarList","LineChart","PieChart","ProportionBar","Sparkline"],"markdown":"```tsx\nimport { BarChart, BarList, LineChart, PieChart, ProportionBar, Sparkline } from \"@kashi-ui/react\";\n```\n\nنمودارهای کاشی برای داشبورد و پنل مدیریت ساخته شده‌اند، نه برای تحلیل سنگین داده. اگر نمودار تعاملی پیچیده لازم دارید، از کتابخانهٔ تخصصی استفاده کنید و رنگ‌ها را از متغیرهای `--k-chart-1` تا `--k-chart-4` بگیرید.\n\n## ستونی\n\nبرای مقایسهٔ مقدار در چند دسته یا دوره. فقط بلندترین ستون برچسب دارد؛ مقدار بقیه با موس یا Tab نمایش داده می‌شود.\n\n```tsx\n<BarChart\n title=\"فروش ماهانه (میلیون تومان)\"\n data={[{ label: \"فروردین\", value: 82 }, { label: \"اردیبهشت\", value: 96 }, …]}\n/>\n```\n\n## ستونی انباشته\n\nبرای ترکیب یک کل در طول زمان، تا چهار سری. بخش‌ها با فاصلهٔ ۲ پیکسلی از هم جدا شده‌اند و راهنما همیشه نمایش داده می‌شود.\n\n```tsx\n<BarChart title=\"سفارش‌ها بر اساس کانال\" labels={months} series={[\n { name: \"وب\", values: [420, 460, …] },\n { name: \"اپ\", values: [260, 300, …] },\n { name: \"حضوری\", values: [120, 110, …] },\n]} />\n```\n\n## خطی\n\nبرای روند در طول زمان، تا چهار سری. محور زمان در جهت خواندن صفحه است: در RTL اولین ماه سمت راست. نام هر سری کنار انتهای خطش آمده تا تشخیص سری فقط به رنگ وابسته نباشد.\n\n```tsx\n<LineChart\n title=\"کاربران فعال روزانه\"\n labels={[\"فروردین\", \"اردیبهشت\", …]}\n series={[\n { name: \"وب\", values: [3200, 3600, …] },\n { name: \"اپ\", values: [1800, 2300, …] },\n { name: \"API\", values: [600, 700, …] },\n ]}\n/>\n```\n\n## میله‌ای افقی\n\nبرای رتبه‌بندی، به‌خصوص وقتی نام‌ها بلندند؛ نام داخل میله و مقدار در انتهای ردیف.\n\n```tsx\n<BarList title=\"پربازدیدترین صفحه‌ها\" data={[\n { label: \"صفحهٔ اصلی\", value: 48210 },\n { label: \"محصولات / مبل راحتی\", value: 21840, href: \"/products/sofa\" },\n …\n]} />\n```\n\n## سهم از کل\n\nجایگزین خواناتر نمودار دایره‌ای: مقایسهٔ طول در یک میله از مقایسهٔ زاویه دقیق‌تر است. حداکثر چهار بخش؛ بقیه را در «سایر» جمع کنید.\n\n```tsx\n<ProportionBar title=\"منبع ورود کاربران\" data={[\n { label: \"جستجوی گوگل\", value: 46200 },\n { label: \"ورود مستقیم\", value: 21800 },\n { label: \"اینستاگرام\", value: 15400 },\n { label: \"سایر\", value: 6100 },\n]} />\n```\n\n## دایره‌ای و دونات\n\nبرای نشان دادن سهم چند بخش از کل، وقتی تعداد بخش‌ها کم است. بخش‌ها از بزرگ به کوچک چیده می‌شوند و بیش از پنج بخش در «سایر» جمع می‌شود. با بردن موس روی هر بخش، درصد آن وسط دونات نمایش داده می‌شود. چشم زاویه را دقیق مقایسه نمی‌کند؛ برای مقایسهٔ دقیق از «سهم از کل» یا نمودار میله‌ای استفاده کنید.\n\n```tsx\n<PieChart title=\"سفارش‌ها بر اساس استان\" data={[\n { label: \"تهران\", value: 48200 },\n { label: \"اصفهان\", value: 18400 },\n …\n]} />\n\n<PieChart title=\"روش پرداخت\" variant=\"pie\" data={[…]} />\n```\n\n## خط روند\n\nشکل روند بدون محور، برای کارت آمار و سلول جدول. نام دسترس‌پذیر را با جهت روند بدهید.\n\n```tsx\n<Sparkline values={[4, 6, 5, 8, 7, 9, 12]} tone=\"success\" aria-label=\"روند فروش: افزایشی\" />\n```\n\n| ویژگی | نوع | پیش‌فرض | توضیح |\n|---|---|---|---|\n| title | string | — | عنوان و نام دسترس‌پذیر (الزامی) |\n| data (BarChart) | { label, value }[] | — | یک سری |\n| labels / series (BarChart) | string[] / { name, values }[] | — | ستون انباشته، حداکثر چهار سری |\n| data (BarList) | { label, value, href? }[] | — | ردیف‌ها به همان ترتیب |\n| data (ProportionBar) | { label, value }[] | — | حداکثر چهار بخش؛ percent برای درصد |\n| data / variant (PieChart) | { label, value }[] / \"donut\" \\| \"pie\" | \"donut\" | حداکثر پنج بخش؛ totalLabel برای متن وسط |\n| values / tone (Sparkline) | number[] / brand \\| success \\| error \\| neutral | — | بدون محور؛ aria-label بدهید |\n| labels / series (LineChart) | string[] / { name, values }[] | — | حداکثر چهار سری؛ بیشتر نادیده گرفته می‌شود |\n| format | (n: number) => string | ارقام فارسی | قالب مقدار در راهنما و جدول |\n| area (LineChart) | boolean | false | سطح کم‌رنگ زیر خط، فقط برای یک سری |\n| labelMax (BarChart) | boolean | true | برچسب مقدار روی بلندترین ستون |\n| height | string | \"14rem\" | یا متغیر --k-chart-height |\n\n## رنگ‌ها\n\nچهار رنگ دسته‌ای با ترتیب ثابت، برای تم تیره و روشن جداگانه انتخاب و با شبیه‌سازی کوررنگی بررسی شده‌اند (فاصلهٔ رنگی دست‌کم ۸). رنگ همیشه به نام سری وابسته است، نه به جایگاهش. نمودار دومحوره نداریم؛ دو مقیاس متفاوت را در دو نمودار جدا نشان دهید.\n\n## دسترس‌پذیری\n\nهر نمودار یک جدول هم‌ارز پنهان دارد که صفحه‌خوان کامل می‌خواند. در نمودار ستونی هر ستون با Tab فوکوس می‌گیرد («آبان: ۱۸۴»)؛ در نمودار خطی، با فوکوس روی نمودار کلیدهای ←→ بین نقطه‌ها حرکت می‌کنند و مقدار همهٔ سری‌ها اعلام می‌شود.\n\n## راهنمای استفاده\n\nکِی استفاده کنیم:\n\n- روند در طول زمان (خطی)، مقایسهٔ دسته‌ها (ستونی)، سهم از کل (دونات یا سهم از کل) و رتبه‌بندی (میله‌ای افقی).\n- داشبورد و گزارش که خواننده باید الگو را با یک نگاه ببیند.\n\nکِی استفاده نکنیم:\n\n- عدد دقیقی که مهم است؛ جدول یا کارت آمار بگذارید. (کارت آمار: /docs/components/stat-card/)\n- دونات با بیش از پنج بخش؛ کاشی بقیه را «سایر» می‌کند، ولی میله‌ای افقی خواناتر است.\n- دو داده با واحدهای مختلف روی یک محور.\n\nدرست و نادرست:\n\n- ✓ درست: عنوان بگوید نمودار چه نشان می‌دهد: «فروش ماهانهٔ ۱۴۰۵ (میلیون تومان)».\n- ✗ نادرست: عنوان «نمودار ۱» یا بدون عنوان؛ صفحه‌خوان هم چیزی برای خواندن ندارد.\n- ✓ درست: برای اعداد بزرگ `format` با واحد کوتاه بدهید تا محور شلوغ نشود.\n- ✗ نادرست: ۱۲۵۰۰۰۰۰۰ روی محور؛ چشم باید صفرها را بشمارد.\n\n## وب کامپوننت\n\nهمین کامپوننت بدون React، برای HTML، وردپرس، Vue، Svelte و Angular: `<kashi-bar-chart>`، `<kashi-line-chart>`، `<kashi-pie-chart>`، `<kashi-bar-list>`، `<kashi-proportion-bar>`، `<kashi-sparkline>` در `@kashi-ui/elements`. عنوان با `label` و داده با ویژگی JSON (`data`، `labels`، `series`) یا با JavaScript (`element.data = …`). هر نمودار جدول هم‌ارز برای صفحه‌خوان دارد.\n\nنصب بدون ابزار ساخت (CDN):\n\n```html\n<link rel=\"stylesheet\" href=\"https://cdn.jsdelivr.net/npm/@kashi-ui/css/dist/kashi.css\">\n<script type=\"module\" src=\"https://cdn.jsdelivr.net/npm/@kashi-ui/elements/dist/cdn/kashi-elements.js\"></script>\n```\n\nیا با npm، فقط همین بخش:\n\n```bash\nnpm i @kashi-ui/elements @kashi-ui/css\n```\n\n```js\nimport \"@kashi-ui/css\";\nimport \"@kashi-ui/elements/charts.js\";\n```\n\n```html\n<kashi-bar-chart label=\"فروش ماهانه\" unit=\"سفارش\" data='[{\"label\": \"فروردین\", \"value\": 120}, {\"label\": \"اردیبهشت\", \"value\": 180}, {\"label\": \"خرداد\", \"value\": 150}]'></kashi-bar-chart>\n<kashi-line-chart label=\"بازدید\" labels='[\"شنبه\", \"یکشنبه\", \"دوشنبه\", \"سه‌شنبه\"]' series='[{\"name\": \"وب\", \"values\": [40, 65, 58, 80]}, {\"name\": \"اپ\", \"values\": [20, 30, 45, 50]}]'></kashi-line-chart>\n<kashi-pie-chart label=\"منبع ورود\" data='[{\"label\": \"جستجو\", \"value\": 520}, {\"label\": \"مستقیم\", \"value\": 310}, {\"label\": \"شبکه‌های اجتماعی\", \"value\": 170}]'></kashi-pie-chart>\n```\n\n| عنصر | کاربرد |\n|---|---|\n| <kashi-bar-chart> | ستونی؛ data، یا انباشته با labels و series |\n| <kashi-line-chart> | خطی تا چهار سری؛ labels و series |\n| <kashi-pie-chart> | دونات یا دایره‌ای (variant=\"pie\")؛ data |\n| <kashi-bar-list> | میله‌های افقی برای رتبه‌بندی؛ data با href اختیاری |\n| <kashi-proportion-bar> | سهم هر بخش در یک میله؛ data |\n| <kashi-sparkline> | خط روند کوچک؛ values=\"12, 18, 15\" |\n\nویژگی‌ها:\n\n| نام | مقدار | پیش‌فرض | توضیح |\n|---|---|---|---|\n| label | متن | — | عنوان نمودار و نام دسترس‌پذیر |\n| data / labels / series | JSON | — | داده |\n| unit | متن | — | واحد کنار اعداد، مثل «سفارش» |\n| height | طول CSS | — | ارتفاع نمودار ستونی و خطی |\n| variant / total-label / size | pie / متن / پیکسل | donut / جمع / 176 | نمودار دایره‌ای |\n| tone / area | brand \\| success \\| error \\| neutral / false | brand | خط روند |\n\nرویدادها و JavaScript:\n\n| نام | توضیح |\n|---|---|\n| element.data / series / labels / values | داده با JavaScript |\n| element.format | قالب عدد: (n) => \"…\" |"},{"slug":"components/checkbox","url":"https://kashi.sepantapouya.com/docs/components/checkbox/","title":"چک‌باکس","description":"برای انتخاب یک یا چند گزینه، یا تأیید یک مورد. روی input بومی ساخته شده است.","group":"کامپوننت‌ها","components":["Checkbox"],"markdown":"```tsx\nimport { Checkbox } from \"@kashi-ui/react\";\n```\n\n## پایه\n\n```tsx\n<Checkbox label=\"مرا به خاطر بسپار\" defaultChecked />\n<Checkbox label=\"خبرنامه\" description=\"ماهی یک ایمیل، بدون تبلیغات.\" />\n```\n\n## حالت‌ها\n\n`indeterminate` برای «انتخاب همه» وقتی فقط بخشی از گزینه‌ها انتخاب شده‌اند.\n\n```tsx\n<Checkbox label=\"انتخاب همه\" indeterminate />\n<Checkbox label=\"قوانین را می‌پذیرم\" invalid />\n<Checkbox label=\"غیرفعال\" disabled />\n<Checkbox label=\"بزرگ\" size=\"lg\" defaultChecked />\n```\n\n## ویژگی‌ها\n\n| ویژگی | نوع | پیش‌فرض | توضیح |\n|---|---|---|---|\n| label | ReactNode | — | برچسب کنار چک‌باکس؛ کل برچسب قابل‌کلیک است |\n| description | ReactNode | — | توضیح زیر برچسب؛ با aria-describedby متصل می‌شود |\n| indeterminate | boolean | false | حالت نیمه‌انتخاب |\n| invalid | boolean | false | حالت خطا |\n| size | \"md\" \\| \"lg\" | \"md\" | ۱۸ یا ۲۰ پیکسل |\n\n## متغیرهای CSS\n\n| متغیر CSS | پیش‌فرض | توضیح |\n|---|---|---|\n| --k-checkbox-size | 1.125rem | اندازه |\n| --k-checkbox-radius | 0.3125rem × radius-factor | گردی گوشه‌ها |\n\n## دسترس‌پذیری\n\n- روی `<input type=\"checkbox\">` بومی ساخته شده؛ کلید Space، فرم‌ها و صفحه‌خوان‌ها بدون کد اضافه کار می‌کنند.\n- در حالت کنتراست بالای ویندوز، ظاهر بومی سیستم نمایش داده می‌شود.\n- برای گروه چک‌باکس‌ها از `<fieldset>` و `<legend>` استفاده کنید.\n\n## راهنمای استفاده\n\nکِی استفاده کنیم:\n\n- انتخاب چند گزینه از یک فهرست کوتاه.\n- پذیرفتن یک مورد در فرم: «شرایط استفاده را خواندم».\n\nکِی استفاده نکنیم:\n\n- تنظیمی که همان لحظه اعمال می‌شود. (سوییچ: /docs/components/switch/)\n- انتخاب فقط یکی از چند گزینه. (رادیو: /docs/components/radio/)\n- فهرست بلند که جا نمی‌شود. (انتخاب چندتایی: /docs/components/multi-select/)\n\nدرست و نادرست:\n\n- ✓ درست: برچسب مثبت بگوید تیک یعنی چه.\n- ✗ نادرست: برچسب منفی که تیک‌زدنش ذهن را می‌پیچاند.\n- ✓ درست: برچسب قابل‌کلیک است؛ کل متن را داخل `label` بدهید.\n- ✗ نادرست: متن جدا از چک‌باکس که کلیک رویش کار نمی‌کند."},{"slug":"components/chips","url":"https://kashi.sepantapouya.com/docs/components/chips/","title":"چیپس","description":"فیلترهای روشن و خاموش‌شونده با تعداد نتیجه، بالای فهرست یا جدول.","group":"کامپوننت‌ها","components":["ChipGroup"],"markdown":"```tsx\nimport { ChipGroup } from \"@kashi-ui/react\";\n```\n\n## نمونه\n\n```tsx\n<ChipGroup aria-label=\"وضعیت سفارش\" value={v} onChange={setV} options={[\n { value: \"paid\", label: \"پرداخت‌شده\", count: 128 },\n { value: \"pending\", label: \"در انتظار\", count: 14 },\n { value: \"shipped\", label: \"ارسال‌شده\", count: 96 },\n]} />\n```\n\n| ویژگی | نوع | پیش‌فرض | توضیح |\n|---|---|---|---|\n| options | { value, label, icon?, count?, disabled? }[] | — | چیپ‌ها |\n| value / defaultValue | string[] | [] | چیپ‌های روشن |\n| onChange | (value: string[]) => void | — | — |\n| single | boolean | false | فقط یکی روشن؛ کلیک دوباره خاموشش می‌کند |\n| aria-label | string | — | نام گروه |\n\n## دسترس‌پذیری\n\nهر چیپ یک دکمه با `aria-pressed` است. حالت روشن با تیک، رنگ و حاشیه نشان داده می‌شود، نه فقط رنگ.\n\n## راهنمای استفاده\n\nکِی استفاده کنیم:\n\n- فیلترهای سریع بالای فهرست یا جدول: «فقط موجود»، «ارسال رایگان».\n- انتخاب یک یا چند دسته با تعداد نتیجهٔ هر کدام.\n\nکِی استفاده نکنیم:\n\n- نمایش وضعیت که کاربر عوضش نمی‌کند. (بج: /docs/components/badge/)\n- گزینه‌های یک فرم که ثبت می‌شوند؛ چک‌باکس یا رادیو روشن‌تر است. (چک‌باکس: /docs/components/checkbox/)\n\nدرست و نادرست:\n\n- ✓ درست: تعداد نتیجهٔ هر فیلتر را با `count` نشان دهید تا کاربر پیش از کلیک بداند.\n- ✗ نادرست: فیلتری که کلیک می‌کنید و فهرست خالی می‌شود.\n\n## وب کامپوننت\n\nهمین کامپوننت بدون React، برای HTML، وردپرس، Vue، Svelte و Angular: `<kashi-chip-group>` در `@kashi-ui/elements`. هر `<button>` یک چیپ است (`data-pressed` برای روشن بودن اولیه، `data-count` برای عدد).\n\nنصب بدون ابزار ساخت (CDN):\n\n```html\n<link rel=\"stylesheet\" href=\"https://cdn.jsdelivr.net/npm/@kashi-ui/css/dist/kashi.css\">\n<script type=\"module\" src=\"https://cdn.jsdelivr.net/npm/@kashi-ui/elements/dist/cdn/kashi-elements.js\"></script>\n```\n\nیا با npm، فقط همین بخش:\n\n```bash\nnpm i @kashi-ui/elements @kashi-ui/css\n```\n\n```js\nimport \"@kashi-ui/css\";\nimport \"@kashi-ui/elements/forms.js\";\n```\n\n```html\n<kashi-chip-group label=\"دسته‌بندی\" name=\"category\">\n <button data-value=\"all\" data-pressed>همه</button>\n <button data-value=\"new\" data-count=\"12\">تازه‌ها</button>\n <button data-value=\"sale\" data-count=\"5\">تخفیف‌دار</button>\n</kashi-chip-group>\n```\n\nویژگی‌ها:\n\n| نام | مقدار | پیش‌فرض | توضیح |\n|---|---|---|---|\n| label | متن | — | نام گروه برای صفحه‌خوان |\n| single | بولی | — | فقط یک چیپ روشن |\n| name | متن | — | مقدار چیپ‌های روشن در فرم |\n\nرویدادها و JavaScript:\n\n| نام | توضیح |\n|---|---|\n| kashi-change | detail: { value: [...] } |"},{"slug":"components/color-picker","url":"https://kashi.sepantapouya.com/docs/components/color-picker/","title":"انتخاب رنگ","description":"انتخاب رنگ برند یا برچسب با نمونه‌رنگ‌ها، پنل انتخاب دلخواه و کد hex، همراه کنتراست متن.","group":"کامپوننت‌ها","components":["ColorPicker"],"markdown":"```tsx\nimport { ColorPicker } from \"@kashi-ui/react\";\n```\n\n## نمونه\n\n```tsx\n<ColorPicker label=\"رنگ برند\" value={color} onChange={setColor} name=\"brand\" />\n```\n\nبا زدن روی مربع رنگ، پنل انتخاب دلخواه باز می‌شود: سطح اشباع و روشنایی، نوار رنگ‌مایه، فیلدهای R، G و B، و در مرورگرهایی که پشتیبانی می‌کنند قطره‌چکان برای برداشتن رنگ از صفحه. در سطح رنگ، کلیدهای جهت یک درصد و با Shift ده درصد جابه‌جا می‌کنند.\n\nزیر انتخابگر، کنتراست رنگ با متن سفید یا مشکی (هر کدام بهتر است) نمایش داده می‌شود تا رنگی انتخاب نشود که متن روی آن خوانا نباشد. تابع `contrastRatio` هم صادر شده است.\n\n| ویژگی | نوع | پیش‌فرض | توضیح |\n|---|---|---|---|\n| value / defaultValue | string | \"#0b8077\" | رنگ hex |\n| onChange | (hex: string) => void | — | — |\n| swatches | { name, value }[] | رنگ‌های کاشی | نمونه‌رنگ‌ها؛ [] برای حذف |\n| showContrast | boolean | true | نمایش کنتراست متن |\n| name | string | — | فیلد پنهان برای فرم |\n\n## دسترس‌پذیری\n\nنمونه‌رنگ‌ها رادیوهای بومی با نام فارسی هستند («فیروزه‌ای»)، نه فقط یک خانهٔ رنگی. کد hex یک فیلد متنی معمولی است، پس کاربر صفحه‌خوان هم می‌تواند رنگ دقیق را وارد کند. سطح رنگ یک slider با مقدار متنی است («اشباع ۹۱٪، روشنایی ۷۰٪») و Esc پنل را می‌بندد و فوکوس را به مربع رنگ برمی‌گرداند.\n\n## راهنمای استفاده\n\nکِی استفاده کنیم:\n\n- انتخاب رنگ برند، رنگ برچسب یا رنگ تم توسط کاربر.\n- جایی که چند رنگ پیشنهادی کافی است و رنگ دلخواه هم ممکن باشد.\n\nکِی استفاده نکنیم:\n\n- انتخاب از چند رنگ ثابت که رنگ دلخواه لازم ندارد؛ `swatches` بدهید یا رادیوی کارتی بسازید. (رادیو: /docs/components/radio/)\n\nدرست و نادرست:\n\n- ✓ درست: کنتراست با متن را نشان دهید (`showContrast`) تا کاربر رنگی نسازد که متن رویش خوانده نشود.\n- ✗ نادرست: هر رنگی بپذیرید و بعد متن سفید روی زرد کم‌رنگ بیاید.\n\n## وب کامپوننت\n\nهمین کامپوننت بدون React، برای HTML، وردپرس، Vue، Svelte و Angular: `<kashi-color-picker>` در `@kashi-ui/elements`.\n\nنصب بدون ابزار ساخت (CDN):\n\n```html\n<link rel=\"stylesheet\" href=\"https://cdn.jsdelivr.net/npm/@kashi-ui/css/dist/kashi.css\">\n<script type=\"module\" src=\"https://cdn.jsdelivr.net/npm/@kashi-ui/elements/dist/cdn/kashi-elements.js\"></script>\n```\n\nیا با npm، فقط همین بخش:\n\n```bash\nnpm i @kashi-ui/elements @kashi-ui/css\n```\n\n```js\nimport \"@kashi-ui/css\";\nimport \"@kashi-ui/elements/pickers.js\";\n```\n\n```html\n<kashi-color-picker label=\"رنگ برند\" value=\"#0b8077\" name=\"brand\"></kashi-color-picker>\n```\n\nویژگی‌ها:\n\n| نام | مقدار | پیش‌فرض | توضیح |\n|---|---|---|---|\n| value | #0b8077 | #0b8077 | رنگ HEX |\n| swatches | «#0b8077 فیروزه‌ای, …» | رنگ‌های کاشی | رنگ‌های پیشنهادی؛ خالی برای هیچ |\n| show-contrast | false | — | بدون کنتراست با متن |\n| label / hint / name | — | — | مثل بقیهٔ فیلدها |\n\nرویدادها و JavaScript:\n\n| نام | توضیح |\n|---|---|\n| kashi-change | detail: { value: \"#0b8077\" } |"},{"slug":"components/combobox","url":"https://kashi.sepantapouya.com/docs/components/combobox/","title":"کامبوباکس","description":"فیلد جستجو با فهرست پیشنهادها (Combobox)؛ برای فهرست‌های بلند مثل شهر، کاربر یا محصول.","group":"کامپوننت‌ها","components":["Combobox"],"markdown":"```tsx\nimport { Combobox } from \"@kashi-ui/react\";\n```\n\nوقتی گزینه‌ها بیشتر از ده‌تاست، به‌جای `Select` از `Combobox` استفاده کنید. جستجو متن فارسی را یکدست می‌کند: «ي» و «ی»، «ك» و «ک» و ارقام فارسی و انگلیسی یکی حساب می‌شوند.\n\n## نمونه\n\n```tsx\nconst [city, setCity] = useState<string | null>(null);\n\n<Combobox label=\"استان\" placeholder=\"جستجو کنید…\"\n options={provinces.map((p) => ({ value: p, label: p }))}\n value={city} onChange={setCity} />\n```\n\n## گروه، توضیح و آیکون\n\n```tsx\n<Combobox label=\"مسئول\" options={[\n { value: \"sara\", label: \"سارا محمدی\", description: \"طراح محصول\", group: \"طراحی\", icon: <Avatar name=\"سارا محمدی\" size=\"xs\" /> },\n { value: \"ali\", label: \"علی رضایی\", group: \"فنی\", keywords: [\"ali\"] },\n { value: \"mina\", label: \"مینا احمدی\", group: \"فنی\", disabled: true },\n]} />\n```\n\nبرای جستجوی سمت سرور، `onInputChange` را بگیرید، `options` را از پاسخ سرور بسازید، `loading` را در زمان درخواست روشن کنید و `filter={() => true}` بدهید.\n\n| ویژگی | نوع | پیش‌فرض | توضیح |\n|---|---|---|---|\n| options | { value, label, description?, icon?, group?, keywords?, disabled? }[] | — | گزینه‌ها؛ گزینه‌های هم‌گروه باید پشت سر هم باشند |\n| value / defaultValue | string \\| null | null | مقدار انتخاب‌شده |\n| onChange | (value: string \\| null) => void | — | پس از انتخاب یا پاک کردن |\n| onInputChange | (query: string) => void | — | تغییر متن جستجو |\n| filter | (option, query) => boolean | defaultFilter | جستجوی سفارشی |\n| loading | boolean | false | اسپینر و متن «در حال جستجو…» |\n| emptyMessage | ReactNode | «نتیجه‌ای پیدا نشد.» | متن فهرست خالی |\n| name | string | — | فیلد پنهان برای ارسال فرم |\n| label / hint / error | ReactNode | — | مثل ورودی |\n\nابزارهای `defaultFilter`، `searchText` و `highlight` هم صادر شده‌اند تا در جستجوهای خودتان استفاده کنید.\n\n## کیبورد\n\n| کلید | عمل |\n|---|---|\n| <kbd>↓</kbd> / <kbd>↑</kbd> | باز کردن فهرست و رفتن به گزینهٔ بعد / قبل |\n| <kbd>Enter</kbd> | انتخاب گزینهٔ فعال |\n| <kbd>Esc</kbd> | بستن فهرست؛ اگر بسته است، پاک کردن مقدار |\n| <kbd>Home</kbd> / <kbd>End</kbd> | اولین / آخرین گزینه (وقتی فهرست باز است) |\n\n## دسترس‌پذیری\n\nالگوی Combobox در WAI-ARIA با `aria-activedescendant`: فوکوس همیشه در فیلد می‌ماند و صفحه‌خوان گزینهٔ فعال را می‌خواند. تعداد نتیجه‌ها پس از هر تایپ با یک ناحیهٔ `aria-live` اعلام می‌شود («۳ نتیجه») و گزینه‌های غیرفعال `aria-disabled` دارند.\n\n## راهنمای استفاده\n\nکِی استفاده کنیم:\n\n- فهرست بلند که کاربر نام گزینه را می‌داند: شهر، کاربر، محصول.\n- جستجو با پیشنهاد از سرور (`onInputChange` و `loading`).\n\nکِی استفاده نکنیم:\n\n- کمتر از هفت گزینه. (رادیو: /docs/components/radio/)\n- فهرست متوسط که کاربر باید مرورش کند و نامش را نمی‌داند. (سلکت: /docs/components/select/)\n- انتخاب چند گزینه. (انتخاب چندتایی: /docs/components/multi-select/)\n\nدرست و نادرست:\n\n- ✓ درست: `keywords` بدهید تا «مشهد» استان خراسان رضوی را پیدا کند و «tea» هم «چای» را.\n- ✗ نادرست: فقط تطبیق دقیق اول متن؛ کاربر باید املای دقیق را بداند.\n- ✓ درست: پیام نتیجهٔ خالی بگوید چه کند: «شهری پیدا نشد؛ نام را کوتاه‌تر بنویسید».\n- ✗ نادرست: فهرست خالی بدون هیچ متنی.\n\n## وب کامپوننت\n\nهمین کامپوننت بدون React، برای HTML، وردپرس، Vue، Svelte و Angular: `<kashi-combobox>` در `@kashi-ui/elements`. گزینه‌ها همان `<option>` و `<optgroup>` معمولی‌اند (`data-description` و `data-keywords` برای جستجو).\n\nنصب بدون ابزار ساخت (CDN):\n\n```html\n<link rel=\"stylesheet\" href=\"https://cdn.jsdelivr.net/npm/@kashi-ui/css/dist/kashi.css\">\n<script type=\"module\" src=\"https://cdn.jsdelivr.net/npm/@kashi-ui/elements/dist/cdn/kashi-elements.js\"></script>\n```\n\nیا با npm، فقط همین بخش:\n\n```bash\nnpm i @kashi-ui/elements @kashi-ui/css\n```\n\n```js\nimport \"@kashi-ui/css\";\nimport \"@kashi-ui/elements/combobox.js\";\n```\n\n```html\n<kashi-combobox label=\"زبان برنامه‌نویسی\" name=\"lang\" placeholder=\"جستجو…\">\n <optgroup label=\"وب\">\n <option value=\"ts\" data-description=\"TypeScript\">تایپ‌اسکریپت</option>\n <option value=\"js\">جاوااسکریپت</option>\n </optgroup>\n <option value=\"py\">پایتون</option>\n <option value=\"go\">گو</option>\n</kashi-combobox>\n```\n\nویژگی‌ها:\n\n| نام | مقدار | پیش‌فرض | توضیح |\n|---|---|---|---|\n| label | متن | نام فارسی فیلد | برچسب بالای فیلد؛ بدون آن aria-label بدهید |\n| name | متن | — | نام فیلد در فرم HTML؛ مقدار خام ارسال می‌شود |\n| value | متن | — | مقدار اولیه |\n| hint | متن | — | راهنمای زیر فیلد |\n| error | متن | — | خطای دلخواه، مثلاً از اعتبارسنجی سرور |\n| required / disabled | بولی | — | مثل input معمولی |\n| placeholder | متن | — | متن خالی فیلد |\n| empty-message | متن | نتیجه‌ای پیدا نشد. | وقتی جستجو نتیجه ندارد |\n\nرویدادها و JavaScript:\n\n| نام | توضیح |\n|---|---|\n| kashi-change | detail: { value, option } |\n| kashi-input | با هر تایپ؛ detail: { query }، برای جستجوی سمت سرور |\n| element.options | گزینه‌ها با JavaScript: [{ value, label, description, group }] |"},{"slug":"components/command-palette","url":"https://kashi.sepantapouya.com/docs/components/command-palette/","title":"کامند پالت","description":"جستجوی سریع صفحه‌ها و عملیات با Ctrl+K، به سبک ابزارهای حرفه‌ای.","group":"کامپوننت‌ها","components":["CommandPalette"],"markdown":"```tsx\nimport { CommandPalette, type CommandItem } from \"@kashi-ui/react\";\n```\n\n## نمونه\n\n```tsx\nconst [open, setOpen] = useState(false);\nconst items: CommandItem[] = [\n { id: \"home\", label: \"داشبورد\", group: \"صفحه‌ها\", icon: <HomeIcon />, shortcut: \"G D\", href: \"/\" },\n { id: \"users\", label: \"کاربران\", group: \"صفحه‌ها\", keywords: [\"users\"], href: \"/users\" },\n { id: \"new\", label: \"پروژهٔ جدید\", group: \"عملیات\", shortcut: \"N\", onSelect: createProject },\n];\n\n<Button onClick={() => setOpen(true)}>جستجوی دستور <Kbd keys={[\"Ctrl\", \"K\"]} /></Button>\n<CommandPalette open={open} onOpenChange={setOpen} items={items} />\n```\n\nپالت را یک بار در ریشهٔ برنامه بگذارید. با `shortcut` (پیش‌فرض روشن) میان‌بر <kbd>Ctrl</kbd>+<kbd>K</kbd> (و <kbd>⌘</kbd>+<kbd>K</kbd> در مک) در کل صفحه فعال است.\n\n| ویژگی | نوع | پیش‌فرض | توضیح |\n|---|---|---|---|\n| open / onOpenChange | boolean / (open) => void | — | کنترل باز و بسته بودن |\n| items | { id, label, group?, icon?, shortcut?, description?, keywords?, href?, onSelect? }[] | — | فرمان‌ها؛ با href به آن آدرس می‌رود |\n| shortcut | boolean | true | میان‌بر سراسری Ctrl/⌘+K |\n| placeholder | string | «جستجوی صفحه یا فرمان…» | — |\n| emptyMessage | ReactNode | «نتیجه‌ای پیدا نشد.» | — |\n| labels | { title?, navigate?, select?, close?, results? } | — | متن‌های راهنما و صفحه‌خوان |\n\n## دسترس‌پذیری\n\nروی `<dialog>` بومی ساخته شده، پس فوکوس داخل پنجره می‌ماند و پشت آن غیرفعال است. فیلد جستجو `role=\"combobox\"` است و فهرست `listbox`؛ با <kbd>↑</kbd>/<kbd>↓</kbd> حرکت، <kbd>Enter</kbd> اجرا و <kbd>Esc</kbd> بسته می‌شود و فوکوس به دکمه‌ای که پالت را باز کرد برمی‌گردد.\n\n## راهنمای استفاده\n\nکِی استفاده کنیم:\n\n- پنل‌ها و ابزارهای پرکار که کاربر حرفه‌ای می‌خواهد بدون موس جابه‌جا شود.\n- جستجوی یک‌جای صفحه‌ها، تنظیمات و عمل‌ها.\n\nکِی استفاده نکنیم:\n\n- تنها راه رسیدن به یک عمل؛ همه‌چیز باید از منو و دکمه هم در دسترس باشد.\n- سایت محتوایی ساده؛ جستجوی معمولی کافی است.\n\nدرست و نادرست:\n\n- ✓ درست: عمل‌ها را با فعل بنویسید و گروه‌بندی کنید: «ساخت پروژهٔ تازه» در گروه «پروژه‌ها».\n- ✗ نادرست: فهرست بلند و بی‌گروه با نام‌های فنی.\n\n## وب کامپوننت\n\nهمین کامپوننت بدون React، برای HTML، وردپرس، Vue، Svelte و Angular: `<kashi-command-palette>` در `@kashi-ui/elements`. با Ctrl+K یا ⌘K، یا دکمه‌ای با `data-kashi-command`؛ لینک‌ها باز می‌شوند و دکمه‌ها رویداد `kashi-select` می‌فرستند.\n\nنصب بدون ابزار ساخت (CDN):\n\n```html\n<link rel=\"stylesheet\" href=\"https://cdn.jsdelivr.net/npm/@kashi-ui/css/dist/kashi.css\">\n<script type=\"module\" src=\"https://cdn.jsdelivr.net/npm/@kashi-ui/elements/dist/cdn/kashi-elements.js\"></script>\n```\n\nیا با npm، فقط همین بخش:\n\n```bash\nnpm i @kashi-ui/elements @kashi-ui/css\n```\n\n```js\nimport \"@kashi-ui/css\";\nimport \"@kashi-ui/elements/nav.js\";\n```\n\n```html\n<button class=\"k-btn k-btn--secondary\" data-kashi-command=\"palette\">جستجو <kbd class=\"k-kbd\">Ctrl K</kbd></button>\n<kashi-command-palette id=\"palette\">\n <a href=\"/docs/\" data-group=\"صفحه‌ها\">مستندات</a>\n <a href=\"/docs/web-components/\" data-group=\"صفحه‌ها\" data-keywords=\"html, vue\">وب کامپوننت‌ها</a>\n <button data-value=\"theme\" data-group=\"دستورها\" data-shortcut=\"⌘D\">تغییر تم</button>\n</kashi-command-palette>\n```\n\nویژگی‌ها:\n\n| نام | مقدار | پیش‌فرض | توضیح |\n|---|---|---|---|\n| label / placeholder | متن | پالت فرمان | عنوان و متن فیلد جستجو |\n| shortcut | false | — | بدون Ctrl+K |\n| data-group / data-description / data-shortcut / data-keywords | روی هر دستور | — | گروه، توضیح، میان‌بر و کلمه‌های جستجو |\n\nرویدادها و JavaScript:\n\n| نام | توضیح |\n|---|---|\n| kashi-select | detail: { value, item } |\n| element.show() / close() | باز و بستن |\n| element.items | دستورها با JavaScript |"},{"slug":"components/context-menu","url":"https://kashi.sepantapouya.com/docs/components/context-menu/","title":"منوی راست‌کلیک","description":"منوی میان‌بر روی فایل، ردیف جدول یا کارت؛ با موس و کیبورد.","group":"کامپوننت‌ها","components":["ContextMenu","MenuItem","MenuSeparator"],"markdown":"```tsx\nimport { ContextMenu, MenuItem, MenuSeparator } from \"@kashi-ui/react\";\n```\n\n## نمونه\n\n```tsx\n<ContextMenu label=\"عملیات فایل\" menu={\n <>\n <MenuItem onSelect={open}>باز کردن</MenuItem>\n <MenuItem shortcut=\"Ctrl+D\" onSelect={copy}>کپی</MenuItem>\n <MenuSeparator />\n <MenuItem tone=\"danger\" onSelect={remove}>حذف</MenuItem>\n </>\n}>\n <FileCard tabIndex={0} />\n</ContextMenu>\n```\n\nگزینه‌ها همان `MenuItem`، `MenuSeparator` و `MenuLabel` دراپ‌داون‌اند. منو کنار نشانگر باز می‌شود و از لبهٔ صفحه بیرون نمی‌رود.\n\n| ویژگی | نوع | پیش‌فرض | توضیح |\n|---|---|---|---|\n| children | ReactNode | — | ناحیهٔ راست‌کلیک |\n| menu | ReactNode | — | گزینه‌ها |\n| label | string | «گزینه‌ها» | نام منو |\n\n## دسترس‌پذیری\n\nراست‌کلیک برای کاربر کیبورد و صفحه‌خوان در دسترس نیست، پس منو با کلید ContextMenu یا <kbd>Shift</kbd>+<kbd>F10</kbd> روی عنصر فوکوس‌شده هم باز می‌شود و این راهنما به ناحیه وصل است. همهٔ عمل‌های منو باید از راه دیگری (دکمه یا منوی کشویی) هم در دسترس باشند.\n\n## راهنمای استفاده\n\nکِی استفاده کنیم:\n\n- عمل‌های یک مورد مشخص (فایل، ردیف جدول، کارت) برای کاربرانی که به راست‌کلیک عادت دارند.\n- ابزارهای حرفه‌ای مثل مدیر فایل یا ویرایشگر.\n\nکِی استفاده نکنیم:\n\n- تنها راه انجام یک عمل؛ راست‌کلیک در موبایل نیست و خیلی‌ها نمی‌دانندش. همان عمل‌ها را در منوی سه‌نقطه هم بگذارید. (دراپ‌داون: /docs/components/dropdown-menu/)\n\nدرست و نادرست:\n\n- ✓ درست: همان گزینه‌ها و همان ترتیب منوی سه‌نقطهٔ همان مورد.\n- ✗ نادرست: عمل‌هایی که فقط با راست‌کلیک پیدا می‌شوند.\n\n## وب کامپوننت\n\nهمین کامپوننت بدون React، برای HTML، وردپرس، Vue، Svelte و Angular: `<kashi-context-menu>` در `@kashi-ui/elements`. محتوا ناحیهٔ کلیک است و گزینه‌ها داخل `<div data-menu>`؛ با کیبورد، کلید ContextMenu یا Shift+F10.\n\nنصب بدون ابزار ساخت (CDN):\n\n```html\n<link rel=\"stylesheet\" href=\"https://cdn.jsdelivr.net/npm/@kashi-ui/css/dist/kashi.css\">\n<script type=\"module\" src=\"https://cdn.jsdelivr.net/npm/@kashi-ui/elements/dist/cdn/kashi-elements.js\"></script>\n```\n\nیا با npm، فقط همین بخش:\n\n```bash\nnpm i @kashi-ui/elements @kashi-ui/css\n```\n\n```js\nimport \"@kashi-ui/css\";\nimport \"@kashi-ui/elements/nav.js\";\n```\n\n```html\n<kashi-context-menu label=\"گزینه‌های فایل\">\n <div class=\"k-card\" tabindex=\"0\" style=\"padding: 2rem; text-align: center\">روی این کارت راست‌کلیک کنید</div>\n <div data-menu>\n <button data-value=\"rename\">تغییر نام</button>\n <button data-value=\"copy\" data-shortcut=\"⌘C\">کپی</button>\n <hr>\n <button data-value=\"delete\" data-tone=\"danger\">حذف</button>\n </div>\n</kashi-context-menu>\n```\n\nویژگی‌ها:\n\n| نام | مقدار | پیش‌فرض | توضیح |\n|---|---|---|---|\n| label | متن | گزینه‌ها | نام منو |\n\nرویدادها و JavaScript:\n\n| نام | توضیح |\n|---|---|\n| kashi-select | detail: { value } |"},{"slug":"components/copy-button","url":"https://kashi.sepantapouya.com/docs/components/copy-button/","title":"دکمهٔ کپی","description":"کپی متن در کلیپ‌بورد با بازخورد «کپی شد»، برای کلید API، شماره شبا یا کد تخفیف.","group":"کامپوننت‌ها","components":["CopyButton"],"markdown":"```tsx\nimport { CopyButton } from \"@kashi-ui/react\";\n```\n\n## نمونه\n\n```tsx\n<Input aria-label=\"کلید API\" readOnly value=\"sk_live_4f9a…b21c\" />\n<CopyButton value={apiKey} />\n<CopyButton value={iban} iconOnly labels={{ copy: \"کپی شماره شبا\" }} />\n```\n\n| ویژگی | نوع | پیش‌فرض | توضیح |\n|---|---|---|---|\n| value | string | — | متنی که کپی می‌شود |\n| onCopy | (value: string) => void | — | پس از کپی موفق |\n| iconOnly | boolean | false | فقط آیکون؛ نام از labels.copy |\n| timeout | number | 2000 | مدت نمایش «کپی شد» به میلی‌ثانیه |\n| labels | { copy?, copied?, failed? } | «کپی»، «کپی شد»، «کپی نشد» | — |\n| variant / size | ButtonProps | \"secondary\" / \"sm\" | همهٔ props دکمه |\n\n## دسترس‌پذیری\n\nنتیجه («کپی شد» یا «کپی نشد») با `aria-live` اعلام می‌شود، پس کاربر صفحه‌خوان هم بازخورد می‌گیرد. در حالت فقط‌آیکون، نام دکمه هم به «کپی شد» تغییر می‌کند. کلیپ‌بورد فقط در HTTPS یا localhost در دسترس است.\n\n## راهنمای استفاده\n\nکِی استفاده کنیم:\n\n- متنی که کاربر احتمالاً جای دیگری می‌چسباند: شمارهٔ شبا، کد تخفیف، کد پیگیری، کلید API.\n\nکِی استفاده نکنیم:\n\n- متن طولانی یا پاراگراف؛ کاربر خودش انتخابش می‌کند.\n\nدرست و نادرست:\n\n- ✓ درست: دکمه کنار همان متن، با بازخورد «کپی شد».\n- ✗ نادرست: دکمهٔ کپی دور از متن که معلوم نیست چه را کپی می‌کند.\n\n## وب کامپوننت\n\nهمین کامپوننت بدون React، برای HTML، وردپرس، Vue، Svelte و Angular: `<kashi-copy-button>` در `@kashi-ui/elements`.\n\nنصب بدون ابزار ساخت (CDN):\n\n```html\n<link rel=\"stylesheet\" href=\"https://cdn.jsdelivr.net/npm/@kashi-ui/css/dist/kashi.css\">\n<script type=\"module\" src=\"https://cdn.jsdelivr.net/npm/@kashi-ui/elements/dist/cdn/kashi-elements.js\"></script>\n```\n\nیا با npm، فقط همین بخش:\n\n```bash\nnpm i @kashi-ui/elements @kashi-ui/css\n```\n\n```js\nimport \"@kashi-ui/css\";\nimport \"@kashi-ui/elements/overlay.js\";\n```\n\n```html\n<kashi-copy-button value=\"IR820540102680020817909002\"></kashi-copy-button>\n<kashi-copy-button value=\"6037991234567893\" icon-only label=\"کپی شمارهٔ کارت\"></kashi-copy-button>\n```\n\nویژگی‌ها:\n\n| نام | مقدار | پیش‌فرض | توضیح |\n|---|---|---|---|\n| value | متن | — | متنی که کپی می‌شود |\n| target | انتخابگر CSS | — | یا کپی مقدار یا متن عنصری دیگر |\n| label / copied-label | متن | کپی / کپی شد | متن دکمه |\n| icon-only | بولی | — | فقط آیکون |\n| variant / size | — | secondary / sm | ظاهر دکمه |\n\nرویدادها و JavaScript:\n\n| نام | توضیح |\n|---|---|\n| kashi-copy | پس از کپی موفق؛ detail: { value } |"},{"slug":"components/dialog","url":"https://kashi.sepantapouya.com/docs/components/dialog/","title":"دیالوگ","description":"پنجره‌ای روی صفحه برای کاری کوتاه یا تأیید یک تصمیم، روی dialog بومی مرورگر.","group":"کامپوننت‌ها","components":["Dialog"],"markdown":"```tsx\nimport { Dialog } from \"@kashi-ui/react\";\n```\n\n## نمونه\n\n```tsx\nconst [open, setOpen] = useState(false);\n\n<Button onClick={() => setOpen(true)}>دعوت هم‌تیمی</Button>\n<Dialog\n open={open}\n onOpenChange={setOpen}\n title=\"دعوت به تیم\"\n description=\"ایمیل هم‌تیمی را وارد کنید تا دعوت‌نامه برایش ارسال شود.\"\n icon={<UserIcon />}\n footer={<>\n <Button variant=\"secondary\" onClick={() => setOpen(false)}>انصراف</Button>\n <Button variant=\"primary\">ارسال دعوت</Button>\n </>}\n>\n <Input label=\"ایمیل\" type=\"email\" autoFocus />\n</Dialog>\n\n// تأیید عملیات مخرب\n<Dialog tone=\"danger\" size=\"sm\" title=\"پروژه حذف شود؟\" … />\n```\n\n## کشوی کناری و برگهٔ پایین\n\nهمان دسترس‌پذیری دیالوگ (تلهٔ فوکوس، Esc، برگشت فوکوس) با چیدمان کشو؛ برگهٔ پایین برای گوشی مناسب است.\n\n```tsx\n<Dialog placement=\"end\" open={open} onOpenChange={setOpen} title=\"فیلترها\" footer={…}>\n …\n</Dialog>\n\n<Dialog placement=\"bottom\" …>…</Dialog>\n```\n\n**ویژگی‌های جدید**\n\n| ویژگی | نوع | پیش‌فرض | توضیح |\n|---|---|---|---|\n| placement | \"center\" \\| \"start\" \\| \"end\" \\| \"bottom\" | \"center\" | جایگاه؛ در RTL «start» سمت راست است |\n| size | \"sm\" \\| \"md\" \\| \"lg\" \\| \"full\" | \"md\" | اندازهٔ full اضافه شد |\n\nمتغیرهای CSS: `--k-drawer-width` (پیش‌فرض ۲۶rem) و `--k-dialog-backdrop`.\n\n## ویژگی‌ها\n\n| ویژگی | نوع | پیش‌فرض | توضیح |\n|---|---|---|---|\n| open | boolean | — | باز بودن (کنترل‌شده) |\n| onOpenChange | (open: boolean) => void | — | با Esc، دکمهٔ بستن یا کلیک روی پس‌زمینه |\n| title | ReactNode | — | عنوان — الزامی |\n| description | ReactNode | — | توضیح زیر عنوان |\n| icon | ReactNode | — | آیکون شاخص |\n| tone | \"default\" \\| \"danger\" | \"default\" | رنگ آیکون برای عملیات مخرب |\n| size | \"sm\" \\| \"md\" \\| \"lg\" | \"md\" | عرض: ۲۴، ۳۰ یا ۴۲ rem |\n| footer | ReactNode | — | دکمه‌ها |\n| dismissible | boolean | true | بستن با کلیک روی پس‌زمینه |\n| closeLabel | string | \"بستن\" | نام دکمهٔ بستن |\n\n## دسترس‌پذیری\n\n- روی `<dialog>` بومی با `showModal()` ساخته شده: فوکوس داخل پنجره می‌ماند، بقیهٔ صفحه برای کیبورد و صفحه‌خوان غیرفعال می‌شود و <kbd>Esc</kbd> پنجره را می‌بندد.\n- پس از بستن، فوکوس به دکمه‌ای که پنجره را باز کرده برمی‌گردد.\n- عنوان و توضیح با `aria-labelledby` و `aria-describedby` به پنجره متصل‌اند.\n- برای تأیید عملیات مخرب، دکمهٔ «انصراف» را اول بگذارید و نتیجهٔ کار را در توضیح صریح بنویسید.\n\n## راهنمای استفاده\n\nکِی استفاده کنیم:\n\n- تأیید یک تصمیم مهم یا برگشت‌ناپذیر: حذف، خروج بدون ذخیره.\n- کاری کوتاه که نباید کاربر را از صفحه دور کند: ویرایش یک مورد، افزودن نشانی.\n\nکِی استفاده نکنیم:\n\n- فرم یا محتوای طولانی؛ صفحهٔ جدا بسازید یا کشوی کناری (`placement`).\n- پیام اطلاع‌رسانی که تصمیمی نمی‌خواهد. (توست: /docs/components/toast/)\n- گفتگو روی گفتگو.\n\nدرست و نادرست:\n\n- ✓ درست: عنوان پرسش است و دکمهٔ اصلی همان فعل: «حذف این سفارش؟» با دکمه‌های «انصراف» و «حذف سفارش».\n- ✗ نادرست: «آیا مطمئن هستید؟» با «بله» و «خیر»؛ دکمه‌ها بدون خواندن پرسش معنا ندارند.\n\nمتن:\n\n- در متن گفتگوی حذف بگویید دقیقاً چه چیزی از بین می‌رود و آیا برگشت دارد.\n\n## وب کامپوننت\n\nهمین کامپوننت بدون React، برای HTML، وردپرس، Vue، Svelte و Angular: `<kashi-dialog>` در `@kashi-ui/elements`. دکمه با `data-kashi-open` پنجره را باز می‌کند و `data-kashi-close` داخل پنجره آن را می‌بندد؛ بدون JavaScript.\n\nنصب بدون ابزار ساخت (CDN):\n\n```html\n<link rel=\"stylesheet\" href=\"https://cdn.jsdelivr.net/npm/@kashi-ui/css/dist/kashi.css\">\n<script type=\"module\" src=\"https://cdn.jsdelivr.net/npm/@kashi-ui/elements/dist/cdn/kashi-elements.js\"></script>\n```\n\nیا با npm، فقط همین بخش:\n\n```bash\nnpm i @kashi-ui/elements @kashi-ui/css\n```\n\n```js\nimport \"@kashi-ui/css\";\nimport \"@kashi-ui/elements/overlay.js\";\n```\n\n```html\n<button class=\"k-btn k-btn--danger\" data-kashi-open=\"page-dialog\">حذف فایل</button>\n<kashi-dialog id=\"page-dialog\" heading=\"فایل حذف شود؟\" description=\"این کار قابل بازگشت نیست.\" tone=\"danger\" size=\"sm\">\n <div slot=\"footer\">\n <button class=\"k-btn k-btn--secondary\" data-kashi-close>انصراف</button>\n <button class=\"k-btn k-btn--danger\" data-kashi-close=\"delete\">حذف</button>\n </div>\n</kashi-dialog>\n```\n\nویژگی‌ها:\n\n| نام | مقدار | پیش‌فرض | توضیح |\n|---|---|---|---|\n| heading / description | متن | — | عنوان و توضیح پنجره |\n| tone | danger | — | برای عملیات مخرب |\n| size | sm \\| md \\| lg \\| full | md | اندازه |\n| placement | start \\| end \\| bottom | center | کشوی کناری یا برگهٔ پایین |\n| open | بولی | — | باز بودن |\n| dismissible | false | — | بسته نشدن با کلیک روی پس‌زمینه |\n\nرویدادها و JavaScript:\n\n| نام | توضیح |\n|---|---|\n| element.show() / close() | باز و بستن |\n| kashi-open / kashi-close | detail.returnValue: مقدار data-kashi-close دکمه‌ای که پنجره را بست |"},{"slug":"components/divider","url":"https://kashi.sepantapouya.com/docs/components/divider/","title":"جداکننده و کلید میان‌بر","description":"خط جداکننده با متن اختیاری، و نمایش کلیدهای صفحه‌کلید.","group":"کامپوننت‌ها","components":["Divider","Kbd"],"markdown":"```tsx\nimport { Divider, Kbd } from \"@kashi-ui/react\";\n```\n\n## جداکننده\n\n```tsx\n<Divider />\n<Divider label=\"یا\" />\n```\n\n## کلید صفحه‌کلید\n\n```tsx\n<Kbd keys={[\"Ctrl\", \"K\"]} />\n<Kbd>Esc</Kbd>\n```\n\n**ویژگی‌های Divider و Kbd**\n\n| ویژگی | نوع | پیش‌فرض | توضیح |\n|---|---|---|---|\n| label | ReactNode | — | متن وسط خط (Divider) |\n| vertical | boolean | false | جداکنندهٔ عمودی (Divider) |\n| keys | string[] | — | ترکیب کلیدها (Kbd) |\n\nکلیدها همیشه چپ‌به‌راست نمایش داده می‌شوند تا ترتیب «Ctrl + K» در متن فارسی برعکس نشود.\n\n## راهنمای استفاده\n\nکِی استفاده کنیم:\n\n- جدا کردن بخش‌های یک کارت یا منو.\n- «یا» بین دو راه، مثل ورود با رمز یا با کد یک‌بارمصرف.\n- نمایش میان‌بر صفحه‌کلید با `Kbd`.\n\nکِی استفاده نکنیم:\n\n- جدا کردن هر دو خط محتوا؛ فاصلهٔ خالی معمولاً کافی و آرام‌تر است.\n\nدرست و نادرست:\n\n- ✓ درست: یک جداکننده بین گروه‌های واقعاً جدا.\n- ✗ نادرست: خط زیر هر ردیف فرم که صفحه را خط‌خطی می‌کند."},{"slug":"components/dropdown-menu","url":"https://kashi.sepantapouya.com/docs/components/dropdown-menu/","title":"دراپ‌داون","description":"فهرستی از عمل‌ها که با یک دکمه باز می‌شود؛ با الگوی Menu Button و پیمایش کامل کیبورد.","group":"کامپوننت‌ها","components":["DropdownMenu","MenuItem","MenuSeparator","MenuLabel"],"markdown":"```tsx\nimport { DropdownMenu, MenuItem, MenuSeparator, MenuLabel } from \"@kashi-ui/react\";\n```\n\n## نمونه\n\n```tsx\n<DropdownMenu trigger={<Button>گزینه‌ها</Button>}>\n <MenuLabel>پروژه</MenuLabel>\n <MenuItem icon={<EditIcon />} shortcut=\"Ctrl+E\" onSelect={edit}>ویرایش</MenuItem>\n <MenuItem icon={<CopyIcon />} shortcut=\"Ctrl+D\" onSelect={copy}>کپی</MenuItem>\n <MenuItem icon={<ArchiveIcon />} disabled>بایگانی</MenuItem>\n <MenuSeparator />\n <MenuItem icon={<TrashIcon />} tone=\"danger\" onSelect={remove}>حذف</MenuItem>\n</DropdownMenu>\n\n<DropdownMenu align=\"end\" label=\"عملیات ردیف\"\n trigger={<Button variant=\"tertiary\" iconOnly aria-label=\"عملیات بیشتر\"><DotsIcon /></Button>}>\n …\n</DropdownMenu>\n```\n\n## کیبورد\n\n| کلید | عمل |\n|---|---|\n| <kbd>Enter</kbd> / <kbd>Space</kbd> / <kbd>↓</kbd> | باز کردن و رفتن به اولین گزینه |\n| <kbd>↑</kbd> روی دکمه | باز کردن و رفتن به آخرین گزینه |\n| <kbd>↑</kbd> / <kbd>↓</kbd> | گزینهٔ قبل / بعد (گزینه‌های غیرفعال رد می‌شوند) |\n| <kbd>Home</kbd> / <kbd>End</kbd> | اولین / آخرین گزینه |\n| حرف | پرش به گزینه‌ای که با آن حرف شروع می‌شود |\n| <kbd>Esc</kbd> | بستن و برگشت به دکمه |\n| <kbd>Tab</kbd> | بستن و رفتن به عنصر بعدی صفحه |\n\n## گزینه‌های تیک‌دار\n\nبرای تنظیماتی مثل نمایش ستون‌ها؛ منو پس از انتخاب باز می‌ماند تا چند گزینه پشت سر هم عوض شوند.\n\n```tsx\n<MenuItem checked={cols.date} onSelect={() => toggle(\"date\")}>تاریخ</MenuItem>\n```\n\n`checked` نقش گزینه را `menuitemcheckbox` با `aria-checked` می‌کند. متغیرهای CSS: `--k-menu-min-width`، `--k-menu-radius`، `--k-menu-item-height`، `--k-menu-bg`.\n\n## ویژگی‌ها\n\n| ویژگی | نوع | پیش‌فرض | توضیح |\n|---|---|---|---|\n| trigger | ReactElement | — | دکمهٔ بازکننده (DropdownMenu) |\n| label | string | متن دکمه | نام منو برای صفحه‌خوان |\n| align | \"start\" \\| \"end\" | \"start\" | هم‌ترازی با دکمه |\n| onSelect | () => void | — | با انتخاب گزینه؛ منو بسته می‌شود (MenuItem) |\n| icon / shortcut | ReactNode / string | — | آیکون و میان‌بر نمایشی |\n| tone | \"default\" \\| \"danger\" | \"default\" | رنگ قرمز برای عملیات مخرب |\n| href | string | — | گزینهٔ لینکی |\n| disabled | boolean | false | غیرفعال؛ در پیمایش رد می‌شود |\n\n## دسترس‌پذیری\n\n- دکمه `aria-haspopup=\"menu\"` و `aria-expanded` دارد؛ منو `role=\"menu\"` با گزینه‌های `menuitem`.\n- فوکوس داخل منو با کلیدهای جهت‌دار جابه‌جا می‌شود، نه Tab؛ پس از انتخاب یا Esc، فوکوس به دکمه برمی‌گردد.\n- منو برای **عمل‌ها** است. برای انتخاب یک مقدار در فرم از [سلکت](/docs/components/select/) استفاده کنید.\n\n## راهنمای استفاده\n\nکِی استفاده کنیم:\n\n- عمل‌های کم‌کاربرد یک مورد، پشت دکمهٔ سه‌نقطه: ویرایش، کپی، حذف.\n- منوی حساب کاربری.\n\nکِی استفاده نکنیم:\n\n- انتخاب یک مقدار برای فرم. (سلکت: /docs/components/select/)\n- عمل اصلی صفحه؛ آن را به‌شکل دکمه نشان دهید. (دکمه: /docs/components/button/)\n- پیوندهای ناوبری اصلی سایت. (سایدبار: /docs/components/side-nav/)\n\nدرست و نادرست:\n\n- ✓ درست: گزینهٔ خطرناک آخر فهرست، جدا با خط و به رنگ خطر.\n- ✗ نادرست: «حذف» بین «ویرایش» و «کپی»، جایی که با یک لغزش زده می‌شود.\n\n## وب کامپوننت\n\nهمین کامپوننت بدون React، برای HTML، وردپرس، Vue، Svelte و Angular: `<kashi-dropdown-menu>` در `@kashi-ui/elements`. اولین فرزند دکمهٔ بازکننده است؛ بقیه گزینه‌ها: `<button>`، `<a href>`، `<hr>` برای جداکننده و عنصر `data-label` برای عنوان گروه.\n\nنصب بدون ابزار ساخت (CDN):\n\n```html\n<link rel=\"stylesheet\" href=\"https://cdn.jsdelivr.net/npm/@kashi-ui/css/dist/kashi.css\">\n<script type=\"module\" src=\"https://cdn.jsdelivr.net/npm/@kashi-ui/elements/dist/cdn/kashi-elements.js\"></script>\n```\n\nیا با npm، فقط همین بخش:\n\n```bash\nnpm i @kashi-ui/elements @kashi-ui/css\n```\n\n```js\nimport \"@kashi-ui/css\";\nimport \"@kashi-ui/elements/menu.js\";\n```\n\n```html\n<kashi-dropdown-menu label=\"عملیات\">\n <button class=\"k-btn k-btn--secondary\">بیشتر</button>\n <button data-value=\"edit\" data-shortcut=\"⌘E\">ویرایش</button>\n <button data-value=\"pin\" data-checked=\"false\">سنجاق کردن</button>\n <hr>\n <button data-value=\"delete\" data-tone=\"danger\">حذف</button>\n</kashi-dropdown-menu>\n```\n\nویژگی‌ها:\n\n| نام | مقدار | پیش‌فرض | توضیح |\n|---|---|---|---|\n| label | متن | — | نام منو برای صفحه‌خوان |\n| align | end | — | چسبیدن منو به سمت دیگر دکمه |\n| data-value / data-shortcut | روی گزینه | — | مقدار و میان‌بر نمایشی |\n| data-tone / data-checked / disabled | danger / true \\| false | — | گزینهٔ خطرناک، تیک‌دار یا غیرفعال |\n\nرویدادها و JavaScript:\n\n| نام | توضیح |\n|---|---|\n| kashi-select | detail: { value, checked } |"},{"slug":"components/empty-state","url":"https://kashi.sepantapouya.com/docs/components/empty-state/","title":"حالت خالی","description":"وقتی داده‌ای برای نمایش نیست؛ توضیح بدهید چرا و قدم بعدی را پیشنهاد کنید.","group":"کامپوننت‌ها","components":["EmptyState"],"markdown":"```tsx\nimport { EmptyState } from \"@kashi-ui/react\";\n```\n\n## نمونه\n\n```tsx\n<EmptyState\n icon={<FolderIcon />}\n title=\"هنوز پروژه‌ای ندارید\"\n description=\"اولین پروژه را بسازید یا از یکی از قالب‌های آماده شروع کنید.\"\n actions={<>\n <Button variant=\"secondary\">مشاهدهٔ قالب‌ها</Button>\n <Button variant=\"primary\">پروژهٔ جدید</Button>\n </>}\n/>\n```\n\n| ویژگی | نوع | پیش‌فرض | توضیح |\n|---|---|---|---|\n| title | ReactNode | — | عنوان — الزامی |\n| description | ReactNode | — | توضیح و راهنمایی |\n| icon | ReactNode | — | آیکون |\n| actions | ReactNode | — | دکمه‌ها |\n\nداخل [جدول داده](/docs/components/table/) هم می‌توانید آن را به‌عنوان `empty` بدهید.\n\n## راهنمای استفاده\n\nکِی استفاده کنیم:\n\n- فهرست، جدول یا صفحه‌ای که هنوز داده‌ای ندارد.\n- نتیجهٔ خالی جستجو یا فیلتر.\n\nکِی استفاده نکنیم:\n\n- حالت بارگذاری. (اسکلتون: /docs/components/skeleton/)\n- خطای دریافت داده؛ آن را با هشدار و دکمهٔ تلاش دوباره نشان دهید. (هشدار: /docs/components/alert/)\n\nدرست و نادرست:\n\n- ✓ درست: بگویید چرا خالی است و قدم بعدی را پیشنهاد کنید.\n- ✗ نادرست: پیام فنی بدون راه بعدی."},{"slug":"components/file-upload","url":"https://kashi.sepantapouya.com/docs/components/file-upload/","title":"آپلود فایل","description":"کشیدن و رها کردن یا انتخاب فایل، با بررسی نوع و حجم و نوار پیشرفت هر فایل.","group":"کامپوننت‌ها","components":["FileUpload"],"markdown":"```tsx\nimport { FileUpload } from \"@kashi-ui/react\";\n```\n\n## نمونه\n\nفایلی که نامش «خطا» دارد، در این نمونه با شکست روبه‌رو می‌شود.\n\n```tsx\n<FileUpload\n accept=\"image/png,image/jpeg,application/pdf\"\n maxSize={5 * 1024 * 1024}\n maxFiles={4}\n hint=\"PNG، JPG یا PDF؛ هر فایل تا ۵ مگابایت\"\n uploader={(file, onProgress) => upload(file, { onProgress })}\n/>\n```\n\n`uploader` هر فایل را می‌گیرد و یک Promise برمی‌گرداند؛ با `onProgress(درصد)` نوار پیشرفت به‌روز می‌شود. بدون `uploader`، فایل‌ها فقط فهرست می‌شوند و با `onFilesChange` به شما می‌رسند (یا با `name` در فرم ارسال می‌شوند).\n\n| ویژگی | نوع | پیش‌فرض | توضیح |\n|---|---|---|---|\n| accept | string | — | مثل input بومی |\n| multiple | boolean | true | — |\n| maxSize | number | — | بیشترین حجم هر فایل به بایت |\n| maxFiles | number | — | بیشترین تعداد |\n| uploader | (file, onProgress) => Promise<void> | — | بارگذاری هر فایل |\n| onFilesChange | (items: FileItem[]) => void | — | وضعیت همهٔ فایل‌ها |\n| hint | ReactNode | — | متن راهنما داخل ناحیه |\n| labels | { browse?, drop?, tooBig?, wrongType?, tooMany?, … } | — | متن‌ها |\n\nتابع `formatBytes` حجم را فارسی می‌نویسد: `formatBytes(1536)` ← «۱٫۵ کیلوبایت».\n\n## دسترس‌پذیری\n\nناحیهٔ رها کردن یک دکمهٔ واقعی «انتخاب فایل» دارد، پس بدون موس هم کار می‌کند. هر فایل نام، حجم و وضعیتش را با متن نشان می‌دهد (نه فقط رنگ) و دکمهٔ حذف آن «حذف نام‌فایل» نام دارد. پایان بارگذاری و خطاها با `aria-live` اعلام می‌شوند.\n\n## راهنمای استفاده\n\nکِی استفاده کنیم:\n\n- بارگذاری مدرک، عکس محصول، فایل اکسل یا رزومه.\n- چند فایل با نوار پیشرفت هر کدام.\n\nکِی استفاده نکنیم:\n\n- متنی که کاربر می‌تواند تایپ کند؛ فرم را با فایل سخت‌تر نکنید.\n\nدرست و نادرست:\n\n- ✓ درست: نوع و حجم مجاز را از قبل بگویید و با `accept` و `maxSize` بررسی کنید: «PDF یا JPG، حداکثر ۵ مگابایت».\n- ✗ نادرست: بعد از بارگذاری کامل یک فایل ۴۰ مگابایتی بگویید «فرمت پشتیبانی نمی‌شود».\n\n## وب کامپوننت\n\nهمین کامپوننت بدون React، برای HTML، وردپرس، Vue، Svelte و Angular: `<kashi-file-upload>` در `@kashi-ui/elements`. با `name` فایل‌ها همراه فرم HTML ارسال می‌شوند (فرم با `enctype=\"multipart/form-data\"`). برای بارگذاری فوری `element.uploader` بدهید.\n\nنصب بدون ابزار ساخت (CDN):\n\n```html\n<link rel=\"stylesheet\" href=\"https://cdn.jsdelivr.net/npm/@kashi-ui/css/dist/kashi.css\">\n<script type=\"module\" src=\"https://cdn.jsdelivr.net/npm/@kashi-ui/elements/dist/cdn/kashi-elements.js\"></script>\n```\n\nیا با npm، فقط همین بخش:\n\n```bash\nnpm i @kashi-ui/elements @kashi-ui/css\n```\n\n```js\nimport \"@kashi-ui/css\";\nimport \"@kashi-ui/elements/pickers.js\";\n```\n\n```html\n<kashi-file-upload name=\"documents\" accept=\".pdf,image/*\" max-size=\"5242880\" hint=\"PDF یا تصویر، حداکثر ۵ مگابایت\"></kashi-file-upload>\n```\n\nویژگی‌ها:\n\n| نام | مقدار | پیش‌فرض | توضیح |\n|---|---|---|---|\n| accept | .pdf,image/* | — | نوع فایل‌های پذیرفته |\n| multiple | false | — | فقط یک فایل |\n| max-size / max-files | بایت / عدد | — | بیشترین حجم و تعداد |\n| name / hint / label / disabled | — | — | نام فرم، راهنما و متن دکمه |\n\nرویدادها و JavaScript:\n\n| نام | توضیح |\n|---|---|\n| element.uploader | (file, onProgress) => Promise؛ درصد پیشرفت را با onProgress بدهید |\n| kashi-change | detail: { files } |\n| element.files | فایل‌های پذیرفته‌شده |"},{"slug":"components/inline-edit","url":"https://kashi.sepantapouya.com/docs/components/inline-edit/","title":"ویرایش درجا","description":"ویرایش متن در همان جا، مثل نام پروژه یا عنوان کارت، بدون فرم جداگانه.","group":"کامپوننت‌ها","components":["InlineEdit"],"markdown":"```tsx\nimport { InlineEdit } from \"@kashi-ui/react\";\n```\n\n## نمونه\n\n```tsx\n<InlineEdit label=\"نام پروژه\" defaultValue=\"بازطراحی فروشگاه\"\n validate={(v) => (v ? undefined : \"نام پروژه نمی‌تواند خالی باشد.\")}\n onSave={(v) => api.rename(v)} />\n```\n\nاگر `onSave` یک Promise برگرداند، دکمهٔ ثبت تا پایانش در حالت بارگذاری می‌ماند و در صورت خطا متن قبلی برمی‌گردد.\n\n| ویژگی | نوع | پیش‌فرض | توضیح |\n|---|---|---|---|\n| label | string | — | نام فیلد برای صفحه‌خوان (الزامی) |\n| value / defaultValue | string | \"\" | — |\n| onSave | (value) => void \\| Promise | — | پس از ثبت |\n| validate | (value) => string \\| undefined | — | پیام خطا |\n| renderView | (value) => ReactNode | — | ظاهر حالت نمایش، مثلاً عنوان |\n| placeholder | string | «خالی» | — |\n\n## کیبورد\n\n| کلید | عمل |\n|---|---|\n| <kbd>Enter</kbd> روی متن | شروع ویرایش |\n| <kbd>Enter</kbd> در فیلد | ثبت |\n| <kbd>Esc</kbd> | انصراف و برگشت به متن |\n\n## دسترس‌پذیری\n\nحالت نمایش یک دکمه با نام کامل است («نام پروژه: بازطراحی فروشگاه، ویرایش»). پس از ثبت یا انصراف، فوکوس به همان دکمه برمی‌گردد.\n\n## راهنمای استفاده\n\nکِی استفاده کنیم:\n\n- تغییر یک مقدار کوتاه در همان جا: نام پروژه، عنوان کارت، برچسب.\n- جایی که ویرایش گاه‌به‌گاه است و فرم جدا سنگین است.\n\nکِی استفاده نکنیم:\n\n- چند فیلد که با هم ثبت می‌شوند؛ فرم یا گفتگو بسازید. (دیالوگ: /docs/components/dialog/)\n- مقدار مهم یا حساس مثل مبلغ پرداخت.\n\nدرست و نادرست:\n\n- ✓ درست: ذخیره با Enter یا دکمهٔ تیک، انصراف با Escape؛ اگر ذخیره شکست خورد پیام بدهید و متن کاربر را نگه دارید.\n- ✗ نادرست: ذخیرهٔ بی‌صدا که اگر نشد، متن کاربر هم از دست برود.\n\n## وب کامپوننت\n\nهمین کامپوننت بدون React، برای HTML، وردپرس، Vue، Svelte و Angular: `<kashi-inline-edit>` در `@kashi-ui/elements`.\n\nنصب بدون ابزار ساخت (CDN):\n\n```html\n<link rel=\"stylesheet\" href=\"https://cdn.jsdelivr.net/npm/@kashi-ui/css/dist/kashi.css\">\n<script type=\"module\" src=\"https://cdn.jsdelivr.net/npm/@kashi-ui/elements/dist/cdn/kashi-elements.js\"></script>\n```\n\nیا با npm، فقط همین بخش:\n\n```bash\nnpm i @kashi-ui/elements @kashi-ui/css\n```\n\n```js\nimport \"@kashi-ui/css\";\nimport \"@kashi-ui/elements/forms.js\";\n```\n\n```html\n<kashi-inline-edit label=\"نام پروژه\" value=\"فروشگاه کاشی\"></kashi-inline-edit>\n```\n\nویژگی‌ها:\n\n| نام | مقدار | پیش‌فرض | توضیح |\n|---|---|---|---|\n| value | متن | — | متن فعلی |\n| label | متن | متن | نام برای صفحه‌خوان |\n| placeholder | متن | خالی | وقتی متن خالی است |\n\nرویدادها و JavaScript:\n\n| نام | توضیح |\n|---|---|\n| kashi-save | پیش از ثبت؛ detail: { value, wait }. برای ذخیرهٔ سمت سرور detail.wait(fetch(…)) را صدا بزنید؛ اگر رد شود، خطا نشان داده می‌شود |\n| kashi-change | پس از ثبت؛ detail: { value } |\n| element.validate | تابع اعتبارسنجی؛ پیام خطا برمی‌گرداند |"},{"slug":"components/input","url":"https://kashi.sepantapouya.com/docs/components/input/","title":"ورودی","description":"فیلد متن تک‌خطی و چندخطی، با برچسب، متن راهنما، پیام خطا و افزونه.","group":"کامپوننت‌ها","components":["Input","Textarea"],"markdown":"```tsx\nimport { Input, Textarea } from \"@kashi-ui/react\";\n```\n\n## پایه\n\nبرچسب، متن راهنما و فیلد به‌صورت خودکار با `id` و `aria-describedby` به هم وصل می‌شوند.\n\n```tsx\n<Input label=\"نام و نام خانوادگی\" placeholder=\"مثلاً سارا احمدی\" hint=\"همان‌طور که در کارت ملی آمده است.\" required />\n```\n\n## خطا\n\nبا دادن `error`، فیلد `aria-invalid` می‌شود و پیام خطا پیش از متن راهنما خوانده می‌شود.\n\n```tsx\n<Input label=\"ایمیل\" type=\"email\" defaultValue=\"sara@example\" error=\"آدرس ایمیل معتبر نیست.\" />\n```\n\n## افزونه‌ها\n\nایمیل، تلفن، آدرس اینترنتی، عدد و رمز عبور به‌صورت خودکار چپ‌به‌راست تایپ می‌شوند، ولی متن راهنمای داخل فیلد راست‌چین می‌ماند.\n\n```tsx\n<Input label=\"ایمیل\" type=\"email\" placeholder=\"you@example.com\" addonStart={<MailIcon />} />\n<Input label=\"مبلغ\" inputMode=\"numeric\" placeholder=\"۰\" addonEnd=\"تومان\" />\n<Input aria-label=\"جستجو\" placeholder=\"جستجو…\" addonStart={<SearchIcon />} size=\"sm\" />\n```\n\n## چندخطی\n\n```tsx\n<Textarea label=\"توضیحات\" placeholder=\"چند خط بنویسید…\" hint=\"حداکثر ۵۰۰ نویسه.\" />\n```\n\n## غیرفعال\n\n```tsx\n<Input label=\"کد ملی\" defaultValue=\"۰۰۱۲۳۴۵۶۷۸\" disabled />\n```\n\n## رمز، پاک کردن، شمارنده و بیشتر\n\n```tsx\n<Input label=\"رمز عبور\" type=\"password\" labelEnd={<a href=\"/forgot\">فراموشی رمز؟</a>} />\n<Input aria-label=\"جستجو\" addonStart={<SearchIcon />} clearable />\n<Input label=\"نام کاربری\" loading hint=\"در حال بررسی…\" />\n<Input label=\"نام شرکت\" optional />\n<Textarea label=\"دربارهٔ شما\" autoResize showCount maxLength={280} />\n```\n\n**ویژگی‌های جدید**\n\n| ویژگی | نوع | پیش‌فرض | توضیح |\n|---|---|---|---|\n| type=\"password\" | — | — | دکمهٔ نمایش/پنهان کردن رمز خودکار اضافه می‌شود؛ با revealable={false} خاموش |\n| clearable / onClear | boolean / () => void | — | دکمهٔ پاک کردن وقتی فیلد مقدار دارد |\n| loading | boolean | false | اسپینر در انتهای فیلد |\n| showCount | boolean | false | شمارندهٔ نویسه؛ با maxLength به شکل «۱۲ / ۲۸۰» |\n| optional | boolean \\| string | — | نشان «(اختیاری)» کنار برچسب |\n| labelEnd | ReactNode | — | محتوای انتهای ردیف برچسب |\n| autoResize / maxRows | boolean / number | — | ارتفاع خودکار Textarea |\n| classNames | { root, label, box, control, addon, hint, error } | — | کلاس دلخواه برای هر بخش داخلی |\n| labels | { clear, show, hide, loading } | فارسی | متن‌های صفحه‌خوان |\n\n## ویژگی‌ها\n\n| ویژگی | نوع | پیش‌فرض | توضیح |\n|---|---|---|---|\n| label | ReactNode | — | برچسب بالای فیلد؛ اگر نمی‌دهید aria-label بدهید |\n| hint | ReactNode | — | متن راهنمای زیر فیلد |\n| error | ReactNode | — | پیام خطا؛ فیلد را aria-invalid می‌کند |\n| size | \"sm\" \\| \"md\" \\| \"lg\" | \"md\" | ارتفاع‌ها ۴۰، ۴۴ و ۴۸ پیکسل (فقط Input) |\n| addonStart | ReactNode | — | آیکون یا متن ابتدای فیلد (فقط Input) |\n| addonEnd | ReactNode | — | آیکون یا متن انتهای فیلد (فقط Input) |\n| fieldClassName | string | — | کلاس ظرف بیرونی k-field |\n| required | boolean | false | ستاره کنار برچسب و ویژگی required |\n\nسایر ویژگی‌های `<input>` و `<textarea>` به خود فیلد منتقل می‌شوند. `ref` به عنصر فیلد اشاره می‌کند.\n\n## متغیرهای CSS\n\n| متغیر CSS | پیش‌فرض | توضیح |\n|---|---|---|\n| --k-input-height-{sm,md,lg} | 2.25rem تا 2.75rem × density | ارتفاع؛ هم‌قد دکمه‌های sm، md و lg |\n| --k-input-radius | var(--k-radius-md) | گردی گوشه‌ها |\n| --k-input-bg | var(--k-bg-primary) | پس‌زمینه |\n| --k-input-border | var(--k-border-input) | رنگ حاشیه در حالت عادی |\n| --k-field-gap | var(--k-space-1_5) | فاصلهٔ برچسب، فیلد و راهنما |\n\n## دسترس‌پذیری\n\n- حاشیهٔ فیلد کنتراست حداقل ۳ به ۱ دارد تا مرز آن برای کاربران کم‌بینا مشخص باشد.\n- متن راهنمای داخل فیلد (placeholder) جایگزین برچسب نیست؛ با تایپ کاربر ناپدید می‌شود.\n- پیام خطا باید بگوید چه اشتباهی رخ داده و چطور درست شود، نه فقط «نامعتبر».\n\n## راهنمای استفاده\n\nکِی استفاده کنیم:\n\n- متن کوتاه یک‌خطی: نام، ایمیل، عنوان. با `Textarea` برای متن چندخطی.\n- هر ورودی فرم که برچسب، راهنما و خطای خودش را لازم دارد.\n\nکِی استفاده نکنیم:\n\n- کد ملی، موبایل، کارت، شبا، مبلغ و کد پستی. (فیلد فارسی: /docs/persian/input/)\n- تاریخ. (دیت‌پیکر شمسی: /docs/persian/date-picker/)\n- عدد با گام مشخص. (ورودی عدد: /docs/components/number-input/)\n\nدرست و نادرست:\n\n- ✓ درست: برچسب بالای فیلد و همیشه دیدنی؛ مثال در `placeholder` یا راهنما.\n- ✗ نادرست: فقط `placeholder` به‌جای برچسب؛ با شروع تایپ ناپدید می‌شود و کاربر یادش می‌رود فیلد چه بود.\n- ✓ درست: خطا کنار همان فیلد، با علت و راه درست کردن.\n- ✗ نادرست: خطای کلی و سرزنش‌گر.\n\nمتن:\n\n- برچسب اسم است، نه دستور: «ایمیل»، نه «ایمیل خود را وارد کنید».\n- فیلد اختیاری را با `optional` نشان دهید، نه همهٔ فیلدهای اجباری را با ستاره، اگر بیشترشان اجباری‌اند.\n\n## وب کامپوننت\n\nهمین کامپوننت بدون React، برای HTML، وردپرس، Vue، Svelte و Angular: `<kashi-input>`، `<kashi-textarea>` در `@kashi-ui/elements`. `input` و `textarea` واقعی داخل عنصرند و با `name` مستقیم در فرم ارسال می‌شوند؛ ویژگی‌هایی مثل `type`، `maxlength` و `autocomplete` همان‌جا می‌روند.\n\nنصب بدون ابزار ساخت (CDN):\n\n```html\n<link rel=\"stylesheet\" href=\"https://cdn.jsdelivr.net/npm/@kashi-ui/css/dist/kashi.css\">\n<script type=\"module\" src=\"https://cdn.jsdelivr.net/npm/@kashi-ui/elements/dist/cdn/kashi-elements.js\"></script>\n```\n\nیا با npm، فقط همین بخش:\n\n```bash\nnpm i @kashi-ui/elements @kashi-ui/css\n```\n\n```js\nimport \"@kashi-ui/css\";\nimport \"@kashi-ui/elements/basic.js\";\n```\n\n```html\n<kashi-input label=\"ایمیل\" name=\"email\" type=\"email\" placeholder=\"you@example.com\" clearable></kashi-input>\n<kashi-input label=\"رمز عبور\" name=\"password\" type=\"password\" hint=\"حداقل ۸ نویسه\"></kashi-input>\n<kashi-textarea label=\"توضیحات\" name=\"bio\" maxlength=\"200\" show-count optional></kashi-textarea>\n```\n\n| عنصر | کاربرد |\n|---|---|\n| <kashi-input> | فیلد یک‌خطی؛ با type=\"password\" دکمهٔ نمایش رمز |\n| <kashi-textarea> | متن چندخطی؛ rows و autoresize |\n\nویژگی‌ها:\n\n| نام | مقدار | پیش‌فرض | توضیح |\n|---|---|---|---|\n| label / hint / error / placeholder | متن | — | برچسب، راهنما، خطا و متن خالی |\n| type / name / value / autocomplete / inputmode | — | text | مثل input معمولی |\n| clearable | بولی | — | دکمهٔ پاک کردن وقتی متن دارد |\n| revealable | false | — | رمز بدون دکمهٔ نمایش |\n| show-count / maxlength | بولی / عدد | — | شمارندهٔ نویسه، مثل «۱۲ / ۲۰۰» |\n| loading | بولی | — | اسپینر در انتهای فیلد، مثلاً هنگام بررسی نام کاربری |\n| optional | متن | اختیاری | نشان «اختیاری» کنار برچسب |\n| size / required / disabled / readonly | sm \\| lg / بولی | — | اندازه و حالت‌ها |\n\nرویدادها و JavaScript:\n\n| نام | توضیح |\n|---|---|\n| kashi-change | با هر تایپ؛ detail: { value } |\n| kashi-clear | پس از پاک کردن با دکمه |"},{"slug":"components/meter","url":"https://kashi.sepantapouya.com/docs/components/meter/","title":"گیج","description":"نمایش یک مقدار در برابر سقف، مثل فضای ذخیره، سهمیهٔ پیامک یا ظرفیت پلن.","group":"کامپوننت‌ها","components":["Meter"],"markdown":"```tsx\nimport { Meter } from \"@kashi-ui/react\";\n```\n\n## نمونه\n\n```tsx\n<Meter label=\"فضای ذخیره‌سازی\" value={4.2} max={10} format={(n) => `${n} گیگابایت`} />\n<Meter label=\"سهمیهٔ پیامک این ماه\" value={8600} max={10000} />\n<Meter label=\"کاربران پلن تیمی\" value={49} max={50} />\n```\n\nفرق آن با `Progress`: نوار پیشرفت برای کاری است که در حال انجام است؛ گیج برای اندازه‌ای است که ثابت است، مثل میزان مصرف. از ۸۰٪ رنگ هشدار و از ۹۵٪ رنگ بحرانی می‌گیرد و وضعیت با متن و آیکون هم گفته می‌شود.\n\n| ویژگی | نوع | پیش‌فرض | توضیح |\n|---|---|---|---|\n| label | ReactNode | — | نام گیج |\n| value / max | number | — / 100 | — |\n| warning / critical | number | ۸۰٪ / ۹۵٪ max | آستانهٔ هشدار و بحرانی |\n| format | (n: number) => string | ارقام فارسی | قالب مقدار |\n| labels | { ok?, warning?, critical?, of? } | — | متن وضعیت‌ها |\n\n## دسترس‌پذیری\n\n`role=\"meter\"` با `aria-valuetext` کامل («۸٬۶۰۰ از ۱۰٬۰۰۰، ۸۶٪، نزدیک به سقف»). وضعیت فقط با رنگ نشان داده نمی‌شود.\n\n## راهنمای استفاده\n\nکِی استفاده کنیم:\n\n- مقدار مصرف‌شده از یک سقف: فضای ذخیره، سهمیهٔ پیامک، ظرفیت پلن.\n- هشدار نزدیک شدن به سقف با `warning` و `critical`.\n\nکِی استفاده نکنیم:\n\n- پیشرفت یک کار که تمام می‌شود. (نوار پیشرفت: /docs/components/progress/)\n- مقدار بدون سقف مشخص. (کارت آمار: /docs/components/stat-card/)\n\nدرست و نادرست:\n\n- ✓ درست: عدد را هم بنویسید: «۸٫۲ از ۱۰ گیگابایت».\n- ✗ نادرست: فقط نوار رنگی که مقدار دقیق را نمی‌گوید."},{"slug":"components/multi-select","url":"https://kashi.sepantapouya.com/docs/components/multi-select/","title":"انتخاب چندتایی","description":"انتخاب چند گزینه از یک فهرست بلند با جستجو، مثل مسئولان پروژه یا برچسب‌های یک محصول.","group":"کامپوننت‌ها","components":["MultiSelect"],"markdown":"```tsx\nimport { MultiSelect } from \"@kashi-ui/react\";\n```\n\n## نمونه\n\n```tsx\n<MultiSelect label=\"مسئولان پروژه\" options={team} value={owners} onChange={setOwners}\n max={3} placeholder=\"جستجوی همکار\" name=\"owners\" />\n```\n\nهمان گزینه‌ها و جستجوی `Combobox` را دارد (یکدست‌سازی «ي» و «ی»، توضیح و آیکون). انتخاب‌ها به‌شکل برچسب داخل فیلد نمایش داده می‌شوند و با رسیدن به `max`، بقیهٔ گزینه‌ها غیرفعال می‌شوند. برای وارد کردن مقدار آزاد، که در فهرست نیست، `TagInput` مناسب‌تر است.\n\n| ویژگی | نوع | پیش‌فرض | توضیح |\n|---|---|---|---|\n| options | ComboboxOption[] | — | مثل Combobox |\n| value / defaultValue | string[] | [] | مقدارهای انتخاب‌شده |\n| onChange | (value: string[]) => void | — | — |\n| max | number | — | بیشترین تعداد انتخاب |\n| name | string | — | یک فیلد پنهان برای هر مقدار |\n| labels | { remove?, selected?, max?, clear? } | — | متن‌های صفحه‌خوان |\n\n## کیبورد\n\n| کلید | عمل |\n|---|---|\n| <kbd>↓</kbd> / <kbd>↑</kbd> | گزینهٔ بعد / قبل |\n| <kbd>Enter</kbd> | انتخاب یا لغو انتخاب گزینهٔ فعال |\n| <kbd>Backspace</kbd> در فیلد خالی | حذف آخرین برچسب |\n| <kbd>Esc</kbd> | بستن فهرست |\n\n## دسترس‌پذیری\n\nالگوی Combobox با `aria-multiselectable`؛ هر گزینه `aria-selected` دارد و تعداد انتخاب‌ها به فیلد وصل است («۲ مورد انتخاب شده»). افزودن و حذف با `aria-live` اعلام می‌شود.\n\n## راهنمای استفاده\n\nکِی استفاده کنیم:\n\n- انتخاب چند گزینه از فهرست بلند با جستجو: مسئولان پروژه، برچسب‌های محصول، شهرهای ارسال.\n\nکِی استفاده نکنیم:\n\n- کمتر از هشت گزینه؛ همه را با چک‌باکس نشان دهید. (چک‌باکس: /docs/components/checkbox/)\n- مقدارهای آزاد که در فهرست نیستند. (ورودی تگ: /docs/components/tag-input/)\n\nدرست و نادرست:\n\n- ✓ درست: اگر سقف دارد، `max` بدهید تا کاربر بداند و پیام «حداکثر ۳ مورد» بگیرد.\n- ✗ نادرست: بعد از ثبت فرم بگویید بیش از سه مورد مجاز نیست.\n\n## وب کامپوننت\n\nهمین کامپوننت بدون React، برای HTML، وردپرس، Vue، Svelte و Angular: `<kashi-multi-select>` در `@kashi-ui/elements`. گزینه‌ها همان `<option>` معمولی‌اند و `selected` انتخاب اولیه است؛ با `name` هر انتخاب یک فیلد هم‌نام در فرم است.\n\nنصب بدون ابزار ساخت (CDN):\n\n```html\n<link rel=\"stylesheet\" href=\"https://cdn.jsdelivr.net/npm/@kashi-ui/css/dist/kashi.css\">\n<script type=\"module\" src=\"https://cdn.jsdelivr.net/npm/@kashi-ui/elements/dist/cdn/kashi-elements.js\"></script>\n```\n\nیا با npm، فقط همین بخش:\n\n```bash\nnpm i @kashi-ui/elements @kashi-ui/css\n```\n\n```js\nimport \"@kashi-ui/css\";\nimport \"@kashi-ui/elements/forms.js\";\n```\n\n```html\n<kashi-multi-select label=\"مهارت‌ها\" name=\"skills\" max=\"4\" placeholder=\"جستجو…\">\n <option value=\"figma\" selected>فیگما</option>\n <option value=\"react\">ری‌اکت</option>\n <option value=\"css\" data-description=\"Cascade Layers\">CSS</option>\n <option value=\"a11y\">دسترس‌پذیری</option>\n</kashi-multi-select>\n```\n\nویژگی‌ها:\n\n| نام | مقدار | پیش‌فرض | توضیح |\n|---|---|---|---|\n| label / hint / error / placeholder | متن | — | مثل بقیهٔ فیلدها |\n| name | متن | — | یک فیلد پنهان برای هر انتخاب |\n| max | عدد | — | بیشترین تعداد انتخاب |\n| disabled | بولی | — | غیرفعال |\n\nرویدادها و JavaScript:\n\n| نام | توضیح |\n|---|---|\n| kashi-change | detail: { value: [...] } |\n| element.options / element.value | گزینه‌ها و انتخاب‌ها با JavaScript |"},{"slug":"components/number-input","url":"https://kashi.sepantapouya.com/docs/components/number-input/","title":"ورودی عدد","description":"فیلد عدد با دکمه‌های افزایش و کاهش، محدودهٔ مجاز و ارقام فارسی.","group":"کامپوننت‌ها","components":["NumberInput"],"markdown":"```tsx\nimport { NumberInput } from \"@kashi-ui/react\";\n```\n\n## نمونه\n\n```tsx\n<NumberInput label=\"تعداد مسافر\" min={1} max={9} value={n} onChange={setN} unit=\"نفر\" />\n<NumberInput label=\"بودجه\" min={0} step={100_000} defaultValue={1_500_000} thousands unit=\"تومان\" />\n```\n\nکاربر می‌تواند با ارقام فارسی یا انگلیسی تایپ کند. هنگام تایپ عدد محدود نمی‌شود تا عدد نیمه‌کاره پاک نشود؛ با خروج از فیلد به محدودهٔ `min` و `max` برمی‌گردد.\n\n| ویژگی | نوع | پیش‌فرض | توضیح |\n|---|---|---|---|\n| value / defaultValue | number \\| null | null | — |\n| onChange | (value: number \\| null) => void | — | — |\n| min / max | number | — | محدودهٔ مجاز |\n| step | number | 1 | گام دکمه‌ها و کلیدهای جهت |\n| thousands | boolean | false | جداکنندهٔ هزارگان |\n| digits | \"fa\" \\| \"latin\" | \"fa\" | ارقام نمایش |\n| unit | ReactNode | — | واحد کنار عدد |\n| labels | { increment?, decrement? } | «افزایش»، «کاهش» | نام دکمه‌ها |\n\n## کیبورد\n\n| کلید | عمل |\n|---|---|\n| <kbd>↑</kbd> / <kbd>↓</kbd> | یک گام بیشتر / کمتر |\n| <kbd>PageUp</kbd> / <kbd>PageDown</kbd> | ده گام |\n| <kbd>Home</kbd> / <kbd>End</kbd> | کمینه / بیشینه |\n\n## دسترس‌پذیری\n\nفیلد `role=\"spinbutton\"` با `aria-valuenow`، `aria-valuemin` و `aria-valuemax` است. دکمه‌های + و − از ترتیب Tab بیرون‌اند (کلیدهای جهت همان کار را می‌کنند) و در مرز محدوده غیرفعال می‌شوند.\n\n## راهنمای استفاده\n\nکِی استفاده کنیم:\n\n- عدد کوچک با گام مشخص: تعداد کالا، تعداد مسافر، سن.\n- جایی که دکمهٔ افزایش و کاهش از تایپ راحت‌تر است.\n\nکِی استفاده نکنیم:\n\n- مبلغ. (فیلد فارسی (مبلغ): /docs/persian/input/)\n- شمارهٔ تلفن، کد پستی یا کد ملی؛ این‌ها عدد نیستند و صفر اولشان مهم است. (فیلد فارسی: /docs/persian/input/)\n- محدودهٔ پیوسته مثل صدا. (اسلایدر: /docs/components/slider/)\n\nدرست و نادرست:\n\n- ✓ درست: `min` و `max` بدهید تا دکمه‌ها در مرز غیرفعال شوند.\n- ✗ نادرست: عدد منفی برای تعداد کالا بپذیرید و بعد خطا بدهید.\n\n## وب کامپوننت\n\nهمین کامپوننت بدون React، برای HTML، وردپرس، Vue، Svelte و Angular: `<kashi-number-input>` در `@kashi-ui/elements`.\n\nنصب بدون ابزار ساخت (CDN):\n\n```html\n<link rel=\"stylesheet\" href=\"https://cdn.jsdelivr.net/npm/@kashi-ui/css/dist/kashi.css\">\n<script type=\"module\" src=\"https://cdn.jsdelivr.net/npm/@kashi-ui/elements/dist/cdn/kashi-elements.js\"></script>\n```\n\nیا با npm، فقط همین بخش:\n\n```bash\nnpm i @kashi-ui/elements @kashi-ui/css\n```\n\n```js\nimport \"@kashi-ui/css\";\nimport \"@kashi-ui/elements/controls.js\";\n```\n\n```html\n<kashi-number-input label=\"تعداد\" name=\"qty\" value=\"1\" min=\"1\" max=\"10\"></kashi-number-input>\n```\n\nویژگی‌ها:\n\n| نام | مقدار | پیش‌فرض | توضیح |\n|---|---|---|---|\n| value / min / max / step | عدد | step: 1 | مقدار و محدوده |\n| thousands | بولی | — | جداکنندهٔ هزارگان |\n| unit | متن | — | واحد کنار فیلد |\n| label / name / hint / error / disabled | — | — | مثل بقیهٔ فیلدها |\n| digits | fa \\| latin | fa | خط ارقام |\n\nرویدادها و JavaScript:\n\n| نام | توضیح |\n|---|---|\n| kashi-change | detail: { value } |"},{"slug":"components/pagination","url":"https://kashi.sepantapouya.com/docs/components/pagination/","title":"صفحه‌بندی","description":"پیمایش بین صفحه‌های یک فهرست، با ارقام فارسی و «…» برای صفحه‌های زیاد.","group":"کامپوننت‌ها","components":["Pagination"],"markdown":"```tsx\nimport { Pagination } from \"@kashi-ui/react\";\n```\n\n## نمونه\n\n```tsx\nconst [page, setPage] = useState(4);\n\n<Pagination page={page} total={12} onChange={setPage} summary=\"صفحهٔ ۴ از ۱۲\" />\n\n// با لینک (بهتر برای موتورهای جستجو)\n<Pagination page={page} total={12} getHref={(p) => `?page=${p}`} />\n```\n\n## ویژگی‌ها\n\n| ویژگی | نوع | پیش‌فرض | توضیح |\n|---|---|---|---|\n| page / total | number | — | صفحهٔ فعلی و تعداد کل |\n| onChange | (page) => void | — | برای صفحه‌بندی با دکمه |\n| getHref | (page) => string | — | برای صفحه‌بندی با لینک |\n| siblings | number | 1 | تعداد صفحه‌های کنار صفحهٔ فعلی |\n| summary | ReactNode | — | متن خلاصه |\n| digits | \"fa\" \\| \"latin\" | \"fa\" | خط ارقام |\n| pageSize / pageSizes / onPageSizeChange | number / number[] / (size) => void | — / [10, 20, 50, 100] | فهرست «در هر صفحه» |\n| jumper | boolean | false | فیلد «برو به صفحه» (با onChange) |\n\n## تعداد در هر صفحه و پرش به صفحه\n\nبرای جدول‌های بلند: با `onPageSizeChange` فهرست «در هر صفحه» و با `jumper` فیلد «برو به صفحه» نمایش داده می‌شود. فیلد پرش ارقام فارسی و انگلیسی را می‌پذیرد.\n\n```tsx\n<Pagination page={page} total={total} onChange={setPage}\n pageSize={size} onPageSizeChange={setSize} pageSizes={[10, 20, 50, 100]}\n jumper summary=\"۴۱ تا ۶۰ از ۴۸۷\" />\n```\n\n## دسترس‌پذیری\n\n- داخل `<nav aria-label=\"صفحه‌بندی\">`؛ صفحهٔ فعلی `aria-current=\"page\"` و هر شماره نام کامل «صفحهٔ ۵» دارد.\n- «قبلی» در صفحهٔ اول و «بعدی» در صفحهٔ آخر غیرفعال‌اند. فوکوس کیبورد پس از تغییر صفحه حفظ می‌شود.\n\n## راهنمای استفاده\n\nکِی استفاده کنیم:\n\n- فهرست‌ها و جدول‌های بلند که کاربر می‌خواهد بداند کجای آن‌هاست: سفارش‌ها، تراکنش‌ها، نتیجهٔ جستجو.\n\nکِی استفاده نکنیم:\n\n- خبرنامه و شبکهٔ اجتماعی که کاربر فقط مرور می‌کند؛ بارگذاری بیشتر با دکمهٔ «نمایش بیشتر» ساده‌تر است.\n- کمتر از دو صفحه؛ صفحه‌بندی را پنهان کنید.\n\nدرست و نادرست:\n\n- ✓ درست: با `getHref` نشانی هر صفحه را بدهید تا قابل‌اشتراک و بازگشت با دکمهٔ مرورگر باشد.\n- ✗ نادرست: صفحه‌بندی فقط با state که با برگشتن به صفحه، کاربر را به صفحهٔ اول می‌برد.\n\n## وب کامپوننت\n\nهمین کامپوننت بدون React، برای HTML، وردپرس، Vue، Svelte و Angular: `<kashi-pagination>` در `@kashi-ui/elements`.\n\nنصب بدون ابزار ساخت (CDN):\n\n```html\n<link rel=\"stylesheet\" href=\"https://cdn.jsdelivr.net/npm/@kashi-ui/css/dist/kashi.css\">\n<script type=\"module\" src=\"https://cdn.jsdelivr.net/npm/@kashi-ui/elements/dist/cdn/kashi-elements.js\"></script>\n```\n\nیا با npm، فقط همین بخش:\n\n```bash\nnpm i @kashi-ui/elements @kashi-ui/css\n```\n\n```js\nimport \"@kashi-ui/css\";\nimport \"@kashi-ui/elements/controls.js\";\n```\n\n```html\n<kashi-pagination page=\"5\" total=\"20\"></kashi-pagination>\n```\n\nویژگی‌ها:\n\n| نام | مقدار | پیش‌فرض | توضیح |\n|---|---|---|---|\n| page / total | عدد | — | صفحهٔ فعلی و تعداد صفحه‌ها |\n| href | ?page={page} | — | لینک به‌جای دکمه |\n| siblings | عدد | 1 | تعداد صفحهٔ کنار صفحهٔ فعلی |\n| summary / label | متن | — | خلاصه کنار دکمه‌ها و نام برای صفحه‌خوان |\n\nرویدادها و JavaScript:\n\n| نام | توضیح |\n|---|---|\n| kashi-change | بدون href؛ detail: { page } |"},{"slug":"components/password-strength","url":"https://kashi.sepantapouya.com/docs/components/password-strength/","title":"قدرت رمز عبور","description":"نوار قدرت رمز و فهرست شرط‌ها زیر فیلد رمز، با هشدار صفحه‌کلید فارسی.","group":"کامپوننت‌ها","components":["PasswordStrength"],"markdown":"```tsx\nimport { PasswordStrength } from \"@kashi-ui/react\";\n```\n\n## نمونه\n\nاگر کاربر با صفحه‌کلید فارسی تایپ کند (مثلاً «سلام» به‌جای «sghl»)، هشدار داده می‌شود؛ اشتباه رایجی که کاربر فارسی‌زبان را پشت در ورود نگه می‌دارد.\n\n```tsx\n<Input label=\"رمز عبور\" type=\"password\" value={p} onChange={…} aria-describedby=\"pw-strength\" />\n<PasswordStrength id=\"pw-strength\" password={p} />\n```\n\n| ویژگی | نوع | پیش‌فرض | توضیح |\n|---|---|---|---|\n| password | string | — | رمز فعلی |\n| rules | { label, test }[] | defaultPasswordRules | شرط‌ها؛ امتیاز به نسبت شرط‌های برآورده‌شده |\n| showRules | boolean | true | نمایش فهرست شرط‌ها |\n| warnPersian | boolean | true | هشدار حروف فارسی در رمز |\n| id | string | — | برای aria-describedby فیلد رمز |\n| labels | { title?, levels?, persian? } | «ضعیف، متوسط، خوب، قوی» | — |\n\nاین کامپوننت فقط راهنمای کاربر است و جایگزین بررسی رمز در سرور نیست.\n\n## دسترس‌پذیری\n\nسطح قدرت با `aria-live` اعلام می‌شود و هر شرط برای صفحه‌خوان «رعایت شده» یا «رعایت نشده» دارد. با `id` و `aria-describedby`، صفحه‌خوان این راهنما را همراه فیلد رمز می‌خواند. نوار رنگی تزئینی است و متن سطح همیشه نمایش داده می‌شود.\n\n## راهنمای استفاده\n\nکِی استفاده کنیم:\n\n- زیر فیلد رمز در ثبت‌نام و تغییر رمز.\n\nکِی استفاده نکنیم:\n\n- فیلد ورود؛ آنجا کاربر رمز قبلی‌اش را می‌زند و قدرتش مهم نیست.\n\nدرست و نادرست:\n\n- ✓ درست: شرط‌ها را از اول نشان دهید و با هر تایپ تیک بخورند.\n- ✗ نادرست: بعد از ارسال فرم بگویید رمز ضعیف است.\n- ✓ درست: هشدار صفحه‌کلید فارسی (`warnPersian`) را روشن بگذارید؛ رمزی که با صفحه‌کلید فارسی تایپ شده، بعداً وارد نمی‌شود.\n- ✗ نادرست: شرط‌های عجیب مثل «حداکثر ۱۲ نویسه» که رمز امن‌تر را رد می‌کند.\n\n## وب کامپوننت\n\nهمین کامپوننت بدون React، برای HTML، وردپرس، Vue، Svelte و Angular: `<kashi-password-strength>` در `@kashi-ui/elements`. با `for` به فیلد رمز وصل می‌شود و با هر تایپ به‌روز می‌شود؛ اگر صفحه‌کلید روی فارسی مانده باشد هشدار می‌دهد.\n\nنصب بدون ابزار ساخت (CDN):\n\n```html\n<link rel=\"stylesheet\" href=\"https://cdn.jsdelivr.net/npm/@kashi-ui/css/dist/kashi.css\">\n<script type=\"module\" src=\"https://cdn.jsdelivr.net/npm/@kashi-ui/elements/dist/cdn/kashi-elements.js\"></script>\n```\n\nیا با npm، فقط همین بخش:\n\n```bash\nnpm i @kashi-ui/elements @kashi-ui/css\n```\n\n```js\nimport \"@kashi-ui/css\";\nimport \"@kashi-ui/elements/forms.js\";\n```\n\n```html\n<label class=\"k-label\" for=\"new-password\">رمز تازه</label>\n<div class=\"k-input\"><input id=\"new-password\" class=\"k-input__control\" type=\"password\" autocomplete=\"new-password\"></div>\n<kashi-password-strength for=\"new-password\"></kashi-password-strength>\n```\n\nویژگی‌ها:\n\n| نام | مقدار | پیش‌فرض | توضیح |\n|---|---|---|---|\n| for | شناسهٔ فیلد | — | فیلد رمز |\n| show-rules | false | — | بدون فهرست شرط‌ها |\n\nرویدادها و JavaScript:\n\n| نام | توضیح |\n|---|---|\n| element.score(password) | امتیاز ۰ تا ۴ |"},{"slug":"components/popover","url":"https://kashi.sepantapouya.com/docs/components/popover/","title":"پاپ‌اور","description":"محتوای تعاملی کوتاه کنار یک دکمه، مثل فیلتر، فرم کوچک یا توضیح.","group":"کامپوننت‌ها","components":["Popover"],"markdown":"```tsx\nimport { Popover } from \"@kashi-ui/react\";\n```\n\nبرخلاف `Tooltip`، داخل `Popover` می‌توان دکمه و فیلد گذاشت؛ برخلاف `Dialog`، بقیهٔ صفحه را مسدود نمی‌کند.\n\n## نمونه\n\n```tsx\n<Popover title=\"فیلتر سفارش‌ها\" trigger={<Button iconStart={<FilterIcon />}>فیلتر</Button>}>\n <Checkbox label=\"پرداخت‌شده\" />\n …\n</Popover>\n\n<Popover placement=\"bottom-end\" aria-label=\"دربارهٔ کارمزد\"\n trigger={<Button variant=\"tertiary\">کارمزد چطور محاسبه می‌شود؟</Button>}>\n کارمزد هر تراکنش ۱٪ مبلغ است.\n</Popover>\n```\n\n| ویژگی | نوع | پیش‌فرض | توضیح |\n|---|---|---|---|\n| trigger | ReactElement | — | دکمهٔ باز کردن |\n| title | ReactNode | — | عنوان و نام پنجره |\n| open / onOpenChange | boolean / (open) => void | — | حالت کنترل‌شده |\n| placement | \"bottom-start\" \\| \"bottom-end\" \\| \"top-start\" \\| \"top-end\" | \"bottom-start\" | جای پنجره |\n| aria-label | string | — | نام پنجره وقتی title ندارد |\n\n## دسترس‌پذیری\n\nدکمه `aria-haspopup=\"dialog\"` و `aria-expanded` می‌گیرد. با باز شدن، فوکوس به اولین عنصر داخل می‌رود؛ <kbd>Esc</kbd> می‌بندد و فوکوس را به دکمه برمی‌گرداند. کلیک بیرون یا رفتن فوکوس به بیرون هم پنجره را می‌بندد.\n\n## راهنمای استفاده\n\nکِی استفاده کنیم:\n\n- محتوای تعاملی کوتاه کنار یک دکمه: فیلتر، فرم دو سه فیلدی، انتخاب سریع.\n\nکِی استفاده نکنیم:\n\n- توضیح کوتاه بدون دکمه و فیلد. (تولتیپ: /docs/components/tooltip/)\n- فهرست عمل‌ها. (دراپ‌داون: /docs/components/dropdown-menu/)\n- کاری که تمرکز کامل می‌خواهد. (دیالوگ: /docs/components/dialog/)\n\nدرست و نادرست:\n\n- ✓ درست: محتوای کوتاه که با کلیک بیرون یا Escape بسته شود و فوکوس به دکمه برگردد.\n- ✗ نادرست: فرم بلند با اسکرول داخل پاپ‌اور.\n\n## وب کامپوننت\n\nهمین کامپوننت بدون React، برای HTML، وردپرس، Vue، Svelte و Angular: `<kashi-popover>` در `@kashi-ui/elements`. اولین فرزند دکمهٔ بازکننده است و بقیه محتوای پنجره.\n\nنصب بدون ابزار ساخت (CDN):\n\n```html\n<link rel=\"stylesheet\" href=\"https://cdn.jsdelivr.net/npm/@kashi-ui/css/dist/kashi.css\">\n<script type=\"module\" src=\"https://cdn.jsdelivr.net/npm/@kashi-ui/elements/dist/cdn/kashi-elements.js\"></script>\n```\n\nیا با npm، فقط همین بخش:\n\n```bash\nnpm i @kashi-ui/elements @kashi-ui/css\n```\n\n```js\nimport \"@kashi-ui/css\";\nimport \"@kashi-ui/elements/menu.js\";\n```\n\n```html\n<kashi-popover heading=\"فیلتر قیمت\">\n <button class=\"k-btn k-btn--secondary\">فیلتر</button>\n <kashi-price label=\"بیشترین قیمت\"></kashi-price>\n</kashi-popover>\n```\n\nویژگی‌ها:\n\n| نام | مقدار | پیش‌فرض | توضیح |\n|---|---|---|---|\n| heading | متن | — | عنوان پنجره |\n| placement | bottom-start \\| bottom-end \\| top-start \\| top-end | bottom-start | جای پنجره |\n| label | متن | — | نام برای صفحه‌خوان وقتی عنوان ندارد |\n\nرویدادها و JavaScript:\n\n| نام | توضیح |\n|---|---|\n| element.show() / close() | باز و بستن |\n| kashi-open / kashi-close | باز و بسته شدن |"},{"slug":"components/progress","url":"https://kashi.sepantapouya.com/docs/components/progress/","title":"نوار پیشرفت","description":"نمایش پیشرفت یک کار طولانی، به‌همراه اسپینر برای انتظارهای کوتاه.","group":"کامپوننت‌ها","components":["Progress","Spinner"],"markdown":"```tsx\nimport { Progress, Spinner } from \"@kashi-ui/react\";\n```\n\n## نمونه\n\n```tsx\n<Progress value={35} label=\"آپلود گزارش.pdf\" showValue />\n<Progress value={100} tone=\"success\" label=\"پشتیبان‌گیری\" showValue size=\"sm\" />\n<Progress aria-label=\"در حال پردازش\" size=\"sm\" /> // بدون value: نامعین\n<Spinner size=\"sm\" />\n```\n\n## دایره‌ای\n\n```tsx\n<Progress variant=\"circular\" value={72} showValue aria-label=\"پیشرفت پروژه\" />\n<Progress variant=\"circular\" size=\"sm\" aria-label=\"در حال پردازش\" />\n```\n\n`variant=\"circular\"` با همان `value`، `size`، `tone` و `showValue`. متغیرهای CSS: `--k-progress-height`، `--k-progress-color`، `--k-progress-ring-size`.\n\n## ویژگی‌ها\n\n| ویژگی | نوع | پیش‌فرض | توضیح |\n|---|---|---|---|\n| value | number | — | مقدار؛ بدون آن نوار نامعین است |\n| max | number | 100 | حداکثر |\n| label | ReactNode | — | برچسب؛ اگر نمی‌دهید aria-label بدهید |\n| showValue | boolean | false | نمایش درصد |\n| size | \"sm\" \\| \"md\" \\| \"lg\" | \"md\" | ضخامت |\n| tone | \"brand\" \\| \"success\" \\| \"warning\" \\| \"error\" | \"brand\" | رنگ |\n\n## دسترس‌پذیری\n\n- `role=\"progressbar\"` با `aria-valuenow` و `aria-valuetext` فارسی («۳۵٪»)؛ در حالت نامعین `aria-valuenow` حذف می‌شود.\n- اسپینر `role=\"status\"` با متن «در حال بارگذاری» برای صفحه‌خوان دارد. با `prefers-reduced-motion` انیمیشن‌ها کند یا متوقف می‌شوند.\n\n## راهنمای استفاده\n\nکِی استفاده کنیم:\n\n- کار طولانی با پیشرفت مشخص: بارگذاری فایل، پردازش، درون‌ریزی.\n- اسپینر برای انتظار کوتاه و نامعلوم، مثل ارسال فرم.\n\nکِی استفاده نکنیم:\n\n- مقدار مصرف از سقف. (گیج: /docs/components/meter/)\n- بارگذاری محتوای صفحه؛ اسکلتون پرش کمتری دارد. (اسکلتون: /docs/components/skeleton/)\n- مراحل یک فرایند. (استپر: /docs/components/stepper/)\n\nدرست و نادرست:\n\n- ✓ درست: اگر درصد را می‌دانید، `value` بدهید؛ نوار نامعلوم فقط وقتی که واقعاً نمی‌دانید.\n- ✗ نادرست: نوار نامعلومی که سه دقیقه بی‌پایان می‌چرخد."},{"slug":"components/radio","url":"https://kashi.sepantapouya.com/docs/components/radio/","title":"رادیو","description":"انتخاب دقیقاً یک گزینه از چند گزینه؛ به شکل فهرست، افقی یا کارت‌های قابل‌انتخاب.","group":"کامپوننت‌ها","components":[],"markdown":"```tsx\n```\n\n## فهرست\n\n```tsx\n<RadioGroup label=\"روش ارسال\" name=\"shipping\" defaultValue=\"post\">\n <Radio value=\"post\" label=\"پست پیشتاز\" description=\"۳ تا ۵ روز کاری\" />\n <Radio value=\"courier\" label=\"پیک\" description=\"همان روز، فقط تهران\" />\n <Radio value=\"pickup\" label=\"تحویل حضوری\" disabled />\n</RadioGroup>\n```\n\n## افقی\n\n```tsx\n<RadioGroup label=\"اندازه\" variant=\"inline\" name=\"size\" defaultValue=\"m\">\n <Radio value=\"s\" label=\"کوچک\" />\n <Radio value=\"m\" label=\"متوسط\" />\n <Radio value=\"l\" label=\"بزرگ\" />\n</RadioGroup>\n```\n\n## کارت‌های قابل‌انتخاب\n\nبرای گزینه‌هایی که توضیح بیشتری لازم دارند، مثل انتخاب پلن.\n\n```tsx\n<RadioGroup label=\"پلن اشتراک\" variant=\"cards\" value={plan} onChange={setPlan}>\n <Radio value=\"basic\" label=\"پایه\" description=\"۱ کاربر · ۹۰ هزار تومان در ماه\" />\n <Radio value=\"pro\" label=\"حرفه‌ای\" description=\"تا ۲۰ کاربر · ۴۹۰ هزار تومان در ماه\" />\n <Radio value=\"org\" label=\"سازمانی\" description=\"کاربر نامحدود · تماس بگیرید\" />\n</RadioGroup>\n```\n\n## ویژگی‌های RadioGroup\n\n| ویژگی | نوع | پیش‌فرض | توضیح |\n|---|---|---|---|\n| label | ReactNode | — | عنوان گروه (legend) — الزامی |\n| name | string | خودکار | نام فیلد فرم |\n| value / defaultValue | string | — | گزینهٔ انتخاب‌شده |\n| onChange | (value: string) => void | — | با تغییر انتخاب |\n| variant | \"list\" \\| \"inline\" \\| \"cards\" | \"list\" | چیدمان |\n| hint / error | ReactNode | — | راهنما و خطای گروه |\n| required / disabled | boolean | false | الزامی یا غیرفعال کردن کل گروه |\n\n## دسترس‌پذیری\n\n- گروه در `<fieldset>` با `<legend>` است؛ صفحه‌خوان عنوان گروه را پیش از هر گزینه می‌خواند.\n- رادیوها بومی‌اند: <kbd>Tab</kbd> وارد گروه می‌شود و کلیدهای جهت‌دار بین گزینه‌ها جابه‌جا می‌کنند.\n- اگر کاربر باید چند گزینه انتخاب کند، از [چک‌باکس](/docs/components/checkbox/) استفاده کنید.\n\n## راهنمای استفاده\n\nکِی استفاده کنیم:\n\n- انتخاب دقیقاً یکی از دو تا هفت گزینه که کاربر باید همه را ببیند: روش ارسال، روش پرداخت.\n- `variant=\"cards\"` وقتی هر گزینه توضیح یا قیمت دارد.\n\nکِی استفاده نکنیم:\n\n- بیش از هفت گزینه. (سلکت: /docs/components/select/)\n- روشن و خاموش یک تنظیم. (سوییچ: /docs/components/switch/)\n- انتخاب چند گزینه. (چک‌باکس: /docs/components/checkbox/)\n\nدرست و نادرست:\n\n- ✓ درست: گزینهٔ پیش‌فرضِ رایج را از اول انتخاب کنید تا کاربر کمتر کار کند.\n- ✗ نادرست: پیش‌فرضی که به نفع کسب‌وکار است نه کاربر، مثل بیمهٔ اختیاری از پیش انتخاب‌شده."},{"slug":"components/rating","url":"https://kashi.sepantapouya.com/docs/components/rating/","title":"امتیاز ستاره‌ای","description":"دریافت یا نمایش امتیاز ۱ تا ۵، با نیم‌ستاره در حالت نمایش.","group":"کامپوننت‌ها","components":["Rating"],"markdown":"```tsx\nimport { Rating } from \"@kashi-ui/react\";\n```\n\n## نمونه\n\n```tsx\n<Rating label=\"امتیاز شما به این محصول\" value={v} onChange={setV} showValue />\n\n<Rating value={4.5} readOnly size=\"sm\" showValue />\n```\n\n| ویژگی | نوع | پیش‌فرض | توضیح |\n|---|---|---|---|\n| value / defaultValue | number | 0 | امتیاز؛ در readOnly اعشار هم می‌پذیرد |\n| onChange | (value: number) => void | — | — |\n| max | number | 5 | تعداد ستاره‌ها |\n| readOnly | boolean | false | فقط نمایش |\n| showValue | boolean | false | عدد کنار ستاره‌ها |\n| size | \"sm\" \\| \"md\" \\| \"lg\" | \"md\" | — |\n| label | string | «امتیاز» | نام گروه |\n| starLabel | (n, max) => string | «n از max» | نام هر ستاره |\n\n## دسترس‌پذیری\n\nحالت قابل‌انتخاب یک `fieldset` با رادیوهای بومی است: <kbd>←</kbd>/<kbd>→</kbd> امتیاز را عوض می‌کند و هر ستاره نام «۳ از ۵» دارد. حالت فقط‌خواندنی یک تصویر با نام کامل است («امتیاز: ۴٫۵ از ۵») تا صفحه‌خوان ستاره‌ها را یکی‌یکی نخواند.\n\n## راهنمای استفاده\n\nکِی استفاده کنیم:\n\n- امتیاز دادن به محصول، سفارش یا پشتیبانی.\n- نمایش میانگین امتیاز با `readOnly` و نیم‌ستاره.\n\nکِی استفاده نکنیم:\n\n- نظرسنجی دقیق با معیارهای مختلف؛ پرسش‌های جدا با رادیو روشن‌ترند. (رادیو: /docs/components/radio/)\n\nدرست و نادرست:\n\n- ✓ درست: کنار میانگین تعداد امتیازها را هم بنویسید: «۴٫۵ از ۱۲۸ نظر».\n- ✗ نادرست: پنج ستاره بدون اینکه بگویید از چند نظر آمده.\n\n## وب کامپوننت\n\nهمین کامپوننت بدون React، برای HTML، وردپرس، Vue، Svelte و Angular: `<kashi-rating>` در `@kashi-ui/elements`.\n\nنصب بدون ابزار ساخت (CDN):\n\n```html\n<link rel=\"stylesheet\" href=\"https://cdn.jsdelivr.net/npm/@kashi-ui/css/dist/kashi.css\">\n<script type=\"module\" src=\"https://cdn.jsdelivr.net/npm/@kashi-ui/elements/dist/cdn/kashi-elements.js\"></script>\n```\n\nیا با npm، فقط همین بخش:\n\n```bash\nnpm i @kashi-ui/elements @kashi-ui/css\n```\n\n```js\nimport \"@kashi-ui/css\";\nimport \"@kashi-ui/elements/controls.js\";\n```\n\n```html\n<kashi-rating name=\"stars\" value=\"4\" show-value></kashi-rating>\n<kashi-rating value=\"3.5\" readonly></kashi-rating>\n```\n\nویژگی‌ها:\n\n| نام | مقدار | پیش‌فرض | توضیح |\n|---|---|---|---|\n| value / max | عدد | 0 / 5 | امتیاز و تعداد ستاره |\n| readonly | بولی | — | فقط نمایش، با نیم‌ستاره |\n| show-value | بولی | — | عدد امتیاز کنار ستاره‌ها |\n| name / label / size / disabled | — | — | نام فرم، نام صفحه‌خوان و اندازه |\n\nرویدادها و JavaScript:\n\n| نام | توضیح |\n|---|---|\n| kashi-change | detail: { value } |"},{"slug":"components/segmented-control","url":"https://kashi.sepantapouya.com/docs/components/segmented-control/","title":"سگمنت","description":"انتخاب یکی از چند گزینهٔ کوتاه کنار هم، مثل حالت نمایش یا بازهٔ زمانی گزارش.","group":"کامپوننت‌ها","components":["SegmentedControl"],"markdown":"```tsx\nimport { SegmentedControl } from \"@kashi-ui/react\";\n```\n\nبرای دو تا پنج گزینهٔ کوتاه که نتیجه‌شان بلافاصله دیده می‌شود. برای گزینه‌های بیشتر یا فرم‌ها از `RadioGroup` یا `Select` استفاده کنید؛ برای جابه‌جایی بین محتواهای مختلف از `Tabs`.\n\n## نمونه\n\n```tsx\n<SegmentedControl aria-label=\"نمایش\" value={view} onChange={setView} options={[\n { value: \"list\", label: \"فهرست\", icon: <ListIcon /> },\n { value: \"grid\", label: \"کارت\", icon: <GridIcon /> },\n]} />\n\n<SegmentedControl aria-label=\"بازهٔ زمانی\" size=\"sm\" defaultValue=\"30\" options={[\n { value: \"1\", label: \"امروز\" }, { value: \"7\", label: \"۷ روز\" },\n { value: \"30\", label: \"۳۰ روز\" }, { value: \"365\", label: \"یک سال\" },\n]} />\n```\n\n| ویژگی | نوع | پیش‌فرض | توضیح |\n|---|---|---|---|\n| options | { value, label, icon?, disabled?, aria-label? }[] | — | گزینه‌ها |\n| value / defaultValue | string | اولین گزینهٔ فعال | — |\n| onChange | (value: string) => void | — | — |\n| aria-label / aria-labelledby | string | — | نام گروه (الزامی) |\n| size | \"sm\" \\| \"md\" | \"md\" | هم‌قد دکمه‌ها: ۳۶ و ۴۰ پیکسل |\n| block | boolean | false | تمام‌عرض با بخش‌های هم‌اندازه |\n| name | string | خودکار | نام رادیوها برای ارسال فرم |\n\n## دسترس‌پذیری\n\nروی رادیوهای بومی ساخته شده: <kbd>Tab</kbd> وارد گروه می‌شود و <kbd>←</kbd>/<kbd>→</kbd> گزینه را عوض می‌کند. گروه `role=\"radiogroup\"` با نام دارد و گزینهٔ انتخاب‌شده با سطح برجسته و سایه، نه فقط رنگ، مشخص است. اگر گزینه فقط آیکون دارد، `aria-label` بدهید.\n\n## راهنمای استفاده\n\nکِی استفاده کنیم:\n\n- انتخاب یکی از دو تا پنج گزینهٔ کوتاه که نمای همان بخش را عوض می‌کند: «روز / هفته / ماه»، «جدول / کارت».\n\nکِی استفاده نکنیم:\n\n- عوض کردن کل محتوای پایین با عنوان‌های جدا. (زبانه‌ها: /docs/components/tabs/)\n- گزینه‌های فرم که ثبت می‌شوند. (رادیو: /docs/components/radio/)\n- عمل‌ها. (گروه دکمه: /docs/components/button-group/)\n\nدرست و نادرست:\n\n- ✓ درست: برچسب‌های یک یا دو کلمه‌ای هم‌اندازه.\n- ✗ نادرست: گزینه‌های بلند که در موبایل به دو خط می‌شکنند."},{"slug":"components/select","url":"https://kashi.sepantapouya.com/docs/components/select/","title":"سلکت","description":"انتخاب یک گزینه از فهرست بلند، روی select بومی مرورگر.","group":"کامپوننت‌ها","components":["Select"],"markdown":"```tsx\nimport { Select } from \"@kashi-ui/react\";\n```\n\n## نمونه\n\n```tsx\n<Select\n label=\"استان\"\n placeholder=\"انتخاب کنید\"\n hint=\"برای محاسبهٔ هزینهٔ ارسال\"\n options={[\n { value: \"thr\", label: \"تهران\" },\n { value: \"isf\", label: \"اصفهان\" },\n { value: \"frs\", label: \"فارس\" },\n { value: \"krm\", label: \"کرمان\" },\n ]}\n/>\n```\n\n## خطا و اندازه‌ها\n\n```tsx\n<Select label=\"نقش\" error=\"یک نقش انتخاب کنید.\" placeholder=\"انتخاب کنید\" required>\n <option value=\"admin\">مدیر</option>\n <option value=\"editor\">ویرایشگر</option>\n</Select>\n<Select label=\"مرتب‌سازی\" size=\"sm\" defaultValue=\"new\">\n <option value=\"new\">جدیدترین</option>\n <option value=\"price\">ارزان‌ترین</option>\n</Select>\n```\n\n## چرا select بومی؟\n\nفهرست بومی مرورگر در گوشی انتخاب‌گر سیستم‌عامل را باز می‌کند (که برای انگشت ساخته شده)، با صفحه‌خوان‌ها بی‌نقص کار می‌کند و در فرم‌ها بدون کد اضافه ارسال می‌شود. کاشی فقط ظاهر آن را هماهنگ می‌کند.\n\n## ویژگی‌ها\n\n| ویژگی | نوع | پیش‌فرض | توضیح |\n|---|---|---|---|\n| label / hint / error | ReactNode | — | مثل Input |\n| placeholder | string | — | گزینهٔ خالی اول که قابل‌انتخاب نیست |\n| options | { value, label, disabled? }[] | — | گزینه‌ها؛ یا <option> به‌عنوان children |\n| size | \"sm\" \\| \"md\" \\| \"lg\" | \"md\" | ارتفاع |\n\n## راهنمای استفاده\n\nکِی استفاده کنیم:\n\n- انتخاب یکی از هشت تا حدود سی گزینه که کاربر مرورشان می‌کند.\n- فرم‌هایی که روی موبایل باید انتخاب‌گر بومی گوشی باز شود.\n\nکِی استفاده نکنیم:\n\n- کمتر از هشت گزینه که بهتر است همه دیده شوند. (رادیو: /docs/components/radio/)\n- فهرست خیلی بلند که باید جستجو شود. (کامبوباکس: /docs/components/combobox/)\n- استان و شهر. (انتخاب استان: /docs/persian/province/)\n\nدرست و نادرست:\n\n- ✓ درست: گزینه‌ها به ترتیبی که کاربر انتظار دارد: الفبایی، یا پرکاربردها اول.\n- ✗ نادرست: ترتیب تصادفی یا ترتیب پایگاه داده."},{"slug":"components/side-nav","url":"https://kashi.sepantapouya.com/docs/components/side-nav/","title":"سایدبار","description":"منوی کناری پنل مدیریت با گروه‌ها، زیرمنو، نشان و حالت جمع‌شده.","group":"کامپوننت‌ها","components":["SideNav"],"markdown":"```tsx\nimport { SideNav } from \"@kashi-ui/react\";\n```\n\n## نمونه\n\n```tsx\n<SideNav\n collapsed={collapsed}\n header={<Logo />}\n sections={[\n { items: [\n { label: \"داشبورد\", href: \"/\", icon: <HomeIcon />, current: true },\n { label: \"صندوق ورودی\", href: \"/inbox\", icon: <InboxIcon />, badge: <Badge size=\"sm\" tone=\"brand\">۱۲</Badge> },\n { label: \"گزارش‌ها\", icon: <ChartIcon />, items: [\n { label: \"فروش\", href: \"/reports/sales\" },\n { label: \"کاربران\", href: \"/reports/users\" },\n ] },\n ] },\n { label: \"مدیریت\", items: [{ label: \"تیم\", href: \"/team\", icon: <UsersIcon /> }] },\n ]}\n footer={<UserMenu />}\n/>\n```\n\n| ویژگی | نوع | پیش‌فرض | توضیح |\n|---|---|---|---|\n| sections | { label?, items: SideNavItem[] }[] | — | گروه‌های منو |\n| SideNavItem | { label, href?, icon?, badge?, current?, onClick?, items? } | — | با items، زیرمنوی باز و بسته‌شونده |\n| collapsed | boolean | false | فقط آیکون‌ها |\n| header / footer | ReactNode | — | بالا و پایین منو |\n| aria-label | string | «اصلی» | نام ناحیهٔ ناوبری |\n\nعرض با متغیر `--k-sidenav-width` (پیش‌فرض ۱۷rem) عوض می‌شود.\n\n## دسترس‌پذیری\n\nناحیهٔ `<nav>` با نام «اصلی». صفحهٔ فعلی `aria-current=\"page\"` دارد و زیرمنویی که صفحهٔ فعلی در آن است از اول باز است. والد زیرمنو دکمه‌ای با `aria-expanded` است. در حالت جمع‌شده، نام هر مورد با `aria-label` و `title` حفظ می‌شود.\n\n## راهنمای استفاده\n\nکِی استفاده کنیم:\n\n- ناوبری اصلی پنل مدیریت و داشبورد با چند بخش.\n- گروه‌بندی صفحه‌ها با عنوان و زیرمنو.\n\nکِی استفاده نکنیم:\n\n- سایت محتوایی کم‌صفحه؛ منوی بالای صفحه کافی است.\n- زیرمنوهای بیش از دو سطح؛ ساختار را ساده‌تر کنید. (نمای درختی: /docs/components/tree-view/)\n\nدرست و نادرست:\n\n- ✓ درست: صفحهٔ فعلی با `current` مشخص شود و در حالت جمع‌شده نام‌ها در تولتیپ بیایند.\n- ✗ نادرست: حالت جمع‌شده با آیکون‌های بی‌نام که فقط کاربر قدیمی می‌شناسدشان.\n\n## وب کامپوننت\n\nهمین کامپوننت بدون React، برای HTML، وردپرس، Vue، Svelte و Angular: `<kashi-side-nav>` در `@kashi-ui/elements`. همان HTML منوی کناری (تب HTML بالای صفحه) را داخلش بگذارید؛ زیرمنوها با کلیک باز و بسته می‌شوند.\n\nنصب بدون ابزار ساخت (CDN):\n\n```html\n<link rel=\"stylesheet\" href=\"https://cdn.jsdelivr.net/npm/@kashi-ui/css/dist/kashi.css\">\n<script type=\"module\" src=\"https://cdn.jsdelivr.net/npm/@kashi-ui/elements/dist/cdn/kashi-elements.js\"></script>\n```\n\nیا با npm، فقط همین بخش:\n\n```bash\nnpm i @kashi-ui/elements @kashi-ui/css\n```\n\n```js\nimport \"@kashi-ui/css\";\nimport \"@kashi-ui/elements/nav.js\";\n```\n\n```html\n<kashi-side-nav>\n <nav class=\"k-sidenav\" aria-label=\"اصلی\">\n <ul class=\"k-sidenav__group\" role=\"list\">\n <li><a class=\"k-sidenav__item\" href=\"#\" aria-current=\"page\"><span class=\"k-sidenav__text\">داشبورد</span></a></li>\n <li><button type=\"button\" class=\"k-sidenav__item\" aria-expanded=\"false\" aria-controls=\"nav-orders\"><span class=\"k-sidenav__text\">سفارش‌ها</span></button>\n <ul class=\"k-sidenav__sub\" id=\"nav-orders\" role=\"list\" hidden>\n <li><a class=\"k-sidenav__item\" href=\"#\"><span class=\"k-sidenav__text\">همه</span></a></li>\n <li><a class=\"k-sidenav__item\" href=\"#\"><span class=\"k-sidenav__text\">در انتظار</span></a></li>\n </ul>\n </li>\n </ul>\n </nav>\n</kashi-side-nav>\n```\n\nویژگی‌ها:\n\n| نام | مقدار | پیش‌فرض | توضیح |\n|---|---|---|---|\n| collapsed | بولی | — | فقط آیکون‌ها، با نام در راهنما |\n\nرویدادها و JavaScript:\n\n| نام | توضیح |\n|---|---|\n| kashi-toggle | باز و بسته شدن زیرمنو؛ detail: { id, open } |"},{"slug":"components/skeleton","url":"https://kashi.sepantapouya.com/docs/components/skeleton/","title":"اسکلتون","description":"جای خالی محتوا هنگام بارگذاری؛ از پرش صفحه جلوگیری می‌کند.","group":"کامپوننت‌ها","components":["Skeleton"],"markdown":"```tsx\nimport { Skeleton } from \"@kashi-ui/react\";\n```\n\n## نمونه\n\n```tsx\n<div aria-busy=\"true\" aria-label=\"در حال بارگذاری\">\n <Skeleton circle width={40} />\n <Skeleton lines={2} />\n <Skeleton height={120} />\n <Skeleton lines={3} />\n</div>\n```\n\n## ویژگی‌ها\n\n| ویژگی | نوع | پیش‌فرض | توضیح |\n|---|---|---|---|\n| width / height | string \\| number | 100% / 1em | اندازه |\n| circle | boolean | false | شکل دایره |\n| lines | number | — | چند خط متن؛ خط آخر کوتاه‌تر است |\n\n## دسترس‌پذیری\n\nاسکلت تزئینی است (`aria-hidden`). روی ظرف بارگذاری `aria-busy=\"true\"` و یک `aria-label` بگذارید تا صفحه‌خوان بداند محتوا در راه است. با `prefers-reduced-motion` درخشش متوقف می‌شود.\n\n## راهنمای استفاده\n\nکِی استفاده کنیم:\n\n- محتوای صفحه یا کارت هنگام بارگذاری، به همان شکل محتوای نهایی.\n- جلوگیری از پرش صفحه وقتی داده می‌رسد.\n\nکِی استفاده نکنیم:\n\n- انتظار کوتاه یک عمل مثل ارسال فرم. (اسپینر: /docs/components/progress/)\n- بیش از چند ثانیه؛ اگر بارگذاری طول می‌کشد، بگویید چه خبر است.\n\nدرست و نادرست:\n\n- ✓ درست: اسکلتون همان چیدمان محتوای واقعی: عکس، عنوان، دو خط متن.\n- ✗ نادرست: یک مستطیل خاکستری بزرگ که هیچ شباهتی به صفحه ندارد."},{"slug":"components/slider","url":"https://kashi.sepantapouya.com/docs/components/slider/","title":"اسلایدر","description":"انتخاب یک عدد یا یک بازه با کشیدن، مثل صدا، روشنایی یا بازهٔ قیمت.","group":"کامپوننت‌ها","components":["Slider"],"markdown":"```tsx\nimport { Slider } from \"@kashi-ui/react\";\n```\n\n## مقدار تکی و بازه\n\nبرای بازه، به‌جای عدد یک آرایهٔ دوتایی بدهید؛ دستگیره‌ها از روی هم رد نمی‌شوند.\n\n```tsx\n<Slider label=\"روشنایی\" value={v} onChange={setV} format={(n) => `${toFaDigits(n)}٪`} />\n\n<Slider label=\"قیمت\" min={0} max={10_000_000} step={500_000}\n value={price} onChange={setPrice}\n format={(n) => `${formatNumber(n)} تومان`}\n marks={[0, 5_000_000, 10_000_000]} />\n```\n\n| ویژگی | نوع | پیش‌فرض | توضیح |\n|---|---|---|---|\n| value / defaultValue | number \\| [number, number] | — | آرایه برای بازه |\n| onChange | (value) => void | — | — |\n| min / max / step | number | 0 / 100 / 1 | — |\n| format | (n: number) => string | formatNumber | قالب نمایش و متن صفحه‌خوان |\n| showValue | boolean | true | مقدار کنار برچسب |\n| marks | number[] | — | برچسب‌های زیر نوار |\n| name | string | — | در بازه: name_min و name_max |\n| labels | { from?, to?, separator? } | «حداقل»، «حداکثر» | — |\n\n## دسترس‌پذیری\n\nروی `<input type=\"range\">` بومی ساخته شده، پس همهٔ کلیدهای استاندارد (<kbd>←</kbd>/<kbd>→</kbd>، <kbd>PageUp</kbd>، <kbd>Home</kbd>/<kbd>End</kbd>) کار می‌کنند. `aria-valuetext` مقدار قالب‌بندی‌شده را می‌خواند («۲٬۰۰۰٬۰۰۰ تومان») و در بازه، هر دستگیره نام جدا دارد: «حداقل قیمت» و «حداکثر قیمت».\n\n## راهنمای استفاده\n\nکِی استفاده کنیم:\n\n- مقدار تقریبی در یک محدوده: صدا، روشنایی، بازهٔ قیمت در فیلتر.\n\nکِی استفاده نکنیم:\n\n- عدد دقیق؛ کشیدن تا عدد دقیق سخت است. (ورودی عدد: /docs/components/number-input/)\n- مبلغ پرداخت. (فیلد فارسی (مبلغ): /docs/persian/input/)\n\nدرست و نادرست:\n\n- ✓ درست: مقدار فعلی را کنار برچسب بنویسید (`showValue`) تا کاربر بداند کجاست.\n- ✗ نادرست: اسلایدر بدون عدد که کاربر حدس بزند ۴۰ است یا ۶۰.\n\n## وب کامپوننت\n\nهمین کامپوننت بدون React، برای HTML، وردپرس، Vue، Svelte و Angular: `<kashi-slider>` در `@kashi-ui/elements`. روی `input type=range` بومی؛ با `range` دو دستگیره و دو فیلد `name_min` و `name_max` در فرم.\n\nنصب بدون ابزار ساخت (CDN):\n\n```html\n<link rel=\"stylesheet\" href=\"https://cdn.jsdelivr.net/npm/@kashi-ui/css/dist/kashi.css\">\n<script type=\"module\" src=\"https://cdn.jsdelivr.net/npm/@kashi-ui/elements/dist/cdn/kashi-elements.js\"></script>\n```\n\nیا با npm، فقط همین بخش:\n\n```bash\nnpm i @kashi-ui/elements @kashi-ui/css\n```\n\n```js\nimport \"@kashi-ui/css\";\nimport \"@kashi-ui/elements/forms.js\";\n```\n\n```html\n<kashi-slider label=\"حجم صدا\" value=\"40\" name=\"volume\"></kashi-slider>\n<kashi-slider label=\"قیمت\" range min=\"0\" max=\"1000000\" step=\"50000\" value=\"200000, 750000\" unit=\"تومان\" name=\"price\"></kashi-slider>\n```\n\nویژگی‌ها:\n\n| نام | مقدار | پیش‌فرض | توضیح |\n|---|---|---|---|\n| label | متن | — | برچسب |\n| min / max / step | عدد | 0 / 100 / 1 | محدوده و گام |\n| value | عدد یا «کمینه، بیشینه» | — | مقدار اولیه |\n| range | بولی | — | انتخاب بازه با دو دستگیره |\n| unit / marks | متن / «0, 50, 100» | — | واحد کنار عدد و علامت‌های زیر نوار |\n| show-value | false | — | پنهان کردن عدد بالای نوار |\n\nرویدادها و JavaScript:\n\n| نام | توضیح |\n|---|---|\n| kashi-change | detail: { value }؛ در range آرایهٔ [کمینه، بیشینه] |"},{"slug":"components/splitter","url":"https://kashi.sepantapouya.com/docs/components/splitter/","title":"اسپلیتر","description":"دو پنل کنار هم یا زیر هم که کاربر اندازه‌شان را با کشیدن مرز تغییر می‌دهد.","group":"کامپوننت‌ها","components":["Splitter"],"markdown":"```tsx\nimport { Splitter } from \"@kashi-ui/react\";\n```\n\n## نمونه\n\n```tsx\n<Splitter label=\"اندازهٔ فهرست فایل‌ها\" defaultSize={35} height=\"16rem\">\n <FileList />\n <Preview />\n</Splitter>\n```\n\n| ویژگی | نوع | پیش‌فرض | توضیح |\n|---|---|---|---|\n| children | [ReactNode, ReactNode] | — | پنل ابتدا و پنل انتها |\n| orientation | \"horizontal\" \\| \"vertical\" | \"horizontal\" | کنار هم یا زیر هم |\n| size / defaultSize | number | 40 | اندازهٔ پنل اول به درصد |\n| onSizeChange | (percent) => void | — | برای ذخیرهٔ اندازه |\n| min / max | number | 15 / 85 | محدودهٔ درصد |\n| label | string | «تغییر اندازهٔ پنل‌ها» | نام دستگیره |\n\n## دسترس‌پذیری\n\nدستگیره یک `role=\"separator\"` فوکوس‌پذیر با `aria-valuenow` است. با ←→ (یا ↑↓ در حالت عمودی) پنج درصد، و با <kbd>Home</kbd>/<kbd>End</kbd> به کمینه و بیشینه می‌رود. دوبار کلیک اندازهٔ اولیه را برمی‌گرداند.\n\n## راهنمای استفاده\n\nکِی استفاده کنیم:\n\n- دو پنل کاری کنار هم که کاربر نسبتشان را تنظیم می‌کند: فهرست و جزئیات، کد و پیش‌نمایش.\n\nکِی استفاده نکنیم:\n\n- صفحه‌های معمولی سایت؛ چیدمان ثابت ساده‌تر است.\n- موبایل؛ آنجا پنل‌ها را زیر هم بگذارید.\n\nدرست و نادرست:\n\n- ✓ درست: `min` و `max` بدهید تا هیچ پنلی آن‌قدر کوچک نشود که بی‌استفاده شود.\n- ✗ نادرست: پنلی که تا صفر کوچک می‌شود و کاربر نمی‌فهمد کجا رفت.\n\n## وب کامپوننت\n\nهمین کامپوننت بدون React، برای HTML، وردپرس، Vue، Svelte و Angular: `<kashi-splitter>` در `@kashi-ui/elements`.\n\nنصب بدون ابزار ساخت (CDN):\n\n```html\n<link rel=\"stylesheet\" href=\"https://cdn.jsdelivr.net/npm/@kashi-ui/css/dist/kashi.css\">\n<script type=\"module\" src=\"https://cdn.jsdelivr.net/npm/@kashi-ui/elements/dist/cdn/kashi-elements.js\"></script>\n```\n\nیا با npm، فقط همین بخش:\n\n```bash\nnpm i @kashi-ui/elements @kashi-ui/css\n```\n\n```js\nimport \"@kashi-ui/css\";\nimport \"@kashi-ui/elements/nav.js\";\n```\n\n```html\n<kashi-splitter size=\"35\" height=\"12rem\">\n <div style=\"padding: 1rem\">فهرست</div>\n <div style=\"padding: 1rem\">جزئیات</div>\n</kashi-splitter>\n```\n\nویژگی‌ها:\n\n| نام | مقدار | پیش‌فرض | توضیح |\n|---|---|---|---|\n| size | درصد | 40 | اندازهٔ اولیهٔ پنل اول |\n| min / max | درصد | 15 / 85 | محدودهٔ اندازه |\n| orientation | vertical | — | پنل‌ها زیر هم |\n| height / label | طول CSS / متن | — | ارتفاع و نام جداکننده |\n\nرویدادها و JavaScript:\n\n| نام | توضیح |\n|---|---|\n| kashi-change | detail: { value } |"},{"slug":"components/stat-card","url":"https://kashi.sepantapouya.com/docs/components/stat-card/","title":"کارت آمار","description":"نمایش شاخص‌های کلیدی (KPI) داشبورد با روند تغییر.","group":"کامپوننت‌ها","components":["StatCard","StatGrid"],"markdown":"```tsx\nimport { StatCard, StatGrid } from \"@kashi-ui/react\";\n```\n\n## نمونه\n\n```tsx\n<StatGrid>\n <StatCard label=\"فروش این ماه\" value=\"۱۸۴٬۵۰۰٬۰۰۰\" unit=\"تومان\" icon={<WalletIcon />}\n change={12.4} caption=\"نسبت به ماه قبل\">\n <Sparkline points={…} />\n </StatCard>\n <StatCard label=\"کاربران فعال\" value=\"۳٬۸۲۰\" unit=\"نفر\" change={-2} caption=\"نسبت به هفتهٔ قبل\" />\n <StatCard label=\"نرخ خطا\" value=\"۰٫۸٪\" change={-35} invertTrend />\n</StatGrid>\n```\n\n## ساده\n\n```tsx\n<StatCard label=\"سفارش‌های امروز\" value=\"۴۸\" />\n```\n\n| ویژگی | نوع | پیش‌فرض | توضیح |\n|---|---|---|---|\n| label | ReactNode | — | نام شاخص |\n| value | ReactNode | — | مقدار قالب‌بندی‌شده |\n| unit | ReactNode | — | واحد کوچک کنار عدد، مثل «تومان» |\n| icon | ReactNode | — | آیکون تزئینی |\n| change | number | — | درصد تغییر؛ منفی یعنی کاهش |\n| invertTrend | boolean | false | وقتی کاهش خوب است (رنگ سبز برای کاهش) |\n| caption | ReactNode | — | متن کنار روند |\n| children | ReactNode | — | نمودار کوچک یا محتوای اضافه |\n\n`StatGrid` کارت‌ها را در ستون‌های خودکار (حداقل ۱۴rem) می‌چیند.\n\nعدد با ارقام فارسی و عرض طبیعی نمایش داده می‌شود (نه `tabular-nums`)، چون ارقام فارسی با عرض ثابت از هم فاصله می‌گیرند. اندازهٔ عدد با `--k-stat-value-size` عوض می‌شود.\n\n## دسترس‌پذیری\n\nجهت روند فقط با رنگ و فلش نشان داده نمی‌شود: صفحه‌خوان متن کامل را می‌خواند («۱۲٫۴٪ افزایش»). رنگ روند بر اساس خوب یا بد بودن است، نه جهت.\n\n## راهنمای استفاده\n\nکِی استفاده کنیم:\n\n- شاخص‌های کلیدی بالای داشبورد: فروش امروز، سفارش‌ها، کاربران فعال.\n- عدد مهم با روند تغییر نسبت به دورهٔ قبل.\n\nکِی استفاده نکنیم:\n\n- بیش از چهار پنج کارت در یک ردیف؛ مهم‌ترین‌ها را انتخاب کنید.\n- روند در طول زمان. (نمودار: /docs/components/charts/)\n\nدرست و نادرست:\n\n- ✓ درست: بگویید تغییر نسبت به چه دوره‌ای است: «۱۲٪ بیشتر از ماه قبل».\n- ✗ نادرست: فقط «+۱۲٪» که معلوم نیست نسبت به دیروز است یا پارسال."},{"slug":"components/stepper","url":"https://kashi.sepantapouya.com/docs/components/stepper/","title":"استپر","description":"نشان دادن مراحل یک فرایند چندبخشی، مثل خرید یا ثبت‌نام.","group":"کامپوننت‌ها","components":["Stepper"],"markdown":"```tsx\nimport { Stepper } from \"@kashi-ui/react\";\n```\n\n## افقی\n\n```tsx\n<Stepper current={1} steps={[\n { title: \"سبد خرید\", description: \"۳ کالا\" },\n { title: \"اطلاعات ارسال\", description: \"آدرس و زمان\" },\n { title: \"پرداخت\" },\n { title: \"تأیید\" },\n]} />\n```\n\n## عمودی\n\n`} align=\"start\" code={`<Stepper orientation=\"vertical\" current={2} steps={[…]} />`}>\n\n| ویژگی | نوع | پیش‌فرض | توضیح |\n|---|---|---|---|\n| steps | { title, description? }[] | — | مراحل |\n| current | number | — | مرحلهٔ فعلی از صفر |\n| orientation | \"horizontal\" \\| \"vertical\" | \"horizontal\" | جهت |\n| label / labels | string / { complete?, current? } | «مراحل» | متن‌های صفحه‌خوان |\n\n## دسترس‌پذیری\n\nفهرست `<ol>` با نام «مراحل»؛ مرحلهٔ فعلی `aria-current=\"step\"` دارد و صفحه‌خوان وضعیت هر مرحله را می‌خواند («ارسال، مرحلهٔ فعلی»، «سبد خرید، انجام شده»). وضعیت فقط با رنگ نمایش داده نمی‌شود: مراحل انجام‌شده تیک دارند.\n\n## راهنمای استفاده\n\nکِی استفاده کنیم:\n\n- فرایند سه تا شش مرحله‌ای که کاربر باید بداند چقدر مانده: خرید، ثبت‌نام، احراز هویت.\n\nکِی استفاده نکنیم:\n\n- دو مرحله یا کمتر.\n- بخش‌های مستقل که ترتیب ندارند. (زبانه‌ها: /docs/components/tabs/)\n- مکان در ساختار سایت. (بردکرامب: /docs/components/breadcrumb/)\n\nدرست و نادرست:\n\n- ✓ درست: عنوان مرحله‌ها کوتاه و اسم: «سبد خرید»، «نشانی و ارسال»، «پرداخت».\n- ✗ نادرست: هشت مرحله که در موبایل جا نمی‌شود؛ مرحله‌ها را ادغام کنید."},{"slug":"components/switch","url":"https://kashi.sepantapouya.com/docs/components/switch/","title":"سوییچ","description":"برای روشن و خاموش کردن تنظیمی که بلافاصله اعمال می‌شود.","group":"کامپوننت‌ها","components":["Switch"],"markdown":"```tsx\nimport { Switch } from \"@kashi-ui/react\";\n```\n\n## پایه\n\nدر راست‌به‌چپ، دستگیرهٔ سوییچ در حالت روشن به سمت چپ می‌رود؛ این رفتار خودکار است.\n\n```tsx\n<Switch label=\"اعلان‌های ایمیلی\" description=\"خلاصهٔ هفتگی فعالیت‌ها\" defaultChecked />\n<Switch label=\"حالت تعطیلات\" />\n```\n\n## اندازه‌ها و حالت‌ها\n\n```tsx\n<Switch label=\"کوچک\" size=\"sm\" defaultChecked />\n<Switch label=\"متوسط\" defaultChecked />\n<Switch label=\"بزرگ\" size=\"lg\" defaultChecked />\n<Switch label=\"غیرفعال\" disabled />\n```\n\n## سوییچ یا چک‌باکس؟\n\n- **سوییچ:** تغییر فوراً اعمال می‌شود، مثل روشن کردن اعلان‌ها.\n- **چک‌باکس:** انتخاب پس از ارسال فرم اعمال می‌شود، مثل پذیرفتن قوانین.\n\n## فهرست تنظیمات\n\nبا `labelPosition=\"start\"` برچسب پیش از کنترل می‌آید و کنترل به انتهای ردیف می‌رود؛ چیدمان رایج صفحه‌های تنظیمات. برای Checkbox هم کار می‌کند.\n\n```tsx\n<Switch labelPosition=\"start\" label=\"اعلان‌های ایمیلی\" description=\"خلاصهٔ هفتگی فعالیت‌ها\" />\n```\n\n**ویژگی‌های جدید**\n\n| ویژگی | نوع | پیش‌فرض | توضیح |\n|---|---|---|---|\n| labelPosition | \"start\" \\| \"end\" | \"end\" | جای برچسب (Switch و Checkbox) |\n| classNames | { root, control, label, description } | — | کلاس دلخواه برای هر بخش |\n\n## ویژگی‌ها\n\n| ویژگی | نوع | پیش‌فرض | توضیح |\n|---|---|---|---|\n| label | ReactNode | — | برچسب کنار سوییچ |\n| description | ReactNode | — | توضیح زیر برچسب |\n| size | \"sm\" \\| \"md\" \\| \"lg\" | \"md\" | اندازه |\n| invalid | boolean | false | حالت خطا |\n\n## متغیرهای CSS\n\n| متغیر CSS | پیش‌فرض | توضیح |\n|---|---|---|\n| --k-switch-width | 2.25rem | عرض |\n| --k-switch-height | 1.25rem | ارتفاع |\n| --k-switch-track-on | var(--k-bg-brand) | رنگ مسیر در حالت روشن |\n| --k-switch-track-off | var(--k-border-input) | رنگ مسیر در حالت خاموش |\n| --k-switch-thumb | #fff | رنگ دستگیره |\n\n## دسترس‌پذیری\n\n- روی `<input type=\"checkbox\" role=\"switch\">` ساخته شده؛ صفحه‌خوان آن را «سوییچ، روشن» یا «خاموش» اعلام می‌کند.\n- مسیر خاموش کنتراست حداقل ۳ به ۱ با پس‌زمینه دارد.\n\n## راهنمای استفاده\n\nکِی استفاده کنیم:\n\n- تنظیمی که همان لحظه اعمال می‌شود: اعلان‌ها، حالت تیره، نمایش عمومی پروفایل.\n\nکِی استفاده نکنیم:\n\n- گزینه‌ای در فرم که با دکمهٔ ثبت ذخیره می‌شود. (چک‌باکس: /docs/components/checkbox/)\n- دو گزینه با نام‌های جدا، مثل «ماهانه / سالانه». (سگمنت: /docs/components/segmented-control/)\n\nدرست و نادرست:\n\n- ✓ درست: برچسب اسم تنظیم است و روشن یعنی فعال.\n- ✗ نادرست: برچسبی که وضعیت را می‌گوید و با هر تغییر عوض می‌شود."},{"slug":"components/table","url":"https://kashi.sepantapouya.com/docs/components/table/","title":"جدول داده","description":"جدول با مرتب‌سازی، انتخاب ردیف‌ها، ستون‌های عددی و حالت خالی.","group":"کامپوننت‌ها","components":["DataTable"],"markdown":"```tsx\nimport { DataTable } from \"@kashi-ui/react\";\n```\n\n## نمونه\n\nروی سرستون «مشتری» یا «مبلغ» بزنید تا مرتب شود. نام‌ها به ترتیب الفبای فارسی مرتب می‌شوند.\n\n```tsx\n<DataTable\n caption=\"تراکنش‌های اخیر\"\n description=\"آخرین پرداخت‌های مشتریان در این ماه\"\n rows={transactions}\n getRowId={(r) => r.id}\n selectable\n selected={selected}\n onSelectedChange={setSelected}\n defaultSort={{ key: \"amount\", direction: \"descending\" }}\n columns={[\n { key: \"customer\", header: \"مشتری\", sortable: true },\n { key: \"date\", header: \"تاریخ\" },\n { key: \"status\", header: \"وضعیت\", render: (r) => <Badge tone=\"success\" dot>…</Badge> },\n { key: \"amount\", header: \"مبلغ (تومان)\", sortable: true, numeric: true,\n render: (r) => formatNumber(r.amount) },\n ]}\n/>\n```\n\n## بارگذاری، راه‌راه، سرستون ثابت و صفحه‌بندی\n\nروی «بارگذاری دوباره» بزنید تا ردیف‌های اسکلت را ببینید؛ روی هر ردیف بزنید تا رویداد کلیک را ببینید. جدول را اسکرول کنید: سرستون ثابت می‌ماند.\n\n```tsx\n<DataTable\n …\n striped\n stickyHeader\n maxHeight=\"16rem\"\n loading={isLoading ? 4 : false}\n onRowClick={(row) => open(row)}\n columns={[\n { key: \"date\", header: \"تاریخ\", hideBelow: \"sm\" },\n { key: \"status\", header: \"وضعیت\", align: \"center\" },\n …\n ]}\n footer={<Pagination page={page} total={8} onChange={setPage} />}\n/>\n```\n\n**ویژگی‌های جدید**\n\n| ویژگی | نوع | پیش‌فرض | توضیح |\n|---|---|---|---|\n| striped | boolean | false | ردیف‌های راه‌راه |\n| stickyHeader / maxHeight | boolean / string | — | سرستون ثابت هنگام اسکرول |\n| loading | boolean \\| number | false | ردیف‌های اسکلت؛ عدد = تعداد ردیف |\n| onRowClick | (row) => void | — | کلیک روی ردیف (کلیک روی دکمه و لینک داخل ردیف را نمی‌گیرد) |\n| footer | ReactNode | — | محتوای پایین جدول |\n| align / hideBelow / className (ستون) | — | — | چینش، پنهان شدن در موبایل، کلاس خانه‌ها |\n| classNames | { root, table, row, cell, header, footer } | — | کلاس دلخواه برای هر بخش |\n\nمتغیرهای CSS: `--k-table-radius`، `--k-table-cell-px`، `--k-table-cell-py`، `--k-table-header-bg`، `--k-table-stripe`، `--k-table-row-hover`.\n\n## ویژگی‌ها\n\n| ویژگی | نوع | پیش‌فرض | توضیح |\n|---|---|---|---|\n| columns | Column[] | — | ستون‌ها: key، header، render، sortable، sortValue، numeric، width |\n| rows | T[] | — | ردیف‌ها |\n| getRowId | (row) => string | — | شناسهٔ یکتای هر ردیف |\n| caption | ReactNode | — | عنوان جدول — الزامی، برای صفحه‌خوان |\n| description / hideCaption | ReactNode / boolean | — | توضیح زیر عنوان / پنهان کردن عنوان از دید |\n| selectable | boolean | false | ستون انتخاب با «انتخاب همه» |\n| selected / onSelectedChange | string[] | — | ردیف‌های انتخاب‌شده |\n| sort / defaultSort / onSortChange | { key, direction } \\| null | — | مرتب‌سازی؛ کنترل‌شده برای مرتب‌سازی سمت سرور |\n| empty | ReactNode | — | محتوای جدول خالی، مثلاً یک EmptyState |\n| compact | boolean | false | ردیف‌های فشرده |\n\n## دسترس‌پذیری\n\n- جدول واقعی HTML با `<caption>` و سرستون‌های `scope=\"col\"`؛ صفحه‌خوان‌ها می‌توانند بین خانه‌ها پیمایش کنند.\n- سرستون مرتب‌شده `aria-sort` دارد و مرتب‌سازی با یک دکمهٔ واقعی انجام می‌شود.\n- چک‌باکس هر ردیف نام اختصاصی می‌گیرد (`labels.selectRow`)، مثل «انتخاب تراکنش سارا احمدی».\n- ظرف جدول در صفحه‌های باریک اسکرول افقی دارد و با کیبورد (Tab) قابل‌اسکرول است.\n\n## راهنمای استفاده\n\nکِی استفاده کنیم:\n\n- داده‌های ردیفی که مقایسه، مرتب و انتخاب می‌شوند: سفارش‌ها، تراکنش‌ها، کاربران.\n\nکِی استفاده نکنیم:\n\n- دو سه ستون که در موبایل بهتر است فهرست یا کارت باشند. (کارت: /docs/components/card/)\n- چیدمان صفحه؛ جدول برای داده است.\n\nدرست و نادرست:\n\n- ✓ درست: ستون‌های عددی و مبلغ `numeric` بگیرند تا هم‌تراز شوند و رقم‌های یکان و دهگان زیر هم بیایند.\n- ✗ نادرست: مبلغ‌ها با ارقام لاتین و چپ‌چین در جدول فارسی.\n- ✓ درست: حالت خالی (`empty`) که بگوید چرا خالی است.\n- ✗ نادرست: جدول بی‌ردیف با فقط سرستون‌ها.\n\n## وب کامپوننت\n\nهمین کامپوننت بدون React، برای HTML، وردپرس، Vue، Svelte و Angular: `<kashi-data-table>` در `@kashi-ui/elements`. یک `<table>` معمولی را بهتر می‌کند: `th[data-sort]` مرتب‌شدنی، `data-numeric` برای عدد و `tr[data-id]` شناسهٔ ردیف. بدون JavaScript همان جدول با ظاهر کاشی دیده می‌شود.\n\nنصب بدون ابزار ساخت (CDN):\n\n```html\n<link rel=\"stylesheet\" href=\"https://cdn.jsdelivr.net/npm/@kashi-ui/css/dist/kashi.css\">\n<script type=\"module\" src=\"https://cdn.jsdelivr.net/npm/@kashi-ui/elements/dist/cdn/kashi-elements.js\"></script>\n```\n\nیا با npm، فقط همین بخش:\n\n```bash\nnpm i @kashi-ui/elements @kashi-ui/css\n```\n\n```js\nimport \"@kashi-ui/css\";\nimport \"@kashi-ui/elements/table.js\";\n```\n\n```html\n<kashi-data-table selectable searchable name=\"orders\">\n <table>\n <caption>سفارش‌های امروز</caption>\n <thead><tr><th data-sort>مشتری</th><th data-sort>شهر</th><th data-sort data-numeric>مبلغ (تومان)</th></tr></thead>\n <tbody>\n <tr data-id=\"1\"><td>سارا احمدی</td><td>تهران</td><td>۱٬۲۰۰٬۰۰۰</td></tr>\n <tr data-id=\"2\"><td>علی رضایی</td><td>اصفهان</td><td>۹۸۰٬۰۰۰</td></tr>\n <tr data-id=\"3\"><td>مینا کریمی</td><td>شیراز</td><td>۳۵۰٬۰۰۰</td></tr>\n </tbody>\n </table>\n</kashi-data-table>\n```\n\nویژگی‌ها:\n\n| نام | مقدار | پیش‌فرض | توضیح |\n|---|---|---|---|\n| selectable | بولی | — | چک‌باکس انتخاب ردیف |\n| searchable | بولی | — | جستجو بالای جدول |\n| name | متن | — | شناسهٔ ردیف‌های انتخاب‌شده در فرم |\n| compact / striped / sticky-header | بولی | — | ظاهر جدول |\n\nرویدادها و JavaScript:\n\n| نام | توضیح |\n|---|---|\n| kashi-change | انتخاب ردیف؛ detail: { selected } |\n| kashi-sort | detail: { column, direction } |\n| element.columns / element.rows | ساخت جدول از داده: [{ key, header, sortable, numeric, format }] |\n| element.selected | شناسهٔ ردیف‌های انتخاب‌شده |"},{"slug":"components/tabs","url":"https://kashi.sepantapouya.com/docs/components/tabs/","title":"تب‌ها","description":"نمایش بخش‌های مرتبط یک محتوا در یک فضا، با سه سبک و پیمایش کامل کیبورد.","group":"کامپوننت‌ها","components":["Tabs","TabList","Tab","TabPanel"],"markdown":"```tsx\nimport { Tabs, TabList, Tab, TabPanel } from \"@kashi-ui/react\";\n```\n\n## خطی\n\n```tsx\n<Tabs defaultValue=\"account\">\n <TabList aria-label=\"تنظیمات\">\n <Tab value=\"account\">حساب کاربری</Tab>\n <Tab value=\"security\">امنیت</Tab>\n <Tab value=\"billing\">پرداخت <Badge tone=\"brand\" size=\"sm\">۲</Badge></Tab>\n <Tab value=\"api\" disabled>API</Tab>\n </TabList>\n <TabPanel value=\"account\">…</TabPanel>\n <TabPanel value=\"security\">…</TabPanel>\n <TabPanel value=\"billing\">…</TabPanel>\n</Tabs>\n```\n\n## قرصی\n\n```tsx\n<Tabs defaultValue=\"account\" variant=\"pill\">…</Tabs>\n```\n\n## چندبخشی\n\n```tsx\n<Tabs defaultValue=\"account\" variant=\"segmented\">…</Tabs>\n```\n\n## کیبورد\n\n| کلید | عمل |\n|---|---|\n| <kbd>Tab</kbd> | ورود به زبانهٔ فعال، سپس رفتن به محتوای پنل |\n| <kbd>←</kbd> / <kbd>→</kbd> | زبانهٔ بعد / قبل (در راست‌به‌چپ) و فعال‌سازی آن |\n| <kbd>Home</kbd> / <kbd>End</kbd> | اولین / آخرین زبانه |\n\n## عمودی\n\nبرای صفحه‌های تنظیمات؛ کلیدهای ↑ و ↓ بین زبانه‌ها جابه‌جا می‌کنند. در صفحه‌های باریک خودکار افقی می‌شود.\n\n```tsx\n<Tabs defaultValue=\"profile\" orientation=\"vertical\">\n <TabList aria-label=\"تنظیمات حساب\">…</TabList>\n …\n</Tabs>\n```\n\n**ویژگی‌های جدید**\n\n| ویژگی | نوع | پیش‌فرض | توضیح |\n|---|---|---|---|\n| orientation | \"horizontal\" \\| \"vertical\" | \"horizontal\" | جهت؛ aria-orientation و کلیدهای پیمایش هم تغییر می‌کنند |\n| size | \"sm\" \\| \"md\" | \"md\" | اندازه |\n\nمتغیرهای CSS: `--k-tab-height`، `--k-tab-radius`، `--k-tabs-gap`، `--k-tabs-indicator`، `--k-tabs-list-width`.\n\n## ویژگی‌ها\n\n| ویژگی | نوع | پیش‌فرض | توضیح |\n|---|---|---|---|\n| value / defaultValue | string | — | زبانهٔ فعال (Tabs) |\n| onChange | (value: string) => void | — | با تغییر زبانه |\n| variant | \"line\" \\| \"pill\" \\| \"segmented\" | \"line\" | سبک |\n| block | boolean | false | زبانه‌ها تمام عرض را پر کنند |\n| aria-label | string | — | نام فهرست زبانه‌ها (TabList) — الزامی |\n| value | string | — | شناسهٔ زبانه و پنل متناظر (Tab و TabPanel) |\n\nنشانگر زبانهٔ فعال (خط زیر زبانه، یا پس‌زمینه در گونه‌های قرصی و چندبخشی) با تغییر زبانه به‌نرمی جابه‌جا می‌شود؛ با تنظیم «کاهش حرکت» در سیستم، بدون انیمیشن.\n\n## دسترس‌پذیری\n\n- الگوی Tabs در WAI-ARIA: `tablist`، `tab` و `tabpanel` با اتصال `aria-controls` و `aria-labelledby`.\n- فقط زبانهٔ فعال در ترتیب Tab است؛ زبانه‌های غیرفعال (`disabled`) در پیمایش رد می‌شوند.\n- برای ناوبری بین صفحه‌ها از زبانه استفاده نکنید؛ لینک‌ها مناسب‌ترند.\n\n## راهنمای استفاده\n\nکِی استفاده کنیم:\n\n- چند بخش هم‌سطح یک محتوا در یک فضا: «مشخصات / نظرها / پرسش‌ها» در صفحهٔ محصول.\n\nکِی استفاده نکنیم:\n\n- مراحل پشت‌سرهم. (استپر: /docs/components/stepper/)\n- عوض کردن نمای همان داده (جدول یا کارت). (سگمنت: /docs/components/segmented-control/)\n- محتوایی که کاربر باید با هم مقایسه کند؛ زبانه‌ها فقط یکی را در هر زمان نشان می‌دهند.\n\nدرست و نادرست:\n\n- ✓ درست: دو تا شش زبانه با عنوان یکی دو کلمه‌ای.\n- ✗ نادرست: زبانه‌های زیاد که اسکرول افقی می‌خواهند.\n\n## وب کامپوننت\n\nهمین کامپوننت بدون React، برای HTML، وردپرس، Vue، Svelte و Angular: `<kashi-tabs>` در `@kashi-ui/elements`. هر فرزند با `data-tab` یک زبانه است (`data-value` برای مقدار، `data-disabled` برای غیرفعال). بدون JavaScript همهٔ بخش‌ها دیده می‌شوند.\n\nنصب بدون ابزار ساخت (CDN):\n\n```html\n<link rel=\"stylesheet\" href=\"https://cdn.jsdelivr.net/npm/@kashi-ui/css/dist/kashi.css\">\n<script type=\"module\" src=\"https://cdn.jsdelivr.net/npm/@kashi-ui/elements/dist/cdn/kashi-elements.js\"></script>\n```\n\nیا با npm، فقط همین بخش:\n\n```bash\nnpm i @kashi-ui/elements @kashi-ui/css\n```\n\n```js\nimport \"@kashi-ui/css\";\nimport \"@kashi-ui/elements/overlay.js\";\n```\n\n```html\n<kashi-tabs label=\"تنظیمات حساب\">\n <section data-tab=\"پروفایل\">نام، تصویر و نام کاربری.</section>\n <section data-tab=\"امنیت\">رمز عبور و ورود دومرحله‌ای.</section>\n <section data-tab=\"اعلان‌ها\">پیامک و ایمیل.</section>\n</kashi-tabs>\n```\n\nویژگی‌ها:\n\n| نام | مقدار | پیش‌فرض | توضیح |\n|---|---|---|---|\n| label | متن | — | نام فهرست زبانه‌ها برای صفحه‌خوان |\n| value | متن | اولین زبانه | زبانهٔ فعال |\n| variant | line \\| pill \\| segmented | line | ظاهر |\n| orientation | vertical | — | زبانه‌ها در ستون کناری |\n| size / block | sm / بولی | — | اندازه و تمام‌عرض |\n\nرویدادها و JavaScript:\n\n| نام | توضیح |\n|---|---|\n| kashi-change | detail: { value } |"},{"slug":"components/tag-input","url":"https://kashi.sepantapouya.com/docs/components/tag-input/","title":"ورودی تگ","description":"نوشتن چند مقدار در یک فیلد، مثل مهارت‌ها، برچسب‌ها یا گیرندگان ایمیل.","group":"کامپوننت‌ها","components":["TagInput"],"markdown":"```tsx\nimport { TagInput } from \"@kashi-ui/react\";\n```\n\n## نمونه\n\n```tsx\nconst [tags, setTags] = useState([\"طراحی\", \"React\"]);\n\n<TagInput label=\"مهارت‌ها\" value={tags} onChange={setTags} max={5}\n hint=\"با Enter یا ویرگول جدا کنید؛ حداکثر ۵ مورد.\" />\n```\n\nبا <kbd>Enter</kbd>، ویرگول انگلیسی و ویرگول فارسی «،» برچسب ساخته می‌شود؛ متن چسبانده‌شده هم جدا می‌شود. <kbd>Backspace</kbd> در فیلد خالی آخرین برچسب را حذف می‌کند.\n\n## اعتبارسنجی\n\n```tsx\n<TagInput label=\"گیرندگان\" tone=\"neutral\"\n validate={(t) => (isEmail(t) ? undefined : \"این ایمیل معتبر نیست.\")} />\n```\n\n| ویژگی | نوع | پیش‌فرض | توضیح |\n|---|---|---|---|\n| value / defaultValue | string[] | [] | برچسب‌ها |\n| onChange | (tags: string[]) => void | — | — |\n| separators | string[] | [\"Enter\", \",\", \"،\"] | کلیدهای جداکننده |\n| validate | (tag) => string \\| undefined | — | پیام خطا برای برچسب نامعتبر |\n| max | number | — | بیشترین تعداد |\n| allowDuplicates | boolean | false | — |\n| tone | BadgeTone | \"brand\" | رنگ برچسب‌ها |\n| name | string | — | یک فیلد پنهان برای هر برچسب |\n\n## دسترس‌پذیری\n\nهر برچسب دکمهٔ حذف با نام «حذف React» دارد. افزودن، حذف و خطاها (تکراری، بیش از حد مجاز) با `aria-live` اعلام می‌شوند و پیام خطا به فیلد وصل است.\n\n## راهنمای استفاده\n\nکِی استفاده کنیم:\n\n- چند مقدار آزاد در یک فیلد: مهارت‌ها، برچسب‌ها، ایمیل گیرندگان.\n\nکِی استفاده نکنیم:\n\n- انتخاب از فهرست مشخص. (انتخاب چندتایی: /docs/components/multi-select/)\n- یک مقدار. (ورودی: /docs/components/input/)\n\nدرست و نادرست:\n\n- ✓ درست: با `validate` همان لحظه بررسی کنید (مثلاً ایمیل درست) و برچسب تکراری نپذیرید.\n- ✗ نادرست: هر متنی بپذیرید و بعد از ارسال بگویید یکی از ایمیل‌ها اشتباه بود.\n\n## وب کامپوننت\n\nهمین کامپوننت بدون React، برای HTML، وردپرس، Vue، Svelte و Angular: `<kashi-tag-input>` در `@kashi-ui/elements`.\n\nنصب بدون ابزار ساخت (CDN):\n\n```html\n<link rel=\"stylesheet\" href=\"https://cdn.jsdelivr.net/npm/@kashi-ui/css/dist/kashi.css\">\n<script type=\"module\" src=\"https://cdn.jsdelivr.net/npm/@kashi-ui/elements/dist/cdn/kashi-elements.js\"></script>\n```\n\nیا با npm، فقط همین بخش:\n\n```bash\nnpm i @kashi-ui/elements @kashi-ui/css\n```\n\n```js\nimport \"@kashi-ui/css\";\nimport \"@kashi-ui/elements/controls.js\";\n```\n\n```html\n<kashi-tag-input label=\"مهارت‌ها\" name=\"skills\" value=\"طراحی، فیگما\" max=\"5\" placeholder=\"بنویسید و Enter بزنید\"></kashi-tag-input>\n```\n\nویژگی‌ها:\n\n| نام | مقدار | پیش‌فرض | توضیح |\n|---|---|---|---|\n| label / hint / error / placeholder | متن | — | مثل بقیهٔ فیلدها |\n| name | متن | — | هر برچسب یک فیلد هم‌نام در فرم |\n| value | جدا با «،» | — | برچسب‌های اولیه |\n| max | عدد | — | بیشترین تعداد |\n| allow-duplicates / tone | بولی / رنگ نشان | brand | تکراری و رنگ برچسب‌ها |\n\nرویدادها و JavaScript:\n\n| نام | توضیح |\n|---|---|\n| kashi-change | detail: { value: [...] } |\n| element.validate | تابع اعتبارسنجی هر برچسب؛ پیام خطا برمی‌گرداند |"},{"slug":"components/timeline","url":"https://kashi.sepantapouya.com/docs/components/timeline/","title":"تایم‌لاین","description":"نمایش رویدادها و فعالیت‌ها به ترتیب زمان، مثل تاریخچهٔ سفارش یا فعالیت تیم.","group":"کامپوننت‌ها","components":["Timeline"],"markdown":"```tsx\nimport { Timeline } from \"@kashi-ui/react\";\n```\n\n## نمونه\n\n```tsx\n<Timeline items={[\n { title: <><strong>سارا محمدی</strong> سفارش را تأیید کرد</>, time: new Date(…), tone: \"success\", icon: <CheckIcon /> },\n { title: <><strong>رضا کریمی</strong> نظر گذاشت</>, time: new Date(…), icon: <Avatar name=\"رضا کریمی\" size=\"sm\" />,\n content: \"بسته‌بندی را دوبل کنید؛ کالا شکستنی است.\" },\n { title: \"سفارش ثبت شد\", time: \"۱۲ آبان، ۱۰:۲۰\" },\n]} />\n```\n\nاگر `time` از نوع `Date` باشد، به شکل نسبی («۵ دقیقه پیش») با `RelativeTime` نمایش داده و خودکار به‌روز می‌شود؛ متن هم می‌توانید بدهید.\n\n| ویژگی | نوع | پیش‌فرض | توضیح |\n|---|---|---|---|\n| items | { id?, title, time?, icon?, tone?, content? }[] | — | رویدادها به ترتیب نمایش |\n| items[].tone | \"neutral\" \\| \"brand\" \\| \"success\" \\| \"error\" | \"neutral\" | رنگ نشانگر |\n| digits | \"fa\" \\| \"latin\" | \"fa\" | ارقام زمان نسبی |\n\n## دسترس‌پذیری\n\nفهرست مرتب `<ol>` است، پس صفحه‌خوان تعداد و ترتیب رویدادها را می‌گوید. نشانگرها تزئینی‌اند و زمان در عنصر `<time>` با تاریخ دقیق است.\n\n## راهنمای استفاده\n\nکِی استفاده کنیم:\n\n- رویدادهای یک مورد به ترتیب زمان: تاریخچهٔ سفارش، فعالیت تیم، تغییرات یک سند.\n\nکِی استفاده نکنیم:\n\n- مراحلی که هنوز انجام نشده‌اند و کاربر طی می‌کند. (استپر: /docs/components/stepper/)\n\nدرست و نادرست:\n\n- ✓ درست: زمان نسبی برای رویدادهای تازه («۵ دقیقه پیش») و تاریخ کامل در `title`.\n- ✗ نادرست: تاریخ و ساعت کامل برای همه، که خواندن را کند می‌کند."},{"slug":"components/toast","url":"https://kashi.sepantapouya.com/docs/components/toast/","title":"توست","description":"پیام کوتاه و موقت پس از یک عمل، مثل «ذخیره شد»؛ با توقف هنگام hover و امکان واگرد.","group":"کامپوننت‌ها","components":["Toaster"],"markdown":"```tsx\nimport { Toaster, toast } from \"@kashi-ui/react\";\n```\n\n`<Toaster />` را یک بار در ریشهٔ برنامه بگذارید؛ سپس از هر جای برنامه `toast()` را صدا بزنید.\n\n## نمونه\n\n```tsx\ntoast.success(\"تغییرات ذخیره شد\", { description: \"…\" });\ntoast.error(\"پرداخت ناموفق بود\");\ntoast.warning(\"اتصال اینترنت ناپایدار است\");\ntoast({\n title: \"پیام بایگانی شد\",\n action: { label: \"واگرد\", onClick: () => restore() },\n});\n\n// در ریشهٔ برنامه\n<Toaster />\n```\n\n## رفتار\n\n- **زمان:** پیش‌فرض ۵ ثانیه. نوار پایین زمان باقی‌مانده را نشان می‌دهد و با hover یا فوکوس متوقف می‌شود.\n- **با عمل:** اعلانی که دکمه دارد (مثل «واگرد») خودکار بسته نمی‌شود تا کاربر فرصت کافی داشته باشد.\n- **بستن:** با دکمهٔ بستن یا <kbd>Esc</kbd> وقتی فوکوس روی اعلان است.\n\n## کار زمان‌بر و جایگاه\n\n`toast.promise` اعلان «در حال ذخیره…» را نشان می‌دهد و پس از پایان کار، **همان اعلان** را به پیام موفقیت یا خطا تغییر می‌دهد. (در این نمونه از هر چهار بار، حدود یک بار خطا شبیه‌سازی می‌شود.)\n\n```tsx\ntoast.promise(saveFiles(), {\n loading: \"در حال ذخیره…\",\n success: (n) => `${n} فایل ذخیره شد`,\n error: (e) => `ذخیره نشد: ${e.message}`,\n});\n\n<Toaster placement=\"top-center\" max={3} />\n```\n\n**ویژگی‌های جدید**\n\n| ویژگی | نوع | پیش‌فرض | توضیح |\n|---|---|---|---|\n| placement (Toaster) | \"top-start\" \\| \"top-center\" \\| \"top-end\" \\| \"bottom-start\" \\| \"bottom-center\" \\| \"bottom-end\" | \"bottom-end\" | جایگاه؛ در RTL «end» سمت چپ است |\n| max (Toaster) | number | 5 | بیشترین اعلان هم‌زمان |\n| toast.promise | (promise, { loading, success, error }) | — | اعلان یک کار زمان‌بر |\n| id (toast) | number \\| string | — | با شناسهٔ تکراری، اعلان قبلی به‌روز می‌شود |\n\n## ویژگی‌های toast()\n\n| ویژگی | نوع | پیش‌فرض | توضیح |\n|---|---|---|---|\n| title | ReactNode | — | متن اصلی |\n| description | ReactNode | — | توضیح |\n| tone | \"neutral\" \\| \"success\" \\| \"error\" \\| \"warning\" \\| \"info\" | \"neutral\" | رنگ و آیکون |\n| duration | number | ۵۰۰۰، یا بی‌نهایت با action | میلی‌ثانیه |\n| action | { label, onClick } | — | دکمهٔ عمل |\n\n## دسترس‌پذیری\n\n- ناحیهٔ اعلان‌ها با `aria-live=\"polite\"` از ابتدا در صفحه است تا اعلان‌های جدید بدون جابه‌جایی فوکوس خوانده شوند؛ اعلان خطا با `role=\"alert\"` فوراً خوانده می‌شود.\n- توقف زمان هنگام hover و فوکوس و ماندن اعلان‌های دارای عمل، معیار WCAG 2.2.1 (زمان کافی) را برآورده می‌کنند.\n- اطلاعات حیاتی را فقط در توست نگذارید؛ ممکن است کاربر آن را نبیند.\n\n## راهنمای استفاده\n\nکِی استفاده کنیم:\n\n- تأیید کوتاه پس از یک عمل: «ذخیره شد»، «به سبد اضافه شد».\n- عمل برگشت‌پذیر با دکمهٔ «واگرد» (`action`).\n\nکِی استفاده نکنیم:\n\n- خطایی که کاربر باید درستش کند؛ کنار همان بخش نشانش دهید. (هشدار: /docs/components/alert/)\n- اطلاعات مهمی که نباید از دست برود، مثل کد پیگیری پرداخت. (رسید پرداخت: /docs/persian/receipt/)\n- تأیید پیش از عمل. (دیالوگ: /docs/components/dialog/)\n\nدرست و نادرست:\n\n- ✓ درست: یک جملهٔ کوتاه: «سفارش ذخیره شد».\n- ✗ نادرست: دو پاراگراف که پیش از خوانده شدن ناپدید می‌شود.\n- ✓ درست: برای حذف، به‌جای گفتگوی تأیید، حذف کنید و «واگرد» بدهید.\n- ✗ نادرست: چند توست پشت‌سرهم برای هر قدم یک کار.\n\n## وب کامپوننت\n\nهمین کامپوننت بدون React، برای HTML، وردپرس، Vue، Svelte و Angular: `<kashi-toaster>` در `@kashi-ui/elements`. دکمه با `data-kashi-toast` بدون JavaScript اعلان نشان می‌دهد. اگر `<kashi-toaster>` در صفحه نباشد، خودکار ساخته می‌شود.\n\nنصب بدون ابزار ساخت (CDN):\n\n```html\n<link rel=\"stylesheet\" href=\"https://cdn.jsdelivr.net/npm/@kashi-ui/css/dist/kashi.css\">\n<script type=\"module\" src=\"https://cdn.jsdelivr.net/npm/@kashi-ui/elements/dist/cdn/kashi-elements.js\"></script>\n```\n\nیا با npm، فقط همین بخش:\n\n```bash\nnpm i @kashi-ui/elements @kashi-ui/css\n```\n\n```js\nimport \"@kashi-ui/css\";\nimport \"@kashi-ui/elements/toast.js\";\n```\n\n```html\n<button class=\"k-btn k-btn--primary\" data-kashi-toast=\"ذخیره شد\" data-description=\"تغییرات شما اعمال شد.\" data-tone=\"success\">نمایش اعلان</button>\n```\n\nویژگی‌ها:\n\n| نام | مقدار | پیش‌فرض | توضیح |\n|---|---|---|---|\n| placement | top-start … bottom-end | bottom-end | kashi-toaster: جای اعلان‌ها |\n| max | عدد | 5 | kashi-toaster: بیشترین تعداد هم‌زمان |\n| data-kashi-toast / data-description / data-tone | متن | — | روی هر دکمه: عنوان، توضیح و رنگ اعلان |\n\nرویدادها و JavaScript:\n\n| نام | توضیح |\n|---|---|\n| toast(\"…\") / toast.success / error / warning / info | import { toast } from \"@kashi-ui/elements\" |\n| toast.promise(promise, { loading, success, error }) | اعلان برای کار زمان‌بر |\n| toast.dismiss(id) | بستن اعلان |"},{"slug":"components/tooltip","url":"https://kashi.sepantapouya.com/docs/components/tooltip/","title":"تولتیپ","description":"توضیح کوتاه یک کنترل هنگام hover یا فوکوس.","group":"کامپوننت‌ها","components":["Tooltip"],"markdown":"```tsx\nimport { Tooltip } from \"@kashi-ui/react\";\n```\n\n## نمونه\n\nنشانگر را روی دکمه‌ها ببرید یا با <kbd>Tab</kbd> روی آن‌ها بروید.\n\n```tsx\n<Tooltip content=\"جستجو در همهٔ پروژه‌ها\">\n <Button iconOnly aria-label=\"جستجو\"><SearchIcon /></Button>\n</Tooltip>\n\n<Tooltip content=\"ایمیل برای همه قابل‌مشاهده نیست\" placement=\"bottom\">\n …\n</Tooltip>\n```\n\n## چهار جایگاه\n\n```tsx\n<Tooltip content=\"…\" placement=\"start\">…</Tooltip>\n```\n\n`placement` حالا `\"start\"` و `\"end\"` هم می‌پذیرد. متغیرهای CSS: `--k-tooltip-bg`، `--k-tooltip-fg`، `--k-tooltip-max-width`، `--k-tooltip-radius`.\n\n## ویژگی‌ها\n\n| ویژگی | نوع | پیش‌فرض | توضیح |\n|---|---|---|---|\n| content | ReactNode | — | متن راهنما |\n| children | ReactElement | — | یک عنصر فوکوس‌پذیر، معمولاً دکمه |\n| placement | \"top\" \\| \"bottom\" | \"top\" | جایگاه |\n| delay | number | 300 | تأخیر نمایش با hover (میلی‌ثانیه)؛ با فوکوس فوری است |\n\n## دسترس‌پذیری\n\n- با `aria-describedby` به عنصر متصل است؛ صفحه‌خوان آن را پس از نام دکمه می‌خواند.\n- طبق WCAG 1.4.13: با <kbd>Esc</kbd> بسته می‌شود، نشانگر می‌تواند روی خود راهنما برود بدون اینکه بسته شود، و تا وقتی hover یا فوکوس ادامه دارد می‌ماند.\n- تولتیپ **جایگزین برچسب نیست**: دکمهٔ فقط‌آیکون همچنان `aria-label` لازم دارد. اطلاعات ضروری را در آن پنهان نکنید؛ کاربران لمسی آن را نمی‌بینند.\n\n## راهنمای استفاده\n\nکِی استفاده کنیم:\n\n- نام دکمهٔ فقط‌آیکون.\n- توضیح یک خط برای واژه یا نماد ناآشنا.\n\nکِی استفاده نکنیم:\n\n- اطلاعاتی که کاربر برای کارش لازم دارد؛ تولتیپ در موبایل دیده نمی‌شود. آن را در متن راهنمای فیلد بنویسید.\n- محتوای تعاملی مثل پیوند یا دکمه. (پاپ‌اور: /docs/components/popover/)\n\nدرست و نادرست:\n\n- ✓ درست: چند کلمه که همان برچسب صفحه‌خوان هم هست: «حذف سفارش».\n- ✗ نادرست: پاراگراف راهنما که روی محتوا می‌افتد.\n\n## وب کامپوننت\n\nهمین کامپوننت بدون React، برای HTML، وردپرس، Vue، Svelte و Angular: `<kashi-tooltip>` در `@kashi-ui/elements`.\n\nنصب بدون ابزار ساخت (CDN):\n\n```html\n<link rel=\"stylesheet\" href=\"https://cdn.jsdelivr.net/npm/@kashi-ui/css/dist/kashi.css\">\n<script type=\"module\" src=\"https://cdn.jsdelivr.net/npm/@kashi-ui/elements/dist/cdn/kashi-elements.js\"></script>\n```\n\nیا با npm، فقط همین بخش:\n\n```bash\nnpm i @kashi-ui/elements @kashi-ui/css\n```\n\n```js\nimport \"@kashi-ui/css\";\nimport \"@kashi-ui/elements/overlay.js\";\n```\n\n```html\n<kashi-tooltip content=\"تغییرات در پیش‌نویس ذخیره می‌شود\">\n <button class=\"k-btn k-btn--secondary\">ذخیره</button>\n</kashi-tooltip>\n```\n\nویژگی‌ها:\n\n| نام | مقدار | پیش‌فرض | توضیح |\n|---|---|---|---|\n| content | متن | — | متن راهنما |\n| placement | top \\| bottom \\| start \\| end | top | جای راهنما |\n| delay | میلی‌ثانیه | 300 | تأخیر نمایش با hover |"},{"slug":"components/tour","url":"https://kashi.sepantapouya.com/docs/components/tour/","title":"تور معرفی","description":"معرفی بخش‌های تازهٔ محصول به کاربر، مرحله به مرحله، با روشن کردن همان بخش.","group":"کامپوننت‌ها","components":["Tour"],"markdown":"```tsx\nimport { Tour } from \"@kashi-ui/react\";\n```\n\n## نمونه\n\n```tsx\n<Tour open={open} onOpenChange={setOpen} steps={[\n { target: \"#search\", title: \"جستجوی سریع\", content: \"نام مشتری یا شمارهٔ سفارش را بنویسید.\" },\n { target: \"#export\", title: \"خروجی اکسل\", content: \"فهرست فیلترشده را دانلود کنید.\" },\n]} />\n```\n\nراهنما را کوتاه نگه دارید (سه تا پنج مرحله) و همیشه امکان «رد کردن» بدهید. بهتر است فقط یک بار، برای کاربر تازه یا بخش تازه، نمایش داده شود.\n\n| ویژگی | نوع | پیش‌فرض | توضیح |\n|---|---|---|---|\n| steps | { target, title, content }[] | — | target یک CSS selector است |\n| open / onOpenChange | boolean / (open) => void | — | — |\n| onFinish | () => void | — | پس از آخرین مرحله |\n| labels | { next?, prev?, done?, skip?, step? } | — | متن دکمه‌ها |\n\n## دسترس‌پذیری\n\nهر مرحله یک `role=\"dialog\"` غیرمودال با عنوان و توضیح است و فوکوس به آن می‌رود. <kbd>Esc</kbd> می‌بندد، ←→ بین مرحله‌ها حرکت می‌کند و پس از پایان فوکوس به جای قبلی برمی‌گردد.\n\n## راهنمای استفاده\n\nکِی استفاده کنیم:\n\n- معرفی قابلیت تازه یا تغییر مهم رابط به کاربران فعلی، در سه تا پنج قدم.\n\nکِی استفاده نکنیم:\n\n- آموزش کامل محصول؛ کاربر قدم‌ها را رد می‌کند. راهنمای جدا بنویسید.\n- هر بار ورود؛ فقط یک بار و با امکان رد کردن نشانش دهید.\n\nدرست و نادرست:\n\n- ✓ درست: هر قدم یک جمله دربارهٔ اینکه این بخش برای کاربر چه می‌کند.\n- ✗ نادرست: قدم‌هایی که فقط نام دکمه‌ها را تکرار می‌کنند: «این دکمهٔ ذخیره است».\n\n## وب کامپوننت\n\nهمین کامپوننت بدون React، برای HTML، وردپرس، Vue، Svelte و Angular: `<kashi-tour>` در `@kashi-ui/elements`. هر مرحله یک `<div data-target data-title>` است؛ با دکمه‌ای با `data-kashi-tour` یا `element.start()` شروع می‌شود.\n\nنصب بدون ابزار ساخت (CDN):\n\n```html\n<link rel=\"stylesheet\" href=\"https://cdn.jsdelivr.net/npm/@kashi-ui/css/dist/kashi.css\">\n<script type=\"module\" src=\"https://cdn.jsdelivr.net/npm/@kashi-ui/elements/dist/cdn/kashi-elements.js\"></script>\n```\n\nیا با npm، فقط همین بخش:\n\n```bash\nnpm i @kashi-ui/elements @kashi-ui/css\n```\n\n```js\nimport \"@kashi-ui/css\";\nimport \"@kashi-ui/elements/nav.js\";\n```\n\n```html\n<button class=\"k-btn k-btn--primary\" id=\"tour-start\" data-kashi-tour=\"intro\">شروع تور</button>\n<kashi-tour id=\"intro\">\n <div data-target=\"#tour-start\" data-title=\"شروع از اینجا\">با این دکمه تور دوباره شروع می‌شود.</div>\n <div data-target=\".page-head h1\" data-title=\"عنوان صفحه\">نام کامپوننت و توضیح کوتاهش.</div>\n</kashi-tour>\n```\n\nویژگی‌ها:\n\n| نام | مقدار | پیش‌فرض | توضیح |\n|---|---|---|---|\n| data-target / data-title | روی هر مرحله | — | انتخابگر CSS بخش و عنوان مرحله |\n\nرویدادها و JavaScript:\n\n| نام | توضیح |\n|---|---|\n| element.start() / stop() | شروع و پایان |\n| kashi-finish | پس از آخرین مرحله |\n| element.steps | مراحل با JavaScript: [{ target, title, content }] |"},{"slug":"components/tree-view","url":"https://kashi.sepantapouya.com/docs/components/tree-view/","title":"نمای درختی","description":"نمایش ساختار سلسله‌مراتبی مثل پوشه‌ها، دسته‌بندی محصولات یا نمودار سازمانی.","group":"کامپوننت‌ها","components":["TreeView"],"markdown":"```tsx\nimport { TreeView, type TreeNode } from \"@kashi-ui/react\";\n```\n\n## نمونه\n\n```tsx\nconst items: TreeNode[] = [\n { id: \"docs\", label: \"اسناد\", icon: <FolderIcon />, meta: \"۳ پوشه\", children: [\n { id: \"contracts\", label: \"قراردادها\", icon: <FolderIcon />, children: [\n { id: \"c1\", label: \"قرارداد-۱۴۰۵.pdf\", icon: <FileIcon />, meta: \"۲٫۴ مگابایت\" },\n ] },\n { id: \"archive\", label: \"بایگانی\", disabled: true },\n ] },\n];\n\n<TreeView aria-label=\"فایل‌ها\" items={items} selected={sel} onSelect={setSel}\n defaultExpanded={[\"docs\", \"contracts\"]} />\n```\n\n| ویژگی | نوع | پیش‌فرض | توضیح |\n|---|---|---|---|\n| items | { id, label, icon?, meta?, disabled?, children? }[] | — | گره‌ها |\n| selected / defaultSelected | string \\| null | null | گره انتخاب‌شده |\n| onSelect | (id, node) => void | — | — |\n| expanded / defaultExpanded | string[] | [] | شناسهٔ گره‌های باز |\n| onExpandedChange | (ids: string[]) => void | — | — |\n| aria-label | string | — | نام درخت (الزامی) |\n\n## کیبورد\n\n| کلید | عمل |\n|---|---|\n| <kbd>↓</kbd> / <kbd>↑</kbd> | ردیف بعد / قبل |\n| <kbd>←</kbd> (در RTL) | باز کردن، یا رفتن به اولین زیرشاخه |\n| <kbd>→</kbd> (در RTL) | بستن، یا رفتن به والد |\n| <kbd>Home</kbd> / <kbd>End</kbd> | اولین / آخرین ردیف |\n| <kbd>Enter</kbd> / <kbd>Space</kbd> | انتخاب |\n\nدر صفحه‌های چپ‌به‌راست، جهت ← و → برعکس است.\n\n## دسترس‌پذیری\n\nالگوی Tree در WAI-ARIA با `role=\"tree\"`، `treeitem` و `group`؛ هر گره `aria-level` و `aria-expanded` دارد و فقط یک گره در ترتیب Tab است (roving tabindex). زیرشاخه‌های بسته رندر نمی‌شوند.\n\n## راهنمای استفاده\n\nکِی استفاده کنیم:\n\n- ساختار سلسله‌مراتبی که کاربر بازش می‌کند: پوشه‌ها، دسته‌بندی کالا، نمودار سازمانی.\n\nکِی استفاده نکنیم:\n\n- فقط یک سطح. (سایدبار: /docs/components/side-nav/)\n- انتخاب یک دسته در فرم؛ دو سلسله سلکت یا کامبوباکس ساده‌تر است. (کامبوباکس: /docs/components/combobox/)\n\nدرست و نادرست:\n\n- ✓ درست: مسیر مورد انتخاب‌شده از اول باز باشد (`expanded`).\n- ✗ نادرست: همه‌چیز بسته، و کاربر باید چهار سطح را باز کند تا جای فعلی‌اش را پیدا کند.\n\n## وب کامپوننت\n\nهمین کامپوننت بدون React، برای HTML، وردپرس، Vue، Svelte و Angular: `<kashi-tree-view>` در `@kashi-ui/elements`. ساختار همان فهرست تودرتوی `<ul>` و `<li>` است (`data-id`، `data-meta`، `data-disabled`) یا `element.items`.\n\nنصب بدون ابزار ساخت (CDN):\n\n```html\n<link rel=\"stylesheet\" href=\"https://cdn.jsdelivr.net/npm/@kashi-ui/css/dist/kashi.css\">\n<script type=\"module\" src=\"https://cdn.jsdelivr.net/npm/@kashi-ui/elements/dist/cdn/kashi-elements.js\"></script>\n```\n\nیا با npm، فقط همین بخش:\n\n```bash\nnpm i @kashi-ui/elements @kashi-ui/css\n```\n\n```js\nimport \"@kashi-ui/css\";\nimport \"@kashi-ui/elements/nav.js\";\n```\n\n```html\n<kashi-tree-view label=\"فایل‌های پروژه\" expanded=\"src\" selected=\"app\">\n <ul>\n <li data-id=\"src\">src\n <ul>\n <li data-id=\"app\" data-meta=\"۲ کیلوبایت\">App.tsx</li>\n <li data-id=\"main\">main.tsx</li>\n </ul>\n </li>\n <li data-id=\"readme\">README.md</li>\n </ul>\n</kashi-tree-view>\n```\n\nویژگی‌ها:\n\n| نام | مقدار | پیش‌فرض | توضیح |\n|---|---|---|---|\n| label | متن | — | نام درخت |\n| selected | شناسه | — | گرهٔ انتخاب‌شده |\n| expanded | شناسه‌ها با «,» | — | گره‌های باز |\n\nرویدادها و JavaScript:\n\n| نام | توضیح |\n|---|---|\n| kashi-change | detail: { value, node } |\n| kashi-expand | detail: { expanded: [...] } |\n| element.items | درخت با JavaScript: [{ id, label, children }] |"},{"slug":"i18n","url":"https://kashi.sepantapouya.com/docs/i18n/","title":"دوزبانه، فارسی و انگلیسی","description":"همهٔ متن‌های داخلی کامپوننت‌ها (دکمه‌ها، متن صفحه‌خوان، پیام‌های خطا، نام ماه‌ها و ارقام) با یک تنظیم انگلیسی می‌شوند؛ برای React و وب کامپوننت‌ها.","group":"شروع","components":[],"markdown":"کاشی به‌طور پیش‌فرض فارسی است. اگر محصولتان نسخهٔ انگلیسی هم دارد، لازم نیست برچسب هیچ دکمه‌ای را دستی عوض کنید: با یک تنظیم در ریشهٔ برنامه، همهٔ متن‌های داخلی کامپوننت‌ها انگلیسی می‌شوند. تقویم همچنان شمسی است، فقط نام ماه‌ها و روزها به انگلیسی نوشته می‌شود (Farvardin، Aban…).\n\n## نمونه\n\n```en\n<html lang=\"en\" dir=\"ltr\">\n …\n <KashiProvider locale=\"en\">\n <DatePicker label=\"Delivery date\" value={date} onChange={setDate} />\n <PriceInput label=\"Amount\" defaultValue={1250000} />\n <Pagination page={page} total={12} onChange={setPage} />\n </KashiProvider>\n```\n\n## چه چیزهایی عوض می‌شود\n\n| | فارسی | انگلیسی |\n|---|---|---|\n| دکمه‌ها و برچسب‌ها | بستن، قبلی، بعدی، تأیید | Close، Previous، Next، Apply |\n| متن صفحه‌خوان | «انتخاب تاریخ از تقویم» | «Choose a date from the calendar» |\n| پیام‌های خطا | کد ملی معتبر نیست. | Invalid national ID. |\n| ارقام | ۱٬۲۵۰٬۰۰۰ | 1,250,000 |\n| تاریخ | شنبه ۱۶ آبان ۱۴۰۵ | Saturday, 16 Aban 1405 |\n| مبلغ به حروف | یک میلیون و دویست و پنجاه هزار تومان | one million two hundred fifty thousand Toman |\n| نام بانک و اپراتور | بانک ملت، همراه اول | Bank Mellat، Hamrah-e Aval |\n| تعطیلی‌ها | عید نوروز | Nowruz |\n\nنام استان‌ها و شهرها، حروف پلاک خودرو و نوشتهٔ «ایران» روی پلاک فارسی می‌مانند، چون بخشی از خود داده‌اند. جهت صفحه را خودتان با `dir=\"ltr\"` روی `<html>` بگذارید. همهٔ استایل‌های کاشی با Logical Properties نوشته شده‌اند و در هر دو جهت درست می‌نشینند.\n\n## React\n\n```tsx\nimport { KashiProvider } from \"@kashi-ui/react\";\n\nexport default function App({ children }) {\n return <KashiProvider locale=\"en\">{children}</KashiProvider>;\n}\n```\n\nبدون `KashiProvider` همه‌چیز مثل قبل فارسی است. هر prop متنی که خودتان بدهید (`label`، `labels`، `invalidMessage`…) همچنان بر متن پیش‌فرض مقدم است.\n\n| ویژگی | نوع | پیش‌فرض | توضیح |\n|---|---|---|---|\n| locale | \"fa\" \\| \"en\" | \"fa\" | زبان متن‌های داخلی، نام ماه‌ها و روزها |\n| messages | KashiMessagesInput | — | عوض کردن هر متن دلخواه؛ بقیه از زبان پایه می‌آیند |\n| digits | \"fa\" \\| \"latin\" | \"fa\" برای فارسی، \"latin\" برای انگلیسی | ارقام پیش‌فرض؛ prop ‏digits هر کامپوننت بر آن مقدم است |\n\n### عوض کردن چند متن\n\n```tsx\n<KashiProvider locale=\"en\" messages={{ common: { close: \"Dismiss\" }, address: { plaque: \"Building no.\" } }}>\n```\n\nفرهنگ کامل متن‌ها با `faMessages` و `enMessages` در دسترس است تا کلیدها را ببینید یا زبان دیگری بسازید. در کامپوننت‌های خودتان هم با `useKashi()` به همان زبان، ارقام و نام ماه‌ها دسترسی دارید:\n\n```tsx\nimport { useKashi } from \"@kashi-ui/react\";\n\nfunction OrderCount({ count }: { count: number }) {\n const { n, locale } = useKashi();\n return <span>{locale === \"en\" ? `${n(count)} orders` : `${n(count)} سفارش`}</span>;\n}\n```\n\n## وب کامپوننت‌ها\n\n```html\n<script type=\"module\">\n import { setLocale } from \"https://cdn.jsdelivr.net/npm/@kashi-ui/elements/dist/cdn/kashi-elements.js\";\n setLocale(\"en\");\n</script>\n```\n\nبا npm:\n\n```ts\nimport { setLocale } from \"@kashi-ui/elements\";\nsetLocale(\"en\", { messages: { common: { close: \"Dismiss\" } } });\n```\n\n`setLocale` را پیش از ساخته شدن عنصرها، بالای صفحه صدا بزنید. هر ویژگی متنی عنصر (`label`، `placeholder`، `close-label`…) همچنان بر متن پیش‌فرض مقدم است.\n\n## ابزارهای فارسی\n\nتابع‌های `@kashi-ui/persian` هم گزینهٔ `locale` دارند:\n\n```ts\nformatJalali(date, { style: \"full\", locale: \"en\" }); // Saturday, 16 Aban 1405\nformatRelative(date, { locale: \"en\" }); // 5 minutes ago\nformatMoney(1250000, { locale: \"en\" }); // 1,250,000 Toman\nnumberToWords(1250000, { locale: \"en\" }); // one million two hundred fifty thousand\ngetIranHoliday(date, { locale: \"en\" }); // Nowruz\nbankName(getBankByCard(\"6104…\")!, \"en\"); // Bank Mellat\n```\n\n## تضمین\n\nتست خودکار هر کامپوننت React و هر وب کامپوننت را با زبان انگلیسی رندر می‌کند و اگر حتی یک حرف فارسی در متن یا متن صفحه‌خوانش بماند، شکست می‌خورد. کامپوننت تازه بدون ترجمهٔ انگلیسی منتشر نمی‌شود."},{"slug":"icons/usage","url":"https://kashi.sepantapouya.com/docs/icons/usage/","title":"راهنمای آیکون‌ها","description":"۴۵۱۰ آیکون نرم در شش سبک با گوشه‌های پیوسته، هم‌خانواده با کامپوننت‌های کاشی؛ برای React، HTML، هر فریم‌ورک دیگر و فیگما.","group":"شروع","components":[],"markdown":"آیکون‌های کاشی برای خود کاشی طراحی شده‌اند؛ همان سبکی که داخل دکمه‌ها، فیلدها و هشدارها می‌بینید. همه با `currentColor` رنگ می‌گیرند، پس رنگ متن اطراف را به ارث می‌برند و در تم تیره و روشن درست دیده می‌شوند. مجوز MIT است و در هر پروژه‌ای، حتی تجاری، آزادانه قابل استفاده‌اند.\n\n```bash\nnpm i @kashi-ui/icons\n```\n\nهمهٔ آیکون‌ها را دسته به دسته در [فهرست آیکون‌ها](/docs/icons/) ببینید.\n\n## شش سبک\n\nهر آیکون در شش سبک هست و همه از یک طرح خطی ساخته می‌شوند: `light` (خطی، پیش‌فرض)، `bold` (توپر)، `bulk` (حجمی)، `two-tone` (دورنگ)، `broken` (شکسته) و `curved` (منحنی).\n\n```tsx\n<IconWallet variant=\"bold\" />\n<Icon name=\"calendar\" variant=\"bulk\" />\n```\n\n```js\niconSvg(\"heart\", { variant: \"two-tone\" });\n```\n\nهر سبک اسپرایت جدای خودش را دارد (`@kashi-ui/icons/sprite-bold.svg` با شناسهٔ `#k-home--bold`) و فایل‌های جدا در `@kashi-ui/icons/svg/<variant>/<name>.svg` هستند. سبک `bold` جزئیات را با ماسک SVG از دل شکل سوراخ می‌کند؛ هر نمونه شناسهٔ ماسک یکتای خودش را می‌گیرد.\n\n## اصول طراحی\n\nهمهٔ آیکون‌ها از چند قاعدهٔ ساده پیروی می‌کنند تا کنار هم یکدست دیده شوند:\n\n## React\n\nهر آیکون یک کامپوننت جداست؛ فقط آیکون‌هایی که import می‌کنید در باندل می‌آیند.\n\n```tsx\nimport { IconSearch, IconTrash } from \"@kashi-ui/icons/react\";\n\n<IconSearch />\n<IconTrash size={24} strokeWidth={2} className=\"danger\" />\n```\n\nوقتی نام آیکون از داده می‌آید، از `Icon` استفاده کنید:\n\n```tsx\nimport { Icon } from \"@kashi-ui/icons/react\";\n\n<Icon name={item.icon} />\n```\n\n| ویژگی | نوع | پیش‌فرض | توضیح |\n|---|---|---|---|\n| `size` | `number \\| string` | `20` | عرض و ارتفاع؛ عدد یعنی پیکسل، یا هر واحد CSS مثل `\"1.25em\"` |\n| `strokeWidth` | `number` | `1.5` | ضخامت خط |\n| `title` | `string` | — | متن برای صفحه‌خوان؛ بدون آن آیکون تزئینی و پنهان است |\n| `mirror` | `boolean` | `false` | قرینهٔ افقی؛ برای فلش‌ها در طرح‌های دوجهته |\n| `name` | `IconName` | — | فقط در `Icon`؛ نام آیکون |\n\nبقیهٔ ویژگی‌های `<svg>` (مثل `className`، `style` و `onClick`) هم پذیرفته می‌شوند و `ref` به خود `<svg>` می‌رسد.\n\n## HTML و فریم‌ورک‌های دیگر\n\nبرای Vue، Svelte، Angular یا HTML ساده سه راه دارید.\n\n**۱. کد SVG با جاوااسکریپت.** `iconSvg` رشتهٔ SVG برمی‌گرداند:\n\n```js\nimport { iconSvg } from \"@kashi-ui/icons\";\n\nbutton.innerHTML = iconSvg(\"trash\", { size: 16 }) + \" حذف\";\n```\n\n**۲. اسپرایت.** فایل `@kashi-ui/icons/sprite.svg` را یک بار در صفحه بگذارید یا کنار سایتتان منتشر کنید و هر جا لازم شد صدا بزنید:\n\n```html\n<svg width=\"20\" height=\"20\" aria-hidden=\"true\">\n <use href=\"/sprite.svg#k-search\" />\n</svg>\n```\n\n**۳. فایل SVG جدا.** هر آیکون در `@kashi-ui/icons/svg/<name>.svg` هم هست؛ برای `<img>` یا ابزارهای ساخت که SVG را import می‌کنند.\n\n## داخل کامپوننت‌های کاشی\n\nآیکون‌ها برای کار کنار کامپوننت‌ها ساخته شده‌اند و اندازه‌شان با دکمه و فیلد هماهنگ است:\n\n```tsx\nimport { Button, Input } from \"@kashi-ui/react\";\nimport { IconPlus, IconSearch } from \"@kashi-ui/icons/react\";\n\n<Button variant=\"primary\" iconStart={<IconPlus />}>پروژهٔ جدید</Button>\n<Input addonStart={<IconSearch />} placeholder=\"جستجو\" />\n```\n\n## دسترس‌پذیری\n\n- آیکون بدون `title` با `aria-hidden` از صفحه‌خوان پنهان می‌شود؛ برای آیکون‌هایی که کنار متن می‌آیند همین درست است.\n- آیکونی که تنها می‌آید و معنا دارد (مثل دکمهٔ فقط‌آیکون) باید نام داشته باشد: یا `title` بدهید، یا بهتر، `aria-label` را روی خود دکمه بگذارید.\n- آیکون‌ها `focusable=\"false\"` دارند تا در اینترنت اکسپلورر و نسخه‌های قدیمی Edge در ترتیب Tab نیایند.\n\n## راست‌به‌چپ\n\nآیکون‌ها به‌طور پیش‌فرض قرینه نمی‌شوند؛ `arrow-left` همیشه به چپ اشاره می‌کند. در رابط فارسی، «بعدی» و «ادامه» یعنی فلش چپ و «بازگشت» یعنی فلش راست. شش آیکون جهت‌دار (فلش‌ها، شورون‌های چپ و راست، ارسال و خروج) در صفحهٔ هر آیکون با نشان «جهت‌دار» مشخص‌اند. اگر طرح شما هم راست‌به‌چپ و هم چپ‌به‌راست است، با `mirror` قرینه‌شان کنید.\n\n## فیگما\n\nهمهٔ آیکون‌ها در صفحهٔ «🧩 Icons» [پلاگین فیگمای کاشی](/docs/figma/) هم هستند، دسته‌به‌دسته، هر کدام به شکل کامپوننت با نام `Icon / <دسته> / <name>` تا در پنل Assets هم پوشه‌بندی شوند؛ با Instance swap داخل دکمه‌ها و فیلدها عوض می‌شوند. فیگما و کد از یک منبع ساخته می‌شوند، پس شکل آیکون‌ها در هر دو یکی است.\n\n## پیشنهاد آیکون تازه\n\nآیکونی کم دارید؟ در [GitHub](https://github.com/Sepantapouya/Kashi/issues) بنویسید. آیکون‌های تازه با همین شبکه و ضخامت کشیده می‌شوند تا مجموعه یکدست بماند."},{"slug":"intro","url":"https://kashi.sepantapouya.com/docs/","title":"معرفی","description":"کاشی چیست، برای چه کسانی است و از چه بخش‌هایی تشکیل شده.","group":"شروع","components":[],"markdown":"کاشی یک دیزاین‌سیستم متن‌باز برای محصولات فارسی است. نام آن از کاشی‌کاری ایرانی گرفته شده: قطعه‌های کوچک و دقیق که کنار هم یک کل یکپارچه می‌سازند.\n\n```bash\nnpm i @kashi-ui/react @kashi-ui/css\n```\n\n## اصول\n\n- **راست‌به‌چپ از پایه.** هیچ استایلی با `left` و `right` نوشته نشده؛ همه‌چیز با Logical Properties است. صفحه‌ای که با `dir=\"ltr\"` باز شود هم بدون تغییر درست نمایش داده می‌شود.\n- **دسترس‌پذیری به‌عنوان شرط انتشار.** هدف استاندارد WCAG 2.2 در سطح AA است. کنتراست رنگ‌ها در هر بیلد به‌صورت خودکار بررسی می‌شود و تست‌های خودکار ویژگی‌های ARIA را کنترل می‌کنند.\n- **سفارشی‌سازی بدون جنگ با CSS.** همهٔ استایل‌ها در Cascade Layer قرار دارند؛ CSS شما همیشه اولویت دارد، بدون `!important`.\n- **مستقل از فریم‌ورک.** ظاهر یک‌بار در CSS تعریف شده و هر فریم‌ورک لایهٔ نازکی روی آن است: کامپوننت‌های React، و برای بقیه [وب کامپوننت‌ها](/docs/web-components/).\n\n## پکیج‌ها\n\n| پکیج | کاربرد |\n|---|---|\n| `@kashi-ui/tokens` | توکن‌های طراحی به‌صورت CSS Variables، JSON و JavaScript، به‌همراه `createTheme()` |\n| `@kashi-ui/css` | استایل همهٔ کامپوننت‌ها با کلاس‌های `k-*`، به‌همراه فونت وزیرمتن |\n| `@kashi-ui/react` | کامپوننت‌های React با تایپ کامل TypeScript |\n| `@kashi-ui/elements` | [وب کامپوننت‌ها](/docs/web-components/) برای Vue، Svelte، Angular و HTML: فیلدهای فارسی، تقویم شمسی، فرم نشانی، نقشهٔ ایران، جدول داده، منو، پنجره و اعلان |\n\n## وضعیت پروژه\n\nکاشی در نسخهٔ `0.x` است؛ یعنی API ممکن است پیش از نسخهٔ `1.0` تغییر کند. تغییرات ناسازگار همیشه در یادداشت انتشار ذکر می‌شوند."},{"slug":"installation","url":"https://kashi.sepantapouya.com/docs/installation/","title":"نصب","description":"کاشی را با پکیج npm، رجیستری shadcn، Tailwind یا فقط CSS به پروژهٔ React یا هر پروژهٔ وب دیگری بیاورید.","group":"شروع","components":[],"markdown":"## پکیج npm\n\nراه پیشنهادی برای بیشتر پروژه‌ها. کامپوننت‌ها از پکیج می‌آیند و با به‌روز کردن پکیج، اصلاح‌ها و کامپوننت‌های تازه هم می‌رسند.\n\n```bash\nnpm i @kashi-ui/react @kashi-ui/css\n```\n\nفایل CSS را یک بار در ریشهٔ برنامه وارد کنید و جهت صفحه را راست‌به‌چپ قرار دهید:\n\n```tsx\n// main.tsx یا app/layout.tsx\nimport \"@kashi-ui/css\";\n```\n\n```html\n<html lang=\"fa\" dir=\"rtl\">\n```\n\nحالا کامپوننت‌ها آمادهٔ استفاده‌اند:\n\n```tsx\nimport { Button } from \"@kashi-ui/react\";\n\nexport function Example() {\n return <Button variant=\"primary\">ثبت‌نام</Button>;\n}\n```\n\nکاشی با React نسخهٔ ۱۸ و ۱۹ کار می‌کند و با Next.js، Vite و Remix سازگار است. در App Router نکست می‌شود کامپوننت‌ها را مستقیم در صفحه‌های سرور هم استفاده کرد.\n\n## رجیستری shadcn\n\nاگر می‌خواهید کد خودِ کامپوننت در پروژه‌تان باشد و هر تغییری خواستید در آن بدهید، از رجیستری کاشی با ابزار [shadcn](https://ui.shadcn.com/docs/cli) استفاده کنید. فایل‌ها در پوشهٔ `components/kashi` کپی می‌شوند و کامپوننت‌هایی که به آن‌ها وابسته‌اند، CSS کاشی و ابزارهای فارسی هم خودکار نصب می‌شوند.\n\nکاشی در [فهرست رسمی رجیستری‌های shadcn](https://ui.shadcn.com/docs/directory) با نام `@kashi` ثبت شده است، پس بدون هیچ تنظیمی کافی است نام کامپوننت را بدهید:\n\n```bash\nnpx shadcn@latest add @kashi/iran-map\n```\n\n```tsx\nimport { IranMap } from \"@/components/kashi/IranMap\";\n```\n\nروی هر کامپوننت بزنید تا دستور نصبش کپی شود:\n\nاگر نسخهٔ shadcn شما قدیمی است و پیام `Unknown registry \"@kashi\"` داد، یا آدرس کامل آیتم را بدهید:\n\n```bash\nnpx shadcn@latest add https://kashi.sepantapouya.com/r/iran-map.json\n```\n\nیا رجیستری کاشی را یک بار در `components.json` معرفی کنید تا `@kashi` همیشه شناخته شود:\n\n```json\n{\n \"registries\": {\n \"@kashi\": \"https://kashi.sepantapouya.com/r/{name}.json\"\n }\n}\n```\n\n```bash\nnpx shadcn@latest add @kashi/date-picker @kashi/iran-map\n```\n\nپروژه باید پیش‌تر با `npx shadcn@latest init` آماده شده باشد. فهرست کامل آیتم‌ها در [registry.json](/r/registry.json) است.\n\n### npm یا shadcn؟\n\n| | پکیج npm | رجیستری shadcn |\n|---|---|---|\n| کد کامپوننت | داخل `node_modules` | داخل پروژهٔ شما |\n| تغییر دادن کد | با props، کلاس و متغیرهای CSS | هر تغییری، مستقیم در فایل |\n| به‌روزرسانی | با `npm update` | با اجرای دوبارهٔ دستور (تغییرهای شما بازنویسی می‌شود) |\n| مناسب برای | بیشتر پروژه‌ها | تیم‌هایی که کد را از آنِ خود می‌خواهند |\n\n## Tailwind\n\nاگر پروژه‌تان Tailwind دارد، پریست کاشی را هم نصب کنید تا رنگ‌ها، فونت، گردی و سایه‌های کاشی به‌شکل کلاس‌های Tailwind (`bg-brand`، `rounded-lg`، `text-secondary`) در دسترس باشند و با تم تیره و روشن همگام بمانند:\n\n```bash\nnpm i @kashi-ui/tailwind\n```\n\n```css\n@import \"@kashi-ui/tailwind\";\n@import \"tailwindcss\";\n@import \"@kashi-ui/css\";\n```\n\nراهنمای کامل، فهرست کلاس‌ها و تنظیم Tailwind 3: [Tailwind](/docs/tailwind/)\n\n## HTML و فریم‌ورک‌های دیگر\n\nبرای Vue، Svelte، Angular، وردپرس یا HTML ساده دو پکیج دارید: `@kashi-ui/css` برای ظاهر همهٔ کامپوننت‌ها و `@kashi-ui/elements` برای کامپوننت‌هایی که JavaScript لازم دارند (فیلدهای فارسی، تقویم شمسی، کد تأیید، فرم نشانی، نقشهٔ ایران، Combobox، جدول داده، منو، پنجره، زبانه و اعلان) به‌شکل وب کامپوننت.\n\n```bash\nnpm i @kashi-ui/css @kashi-ui/elements\n```\n\nیا بدون نصب، از CDN:\n\n```html\n<link rel=\"stylesheet\" href=\"https://cdn.jsdelivr.net/npm/@kashi-ui/css/dist/kashi.css\">\n<script type=\"module\" src=\"https://cdn.jsdelivr.net/npm/@kashi-ui/elements/dist/cdn/kashi-elements.js\"></script>\n```\n\nسپس از کلاس‌ها و تگ‌ها استفاده کنید. نمونهٔ HTML هر کامپوننت در صفحهٔ همان کامپوننت آمده است.\n\n```html\n<button class=\"k-btn k-btn--primary\">ثبت‌نام</button>\n<kashi-national-id name=\"national_id\" required></kashi-national-id>\n<kashi-date-picker label=\"تاریخ تولد\" name=\"birth\"></kashi-date-picker>\n```\n\nفهرست همهٔ وب کامپوننت‌ها، رویدادها و تنظیم Vue و Angular: [وب کامپوننت‌ها](/docs/web-components/)\n\n## پکیج‌ها\n\nهمهٔ پکیج‌ها هم‌نسخه منتشر می‌شوند. معمولاً فقط `react` و `css` را خودتان نصب می‌کنید و بقیه یا خودکار می‌آیند یا اختیاری‌اند:\n\n## وارد کردن بخشی از CSS\n\nاگر فقط به چند کامپوننت نیاز دارید، فایل‌ها را جداگانه وارد کنید. ترتیب مهم است: توکن‌ها، فونت و پایه، سپس کامپوننت‌ها.\n\n```css\n@import \"@kashi-ui/css/tokens.css\";\n@import \"@kashi-ui/css/font.css\";\n@import \"@kashi-ui/css/base.css\";\n@import \"@kashi-ui/css/components/button.css\";\n@import \"@kashi-ui/css/components/input.css\";\n```\n\n> فایل‌های جداگانه در Cascade Layer قرار ندارند. اگر به اولویت لایه‌ها نیاز دارید، از فایل کامل `kashi.css` استفاده کنید.\n\n## تم روشن\n\nتم پیش‌فرض تیره است. برای تم روشن، ویژگی `data-theme` را روی هر عنصری بگذارید؛ زیرشاخهٔ آن عنصر روشن می‌شود.\n\n```html\n<html lang=\"fa\" dir=\"rtl\" data-theme=\"light\">\n```\n\n## قدم بعدی\n\n- [سفارشی‌سازی و تم](/docs/theming/): رنگ برند و تراکم را عوض کنید.\n- [کامپوننت‌ها](/docs/components/): همهٔ کامپوننت‌ها با پیش‌نمایش زنده و دکمهٔ StackBlitz.\n- [استفاده با هوش مصنوعی](/docs/ai/): مستندات کاشی را با سرور MCP به Claude یا Cursor بدهید."},{"slug":"migration","url":"https://kashi.sepantapouya.com/docs/migration/","title":"مهاجرت به ۱.۰","description":"کاشی ۱.۰ تغییر ناسازگاری ندارد؛ نسخه را بالا ببرید و تمام. قابلیت‌های تازه (دوزبانه، سقف حجم، تست تصویری) اختیاری‌اند.","group":"پایداری و امنیت","components":[],"markdown":"کاشی ۱.۰ همان کاشی ۰.۲۶ است با قولِ پایداری: از این نسخه، هر تغییر ناسازگار فقط در نسخهٔ اصلی بعدی و پس از دوره‌ای «منسوخ» می‌آید ([سیاست تغییرات](/docs/versioning/)).\n\n## به‌روزرسانی\n\n```bash\nnpm i @kashi-ui/react@^1 @kashi-ui/css@^1\n```\n\nهمهٔ پکیج‌های کاشی را با هم به ۱ ببرید (`@kashi-ui/elements`، `@kashi-ui/persian`، `@kashi-ui/tailwind` و…)؛ نسخه‌ها یکی‌اند.\n\n**تغییر کدی لازم نیست.** هیچ prop، کلاس CSS، تگ یا تابعی حذف یا تغییر نام نداده است و متن‌ها و ظاهر پیش‌فرض همان فارسیِ قبل‌اند.\n\n## اگر با shadcn نصب کرده‌اید\n\nکامپوننت‌ها حالا متن‌هایشان را از `KashiProvider` می‌خوانند. پس از به‌روزرسانی، کامپوننت‌هایی را که دارید دوباره اضافه کنید تا فایل تازهٔ `components/kashi/KashiProvider.tsx` هم بیاید:\n\n```bash\nnpx shadcn@latest add @kashi/kashi-provider @kashi/date-picker\n```\n\nاگر در کد کامپوننت‌ها تغییری داده‌اید، پیش از دوباره اضافه کردن آن را نگه دارید؛ shadcn فایل را بازنویسی می‌کند.\n\n## چه چیزهایی تازه است\n\n- **[دوزبانه](/docs/i18n/):** با `<KashiProvider locale=\"en\">` یا `setLocale(\"en\")` همهٔ متن‌های داخلی، ارقام و نام ماه‌ها انگلیسی می‌شوند.\n- **[سیاست تغییرات](/docs/versioning/)** و **[امنیت](/docs/security/):** قول‌های مکتوب برای پایداری و رفع آسیب‌پذیری.\n- **[حجم کامپوننت‌ها](/docs/sizes/):** حجم هر کامپوننت در صفحهٔ خودش و سقفی که تست نمی‌گذارد از آن بیشتر شود.\n- **تست تصویری** از همهٔ کامپوننت‌ها در دو تم.\n- **ابزارهای فارسی** گزینهٔ `locale: \"en\"` گرفته‌اند و `bankName` و `operatorName` برای نام انگلیسی بانک و اپراتور اضافه شده‌اند.\n\n## نکته‌های کوچک\n\n- `defaultPasswordRules` و `defaultRangePresets` همچنان فارسی‌اند. اگر `rules` یا `presets` ندهید، کامپوننت شرط‌ها و میان‌برها را به زبان `KashiProvider` می‌سازد.\n- `formatBytes` در React و وب کامپوننت‌ها با زبان انگلیسی واحدها را `KB` و `MB` می‌نویسد."},{"slug":"persian/activity-heatmap","url":"https://kashi.sepantapouya.com/docs/persian/activity-heatmap/","title":"هیت‌مپ فعالیت","description":"فعالیت روزانهٔ یک سال شمسی به سبک GitHub، با ماه‌های جلالی و هفتهٔ شنبه‌تا‌جمعه.","group":"فارسی","components":["ActivityHeatmap"],"markdown":"```tsx\nimport { ActivityHeatmap } from \"@kashi-ui/react\";\n```\n\n## نمونه\n\n```tsx\n<ActivityHeatmap year={1405} unit=\"کامیت\"\n data={{ \"2026-03-21\": 3, \"2026-03-22\": 7, … }} />\n```\n\nکلید داده تاریخ میلادی `YYYY-MM-DD` است (مثل خروجی `toISODate`) تا مستقیم از پایگاه‌داده بیاید. شدت رنگ نسبت به بیشترین مقدار سال در چهار پله است، با یک رنگ از کم‌رنگ به پررنگ.\n\n| ویژگی | نوع | پیش‌فرض | توضیح |\n|---|---|---|---|\n| data | Record<string, number> | — | تعداد در هر روز؛ کلید YYYY-MM-DD |\n| year | number | سال جاری | سال شمسی |\n| unit | string | «فعالیت» | واحد در راهنما |\n| label | string | — | نام دسترس‌پذیر |\n\n## دسترس‌پذیری\n\nبرای صفحه‌خوان، نقشه یک تصویر با جمع کل است و یک جدول پنهان جمع هر ماه را می‌گوید؛ ۳۶۵ خانه یکی‌یکی خوانده نمی‌شوند. مقدار هر روز با نگه‌داشتن موس نمایش داده می‌شود.\n\n## راهنمای استفاده\n\nکِی استفاده کنیم:\n\n- فعالیت روزانهٔ یک سال شمسی: ورود کاربر، سفارش‌ها، تمرین، مشارکت در پروژه.\n\nکِی استفاده نکنیم:\n\n- مقایسهٔ ماه‌ها یا روند کلی. (نمودار: /docs/components/charts/)\n- داده‌ای که بیشتر روزهایش خالی است؛ نقشه تقریباً سفید می‌ماند و چیزی نمی‌گوید.\n\nدرست و نادرست:\n\n- ✓ درست: `unit` بدهید تا راهنما بگوید «۱۲ سفارش»، نه فقط «۱۲».\n- ✗ نادرست: عدد بدون واحد که معلوم نیست چه شمرده شده.\n\n## وب کامپوننت\n\nهمین کامپوننت بدون React، برای HTML، وردپرس، Vue، Svelte و Angular: `<kashi-activity-heatmap>` در `@kashi-ui/elements`.\n\nنصب بدون ابزار ساخت (CDN):\n\n```html\n<link rel=\"stylesheet\" href=\"https://cdn.jsdelivr.net/npm/@kashi-ui/css/dist/kashi.css\">\n<script type=\"module\" src=\"https://cdn.jsdelivr.net/npm/@kashi-ui/elements/dist/cdn/kashi-elements.js\"></script>\n```\n\nیا با npm، فقط همین بخش:\n\n```bash\nnpm i @kashi-ui/elements @kashi-ui/css\n```\n\n```js\nimport \"@kashi-ui/css\";\nimport \"@kashi-ui/elements/charts.js\";\n```\n\n```html\n<kashi-activity-heatmap year=\"1405\" unit=\"سفارش\" data='{\"2026-03-21\": 4, \"2026-03-22\": 9, \"2026-04-02\": 2, \"2026-05-10\": 7}'></kashi-activity-heatmap>\n```\n\nویژگی‌ها:\n\n| نام | مقدار | پیش‌فرض | توضیح |\n|---|---|---|---|\n| data | JSON | — | تاریخ میلادی YYYY-MM-DD ← تعداد |\n| year | سال شمسی | سال جاری | سال نمایش |\n| unit / label | متن | فعالیت | واحد و نام دسترس‌پذیر |\n\nرویدادها و JavaScript:\n\n| نام | توضیح |\n|---|---|\n| element.data | داده با JavaScript |"},{"slug":"persian/address","url":"https://kashi.sepantapouya.com/docs/persian/address/","title":"فرم نشانی","description":"استان، شهرِ همان استان، نشانی، پلاک، واحد و کد پستی ده‌رقمی در یک فرم؛ با فهرست بیش از ۱۲۰۰ شهر ایران و نقشه برای انتخاب استان.","group":"فارسی","components":["AddressForm"],"markdown":"```tsx\nimport { AddressForm } from \"@kashi-ui/react\";\nimport { EMPTY_ADDRESS, formatAddress, type IranAddress } from \"@kashi-ui/persian\";\n```\n\n## فرم کامل\n\nبا انتخاب استان فقط شهرهای همان استان می‌آیند و با عوض شدن استان، شهرِ نامربوط پاک می‌شود. اگر کاربر اول شهر را بنویسد، استان خودکار پر می‌شود. کد پستی هنگام تایپ قالب می‌گیرد و با قواعد کد پستی ایران بررسی می‌شود.\n\n```tsx\nconst [address, setAddress] = useState<IranAddress>(EMPTY_ADDRESS);\n\n<AddressForm value={address} onChange={setAddress} name=\"shipping\" />\n<p>{formatAddress(address)}</p>\n```\n\nمقدار فرم یک شیء `IranAddress` است:\n\n```ts\ninterface IranAddress {\n province: string | null; // «اصفهان»\n city: string | null; // «کاشان»\n street: string; // «خیابان امیرکبیر، کوچهٔ ۱۲»\n plaque: string;\n unit: string;\n postalCode: string; // ده رقم لاتین، «8713714568»\n}\n```\n\n`formatAddress` همین شیء را در یک خط با ارقام فارسی برمی‌گرداند، مثل «اصفهان، کاشان، خیابان امیرکبیر، پلاک ۱۲، واحد ۳، کد پستی ۸۷۱۳۷-۱۴۵۶۸»؛ برای فاکتور، رسید یا صفحهٔ تأیید سفارش.\n\n## با نقشه\n\nبا `map` نقشهٔ ایران کنار فیلدها می‌آید و استان را می‌شود روی نقشه هم انتخاب کرد. در عرض کم، نقشه زیر فیلدها می‌رود.\n\n```tsx\n<AddressForm value={address} onChange={setAddress} map legend=\"نشانی تحویل\" />\n```\n\n## ارسال با فرم HTML\n\nبا `name` هر فیلد در فرم HTML هم فرستاده می‌شود: `name=\"shipping\"` می‌شود `shipping[province]`، `shipping[city]`، `shipping[street]`، `shipping[plaque]`، `shipping[unit]` و `shipping[postalCode]`. این همان قالبی است که Laravel و PHP به‌صورت آرایه می‌خوانند. خطاهای اعتبارسنجی سرور را با `errors` زیر هر فیلد نشان دهید:\n\n```tsx\n<AddressForm name=\"shipping\" errors={{ plaque: \"پلاک را بنویسید.\" }} />\n```\n\n## انتخاب شهر\n\n`CitySelect` جدا هم در دسترس است. با `province` فقط شهرهای همان استان را نشان می‌دهد و بدون آن همهٔ شهرها را با نام استانشان. جستجو «علی آباد»، «علی‌آباد» و «علیآباد» را یکی می‌داند. شهری که در فهرست نیست (مثلاً شهرهای تازه‌تأسیس) با همان نامی که کاربر نوشته پذیرفته می‌شود؛ برای بستن این امکان `allowCustom={false}` بدهید.\n\n```tsx\n<CitySelect value={city} onChange={(city, province) => { setCity(city); setProvince(province); }} />\n```\n\n| ویژگی | نوع | پیش‌فرض | توضیح |\n|---|---|---|---|\n| value / defaultValue | IranAddress | — | مقدار فرم |\n| onChange | (address: IranAddress) => void | — | با هر تغییر، کل نشانی |\n| errors | Partial<Record<keyof IranAddress, ReactNode>> | — | خطای هر فیلد |\n| name | string | — | پیشوند نام فیلدها برای فرم HTML |\n| legend | ReactNode | «نشانی» | عنوان گروه فیلدها |\n| labels | Partial<Record<keyof IranAddress, string>> | — | برچسب دلخواه هر فیلد |\n| map | boolean | false | نقشهٔ ایران برای انتخاب استان |\n| required | boolean | true | همه اجباری جز واحد |\n| disabled | boolean | false | غیرفعال کردن همهٔ فیلدها |\n\n## ابزارهای فهرست شهرها\n\nفهرست شهرها در `@kashi-ui/persian` است و بدون React هم کار می‌کند:\n\n```ts\nimport { CITIES, getCities, getCityProvinces, formatAddress } from \"@kashi-ui/persian\";\n\ngetCities(\"قم\"); // [\"جعفریه\", \"دستجرد\", \"سلفچگان\", \"قاهان\", \"قم\", \"قنوات\", \"کهک\"]\ngetCityProvinces(\"کاشان\"); // [\"اصفهان\"]\nObject.keys(CITIES).length; // ۳۱ استان\n```\n\n## دسترس‌پذیری\n\n- فیلدها در یک `fieldset` با `legend` هستند و صفحه‌خوان نام گروه را همراه هر فیلد می‌خواند.\n- هر فیلد `autocomplete` استاندارد دارد (`street-address`، `postal-code` و …) تا مرورگر نشانی ذخیره‌شده را پیشنهاد دهد.\n- فیلد اختیاری با «اختیاری» مشخص شده، نه فقط با نبود ستاره.\n- استان و شهر با الگوی Combobox در WAI-ARIA کار می‌کنند و نقشه با کلیدهای جهت‌دار.\n\nفهرست ۱۲۷۸ شهر در ۳۱ استان از [ویکی‌داده](https://www.wikidata.org) (مالکیت عمومی، CC0) گرفته شده است. شهرهای تازه‌تأسیس ممکن است هنوز در آن نباشند؛ برای همین شهر خارج از فهرست هم پذیرفته می‌شود.\n\n## راهنمای استفاده\n\nکِی استفاده کنیم:\n\n- گرفتن نشانی تحویل، صورت‌حساب یا محل خدمت در ایران.\n- فرم‌هایی که شهر باید با استان هم‌خوان باشد و کد پستی ده‌رقمی معتبر بخواهند.\n\nکِی استفاده نکنیم:\n\n- نشانی خارج از ایران.\n- فقط استان یا فقط شهر. (انتخاب استان: /docs/persian/province/)\n\nدرست و نادرست:\n\n- ✓ درست: خطای سرور را با `errors` کنار همان فیلد نشان دهید.\n- ✗ نادرست: یک پیام کلی «نشانی نامعتبر است» بالای فرم.\n- ✓ درست: واحد را اختیاری بگذارید (پیش‌فرض کاشی)؛ خیلی از نشانی‌ها واحد ندارند.\n- ✗ نادرست: همهٔ فیلدها اجباری، تا کاربر برای خانهٔ ویلایی «۰» بنویسد.\n\n## وب کامپوننت\n\nهمین کامپوننت بدون React، برای HTML، وردپرس، Vue، Svelte و Angular: `<kashi-address-form>` در `@kashi-ui/elements`. فیلدها به‌شکل `shipping[province]`، `shipping[city]`، … ارسال می‌شوند.\n\nنصب بدون ابزار ساخت (CDN):\n\n```html\n<link rel=\"stylesheet\" href=\"https://cdn.jsdelivr.net/npm/@kashi-ui/css/dist/kashi.css\">\n<script type=\"module\" src=\"https://cdn.jsdelivr.net/npm/@kashi-ui/elements/dist/cdn/kashi-elements.js\"></script>\n```\n\nیا با npm، فقط همین بخش:\n\n```bash\nnpm i @kashi-ui/elements @kashi-ui/css\n```\n\n```js\nimport \"@kashi-ui/css\";\nimport \"@kashi-ui/elements/map.js\";\n```\n\n```html\n<kashi-address-form name=\"shipping\" legend=\"نشانی تحویل\"></kashi-address-form>\n```\n\nویژگی‌ها:\n\n| نام | مقدار | پیش‌فرض | توضیح |\n|---|---|---|---|\n| name | متن | — | پیشوند نام فیلدها در فرم |\n| legend | متن | نشانی | عنوان گروه فیلدها |\n| map | بولی | — | نقشهٔ ایران کنار فرم |\n| required | false | — | همهٔ فیلدها اختیاری |\n| disabled | بولی | — | غیرفعال |\n| label-province، label-city، … | متن | — | برچسب دلخواه هر فیلد |\n\nرویدادها و JavaScript:\n\n| نام | توضیح |\n|---|---|\n| kashi-change | detail: { value: { province, city, street, plaque, unit, postalCode } } |\n| element.value | خواندن و پر کردن نشانی |\n| element.errors | خطای هر فیلد: { plaque: \"پلاک را بنویسید.\" } |"},{"slug":"persian/appointment","url":"https://kashi.sepantapouya.com/docs/persian/appointment/","title":"انتخاب نوبت","description":"رزرو نوبت با روزهای شمسی و ساعت‌های خالی هر روز؛ برای مطب، آرایشگاه، مشاوره و هر سرویس نوبت‌دهی. جمعه‌ها و تعطیلات رسمی بسته‌اند و نوبت‌های پر و گذشته غیرفعال.","group":"فارسی","components":["AppointmentPicker"],"markdown":"```tsx\nimport { AppointmentPicker } from \"@kashi-ui/react\";\n```\n\n## نمونه\n\nروزها با تاریخ شمسی و تعداد نوبت خالی‌شان در یک ردیف می‌آیند و ساعت‌های همان روز در گروه‌های صبح، بعدازظهر و عصر. جمعه‌ها و تعطیلات رسمی ثابت بسته‌اند، نوبت‌های پرشده خط خورده‌اند و ساعت‌هایی از امروز که گذشته‌اند قابل انتخاب نیستند.\n\n```tsx\nconst [visit, setVisit] = useState<AppointmentValue | null>(null);\n\n<AppointmentPicker\n value={visit}\n onChange={setVisit}\n from=\"09:00\"\n to=\"17:00\"\n step={30}\n booked={{ \"2026-11-04\": [\"09:30\", \"10:00\"] }}\n name=\"visit\"\n/>\n```\n\nبا `from`، `to` و `step` ساعت کاری و طول هر نوبت را بدهید و نوبت‌های پرشده را با `booked` (کلید تاریخ میلادی `YYYY-MM-DD`). با `name` دو فیلد `visit_date` و `visit_time` در فرم HTML ارسال می‌شوند.\n\n## ساعت‌های دلخواه هر روز\n\nاگر ساعت‌ها هر روز فرق می‌کنند (نیمه‌وقت پنجشنبه، استراحت ظهر، چند پزشک)، `getSlots` را بدهید. برای هر روز فهرست ساعت‌ها را برمی‌گرداند؛ هر ساعت یک رشته یا `{ time, available }` است.\n\n```tsx\n<AppointmentPicker\n label=\"نوبت مشاوره\"\n days={10}\n getSlots={(date) =>\n date.getDay() === 4\n ? [\"09:00\", \"10:00\", \"11:00\"]\n : [\"09:00\", \"10:00\", \"11:00\", \"17:00\", \"18:00\", { time: \"19:00\", available: false }]\n }\n/>\n```\n\nروزهای تعطیل را با `closed` عوض کنید: `false` برای باز بودن همهٔ روزها، یا تابعی مثل `(d) => d.getDay() === 4` برای تعطیلی پنجشنبه‌ها. تابع `appointmentSlots` هم جدا در دسترس است تا همان ساعت‌ها را در سرور حساب کنید.\n\n## ویژگی‌ها\n\n| ویژگی | نوع | پیش‌فرض | توضیح |\n|---|---|---|---|\n| value / defaultValue | { date: Date; time: string } \\| null | — | نوبت انتخاب‌شده |\n| onChange | (value) => void | — | با انتخاب هر ساعت |\n| start | Date | امروز | اولین روز |\n| days | number | 7 | تعداد روزهای قابل انتخاب |\n| from / to | string | \"09:00\" / \"17:00\" | ساعت شروع و پایان کار |\n| step | number | 30 | طول هر نوبت به دقیقه |\n| booked | Record<string, string[]> | — | نوبت‌های پرشده؛ کلید YYYY-MM-DD |\n| getSlots | (date: Date) => (string \\| AppointmentSlot)[] | — | ساعت‌های هر روز به‌جای from، to، step و booked |\n| closed | boolean \\| (date: Date) => boolean | true | روزهای تعطیل؛ true یعنی جمعه‌ها و تعطیلات رسمی ثابت |\n| label | string | \"انتخاب نوبت\" | برچسب و نام گروه |\n| name | string | — | فیلدهای name_date و name_time در فرم |\n| digits | \"fa\" \\| \"latin\" | \"fa\" | خط ارقام |\n\n## دسترس‌پذیری\n\n- روزها و ساعت‌ها گروه‌های رادیویی‌اند (`role=\"radiogroup\"`) با فوکوس گردشی: فقط یک گزینه در ترتیب Tab است و کلیدهای جهت بین بقیه حرکت می‌کنند؛ در راست‌به‌چپ، فلش چپ یعنی گزینهٔ بعد.\n- صفحه‌خوان هر روز را با تاریخ کامل و وضعیتش می‌خواند: «سه‌شنبه ۱۳ آبان ۱۴۰۵، ۶ نوبت خالی» یا «تعطیل».\n- نوبت انتخاب‌شده در یک ناحیهٔ زنده (`aria-live`) اعلام می‌شود.\n- نوبت پر فقط با خط‌خوردگی نشان داده نمی‌شود؛ غیرفعال است و صفحه‌خوان «پر» را می‌خواند.\n\n## راهنمای استفاده\n\nکِی استفاده کنیم:\n\n- رزرو نوبت با ساعت مشخص: مطب، آرایشگاه، مشاوره، تحویل سفارش در بازهٔ زمانی.\n\nکِی استفاده نکنیم:\n\n- فقط تاریخ بدون ساعت. (دیت‌پیکر شمسی: /docs/persian/date-picker/)\n- بازهٔ چندروزه مثل رزرو هتل. (بازهٔ تاریخ شمسی: /docs/persian/date-range/)\n\nدرست و نادرست:\n\n- ✓ درست: نوبت‌های پر را با `booked` نشان دهید تا خط‌خورده دیده شوند.\n- ✗ نادرست: نوبت‌های پر را پنهان کنید؛ کاربر فکر می‌کند آن ساعت کاری نیست.\n- ✓ درست: خلاصهٔ پایین («نوبت شما: شنبه ۱۶ آبان، ساعت ۱۰:۰۰») را نگه دارید تا پیش از ثبت دوباره ببیند.\n- ✗ نادرست: ثبت با یک کلیک روی ساعت، بدون دیدن تاریخ و ساعت انتخابی.\n\n## وب کامپوننت\n\nهمین کامپوننت بدون React، برای HTML، وردپرس، Vue، Svelte و Angular: `<kashi-appointment-picker>` در `@kashi-ui/elements`. با `name` دو فیلد `visit_date` و `visit_time` ارسال می‌شوند؛ ساعت‌های دلخواه هر روز با `element.getSlots`.\n\nنصب بدون ابزار ساخت (CDN):\n\n```html\n<link rel=\"stylesheet\" href=\"https://cdn.jsdelivr.net/npm/@kashi-ui/css/dist/kashi.css\">\n<script type=\"module\" src=\"https://cdn.jsdelivr.net/npm/@kashi-ui/elements/dist/cdn/kashi-elements.js\"></script>\n```\n\nیا با npm، فقط همین بخش:\n\n```bash\nnpm i @kashi-ui/elements @kashi-ui/css\n```\n\n```js\nimport \"@kashi-ui/css\";\nimport \"@kashi-ui/elements/appointment.js\";\n```\n\n```html\n<kashi-appointment-picker name=\"visit\" from=\"09:00\" to=\"17:00\" step=\"30\" booked='{\"2026-11-04\": [\"09:30\", \"10:00\"]}'></kashi-appointment-picker>\n```\n\nویژگی‌ها:\n\n| نام | مقدار | پیش‌فرض | توضیح |\n|---|---|---|---|\n| start | YYYY-MM-DD | امروز | اولین روز |\n| days | عدد | 7 | تعداد روزها |\n| from / to / step | HH:MM / دقیقه | 09:00 / 17:00 / 30 | ساعت کاری و طول هر نوبت |\n| booked | JSON | — | نوبت‌های پر: {\"2026-11-04\": [\"09:30\"]} |\n| closed | false | — | همهٔ روزها باز، حتی جمعه و تعطیلات |\n| value | «2026-11-04 10:30» | — | نوبت اولیه |\n| label / name / digits | — | انتخاب نوبت | برچسب، نام فرم و خط ارقام |\n\nرویدادها و JavaScript:\n\n| نام | توضیح |\n|---|---|\n| kashi-change | detail: { date: \"YYYY-MM-DD\", time, value } |\n| element.getSlots | (date) => [\"09:00\", { time, available }] |\n| element.isClosed | (date) => boolean برای روزهای تعطیل دلخواه |\n| element.value | { date, time } |"},{"slug":"persian/bank-card","url":"https://kashi.sepantapouya.com/docs/persian/bank-card/","title":"کارت بانکی","description":"نمایش کارت بانکی با رنگ و لوگوی بانک، شمارهٔ پوشانده و تاریخ انقضای شمسی.","group":"فارسی","components":["BankCard"],"markdown":"```tsx\nimport { BankCard } from \"@kashi-ui/react\";\n```\n\n## نمونه\n\n```tsx\n<BankCard number=\"6104337712345678\" holder=\"سپنتا پویا\"\n expiry={{ year: 1408, month: 5 }} masked />\n```\n\nبانک از شش رقم اول تشخیص داده می‌شود و کارت رنگ و لوگوی همان بانک را می‌گیرد. برای بانک‌های ناشناخته رنگ برند استفاده می‌شود و با `--k-bankcard-color` قابل تغییر است. برای کیف پول، انتخاب کارت ذخیره‌شده و صفحهٔ پرداخت مناسب است. با `masked` فقط شش رقم اول و چهار رقم آخر دیده می‌شود.\n\n| ویژگی | نوع | پیش‌فرض | توضیح |\n|---|---|---|---|\n| number | string | — | با هر خطی |\n| holder | ReactNode | — | نام دارنده |\n| expiry | string \\| { year, month } | — | «۰۸/۰۵» یا سال و ماه شمسی |\n| masked | boolean | false | پنهان کردن ارقام میانی |\n| bank | Bank \\| null | — | بانک دستی |\n| showLogo | boolean | true | — |\n| digits | \"fa\" \\| \"latin\" | \"fa\" | — |\n\n## دسترس‌پذیری\n\nکارت یک `figure` با نام کامل است، مثل «کارت بانک ملت، مختوم به ۵۶۷۸»، تا صفحه‌خوان ۱۶ رقم را جدا جدا نخواند. لوگو تزئینی است. رنگ‌ها تقریبی‌اند و رسمی نیستند.\n\n## راهنمای استفاده\n\nکِی استفاده کنیم:\n\n- کارت‌های ذخیره‌شدهٔ کاربر در کیف پول یا صفحهٔ پرداخت.\n- انتخاب کارت برای برداشت.\n\nکِی استفاده نکنیم:\n\n- گرفتن شمارهٔ کارت. (فیلد فارسی (کارت): /docs/persian/input/)\n\nدرست و نادرست:\n\n- ✓ درست: شماره را با `masked` بپوشانید و فقط چهار رقم آخر را نشان دهید.\n- ✗ نادرست: شانزده رقم کامل روی صفحه، جایی که دیگران هم می‌بینند."},{"slug":"persian/countdown","url":"https://kashi.sepantapouya.com/docs/persian/countdown/","title":"شمارش معکوس","description":"زمان باقی‌مانده با ارقام فارسی، برای ارسال دوبارهٔ کد، پایان تخفیف یا شروع رویداد.","group":"فارسی","components":["Countdown"],"markdown":"```tsx\nimport { Countdown } from \"@kashi-ui/react\";\n```\n\n## نمونه\n\n```tsx\nارسال دوبارهٔ کد تا <Countdown seconds={30} aria-label=\"زمان باقی‌مانده تا ارسال دوباره\" onComplete={() => setDone(true)} />\n\n<Countdown to={launchDate} variant=\"blocks\" aria-label=\"زمان باقی‌مانده تا شروع فروش\" />\n```\n\nبرای شروع دوباره، `key` کامپوننت را عوض کنید. شمارش از ساعت سیستم حساب می‌شود، پس اگر زبانه مدتی پنهان باشد هم درست می‌ماند.\n\n| ویژگی | نوع | پیش‌فرض | توضیح |\n|---|---|---|---|\n| to | Date | — | زمان پایان |\n| seconds | number | — | یا مدت به ثانیه از لحظهٔ نمایش |\n| onComplete | () => void | — | یک بار در پایان |\n| variant | \"clock\" \\| \"blocks\" | \"clock\" | ۰۲:۳۴ یا کادرهای روز و ساعت |\n| digits | \"fa\" \\| \"latin\" | \"fa\" | — |\n| aria-label | string | — | موضوع شمارش |\n| labels | { days?, hours?, minutes?, seconds? } | — | — |\n\n## دسترس‌پذیری\n\n`role=\"timer\"` دارد که صفحه‌خوان آن را هر ثانیه اعلام نمی‌کند. نام دسترس‌پذیر متن کامل است («زمان باقی‌مانده تا ارسال دوباره: ۲ دقیقه و ۳۴ ثانیه»)، پس کاربر هر وقت به آن برسد زمان را می‌شنود.\n\n## راهنمای استفاده\n\nکِی استفاده کنیم:\n\n- زمان تا ارسال دوبارهٔ کد، پایان پیشنهاد ویژه یا شروع رویداد.\n\nکِی استفاده نکنیم:\n\n- فشار ساختگی برای خرید؛ شمارشی که با هر بار باز کردن صفحه از اول شروع می‌شود اعتماد را از بین می‌برد.\n- زمان گذشته («۵ دقیقه پیش»). (زمان نسبی: /docs/persian/utilities/)\n\nدرست و نادرست:\n\n- ✓ درست: بگویید بعد از پایان چه می‌شود: «تا ۰۱:۵۹ دیگر می‌توانید کد را دوباره بخواهید».\n- ✗ نادرست: عدد شمارشگر تنها، بدون اینکه بگوید شمارش برای چیست.\n\n## وب کامپوننت\n\nهمین کامپوننت بدون React، برای HTML، وردپرس، Vue، Svelte و Angular: `<kashi-countdown>` در `@kashi-ui/elements`.\n\nنصب بدون ابزار ساخت (CDN):\n\n```html\n<link rel=\"stylesheet\" href=\"https://cdn.jsdelivr.net/npm/@kashi-ui/css/dist/kashi.css\">\n<script type=\"module\" src=\"https://cdn.jsdelivr.net/npm/@kashi-ui/elements/dist/cdn/kashi-elements.js\"></script>\n```\n\nیا با npm، فقط همین بخش:\n\n```bash\nnpm i @kashi-ui/elements @kashi-ui/css\n```\n\n```js\nimport \"@kashi-ui/css\";\nimport \"@kashi-ui/elements/display.js\";\n```\n\n```html\n<kashi-countdown seconds=\"120\" label=\"ارسال دوباره\"></kashi-countdown>\n<kashi-countdown seconds=\"93784\" variant=\"blocks\"></kashi-countdown>\n```\n\nویژگی‌ها:\n\n| نام | مقدار | پیش‌فرض | توضیح |\n|---|---|---|---|\n| seconds | عدد | — | مدت از لحظهٔ نمایش |\n| to | تاریخ ISO | — | یا زمان پایان |\n| variant | blocks | — | کادرهای روز، ساعت، دقیقه و ثانیه |\n| label | متن | — | نام برای صفحه‌خوان |\n| digits | fa \\| latin | fa | خط ارقام |\n\nرویدادها و JavaScript:\n\n| نام | توضیح |\n|---|---|\n| kashi-complete | در پایان شمارش |\n| element.restart() | شروع دوباره، مثلاً پس از ارسال دوبارهٔ کد |\n| element.remaining | ثانیه‌های باقی‌مانده |"},{"slug":"persian/date-picker","url":"https://kashi.sepantapouya.com/docs/persian/date-picker/","title":"دیت‌پیکر شمسی","description":"فیلد تاریخ با تقویم شمسی؛ تایپ مستقیم یا انتخاب از تقویم، با پیمایش کامل کیبورد و پشتیبانی صفحه‌خوان.","group":"فارسی","components":["DatePicker","Calendar"],"markdown":"```tsx\nimport { DatePicker, Calendar } from \"@kashi-ui/react\";\n```\n\nکاربر می‌تواند تاریخ را مستقیم تایپ کند (با ارقام فارسی یا انگلیسی، مثل `۱۴۰۳/۵/۱۲`) یا آن را از تقویم انتخاب کند. مقدار کامپوننت یک `Date` استاندارد جاوااسکریپت است، پس سرور شما با تاریخ شمسی سروکار ندارد.\n\n## تاریخ تولد\n\nفهرست ماه و سال برای انتخاب سریع تاریخ‌های دور است. با `name`، مقدار میلادی به شکل `YYYY-MM-DD` در فرم ارسال می‌شود.\n\n```tsx\nconst [birth, setBirth] = useState<Date | null>(null);\n\n<DatePicker\n label=\"تاریخ تولد\"\n name=\"birth\"\n max={new Date()}\n yearRange={[1300, 1405]}\n onChange={setBirth}\n/>\n```\n\n## محدود کردن روزها\n\nبا `min`، `max` و `isDateDisabled` روزهای غیرمجاز را مشخص کنید؛ مثلاً برای رزرو نوبت.\n\n```tsx\n<DatePicker\n label=\"تاریخ نوبت\"\n min={new Date()}\n isDateDisabled={(date) => date.getDay() === 5} // جمعه\n/>\n```\n\n## تقویم مستقل\n\n`Calendar` همان تقویم بدون فیلد است؛ برای صفحه‌های رزرو یا داشبورد.\n\n```tsx\nconst [date, setDate] = useState<Date | null>(new Date());\n\n<Calendar value={date} onChange={setDate} />\n```\n\n## کیبورد\n\n| کلید | عمل |\n|---|---|\n| <kbd>←</kbd> / <kbd>→</kbd> | روز بعد / روز قبل (در راست‌به‌چپ) |\n| <kbd>↑</kbd> / <kbd>↓</kbd> | هفتهٔ قبل / هفتهٔ بعد |\n| <kbd>Home</kbd> / <kbd>End</kbd> | شنبه / جمعهٔ همان هفته |\n| <kbd>PageUp</kbd> / <kbd>PageDown</kbd> | ماه قبل / ماه بعد |\n| <kbd>Shift</kbd> + <kbd>PageUp</kbd> / <kbd>PageDown</kbd> | سال قبل / سال بعد |\n| <kbd>Enter</kbd> / <kbd>Space</kbd> | انتخاب روز |\n| <kbd>Esc</kbd> | بستن تقویم و برگشت به دکمه |\n\n## ویژگی‌های DatePicker\n\n| ویژگی | نوع | پیش‌فرض | توضیح |\n|---|---|---|---|\n| value | Date \\| null | — | تاریخ انتخاب‌شده (کنترل‌شده) |\n| defaultValue | Date \\| null | null | مقدار اولیه (کنترل‌نشده) |\n| onChange | (date: Date \\| null) => void | — | با انتخاب، تایپ معتبر یا پاک کردن |\n| min / max | Date | — | محدودهٔ مجاز |\n| isDateDisabled | (date: Date) => boolean | — | غیرفعال کردن روزهای خاص |\n| yearRange | [number, number] | ۱۰۰ سال قبل تا ۲۰ سال بعد | سال‌های فهرست سال (شمسی) |\n| digits | \"fa\" \\| \"latin\" | \"fa\" | خط ارقام |\n| name | string | — | نام فیلد مخفی فرم با مقدار YYYY-MM-DD میلادی |\n| invalidMessage | ReactNode | «تاریخ را به شکل … وارد کنید.» | پیام خطای تایپ نامعتبر |\n| openLabel | string | \"انتخاب تاریخ از تقویم\" | نام دکمهٔ تقویم برای صفحه‌خوان |\n| todayLabel | string | \"امروز\" | متن دکمهٔ امروز |\n\nهمهٔ ویژگی‌های `Input` مثل `label`، `hint` و `error` هم پشتیبانی می‌شوند.\n\n## متغیرهای CSS\n\n| متغیر CSS | پیش‌فرض | توضیح |\n|---|---|---|\n| --k-calendar-day-size | 2.5rem × density | اندازهٔ خانهٔ هر روز |\n| --k-calendar-radius | var(--k-radius-full) | گردی خانهٔ روز |\n| --k-calendar-bg | var(--k-bg-elevated) | پس‌زمینهٔ تقویم شناور |\n| --k-datepicker-width | max-content | عرض تقویم شناور |\n\n## دسترس‌پذیری\n\n- تقویم از الگوی Date Picker در WAI-ARIA پیروی می‌کند: جدول با نقش `grid`، فقط یک روز در ترتیب Tab، و پیمایش با کلیدهای جهت‌دار.\n- هر روز با نام کامل خوانده می‌شود («جمعه ۱۲ مرداد ۱۴۰۳»)، امروز با `aria-current=\"date\"` و روز انتخاب‌شده با `aria-selected` مشخص است.\n- تغییر ماه با یک ناحیهٔ `aria-live` اعلام می‌شود.\n- دکمهٔ تقویم تاریخ فعلی را هم در نام خود دارد، پس کاربر صفحه‌خوان بدون باز کردن تقویم مقدار را می‌شنود.\n- تایپ مستقیم همیشه ممکن است؛ کاربرانی که با تقویم راحت نیستند مجبور به استفاده از آن نیستند.\n\n## تبدیل تاریخ\n\nتبدیل شمسی و میلادی با پکیج `@kashi-ui/persian` انجام می‌شود که برای سال‌های ۱۳۰۰ تا ۱۵۰۰، روزبه‌روز با تقویم رسمی ICU تست شده است. [ابزارهای فارسی](/docs/persian/utilities/) را ببینید.\n\n## تعطیلات\n\nبا `holidays`، جمعه‌ها و تعطیلات رسمی ثابت (نوروز، ۱۲ و ۱۳ فروردین، ۱۴ و ۱۵ خرداد، ۲۲ بهمن و ۲۹ اسفند) قرمز نمایش داده می‌شوند و نامشان برای صفحه‌خوان خوانده می‌شود. تعطیلات قمری هر سال با رؤیت ماه جابه‌جا می‌شوند، پس آن‌ها را از تقویم رسمی همان سال بدهید:\n\n```tsx\nimport { getIranHoliday } from \"@kashi-ui/persian\";\n\n<Calendar holidays /> // فقط جمعه‌ها و تعطیلات ثابت\n\n<Calendar holidays={(d) => getIranHoliday(d, { extra: { \"2026-03-20\": \"عید فطر\" } })} />\n```\n\n## راهنمای استفاده\n\nکِی استفاده کنیم:\n\n- تاریخ شمسی در فرم: تاریخ تولد، تحویل، سررسید.\n- وقتی کاربر هم تایپ می‌کند هم از تقویم انتخاب می‌کند.\n\nکِی استفاده نکنیم:\n\n- بازهٔ دو تاریخ. (بازهٔ تاریخ شمسی: /docs/persian/date-range/)\n- فقط ماه و سال، مثل تاریخ انقضای کارت. (انتخاب ماه: /docs/persian/month-picker/)\n- تاریخ با ساعت نوبت. (انتخاب نوبت: /docs/persian/appointment/)\n\nدرست و نادرست:\n\n- ✓ درست: برای تاریخ تولد `max` امروز بدهید و `yearRange` گسترده تا سال‌های دور با انتخاب سال رسیده شوند.\n- ✗ نادرست: تقویمی که برای سال تولد ۱۳۶۰ باید صدها بار «ماه قبل» زد.\n- ✓ درست: مقدار ارسالی میلادی (`YYYY-MM-DD`) است؛ سرور را با تاریخ شمسی رشته‌ای پر نکنید.\n- ✗ نادرست: ذخیرهٔ «۱۴۰۵/۰۸/۱۶» به‌شکل متن در پایگاه داده.\n\n## وب کامپوننت\n\nهمین کامپوننت بدون React، برای HTML، وردپرس، Vue، Svelte و Angular: `<kashi-date-picker>`، `<kashi-calendar>` در `@kashi-ui/elements`. تاریخ میلادی به شکل `YYYY-MM-DD` در فرم ارسال می‌شود؛ تقویم تنها با `<kashi-calendar>`.\n\nنصب بدون ابزار ساخت (CDN):\n\n```html\n<link rel=\"stylesheet\" href=\"https://cdn.jsdelivr.net/npm/@kashi-ui/css/dist/kashi.css\">\n<script type=\"module\" src=\"https://cdn.jsdelivr.net/npm/@kashi-ui/elements/dist/cdn/kashi-elements.js\"></script>\n```\n\nیا با npm، فقط همین بخش:\n\n```bash\nnpm i @kashi-ui/elements @kashi-ui/css\n```\n\n```js\nimport \"@kashi-ui/css\";\nimport \"@kashi-ui/elements/calendar.js\";\n```\n\n```html\n<kashi-date-picker label=\"تاریخ تحویل\" name=\"delivery\" holidays></kashi-date-picker>\n```\n\nویژگی‌ها:\n\n| نام | مقدار | پیش‌فرض | توضیح |\n|---|---|---|---|\n| label | متن | نام فارسی فیلد | برچسب بالای فیلد؛ بدون آن aria-label بدهید |\n| name | متن | — | نام فیلد در فرم HTML؛ مقدار خام ارسال می‌شود |\n| value | متن | — | مقدار اولیه |\n| hint | متن | — | راهنمای زیر فیلد |\n| error | متن | — | خطای دلخواه، مثلاً از اعتبارسنجی سرور |\n| required / disabled | بولی | — | مثل input معمولی |\n| min / max | 2026-03-21 یا 1405/01/01 | — | کمترین و بیشترین تاریخ |\n| holidays | بولی | — | جمعه‌ها و تعطیلات ثابت رسمی در تقویم |\n| digits | fa \\| latin | fa | خط ارقام |\n| invalid-message | متن | — | پیام تاریخ نامعتبر |\n\nرویدادها و JavaScript:\n\n| نام | توضیح |\n|---|---|\n| kashi-change | detail: { value: \"2026-03-21\", date: Date } |\n| element.date / element.value | تاریخ به‌شکل Date یا YYYY-MM-DD |\n| element.isDateDisabled | تابعی برای غیرفعال کردن روزها، مثل (d) => d.getDay() === 5 |\n| element.open() / close() | باز و بستن تقویم |"},{"slug":"persian/date-range","url":"https://kashi.sepantapouya.com/docs/persian/date-range/","title":"بازهٔ تاریخ شمسی","description":"انتخاب بازهٔ تاریخ با تقویم جلالی و میان‌برهایی مثل «۷ روز اخیر» و «ماه قبل».","group":"فارسی","components":["DateRangePicker"],"markdown":"```tsx\nimport { DateRangePicker } from \"@kashi-ui/react\";\n```\n\n## نمونه\n\nکلیک اول تاریخ شروع و کلیک دوم تاریخ پایان است (ترتیب مهم نیست)؛ پیش از کلیک دوم، بازه با حرکت موس یا فوکوس پیش‌نمایش می‌شود. تغییرها با «تأیید» ثبت می‌شوند.\n\n```tsx\nconst [range, setRange] = useState<DateRange>({ start: null, end: null });\n\n<DateRangePicker label=\"بازهٔ گزارش\" name=\"period\" value={range} onChange={setRange} max={new Date()} />\n```\n\nبا `name`، دو فیلد پنهان `period_start` و `period_end` به شکل میلادی `YYYY-MM-DD` در فرم ارسال می‌شوند.\n\n## میان‌برها\n\nمیان‌برهای پیش‌فرض (`defaultRangePresets`) «۷ روز اخیر»، «۳۰ روز اخیر»، «این ماه» و «ماه قبل» هستند و ماه‌ها شمسی حساب می‌شوند. فهرست خودتان را بدهید یا با `presets={[]}` حذفشان کنید:\n\n```tsx\n<DateRangePicker presets={[\n ...defaultRangePresets,\n { label: \"امسال\", range: (today) => ({ start: toGregorian(toJalali(today).jy, 1, 1), end: today }) },\n]} />\n```\n\n| ویژگی | نوع | پیش‌فرض | توضیح |\n|---|---|---|---|\n| value / defaultValue | { start: Date \\| null; end: Date \\| null } | — | — |\n| onChange | (range) => void | — | پس از «تأیید» |\n| min / max | Date | — | محدودهٔ مجاز |\n| isDateDisabled | (date) => boolean | — | روزهای غیرقابل‌انتخاب |\n| presets | { label, range: (today) => { start, end } }[] | defaultRangePresets | میان‌برها |\n| name | string | — | name_start و name_end |\n| digits | \"fa\" \\| \"latin\" | \"fa\" | — |\n| labels | { placeholder?, separator?, apply?, cancel?, presets?, pickEnd? } | — | متن‌ها |\n\n## دسترس‌پذیری\n\nدکمهٔ باز کردن `aria-haspopup=\"dialog\"` و `aria-expanded` دارد و متن آن بازهٔ انتخاب‌شده است. تقویم همان الگوی grid تقویم تک‌تاریخ را دارد؛ روزهای داخل بازه `aria-selected` می‌گیرند. خلاصهٔ بازه پس از هر انتخاب با `aria-live` اعلام می‌شود و <kbd>Esc</kbd> بدون ثبت تغییر می‌بندد.\n\n## راهنمای استفاده\n\nکِی استفاده کنیم:\n\n- بازهٔ گزارش، فیلتر تاریخ تراکنش‌ها، رزرو چندروزه.\n\nکِی استفاده نکنیم:\n\n- یک تاریخ. (دیت‌پیکر شمسی: /docs/persian/date-picker/)\n- گزارش ماهانه. (انتخاب ماه: /docs/persian/month-picker/)\n\nدرست و نادرست:\n\n- ✓ درست: میان‌برهای پرکار («۷ روز اخیر»، «ماه قبل») را نگه دارید؛ بیشتر کاربران همین‌ها را می‌خواهند.\n- ✗ نادرست: فقط تقویم، تا برای «این ماه» دو کلیک دقیق لازم باشد.\n\n## وب کامپوننت\n\nهمین کامپوننت بدون React، برای HTML، وردپرس، Vue، Svelte و Angular: `<kashi-date-range-picker>` در `@kashi-ui/elements`. با `name` دو فیلد `name_start` و `name_end` با تاریخ میلادی ارسال می‌شوند.\n\nنصب بدون ابزار ساخت (CDN):\n\n```html\n<link rel=\"stylesheet\" href=\"https://cdn.jsdelivr.net/npm/@kashi-ui/css/dist/kashi.css\">\n<script type=\"module\" src=\"https://cdn.jsdelivr.net/npm/@kashi-ui/elements/dist/cdn/kashi-elements.js\"></script>\n```\n\nیا با npm، فقط همین بخش:\n\n```bash\nnpm i @kashi-ui/elements @kashi-ui/css\n```\n\n```js\nimport \"@kashi-ui/css\";\nimport \"@kashi-ui/elements/pickers.js\";\n```\n\n```html\n<kashi-date-range-picker label=\"بازهٔ گزارش\" name=\"report\" holidays></kashi-date-range-picker>\n```\n\nویژگی‌ها:\n\n| نام | مقدار | پیش‌فرض | توضیح |\n|---|---|---|---|\n| label / hint / error | متن | بازهٔ تاریخ | مثل بقیهٔ فیلدها |\n| start / end | 2026-03-21 | — | بازهٔ اولیه |\n| min / max / holidays / digits | — | — | مثل انتخاب تاریخ |\n| presets | false | — | بدون میان‌برهای «۷ روز اخیر» و… |\n\nرویدادها و JavaScript:\n\n| نام | توضیح |\n|---|---|\n| kashi-change | detail: { start, end, value: { start: \"YYYY-MM-DD\", end } } |\n| element.value | { start, end } با Date |"},{"slug":"persian/date-tile","url":"https://kashi.sepantapouya.com/docs/persian/date-tile/","title":"برگ تقویم","description":"کاشی تاریخ شمسی به شکل برگ تقویم، با روز هفته، مناسبت تعطیل و معادل میلادی.","group":"فارسی","components":["DateTile"],"markdown":"```tsx\nimport { DateTile } from \"@kashi-ui/react\";\n```\n\n## نمونه\n\n```tsx\n<DateTile date={new Date()} size=\"lg\" />\n<DateTile date={new Date(2026, 2, 21)} /> {/* نوروز */}\n```\n\nبرای رویدادها، نوبت‌دهی، تاریخ ارسال سفارش و فهرست جلسات. روزهای تعطیل (از جمله جمعه) قرمز می‌شوند و نام مناسبت رسمی نمایش داده می‌شود. تعطیلات قمری را با `holidays={{ extra: { … } }}` اضافه کنید. همان قالب `getIranHoliday` است.\n\n| ویژگی | نوع | پیش‌فرض | توضیح |\n|---|---|---|---|\n| date | Date | — | — |\n| holidays | boolean \\| HolidayOptions | true | مناسبت و رنگ تعطیل |\n| gregorian | boolean | true | معادل میلادی |\n| size | \"sm\" \\| \"md\" \\| \"lg\" | \"md\" | — |\n\n## دسترس‌پذیری\n\nعنصر `<time>` با `datetime` استاندارد است تا موتورهای جستجو و ابزارها تاریخ را بفهمند.\n\n## راهنمای استفاده\n\nکِی استفاده کنیم:\n\n- نمایش برجستهٔ یک تاریخ: زمان رویداد، تاریخ تحویل، تقویم مناسبت‌ها.\n\nکِی استفاده نکنیم:\n\n- تاریخ داخل متن یا جدول؛ همان متن ساده با `formatJalali` بس است. (ابزارهای فارسی: /docs/persian/utilities/)\n\nدرست و نادرست:\n\n- ✓ درست: معادل میلادی را برای مخاطب بین‌المللی روشن بگذارید (`gregorian`).\n- ✗ نادرست: چند برگ تقویم بزرگ برای هر ردیف یک فهرست."},{"slug":"persian/input","url":"https://kashi.sepantapouya.com/docs/persian/input/","title":"فیلد فارسی","description":"یک کامپوننت برای همهٔ داده‌های فارسی؛ کد ملی، شناسهٔ شرکت، موبایل، تلفن، کارت، شبا، مبلغ، کد پستی، ساعت، پلاک و متن با اصلاح زبان صفحه‌کلید.","group":"فارسی","components":["PersianInput"],"markdown":"```tsx\nimport { PersianInput } from \"@kashi-ui/react\";\n```\n\n## نمونه\n\n```tsx\n<PersianInput kind=\"iban\" name=\"iban\" />\n<PersianInput kind=\"price\" min={10000} />\n<PersianInput kind=\"postal\" label=\"کد پستی محل کار\" />\n<PersianInput kind=\"text\" label=\"جستجو\" /> {/* با پیشنهاد «sghl» ← «سلام» */}\n```\n\nبه جای به خاطر سپردن نام یازده کامپوننت، یک فیلد با `kind` بسازید. هر نوع برچسب، قالب، اعتبارسنجی و پیام خطای فارسی خودش را دارد. ویژگی‌های دیگر همان کامپوننت تخصصی است و TypeScript بر اساس `kind` آن‌ها را پیشنهاد می‌دهد. مثلاً `showBank` فقط برای `card` و `iban` مجاز است.\n\n| kind | داده | کامپوننت تخصصی |\n|---|---|---|\n| `text` | متن با پیشنهاد اصلاح زبان صفحه‌کلید | [KeyboardFixInput](#text) |\n| `national-id` | کد ملی | [NationalIdInput](#national-id) |\n| `legal-id` | شناسهٔ ملی شرکت | [LegalIdInput](#legal-id) |\n| `mobile` | موبایل با اپراتور | [MobileInput](#mobile) |\n| `landline` | تلفن ثابت با استان | [LandlineInput](#landline) |\n| `card` | شمارهٔ کارت با بانک | [CardNumberInput](#card) |\n| `iban` | شبا با بانک | [IbanInput](#iban) |\n| `price` | مبلغ با حروف | [PriceInput](#price) |\n| `postal` | کد پستی | [PostalCodeInput](#postal) |\n| `time` | ساعت | [TimeInput](#time) |\n| `plate` | پلاک خودرو | [PlateInput](#plate) |\n\nاگر برچسب ندهید، برچسب پیش‌فرض همان نوع («شمارهٔ شبا»، «کد پستی» و…) گذاشته می‌شود. با `aria-label` برچسب دیده نمی‌شود. فهرست برچسب‌ها در `PERSIAN_INPUT_LABELS` است.\n\n## حجم بارگذاری\n\n`PersianInput` همهٔ یازده فیلد را import می‌کند. اگر در صفحه فقط یکی را لازم دارید، کامپوننت تخصصی را مستقیم import کنید تا حجم کمتری بارگذاری شود.\n\n| ویژگی | نوع | پیش‌فرض | توضیح |\n|---|---|---|---|\n| kind | \"text\" \\| \"national-id\" \\| \"legal-id\" \\| \"mobile\" \\| \"landline\" \\| \"card\" \\| \"iban\" \\| \"price\" \\| \"postal\" \\| \"time\" \\| \"plate\" | — | نوع داده |\n| … | — | — | ویژگی‌های کامپوننت تخصصی همان نوع |\n\n## ویژگی‌های هر نوع\n\nدر بخش‌های زیر، نمونه و ویژگی‌های اختصاصی هر نوع آمده است. همهٔ نوع‌ها ویژگی‌های `Input` مثل `label`، `hint`، `error`، `required` و `disabled` را هم می‌پذیرند.\n\n## کد ملی\n\nفیلد کد ملی با اعتبارسنجی رقم کنترل؛ ارقام فارسی و انگلیسی را می‌پذیرد.\n\n```tsx\n<PersianInput kind=\"national-id\" /> // یا مستقیم: <NationalIdInput />\n```\n\nیک کد ملی آزمایشی معتبر: `0013542419`. یک رقم آخر را عوض کنید تا خطا را ببینید.\n\n```tsx\n<PersianInput kind=\"national-id\"\n label=\"کد ملی\"\n name=\"nationalId\"\n onValueChange={(value, valid) => console.log(value, valid)}\n/>\n```\n\n#### رفتار\n\n- **ارقام:** کاربر می‌تواند با صفحه‌کلید فارسی یا انگلیسی تایپ کند. نمایش با ارقام فارسی است (`digits=\"latin\"` برای انگلیسی)، ولی مقدار همیشه با ارقام انگلیسی به شما داده می‌شود.\n- **محدودیت:** هر نویسهٔ غیر رقمی حذف می‌شود و بیش از ۱۰ رقم پذیرفته نمی‌شود. در گوشی، صفحه‌کلید عددی باز می‌شود.\n- **اعتبارسنجی:** رقم دهم با الگوریتم رسمی رقم کنترل بررسی می‌شود. پیام خطا فقط پس از خروج از فیلد یا کامل شدن ۱۰ رقم نمایش داده می‌شود، نه هنگام تایپ.\n- **فرم:** با `name`، مقدار با ارقام انگلیسی در یک فیلد مخفی ارسال می‌شود.\n\n> اعتبارسنجی رقم کنترل فقط درستی ساختار کد را نشان می‌دهد، نه وجود آن را. برای احراز هویت، کد را با سرویس رسمی استعلام کنید.\n\n#### ویژگی‌ها\n\n| ویژگی | نوع | پیش‌فرض | توضیح |\n|---|---|---|---|\n| value | string | — | مقدار کنترل‌شده با ارقام انگلیسی |\n| defaultValue | string | \"\" | مقدار اولیه |\n| onValueChange | (value: string, valid: boolean) => void | — | با هر تغییر |\n| digits | \"fa\" \\| \"latin\" | \"fa\" | خط ارقام نمایشی |\n| invalidMessage | ReactNode | \"کد ملی معتبر نیست.\" | پیام خطا |\n| name | string | — | نام فیلد مخفی فرم |\n\nهمهٔ ویژگی‌های `Input` مثل `label`، `hint`، `error` و `required` هم پشتیبانی می‌شوند.\n\n#### بدون React\n\n```ts\nisValidNationalId(\"۰۰۱۳۵۴۲۴۱۹\"); // true\nisValidNationalId(\"1111111111\"); // false\n```\n\n#### دسترس‌پذیری\n\n- خطا با `aria-invalid` و `aria-describedby` به فیلد متصل است.\n- فیلد با `dir=\"ltr\"` است تا ترتیب ارقام هنگام تایپ جابه‌جا نشود.\n\n## شناسهٔ ملی شرکت\n\nشناسهٔ ملی ۱۱ رقمی اشخاص حقوقی با بررسی رقم کنترل.\n\n```tsx\n<PersianInput kind=\"legal-id\" /> // یا مستقیم: <LegalIdInput />\n```\n\n```tsx\n<PersianInput kind=\"legal-id\" label=\"شناسهٔ ملی شرکت\" name=\"legalId\"\n onValueChange={(value, valid) => …} />\n```\n\nشناسهٔ ملی شرکت‌ها، مؤسسات و سازمان‌ها ۱۱ رقم دارد و رقم آخر آن رقم کنترل است. برای فرم‌های B2B، فاکتور رسمی و ثبت فروشنده در بازارگاه‌ها لازم است. ارقام فارسی و انگلیسی پذیرفته می‌شوند. بدون React هم با `isValidLegalId` از `@kashi-ui/persian` در دسترس است:\n\n```ts\nisValidLegalId(\"10380284790\"); // true\n```\n\n| ویژگی | نوع | پیش‌فرض | توضیح |\n|---|---|---|---|\n| value / defaultValue | string | — | ارقام لاتین |\n| onValueChange | (value: string, valid: boolean) => void | — | — |\n| digits | \"fa\" \\| \"latin\" | \"fa\" | — |\n| invalidMessage | ReactNode | «شناسهٔ ملی معتبر نیست.» | — |\n| name | string | — | فیلد پنهان با ۱۱ رقم لاتین |\n\n#### دسترس‌پذیری\n\nخطا پس از خروج از فیلد یا کامل شدن ۱۱ رقم نمایش داده می‌شود و با `aria-describedby` به فیلد وصل است.\n\n## شماره موبایل\n\nفیلد شماره موبایل ایران با تبدیل خودکار +98، گروه‌بندی و تشخیص اپراتور.\n\n```tsx\n<PersianInput kind=\"mobile\" /> // یا مستقیم: <MobileInput />\n```\n\nشماره را به هر شکلی بنویسید یا بچسبانید: `09123456789`، `+98 912 345 6789` یا `00989123456789`.\n\n```tsx\n<PersianInput kind=\"mobile\"\n label=\"شماره موبایل\"\n name=\"mobile\"\n onValueChange={(mobile, valid, operator) => console.log(mobile, valid, operator)}\n/>\n```\n\n#### رفتار\n\n- **یکدست‌سازی:** `+98`، `0098`، `98` و شمارهٔ بدون صفر اول (`912…`) به شکل استاندارد `09xxxxxxxxx` درمی‌آیند.\n- **اپراتور:** همراه اول، ایرانسل و رایتل از روی پیش‌شماره تشخیص داده می‌شوند. به دلیل امکان ترابرد شماره، اپراتور فقط برای راهنمایی است.\n- **صفحه‌کلید:** در گوشی صفحه‌کلید تلفن باز می‌شود و مرورگر می‌تواند شمارهٔ ذخیره‌شده را پیشنهاد دهد.\n\n#### ویژگی‌ها\n\n| ویژگی | نوع | پیش‌فرض | توضیح |\n|---|---|---|---|\n| value | string | — | مقدار کنترل‌شده، به هر شکل |\n| defaultValue | string | — | مقدار اولیه |\n| onValueChange | (mobile: string, valid: boolean, operator: string \\| null) => void | — | مقدار به شکل ۰۹xxxxxxxxx |\n| digits | \"fa\" \\| \"latin\" | \"fa\" | خط ارقام نمایشی |\n| showOperator | boolean | true | نمایش نام اپراتور |\n| invalidMessage | ReactNode | \"شماره موبایل معتبر نیست.\" | پیام خطا |\n| name | string | — | نام فیلد مخفی فرم |\n\n#### بدون React\n\n```ts\nnormalizeMobile(\"+98 912 345 6789\"); // \"09123456789\"\ngetMobileOperator(\"0935\")?.name; // \"ایرانسل\"\nformatMobile(\"09123456789\"); // \"0912 345 6789\"\n```\n\n## تلفن ثابت\n\nشمارهٔ تلفن ثابت با پیش‌شماره، گروه‌بندی و تشخیص استان.\n\n```tsx\n<PersianInput kind=\"landline\" /> // یا مستقیم: <LandlineInput />\n```\n\n```tsx\n<PersianInput kind=\"landline\" label=\"تلفن ثابت\" name=\"tel\"\n onValueChange={(value, valid, province) => …} />\n```\n\n+98 و 0098 به ۰ تبدیل می‌شوند و نام استان از روی پیش‌شمارهٔ سه‌رقمی (مثل ۰۳۱ برای اصفهان) کنار فیلد نمایش داده می‌شود. پیش‌شمارهٔ هر ۳۱ استان در `getLandlineProvince` است.\n\n| ویژگی | نوع | پیش‌فرض | توضیح |\n|---|---|---|---|\n| value / defaultValue | string | — | مثل 02188776655 |\n| onValueChange | (value, valid, province) => void | — | — |\n| showProvince | boolean | true | نام استان کنار فیلد |\n| digits | \"fa\" \\| \"latin\" | \"fa\" | — |\n| invalidMessage | ReactNode | «شمارهٔ تلفن ثابت معتبر نیست.» | — |\n| name | string | — | فیلد پنهان با ۱۱ رقم لاتین |\n\n#### دسترس‌پذیری\n\nنام استان با `aria-describedby` به فیلد وصل است. `autocomplete=\"tel-national\"` و صفحه‌کلید عددی گوشی فعال است.\n\n## شماره کارت\n\nفیلد شماره کارت بانکی با گروه‌بندی خودکار، تشخیص بانک و اعتبارسنجی Luhn.\n\n```tsx\n<PersianInput kind=\"card\" /> // یا مستقیم: <CardNumberInput />\n```\n\nشش رقم اول را تایپ کنید، مثلاً `603799` یا `610433`، تا لوگو و نام بانک نمایش داده شوند.\n\n```tsx\n<PersianInput kind=\"card\"\n label=\"شماره کارت\"\n name=\"card\"\n onValueChange={(value, valid, bank) => console.log(value, valid, bank)}\n/>\n```\n\n#### رفتار\n\n- **گروه‌بندی:** ارقام به‌صورت خودکار در گروه‌های چهارتایی نمایش داده می‌شوند. هنگام ویرایش وسط شماره، مکان‌نما سر جای درستش می‌ماند.\n- **چسباندن:** شماره‌ای که با فاصله، خط تیره یا ارقام فارسی کپی شده هم درست خوانده می‌شود.\n- **تشخیص بانک:** لوگو و نام بانک از شش رقم اول (BIN) تشخیص داده می‌شوند. لوگوها فقط وقتی بانک شناخته شد بارگذاری می‌شوند و حجم اولیهٔ برنامه را زیاد نمی‌کنند ([لوگوی بانک‌ها](/docs/persian/bank-logos/)). فهرست بانک‌ها ممکن است کامل نباشد؛ از آن فقط برای راهنمایی کاربر استفاده کنید.\n- **اعتبارسنجی:** شمارهٔ ۱۶ رقمی با الگوریتم Luhn بررسی می‌شود.\n- **تکمیل خودکار:** با `autocomplete=\"cc-number\"`، مرورگر می‌تواند کارت ذخیره‌شده را پیشنهاد دهد.\n\n> این فیلد شماره را فقط در مرورگر قالب‌بندی می‌کند. شمارهٔ کارت داده‌ای حساس است؛ آن را فقط از طریق HTTPS ارسال کنید و بدون نیاز ذخیره نکنید.\n\n#### ویژگی‌ها\n\n| ویژگی | نوع | پیش‌فرض | توضیح |\n|---|---|---|---|\n| value | string | — | مقدار کنترل‌شده: ۱۶ رقم انگلیسی بدون فاصله |\n| defaultValue | string | \"\" | مقدار اولیه |\n| onValueChange | (value: string, valid: boolean, bank: string \\| null) => void | — | با هر تغییر |\n| digits | \"fa\" \\| \"latin\" | \"fa\" | خط ارقام نمایشی |\n| showBank | boolean | true | نمایش نام بانک |\n| showBankLogo | boolean | true | نمایش لوگوی بانک (بارگذاری تنبل) |\n| invalidMessage | ReactNode | \"شماره کارت معتبر نیست.\" | پیام خطا |\n| name | string | — | نام فیلد مخفی فرم با ارقام بدون فاصله |\n\n#### بدون React\n\n```ts\nformatCardNumber(\"6037991234567890\"); // \"6037 9912 3456 7890\"\ngetBankByCard(\"603799\")?.name; // \"بانک ملی\"\nisValidCardNumber(\"4111 1111 1111 1111\"); // true\n```\n\n#### دسترس‌پذیری\n\n- نام بانک با `aria-describedby` به فیلد متصل است و صفحه‌خوان آن را همراه فیلد می‌خواند.\n- خطا پس از خروج از فیلد یا کامل شدن ۱۶ رقم نمایش داده می‌شود، نه هنگام تایپ.\n\n## شماره شبا\n\nفیلد شبا (IBAN ایران) با IR خودکار، گروه‌بندی، لوگو و نام بانک و بررسی رقم‌های کنترل.\n\n```tsx\n<PersianInput kind=\"iban\" /> // یا مستقیم: <IbanInput />\n```\n\nفقط ارقام را تایپ کنید؛ «IR» خودکار اضافه می‌شود. با تایپ ارقام ۳ تا ۵ (کد بانک)، لوگو و نام بانک نمایش داده می‌شوند.\n\n```tsx\n<PersianInput kind=\"iban\"\n label=\"شماره شبا\"\n name=\"iban\"\n onValueChange={(iban, valid, bank) => console.log(iban, valid, bank)}\n/>\n```\n\n#### رفتار\n\n- **ورودی آزاد:** شبا با یا بدون «IR»، با فاصله یا بدون آن، و با ارقام فارسی یا انگلیسی پذیرفته می‌شود.\n- **اعتبارسنجی:** رقم‌های کنترل با استاندارد ISO 13616 (باقیماندهٔ ۹۷) بررسی می‌شوند؛ هر رقم اشتباه یا جابه‌جایی ارقام مجاور تشخیص داده می‌شود.\n- **مقدار:** همیشه به شکل `IR` + ۲۴ رقم بدون فاصله به شما داده می‌شود.\n- **ارقام:** شبا معمولاً با ارقام انگلیسی نوشته می‌شود؛ برای ارقام فارسی `digits=\"fa\"` بدهید.\n\n#### ویژگی‌ها\n\n| ویژگی | نوع | پیش‌فرض | توضیح |\n|---|---|---|---|\n| value | string | — | شبای کنترل‌شده، با یا بدون IR |\n| defaultValue | string | — | مقدار اولیه |\n| onValueChange | (iban: string, valid: boolean, bank: string \\| null) => void | — | با هر تغییر |\n| digits | \"fa\" \\| \"latin\" | \"latin\" | خط ارقام نمایشی |\n| showBank | boolean | true | نمایش نام بانک |\n| showBankLogo | boolean | true | نمایش لوگوی بانک (بارگذاری تنبل) |\n| invalidMessage | ReactNode | \"شماره شبا معتبر نیست.\" | پیام خطا |\n| name | string | — | نام فیلد مخفی فرم با مقدار IR… |\n\n#### بدون React\n\n```ts\nisValidIban(\"IR06 2960 0000 0010 0324 2000 01\");\ngetBankByIban(\"IR..0170...\")?.name; // \"بانک ملی\"\nformatIban(\"062960000000100324200001\"); // \"IR06 2960 0000 0010 0324 2000 01\"\n```\n\n#### دسترس‌پذیری\n\n- نام بانک با `aria-describedby` به فیلد متصل است؛ لوگو تزئینی است (`alt=\"\"`) تا صفحه‌خوان نام بانک را دو بار نخواند.\n\n## مبلغ\n\nفیلد مبلغ با جداکنندهٔ هزارگان فارسی، واحد پول و نمایش مبلغ به حروف.\n\n```tsx\n<PersianInput kind=\"price\" /> // یا مستقیم: <PriceInput />\n```\n\nمبلغ به حروف زیر فیلد نمایش داده می‌شود تا کاربر مطمئن شود صفرها را درست وارد کرده است؛ اشتباهی رایج و پرهزینه در پرداخت‌ها.\n\n```tsx\n<PersianInput kind=\"price\"\n label=\"مبلغ پرداخت\"\n name=\"amount\"\n max={500_000_000}\n onValueChange={(amount) => console.log(amount)}\n/>\n```\n\n#### رفتار\n\n- **قالب‌بندی:** جداکنندهٔ هزارگان فارسی (`٬`) هنگام تایپ اضافه می‌شود و مکان‌نما سر جایش می‌ماند.\n- **به حروف:** «یک میلیون و دویست و پنجاه هزار تومان». با `showWords={false}` خاموش می‌شود.\n- **واحد:** پیش‌فرض «تومان»؛ با `unit=\"ریال\"` یا هر متن دیگری عوض می‌شود.\n- **مقدار:** عدد (`number`) یا `null` برای فیلد خالی. صفرهای ابتدایی حذف می‌شوند. تا ۱۵ رقم.\n- **محدوده:** با `min` و `max`، پس از خروج از فیلد پیام خطا نمایش داده می‌شود.\n\n#### ویژگی‌ها\n\n| ویژگی | نوع | پیش‌فرض | توضیح |\n|---|---|---|---|\n| value | number \\| null | — | مقدار کنترل‌شده |\n| defaultValue | number \\| null | — | مقدار اولیه |\n| onValueChange | (value: number \\| null) => void | — | با هر تغییر |\n| unit | ReactNode | \"تومان\" | واحد پول کنار فیلد |\n| showWords | boolean | true | نمایش مبلغ به حروف در متن راهنما |\n| min / max | number | — | محدودهٔ مجاز |\n| rangeMessage | (min?, max?) => ReactNode | خودکار | پیام خطای محدوده |\n| digits | \"fa\" \\| \"latin\" | \"fa\" | خط ارقام نمایشی |\n| name | string | — | نام فیلد مخفی فرم با مقدار عددی خام |\n\n#### بدون React\n\n```ts\nnumberToWords(1250000); // \"یک میلیون و دویست و پنجاه هزار\"\nformatNumber(1250000); // \"۱٬۲۵۰٬۰۰۰\"\n```\n\n#### دسترس‌پذیری\n\n- مبلغ به حروف در متن راهنما است و با `aria-describedby` به فیلد متصل است؛ صفحه‌خوان پس از نام فیلد آن را می‌خواند.\n- واحد پول کنار فیلد نمایش داده می‌شود؛ آن را در برچسب هم تکرار کنید اگر برای فهم فیلد ضروری است.\n\n## کد پستی\n\nکد پستی ۱۰ رقمی ایران با قالب ۱۲۳۴۵-۶۷۸۹۰ و بررسی قواعد آن.\n\n```tsx\n<PersianInput kind=\"postal\" /> // یا مستقیم: <PostalCodeInput />\n```\n\n```tsx\n<PersianInput kind=\"postal\" label=\"کد پستی\" name=\"postal\"\n onValueChange={(value, valid) => …} />\n```\n\nارقام فارسی و انگلیسی پذیرفته می‌شوند و خط تیره خودکار اضافه می‌شود. کد پستی ایران در پنج رقم اول ۰ و ۲ ندارد و چهار رقم یکسان پشت سر هم ندارد؛ `isValidPostalCode` همین قواعد را بررسی می‌کند. این بررسی وجود واقعی کد را تضمین نمی‌کند.\n\n| ویژگی | نوع | پیش‌فرض | توضیح |\n|---|---|---|---|\n| value / defaultValue | string | — | ارقام لاتین |\n| onValueChange | (value: string, valid: boolean) => void | — | — |\n| digits | \"fa\" \\| \"latin\" | \"fa\" | — |\n| invalidMessage | ReactNode | «کد پستی معتبر نیست.» | — |\n| name | string | — | فیلد پنهان با ۱۰ رقم لاتین |\n\n#### دسترس‌پذیری\n\n`autocomplete=\"postal-code\"` دارد تا مرورگر بتواند آن را پر کند. خطا پس از خروج از فیلد یا کامل شدن ۱۰ رقم نمایش داده می‌شود، نه هنگام تایپ.\n\n## ساعت\n\nورود ساعت ۲۴ساعته با ارقام فارسی، تایپ سریع و گام‌های ↑↓.\n\n```tsx\n<PersianInput kind=\"time\" /> // یا مستقیم: <TimeInput />\n```\n\nکاربر می‌تواند «۹۳۰»، «۹:۳۰» یا «9.30» بنویسد؛ پس از خروج از فیلد به «۰۹:۳۰» تبدیل می‌شود. <kbd>↑</kbd>/<kbd>↓</kbd> به گام بعدی یا قبلی می‌پرد.\n\n```tsx\n<PersianInput kind=\"time\" label=\"ساعت شروع جلسه\" min=\"08:00\" max=\"20:00\" value={t} onChange={setT} name=\"start\" />\n```\n\n| ویژگی | نوع | پیش‌فرض | توضیح |\n|---|---|---|---|\n| value / defaultValue | \"HH:MM\" \\| null | null | با ارقام لاتین |\n| onChange | (value: string \\| null) => void | — | — |\n| step | number | 15 | گام ↑↓ به دقیقه |\n| min / max | \"HH:MM\" | — | محدودهٔ مجاز |\n| digits | \"fa\" \\| \"latin\" | \"fa\" | — |\n| invalidMessage | ReactNode | — | پیام ساعت نامعتبر |\n| rangeMessage | (min, max) => ReactNode | «ساعت باید بین … و … باشد.» | پیام ساعت خارج از محدوده |\n\nابزارهای `parseTime` و `formatTime` در `@kashi-ui/persian` هم در دسترس‌اند.\n\n#### دسترس‌پذیری\n\nیک فیلد متنی ساده با برچسب و راهنما؛ برخلاف `<input type=\"time\">`، در همهٔ مرورگرها یکسان و با ارقام فارسی کار می‌کند. خطا پس از خروج از فیلد نمایش داده و به آن وصل می‌شود.\n\n## پلاک خودرو\n\nورود پلاک خودروی ایران با ظاهر واقعی پلاک: دو رقم، حرف، سه رقم و کد استان.\n\n```tsx\n<PersianInput kind=\"plate\" /> // یا مستقیم: <PlateInput />\n```\n\n```tsx\n<PersianInput kind=\"plate\" label=\"پلاک خودرو\" name=\"car\"\n onChange={(plate, valid) => …} />\n// plate = { first: \"12\", letter: \"ب\", middle: \"345\", region: \"67\" }\n```\n\nپلاک نسبت واقعی ۵۲۰ در ۱۱۰ میلی‌متر را دارد و رنگش مثل پلاک واقعی از روی حرف تعیین می‌شود (`plateType`):\n\n| حرف | نوع | رنگ |\n|---|---|---|\n| ب، ج، د، س… | شخصی | سفید |\n| ت / ع / ک | تاکسی / عمومی / کشاورزی | زرد |\n| الف | دولتی | قرمز |\n| پ / ث | پلیس / سپاه | سبز |\n| ش | ارتش | قهوه‌ای روشن |\n| ز / ف | وزارت دفاع / ستاد کل | آبی روشن |\n| ژ | معلولین | سفید |\n\nارتفاع با `--k-plate-height` (پیش‌فرض 3.5rem) عوض می‌شود و عرض به همان نسبت تغییر می‌کند. با پر شدن هر بخش، فوکوس به بخش بعد می‌رود و <kbd>Backspace</kbd> در بخش خالی به بخش قبل برمی‌گردد. `formatPlate` متن خوانا می‌سازد: «۱۲ ب ۳۴۵ ایران ۶۷».\n\n| ویژگی | نوع | پیش‌فرض | توضیح |\n|---|---|---|---|\n| value / defaultValue | { first, letter, middle, region } | — | ارقام لاتین |\n| onChange | (plate, valid: boolean) => void | — | — |\n| name | string | — | چهار فیلد پنهان: name_first، name_letter، name_middle، name_region |\n| labels | { first?, letter?, middle?, region? } | — | نام هر بخش برای صفحه‌خوان |\n| invalidMessage | ReactNode | «پلاک خودرو معتبر نیست.» | — |\n\n#### دسترس‌پذیری\n\nیک `role=\"group\"` با نام «پلاک خودرو» و چهار کنترل که هرکدام نام خودش را دارد («دو رقم اول پلاک»، «حرف پلاک»…). حرف با `<select>` بومی انتخاب می‌شود. وقتی پلاک کامل و معتبر شد، متن کامل آن با `aria-live` خوانده می‌شود.\n\n## اصلاح زبان صفحه‌کلید\n\nاگر کاربر فارسی را با صفحه‌کلید انگلیسی تایپ کند، شکل درست را پیشنهاد می‌دهد: sghl ← سلام.\n\n```tsx\n<PersianInput kind=\"text\" /> // یا مستقیم: <KeyboardFixInput />\n```\n\n```tsx\n<PersianInput kind=\"text\" label=\"جستجو\" value={query} onValueChange={setQuery} />\n```\n\nکاربر فارسی‌زبان زیاد پیش می‌آید که فراموش کند زبان صفحه‌کلید را عوض کند و «سلام» را «sghl» تایپ کند. این فیلد شکل فارسی را زیر فیلد پیشنهاد می‌دهد و با یک کلیک جایگزین می‌کند. برای جستجو بیشترین کاربرد را دارد.\n\nتشخیص به‌صورت پیش‌فرض حدسی است (`suggest=\"auto\"`): کلمه‌های لاتینِ بدون حرف صدادار یا با `;`، `'` و `[` در وسط کلمه مشکوک‌اند. برای پیشنهاد روی هر متن لاتین از `suggest=\"always\"` استفاده کنید. تبدیل بر اساس چیدمان استاندارد صفحه‌کلید فارسی است.\n\n```ts\nfixKeyboardLayout(\"sghl o,fd?\"); // \"سلام خوبی؟\"\nfixKeyboardLayout(\"لخخلمث\", \"en\"); // \"google\"\nlooksLikeWrongLayout(\"sghl\"); // true\n```\n\n| ویژگی | نوع | پیش‌فرض | توضیح |\n|---|---|---|---|\n| value / defaultValue | string | — | — |\n| onValueChange | (value: string) => void | — | — |\n| suggest | \"auto\" \\| \"always\" | \"auto\" | — |\n| renderSuggestion | (fixed, apply) => ReactNode | — | متن دلخواه پیشنهاد |\n\n#### دسترس‌پذیری\n\nپیشنهاد در ناحیهٔ `role=\"status\"` است تا صفحه‌خوان آن را اعلام کند و دکمهٔ آن با کیبورد در دسترس است. پس از جایگزینی، فوکوس به فیلد برمی‌گردد.\n\n## راهنمای استفاده\n\nکِی استفاده کنیم:\n\n- کد ملی، شناسهٔ شرکت، موبایل، تلفن ثابت، کارت، شبا، مبلغ، کد پستی، ساعت و پلاک.\n- هر جا داده ساختار ایرانی دارد و باید ارقام فارسی و لاتین هر دو پذیرفته شوند.\n\nکِی استفاده نکنیم:\n\n- متن آزاد. (ورودی: /docs/components/input/)\n- عدد کوچک با دکمهٔ افزایش. (ورودی عدد: /docs/components/number-input/)\n\nدرست و نادرست:\n\n- ✓ درست: مبلغ با هزارگان و به حروف زیرش؛ کاربر اشتباه یک صفر را همان لحظه می‌بیند.\n- ✗ نادرست: فیلد متنی معمولی که «1250000» می‌گیرد و هیچ بازخوردی نمی‌دهد.\n- ✓ درست: خطا را بعد از کامل شدن رقم‌ها یا خروج از فیلد نشان دهید (رفتار پیش‌فرض کاشی).\n- ✗ نادرست: با اولین رقم «کد ملی معتبر نیست» قرمز شود.\n\nمتن:\n\n- برچسب نام داده است: «کد ملی»، «شمارهٔ شبا»؛ نوع فیلد را در برچسب نیاورید («فیلد کد ملی»).\n\n## وب کامپوننت\n\nهمین کامپوننت بدون React، برای HTML، وردپرس، Vue، Svelte و Angular: `<kashi-national-id>`، `<kashi-mobile>`، `<kashi-card-number>`، `<kashi-iban>`، `<kashi-price>`، `<kashi-postal-code>`، `<kashi-landline>`، `<kashi-legal-id>`، `<kashi-time-input>`، `<kashi-plate-input>` در `@kashi-ui/elements`. هر فیلد با `name` مقدار خام را با ارقام انگلیسی ارسال می‌کند و مقدار نامعتبر جلوی ارسال فرم را می‌گیرد.\n\nنصب بدون ابزار ساخت (CDN):\n\n```html\n<link rel=\"stylesheet\" href=\"https://cdn.jsdelivr.net/npm/@kashi-ui/css/dist/kashi.css\">\n<script type=\"module\" src=\"https://cdn.jsdelivr.net/npm/@kashi-ui/elements/dist/cdn/kashi-elements.js\"></script>\n```\n\nیا با npm، فقط همین بخش:\n\n```bash\nnpm i @kashi-ui/elements @kashi-ui/css\n```\n\n```js\nimport \"@kashi-ui/css\";\nimport \"@kashi-ui/elements/inputs.js\";\n```\n\n```html\n<kashi-national-id name=\"national_id\" required></kashi-national-id>\n<kashi-mobile name=\"mobile\"></kashi-mobile>\n<kashi-card-number name=\"card\"></kashi-card-number>\n<kashi-price name=\"amount\" max=\"50000000\"></kashi-price>\n```\n\n| عنصر | کاربرد |\n|---|---|\n| <kashi-national-id> | کد ملی، ۱۰ رقم با بررسی رقم کنترل |\n| <kashi-legal-id> | شناسهٔ ملی شرکت، ۱۱ رقم |\n| <kashi-mobile> | موبایل؛ +98 را به ۰۹ تبدیل می‌کند و اپراتور را نشان می‌دهد |\n| <kashi-landline> | تلفن ثابت با نام استان از روی پیش‌شماره |\n| <kashi-card-number> | شمارهٔ کارت با نام و لوگوی بانک و بررسی Luhn |\n| <kashi-iban> | شبا با IR خودکار و نام بانک؛ ارقام لاتین |\n| <kashi-postal-code> | کد پستی ۱۰ رقمی با قالب ۱۲۳۴۵-۶۷۸۹۰ |\n| <kashi-price> | مبلغ با جداکنندهٔ هزارگان و مبلغ به حروف زیر فیلد |\n| <kashi-time-input> | ساعت ۲۴ساعته؛ ↑↓ با گام step دقیقه، min و max؛ بخش npm: forms.js |\n| <kashi-plate-input> | پلاک خودرو در چهار بخش؛ با name فیلدهای name_first، name_letter، name_middle و name_region؛ بخش npm: forms.js |\n\nویژگی‌ها:\n\n| نام | مقدار | پیش‌فرض | توضیح |\n|---|---|---|---|\n| label | متن | نام فارسی فیلد | برچسب بالای فیلد؛ بدون آن aria-label بدهید |\n| name | متن | — | نام فیلد در فرم HTML؛ مقدار خام ارسال می‌شود |\n| value | متن | — | مقدار اولیه |\n| hint | متن | — | راهنمای زیر فیلد |\n| error | متن | — | خطای دلخواه، مثلاً از اعتبارسنجی سرور |\n| required / disabled | بولی | — | مثل input معمولی |\n| digits | fa \\| latin | fa (شبا: latin) | خط ارقام نمایشی |\n| invalid-message | متن | — | پیام خطای دلخواه برای مقدار نامعتبر |\n| show-meta | false | — | پنهان کردن نام بانک، اپراتور یا استان |\n| bank-logo | false | — | کارت و شبا: بدون لوگوی بانک |\n| unit / min / max / show-words | — | تومان | فقط مبلغ: واحد، محدوده و نمایش به حروف |\n\nرویدادها و JavaScript:\n\n| نام | توضیح |\n|---|---|\n| kashi-change | با هر تغییر؛ detail: { value, valid } و برای موبایل operator، کارت و شبا bank، تلفن ثابت province |\n| element.value / element.valid | مقدار خام و معتبر بودن؛ مبلغ element.valueAsNumber هم دارد |"},{"slug":"persian/iran-map","url":"https://kashi.sepantapouya.com/docs/persian/iran-map/","title":"نقشهٔ ایران","description":"نقشهٔ ۳۱ استان ایران برای نمایش داده به تفکیک استان یا انتخاب یک یا چند استان؛ با صفحه‌کلید و جدول هم‌ارز برای صفحه‌خوان.","group":"فارسی","components":["IranMap"],"markdown":"```tsx\nimport { IranMap } from \"@kashi-ui/react\";\n```\n\n## نقشهٔ رنگی\n\nبه هر استان یک عدد بدهید؛ استان‌ها بر اساس مقدار در طیف رنگ برند رنگ می‌گیرند و استان‌های بدون داده خاکستری می‌مانند. با رفتن روی هر استان نام و مقدارش نمایش داده می‌شود. داده‌های این نمونه ساختگی‌اند.\n\n```tsx\n<IranMap\n data={{ \"تهران\": 18420, \"خراسان رضوی\": 7310, \"اصفهان\": 6980, /* … */ }}\n unit=\"سفارش\"\n label=\"سفارش‌های ماه گذشته به تفکیک استان\"\n/>\n```\n\nکلیدها همان نام‌های `PROVINCES` در `@kashi-ui/persian` هستند، مثل `\"تهران\"` یا `\"سیستان و بلوچستان\"`. تعداد پله‌های رنگ با `steps` (پیش‌فرض ۵) و قالب عدد با `format` عوض می‌شود. چون معمولاً یک استان (مثل تهران) خیلی بزرگ‌تر از بقیه است، پله‌ها به‌طور پیش‌فرض بر اساس رتبه پخش می‌شوند (`scale=\"quantile\"`) تا تفاوت بقیهٔ استان‌ها هم دیده شود؛ برای بازه‌های مساوی `scale=\"linear\"` بدهید.\n\n## انتخاب استان\n\nبا دادن `value` یا `onChange` نقشه قابل انتخاب می‌شود. مقدار همان نام استان است، پس با [انتخاب استان](/docs/persian/province/) هماهنگ کار می‌کند:\n\n```tsx\n<ProvinceSelect value={province} onChange={setProvince} />\n<IranMap value={province} onChange={setProvince} label=\"استان محل سکونت\" />\n```\n\n## انتخاب چند استان\n\nبا `multiple` مقدار یک آرایه است؛ مثلاً برای مناطق تحت پوشش ارسال یا فیلتر گزارش:\n\n```tsx\n<IranMap multiple value={regions} onChange={setRegions} label=\"استان‌های تحت پوشش ارسال\" />\n```\n\nداده و انتخاب را می‌شود با هم داشت: استان‌ها رنگ داده را نشان می‌دهند و استان انتخاب‌شده با رنگ برند و حاشیه برجسته می‌شود.\n\n| ویژگی | نوع | پیش‌فرض | توضیح |\n|---|---|---|---|\n| data | Record<string, number> | — | مقدار هر استان برای نقشهٔ رنگی |\n| value / defaultValue | string \\| string[] \\| null | — | استان یا استان‌های انتخاب‌شده |\n| onChange | (value: string \\| string[] \\| null) => void | — | با هر انتخاب؛ نقشه را قابل انتخاب می‌کند |\n| multiple | boolean | false | انتخاب چند استان |\n| label | string | «نقشهٔ استان‌های ایران» | نام دسترس‌پذیر |\n| unit | string | — | واحد در راهنما و جدول، مثل «سفارش» |\n| format | (n: number) => string | ارقام فارسی | قالب عدد |\n| steps | number | 5 | تعداد پله‌های رنگ |\n| scale | \"quantile\" \\| \"linear\" | \"quantile\" | quantile: هر پله تعداد تقریباً برابری استان؛ linear: بازهٔ مساوی |\n| legend | boolean | true با data | راهنمای رنگ زیر نقشه |\n| tooltip | (province, value) => string | — | خط اضافه در راهنمای هر استان |\n\nمرز و نقطهٔ وسط هر استان با `IRAN_PROVINCES` و `IRAN_VIEWBOX` هم در دسترس است، برای وقتی که نقشهٔ خودتان را می‌سازید.\n\n## متغیرهای CSS\n\n| متغیر CSS | پیش‌فرض | توضیح |\n|---|---|---|\n| --k-iran-map-empty | var(--k-bg-tertiary) | رنگ استان بدون داده |\n| --k-iran-map-selected | var(--k-bg-brand) | رنگ استان انتخاب‌شده |\n| --k-iran-map-border | var(--k-bg-primary) | رنگ مرز بین استان‌ها |\n\n## دسترس‌پذیری\n\n- نقشهٔ بدون انتخاب یک تصویر با نام است و همهٔ مقدارها در یک جدول پنهان، از بیشترین به کمترین، برای صفحه‌خوان و چاپ آمده‌اند.\n- در حالت انتخاب، استان‌ها `radio` (یا `checkbox` با `multiple`) هستند و فقط یکی در ترتیب Tab است؛ جهت‌نماها بین استان‌ها جابه‌جا می‌شوند، Home و End اول و آخر، و Enter یا فاصله انتخاب می‌کند. نام هر استان همراه مقدارش خوانده می‌شود.\n- رنگ تنها نشانهٔ انتخاب نیست؛ استان انتخاب‌شده حاشیهٔ پررنگ هم دارد و در حالت forced-colors با رنگ سیستم برجسته می‌شود.\n\nمرزها از [Natural Earth](https://www.naturalearthdata.com) (مالکیت عمومی) گرفته و ساده شده‌اند؛ برای کارهای نقشه‌برداری دقیق یا حقوقی مناسب نیستند.\n\n## راهنمای استفاده\n\nکِی استفاده کنیم:\n\n- داده به تفکیک استان: فروش، کاربران، شعبه‌ها.\n- انتخاب یک یا چند استان، کنار فهرست.\n\nکِی استفاده نکنیم:\n\n- مقایسهٔ دقیق عددها؛ کنار نقشه جدول یا میله‌ای افقی بگذارید. (نمودار: /docs/components/charts/)\n- داده‌ای که فقط برای دو سه استان هست؛ نقشهٔ تقریباً خاکستری چیزی نمی‌گوید.\n\nدرست و نادرست:\n\n- ✓ درست: `unit` و `label` بدهید تا راهنما و جدول صفحه‌خوان «۱۸٬۴۲۰ سفارش» بخوانند.\n- ✗ نادرست: نقشه بدون راهنمای رنگ که معلوم نیست پررنگ یعنی بیشتر یا کمتر.\n\n## وب کامپوننت\n\nهمین کامپوننت بدون React، برای HTML، وردپرس، Vue، Svelte و Angular: `<kashi-iran-map>` در `@kashi-ui/elements`.\n\nنصب بدون ابزار ساخت (CDN):\n\n```html\n<link rel=\"stylesheet\" href=\"https://cdn.jsdelivr.net/npm/@kashi-ui/css/dist/kashi.css\">\n<script type=\"module\" src=\"https://cdn.jsdelivr.net/npm/@kashi-ui/elements/dist/cdn/kashi-elements.js\"></script>\n```\n\nیا با npm، فقط همین بخش:\n\n```bash\nnpm i @kashi-ui/elements @kashi-ui/css\n```\n\n```js\nimport \"@kashi-ui/css\";\nimport \"@kashi-ui/elements/map.js\";\n```\n\n```html\n<kashi-iran-map selectable name=\"province\" unit=\"سفارش\" data='{\"تهران\": 1200, \"اصفهان\": 420, \"خراسان رضوی\": 380, \"فارس\": 260, \"آذربایجان شرقی\": 190}'></kashi-iran-map>\n```\n\nویژگی‌ها:\n\n| نام | مقدار | پیش‌فرض | توضیح |\n|---|---|---|---|\n| data | JSON | — | نام استان ← عدد، برای نقشهٔ رنگی |\n| selectable | بولی | — | انتخاب یک استان |\n| multiple | بولی | — | انتخاب چند استان |\n| value / name | متن | — | استان انتخاب‌شده و نام فیلد فرم |\n| unit | متن | — | واحد در راهنما و جدول، مثل «سفارش» |\n| steps / scale | عدد / linear | 5 / quantile | پله‌های رنگ و نحوهٔ تقسیم داده |\n| legend | false | — | بدون راهنمای رنگ |\n| label | متن | نقشهٔ استان‌های ایران | نام برای صفحه‌خوان |\n\nرویدادها و JavaScript:\n\n| نام | توضیح |\n|---|---|\n| kashi-change | detail: { value }؛ در multiple آرایه |\n| element.data | داده با JavaScript |"},{"slug":"persian/money","url":"https://kashi.sepantapouya.com/docs/persian/money/","title":"نمایش مبلغ","description":"مبلغ با تومان یا ریال، حالت خلاصه مثل «۱٫۲ میلیون» و مبلغ به حروف برای صفحه‌خوان.","group":"فارسی","components":["Money"],"markdown":"```tsx\nimport { Money } from \"@kashi-ui/react\";\n```\n\n## نمونه\n\n```tsx\n<Money value={1250000} />\n<Money value={1250000} compact />\n<Money value={48500000} from=\"rial\" compact /> {/* ریال به تومان */}\n<Money value={-85000} signed />\n```\n\nدر محصولات ایرانی مبلغ معمولاً در پایگاه داده به ریال است و به کاربر به تومان نشان داده می‌شود. با `from=\"rial\"` تبدیل خودکار انجام می‌شود. حالت `compact` برای کارت‌ها و جدول‌ها مناسب است و برای اعداد بسیار بزرگ «همت» (هزار میلیارد تومان) را به کار می‌برد.\n\n```ts\nimport { formatMoney } from \"@kashi-ui/persian\";\nformatMoney(1250000); // \"۱٬۲۵۰٬۰۰۰ تومان\"\nformatMoney(1250000, { compact: true }); // \"۱٫۲۵ میلیون تومان\"\nformatMoney(5000, { unit: \"rial\" }); // \"۵۰٬۰۰۰ ریال\"\n```\n\n| ویژگی | نوع | پیش‌فرض | توضیح |\n|---|---|---|---|\n| value | number | — | — |\n| unit | \"toman\" \\| \"rial\" | \"toman\" | واحد نمایش |\n| from | \"toman\" \\| \"rial\" | \"toman\" | واحد مقدار ورودی |\n| compact | boolean | false | هزار، میلیون، میلیارد، همت |\n| signed | boolean | false | + و رنگ سبز یا قرمز |\n| words | boolean | true | مبلغ به حروف در title و برای صفحه‌خوان |\n| size | \"sm\" \\| \"md\" \\| \"lg\" \\| \"xl\" | \"md\" | — |\n\n## دسترس‌پذیری\n\nعنصر `<data>` مقدار عددی خام را در `value` نگه می‌دارد. صفحه‌خوان به جای ارقام جداشده، مبلغ را به حروف می‌خواند («یک میلیون و دویست و پنجاه هزار تومان»). رنگ مثبت و منفی همیشه همراه علامت + و − است.\n\n## راهنمای استفاده\n\nکِی استفاده کنیم:\n\n- هر مبلغی که نمایش داده می‌شود: قیمت، موجودی، جمع سفارش، تراکنش.\n- مثبت و منفی با `signed` در گردش حساب.\n\nکِی استفاده نکنیم:\n\n- گرفتن مبلغ از کاربر. (فیلد فارسی (مبلغ): /docs/persian/input/)\n\nدرست و نادرست:\n\n- ✓ درست: یک واحد در کل محصول (تومان یا ریال)، و `compact` برای عددهای بزرگ در کارت آمار: «۱٫۲ میلیارد تومان».\n- ✗ نادرست: تومان در یک صفحه و ریال در صفحهٔ دیگر.\n\n## وب کامپوننت\n\nهمین کامپوننت بدون React، برای HTML، وردپرس، Vue، Svelte و Angular: `<kashi-money>` در `@kashi-ui/elements`.\n\nنصب بدون ابزار ساخت (CDN):\n\n```html\n<link rel=\"stylesheet\" href=\"https://cdn.jsdelivr.net/npm/@kashi-ui/css/dist/kashi.css\">\n<script type=\"module\" src=\"https://cdn.jsdelivr.net/npm/@kashi-ui/elements/dist/cdn/kashi-elements.js\"></script>\n```\n\nیا با npm، فقط همین بخش:\n\n```bash\nnpm i @kashi-ui/elements @kashi-ui/css\n```\n\n```js\nimport \"@kashi-ui/css\";\nimport \"@kashi-ui/elements/display.js\";\n```\n\n```html\n<kashi-money value=\"1250000\" size=\"lg\"></kashi-money>\n<kashi-money value=\"3800000000\" compact></kashi-money>\n<kashi-money value=\"-45000\" signed></kashi-money>\n```\n\nویژگی‌ها:\n\n| نام | مقدار | پیش‌فرض | توضیح |\n|---|---|---|---|\n| value | عدد | — | مبلغ |\n| unit / from | toman \\| rial | toman | واحد نمایش و واحد مقدار ورودی |\n| compact | بولی | — | ۱٫۲ میلیون تومان |\n| signed | بولی | — | رنگ سبز و قرمز با علامت + |\n| size | sm \\| md \\| lg \\| xl | md | اندازه |\n| words | false | — | بدون مبلغ به حروف برای صفحه‌خوان |"},{"slug":"persian/month-picker","url":"https://kashi.sepantapouya.com/docs/persian/month-picker/","title":"انتخاب ماه","description":"انتخاب ماه و سال شمسی برای گزارش ماهانه، صورت‌حساب یا تاریخ انقضا.","group":"فارسی","components":["MonthPicker"],"markdown":"```tsx\nimport { MonthPicker, type JalaliMonth } from \"@kashi-ui/react\";\n```\n\n## نمونه\n\n```tsx\nconst [m, setM] = useState<JalaliMonth | null>(null);\n\n<MonthPicker aria-label=\"ماه گزارش\" value={m} onChange={setM} max={{ year: 1405, month: 12 }} />\n```\n\nبرای تبدیل به تاریخ میلادی: `toGregorian(m.year, m.month, 1)` از `@kashi-ui/persian`.\n\n| ویژگی | نوع | پیش‌فرض | توضیح |\n|---|---|---|---|\n| value / defaultValue | { year, month } \\| null | null | ماه شمسی؛ month از ۱ تا ۱۲ |\n| onChange | (value) => void | — | — |\n| min / max | { year, month } | — | محدودهٔ مجاز |\n| digits | \"fa\" \\| \"latin\" | \"fa\" | — |\n| aria-label | string | «انتخاب ماه» | — |\n| labels | { prevYear?, nextYear? } | «سال قبل»، «سال بعد» | — |\n\n## کیبورد\n\n| کلید | عمل |\n|---|---|\n| <kbd>←</kbd> / <kbd>→</kbd> | ماه بعد / قبل (در RTL) |\n| <kbd>↑</kbd> / <kbd>↓</kbd> | سه ماه قبل / بعد |\n| <kbd>PageUp</kbd> / <kbd>PageDown</kbd> | سال قبل / بعد |\n| <kbd>Home</kbd> / <kbd>End</kbd> | فروردین / اسفند |\n\n## دسترس‌پذیری\n\nهر ماه دکمه‌ای با نام کامل («آبان ۱۴۰۵») و `aria-pressed` است؛ ماه جاری `aria-current=\"date\"` دارد. فقط یک ماه در ترتیب Tab است و سال با `aria-live` اعلام می‌شود.\n\n## راهنمای استفاده\n\nکِی استفاده کنیم:\n\n- گزارش ماهانه، صورت‌حساب، تاریخ انقضای کارت.\n\nکِی استفاده نکنیم:\n\n- روز مشخص. (دیت‌پیکر شمسی: /docs/persian/date-picker/)\n\nدرست و نادرست:\n\n- ✓ درست: ماه‌های آینده را برای گزارش با `max` غیرفعال کنید.\n- ✗ نادرست: اجازهٔ انتخاب ماهی که هنوز داده ندارد و گزارش خالی می‌دهد.\n\n## وب کامپوننت\n\nهمین کامپوننت بدون React، برای HTML، وردپرس، Vue، Svelte و Angular: `<kashi-month-picker>` در `@kashi-ui/elements`.\n\nنصب بدون ابزار ساخت (CDN):\n\n```html\n<link rel=\"stylesheet\" href=\"https://cdn.jsdelivr.net/npm/@kashi-ui/css/dist/kashi.css\">\n<script type=\"module\" src=\"https://cdn.jsdelivr.net/npm/@kashi-ui/elements/dist/cdn/kashi-elements.js\"></script>\n```\n\nیا با npm، فقط همین بخش:\n\n```bash\nnpm i @kashi-ui/elements @kashi-ui/css\n```\n\n```js\nimport \"@kashi-ui/css\";\nimport \"@kashi-ui/elements/pickers.js\";\n```\n\n```html\n<kashi-month-picker value=\"1405-01\" name=\"month\"></kashi-month-picker>\n```\n\nویژگی‌ها:\n\n| نام | مقدار | پیش‌فرض | توضیح |\n|---|---|---|---|\n| value | 1405-01 | — | سال و ماه شمسی |\n| min / max | 1404-01 | — | محدودهٔ ماه‌های قابل انتخاب |\n| name / label / digits | — | — | نام فرم، نام صفحه‌خوان و خط ارقام |\n\nرویدادها و JavaScript:\n\n| نام | توضیح |\n|---|---|\n| kashi-change | detail: { value: \"1405-01\", year, month } |"},{"slug":"persian/otp","url":"https://kashi.sepantapouya.com/docs/persian/otp/","title":"کد تأیید (OTP)","description":"ورود کد پیامکی یک‌بارمصرف؛ با پر شدن خودکار از پیامک در گوشی و ارقام فارسی.","group":"فارسی","components":["OtpInput"],"markdown":"```tsx\nimport { OtpInput } from \"@kashi-ui/react\";\n```\n\n## نمونه\n\nکد آزمایشی `123456` را تایپ کنید، یا کد را کپی کنید و بچسبانید.\n\n```tsx\n<OtpInput\n label=\"کد ارسال‌شده به ۰۹۱۲ ••• ۶۷۸۹\"\n length={6}\n groupAfter={3}\n onComplete={(code) => verify(code)}\n/>\n```\n\n## چرا یک input؟\n\nبیشتر پیاده‌سازی‌های OTP شش فیلد جدا دارند که چسباندن، پاک کردن و صفحه‌خوان را خراب می‌کند. کاشی **یک input واقعی** زیر خانه‌های نمایشی دارد، پس:\n\n- **پر شدن خودکار:** با `autocomplete=\"one-time-code\"`، iOS و Android کد پیامک دریافتی را بالای صفحه‌کلید پیشنهاد می‌دهند.\n- **چسباندن:** «۱۲۳ ۴۵۶» یا «123-456» هم درست خوانده می‌شود؛ ارقام فارسی و فاصله‌ها پذیرفته می‌شوند.\n- **صفحه‌خوان:** یک فیلد با یک برچسب می‌خواند، نه شش فیلد بی‌نام.\n- **پاک کردن و جابه‌جایی:** <kbd>Backspace</kbd>، کلیدهای جهت‌دار و انتخاب متن همان رفتار عادی را دارند.\n\n## ویژگی‌ها\n\n| ویژگی | نوع | پیش‌فرض | توضیح |\n|---|---|---|---|\n| length | number | 6 | تعداد رقم |\n| value / defaultValue | string | — | کد با ارقام انگلیسی |\n| onChange | (code) => void | — | با هر تغییر |\n| onComplete | (code) => void | — | وقتی همهٔ رقم‌ها وارد شد |\n| groupAfter | number | — | خط فاصله بعد از این تعداد رقم |\n| label / hint / error | ReactNode | — | مثل Input |\n| digits | \"fa\" \\| \"latin\" | \"fa\" | خط ارقام نمایشی |\n| size | \"sm\" \\| \"md\" | \"md\" | اندازهٔ خانه‌ها |\n| name | string | — | نام فیلد مخفی فرم |\n\n## راهنمای استفاده\n\nکِی استفاده کنیم:\n\n- کد پیامکی یک‌بارمصرف برای ورود، پرداخت یا تأیید شماره.\n\nکِی استفاده نکنیم:\n\n- رمز عبور ثابت. (ورودی: /docs/components/input/)\n\nدرست و نادرست:\n\n- ✓ درست: کنار فیلد بگویید کد به کدام شماره رفت و دکمهٔ ارسال دوباره با شمارش معکوس بگذارید.\n- ✗ نادرست: فیلد کد تنها، بدون راه برای ارسال دوباره یا عوض کردن شماره.\n- ✓ درست: با `onComplete` کد را خودکار بفرستید؛ دکمهٔ «تأیید» اضافه لازم نیست.\n- ✗ نادرست: پاک کردن کد پس از خطا بدون اینکه بگویید اشتباه بود.\n\n## وب کامپوننت\n\nهمین کامپوننت بدون React، برای HTML، وردپرس، Vue، Svelte و Angular: `<kashi-otp>` در `@kashi-ui/elements`.\n\nنصب بدون ابزار ساخت (CDN):\n\n```html\n<link rel=\"stylesheet\" href=\"https://cdn.jsdelivr.net/npm/@kashi-ui/css/dist/kashi.css\">\n<script type=\"module\" src=\"https://cdn.jsdelivr.net/npm/@kashi-ui/elements/dist/cdn/kashi-elements.js\"></script>\n```\n\nیا با npm، فقط همین بخش:\n\n```bash\nnpm i @kashi-ui/elements @kashi-ui/css\n```\n\n```js\nimport \"@kashi-ui/css\";\nimport \"@kashi-ui/elements/fields.js\";\n```\n\n```html\n<kashi-otp label=\"کد ارسال‌شده\" length=\"6\" group-after=\"3\" name=\"code\"></kashi-otp>\n```\n\nویژگی‌ها:\n\n| نام | مقدار | پیش‌فرض | توضیح |\n|---|---|---|---|\n| length | عدد | 6 | تعداد رقم |\n| group-after | عدد | — | خط فاصله پس از این تعداد رقم |\n| label / name / value / hint / error | متن | — | مثل بقیهٔ فیلدها |\n| size | sm | — | کوچک‌تر |\n| digits | fa \\| latin | fa | خط ارقام |\n\nرویدادها و JavaScript:\n\n| نام | توضیح |\n|---|---|\n| kashi-change | detail: { value } |\n| kashi-complete | وقتی همهٔ رقم‌ها وارد شد؛ detail: { value } |"},{"slug":"persian/province","url":"https://kashi.sepantapouya.com/docs/persian/province/","title":"انتخاب استان","description":"انتخاب یکی از ۳۱ استان با جستجو؛ با نام مرکز استان هم پیدا می‌شود.","group":"فارسی","components":["ProvinceSelect"],"markdown":"```tsx\nimport { ProvinceSelect } from \"@kashi-ui/react\";\n```\n\n## نمونه\n\n```tsx\n<ProvinceSelect value={province} onChange={setProvince} name=\"province\" />\n```\n\nروی `Combobox` ساخته شده، پس همهٔ ویژگی‌های آن را دارد. فهرست استان‌ها با مرکز و پیش‌شمارهٔ تلفن ثابت در `PROVINCES` از `@kashi-ui/persian` هم در دسترس است:\n\n```ts\nimport { PROVINCES } from \"@kashi-ui/persian\";\n// [{ name: \"اصفهان\", capital: \"اصفهان\", areaCode: \"31\" }, …]\n```\n\n| ویژگی | نوع | پیش‌فرض | توضیح |\n|---|---|---|---|\n| value / defaultValue | string \\| null | — | نام استان |\n| onChange | (value: string \\| null) => void | — | — |\n| showCapital | boolean | true | مرکز استان زیر نام |\n| … | ComboboxProps | — | همهٔ ویژگی‌های Combobox |\n\n## دسترس‌پذیری\n\nهمان الگوی Combobox در WAI-ARIA؛ تعداد نتیجه‌ها پس از هر تایپ اعلام می‌شود.\n\n## راهنمای استفاده\n\nکِی استفاده کنیم:\n\n- انتخاب استان در فرم یا فیلتر؛ با نام مرکز استان هم پیدا می‌شود.\n\nکِی استفاده نکنیم:\n\n- نشانی کامل. (فرم نشانی: /docs/persian/address/)\n- انتخاب روی نقشه یا چند استان. (نقشهٔ ایران: /docs/persian/iran-map/)\n\nدرست و نادرست:\n\n- ✓ درست: بگذارید کاربر بنویسد «مشهد» و خراسان رضوی را بگیرد.\n- ✗ نادرست: سلکت بومی با ۳۱ گزینهٔ بی‌جستجو.\n\n## وب کامپوننت\n\nهمین کامپوننت بدون React، برای HTML، وردپرس، Vue، Svelte و Angular: `<kashi-province-select>`، `<kashi-city-select>` در `@kashi-ui/elements`. استان با نام مرکزش هم پیدا می‌شود؛ شهر بدون `province` همهٔ شهرهای ایران را با نام استان نشان می‌دهد.\n\nنصب بدون ابزار ساخت (CDN):\n\n```html\n<link rel=\"stylesheet\" href=\"https://cdn.jsdelivr.net/npm/@kashi-ui/css/dist/kashi.css\">\n<script type=\"module\" src=\"https://cdn.jsdelivr.net/npm/@kashi-ui/elements/dist/cdn/kashi-elements.js\"></script>\n```\n\nیا با npm، فقط همین بخش:\n\n```bash\nnpm i @kashi-ui/elements @kashi-ui/css\n```\n\n```js\nimport \"@kashi-ui/css\";\nimport \"@kashi-ui/elements/combobox.js\";\n```\n\n```html\n<kashi-province-select name=\"province\"></kashi-province-select>\n<kashi-city-select name=\"city\"></kashi-city-select>\n```\n\n| عنصر | کاربرد |\n|---|---|\n| <kashi-province-select> | ۳۱ استان با جستجو |\n| <kashi-city-select> | ۱۲۷۸ شهر؛ با province فقط شهرهای همان استان |\n\nویژگی‌ها:\n\n| نام | مقدار | پیش‌فرض | توضیح |\n|---|---|---|---|\n| label | متن | نام فارسی فیلد | برچسب بالای فیلد؛ بدون آن aria-label بدهید |\n| name | متن | — | نام فیلد در فرم HTML؛ مقدار خام ارسال می‌شود |\n| value | متن | — | مقدار اولیه |\n| hint | متن | — | راهنمای زیر فیلد |\n| error | متن | — | خطای دلخواه، مثلاً از اعتبارسنجی سرور |\n| required / disabled | بولی | — | مثل input معمولی |\n| placeholder | متن | — | متن خالی فیلد |\n| show-capital | false | — | استان: بدون نام مرکز استان زیر گزینه‌ها |\n| province | نام استان | — | شهر: فقط شهرهای این استان |\n| allow-custom | false | — | شهر: نپذیرفتن شهر خارج از فهرست |\n\nرویدادها و JavaScript:\n\n| نام | توضیح |\n|---|---|\n| kashi-change | استان: detail: { value, province }؛ شهر: detail: { value, province } |"},{"slug":"persian/receipt","url":"https://kashi.sepantapouya.com/docs/persian/receipt/","title":"رسید پرداخت","description":"صفحهٔ نتیجهٔ پرداخت با وضعیت، مبلغ به حروف، کد پیگیری قابل کپی و کارت پوشانده.","group":"فارسی","components":["PaymentReceipt"],"markdown":"```tsx\nimport { PaymentReceipt } from \"@kashi-ui/react\";\n```\n\n## نمونه\n\n```tsx\n<PaymentReceipt status=\"success\" amount={1250000} merchant=\"فروشگاه کاشی\"\n trackingCode=\"482913\" referenceId=\"140508270123\"\n cardNumber=\"6104337712345678\" date={paidAt}>\n <Button variant=\"primary\">بازگشت به فروشگاه</Button>\n</PaymentReceipt>\n```\n\nصفحه‌ای که کاربر پس از بازگشت از درگاه بانکی می‌بیند. سه وضعیت دارد: `success`، `pending` (بانک هنوز تأیید نکرده) و `failed`. برای پرداخت ناموفق، در `description` توضیح دهید که مبلغ کسرشده کی برمی‌گردد. کارت خودکار پوشانده می‌شود و تاریخ شمسی است. ردیف‌های دیگر را با `rows` اضافه کنید.\n\n| ویژگی | نوع | پیش‌فرض | توضیح |\n|---|---|---|---|\n| status | \"success\" \\| \"pending\" \\| \"failed\" | — | — |\n| amount | number | — | — |\n| unit | \"toman\" \\| \"rial\" | \"toman\" | — |\n| title / description | ReactNode | — | پیش‌فرض بر اساس وضعیت |\n| merchant | ReactNode | — | پذیرنده |\n| trackingCode | string | — | با دکمهٔ کپی |\n| referenceId | string | — | — |\n| cardNumber | string | — | پوشانده با لوگوی بانک |\n| date | Date | — | — |\n| rows | [ReactNode, ReactNode][] | — | ردیف‌های دیگر |\n| children | ReactNode | — | دکمه‌ها |\n\n## دسترس‌پذیری\n\nرسید یک `section` است که عنوانش نام آن است. وضعیت با آیکون، رنگ و متن عنوان بیان می‌شود، نه فقط با رنگ. دکمهٔ کپی کد پیگیری پس از کپی «کپی شد» را اعلام می‌کند.\n\n## راهنمای استفاده\n\nکِی استفاده کنیم:\n\n- صفحهٔ نتیجهٔ پرداخت: موفق، ناموفق، در انتظار.\n\nکِی استفاده نکنیم:\n\n- تأیید کوتاه داخل صفحه؛ رسید جای خودش را می‌خواهد. (توست: /docs/components/toast/)\n\nدرست و نادرست:\n\n- ✓ درست: در پرداخت ناموفق بگویید پول چه شد: «مبلغ تا ۷۲ ساعت به حساب شما برمی‌گردد».\n- ✗ نادرست: فقط «پرداخت ناموفق» که کاربر را نگران پولش می‌گذارد.\n- ✓ درست: کد پیگیری قابل کپی و درشت؛ همان چیزی است که کاربر به پشتیبانی می‌گوید.\n- ✗ نادرست: کد پیگیری کوچک در پایین صفحه که فقط با اسکرین‌شات حفظ می‌شود."},{"slug":"persian/text-fix","url":"https://kashi.sepantapouya.com/docs/persian/text-fix/","title":"ویرایش نگارشی","description":"ناحیهٔ متن که «ي» و «ك» عربی، نیم‌فاصله، علامت‌های فارسی و ارقام را پیدا و با یک کلیک اصلاح می‌کند.","group":"فارسی","components":["PersianTextarea"],"markdown":"```tsx\nimport { PersianTextarea } from \"@kashi-ui/react\";\n```\n\n## نمونه\n\n```tsx\n<PersianTextarea label=\"توضیحات محصول\" value={text} onValueChange={setText} />\n```\n\nمتنی که از ورد، سایت‌های عربی یا صفحه‌کلیدهای مختلف کپی می‌شود معمولاً «ي» و «ك» عربی دارد که جستجو را خراب می‌کند. این فیلد تعداد مشکل‌ها را زیر متن نشان می‌دهد و با «اصلاح نگارش» همه را درست می‌کند:\n\n| اصلاح | قبل | بعد |\n|---|---|---|\n| حروف عربی | كتاب، ايران | کتاب، ایران |\n| نیم‌فاصله | مي خوانم، كتاب ها، بزرگ تر | می‌خوانم، کتاب‌ها، بزرگ‌تر |\n| علامت‌ها | چرا ? خوب , بد | چرا؟ خوب، بد |\n| ارقام | ۲۰ را 20 نوشتن | ۲۰ |\n| فاصله | فاصلهٔ اضافه و فاصله پیش از نقطه | یک فاصله |\n\nبا `fixOnBlur` اصلاح هنگام خروج از فیلد خودکار انجام می‌شود. همین منطق در `fixPersianText` بدون React در دسترس است و تعداد هر نوع اصلاح را هم برمی‌گرداند.\n\n| ویژگی | نوع | پیش‌فرض | توضیح |\n|---|---|---|---|\n| value / defaultValue | string | — | — |\n| onValueChange | (value: string) => void | — | — |\n| fixDigits | boolean | true | ارقام لاتین کنار متن فارسی |\n| fixOnBlur | boolean | false | اصلاح خودکار هنگام خروج |\n| labels | { fix?, clean? } | — | — |\n\nهمهٔ ویژگی‌های `Textarea` مثل `autoResize` و `showCount` را هم می‌پذیرد.\n\n## راهنمای استفاده\n\nکِی استفاده کنیم:\n\n- متن‌هایی که کاربر از جای دیگر کپی می‌کند: توضیح محصول، آگهی، مقاله.\n- پنل مدیریت محتوا که نگارش یکدست مهم است.\n\nکِی استفاده نکنیم:\n\n- فیلد کوتاه مثل نام؛ `normalizePersian` را بی‌صدا هنگام ذخیره صدا بزنید. (ابزارهای فارسی: /docs/persian/utilities/)\n\nدرست و نادرست:\n\n- ✓ درست: اصلاح با دکمه، تا کاربر ببیند چه عوض می‌شود.\n- ✗ نادرست: تغییر بی‌خبر متن کاربر هنگام تایپ.\n\n## وب کامپوننت\n\nهمین کامپوننت بدون React، برای HTML، وردپرس، Vue، Svelte و Angular: `<kashi-persian-textarea>`، `<kashi-keyboard-fix>` در `@kashi-ui/elements`.\n\nنصب بدون ابزار ساخت (CDN):\n\n```html\n<link rel=\"stylesheet\" href=\"https://cdn.jsdelivr.net/npm/@kashi-ui/css/dist/kashi.css\">\n<script type=\"module\" src=\"https://cdn.jsdelivr.net/npm/@kashi-ui/elements/dist/cdn/kashi-elements.js\"></script>\n```\n\nیا با npm، فقط همین بخش:\n\n```bash\nnpm i @kashi-ui/elements @kashi-ui/css\n```\n\n```js\nimport \"@kashi-ui/css\";\nimport \"@kashi-ui/elements/forms.js\";\n```\n\n```html\n<kashi-persian-textarea label=\"توضیحات\" name=\"body\" value=\"اين متن ، ي و ك عربي دارد\"></kashi-persian-textarea>\n<kashi-keyboard-fix label=\"جستجو\" value=\"sghl\"></kashi-keyboard-fix>\n```\n\n| عنصر | کاربرد |\n|---|---|\n| <kashi-persian-textarea> | متن چندخطی با شمارش و اصلاح ی و ک عربی، نیم‌فاصله، علامت‌ها و ارقام |\n| <kashi-keyboard-fix> | فیلد متنی که متن تایپ‌شده با صفحه‌کلید انگلیسی را تشخیص می‌دهد («sghl» ← «سلام») |\n\nویژگی‌ها:\n\n| نام | مقدار | پیش‌فرض | توضیح |\n|---|---|---|---|\n| label / name / value / placeholder / required | متن | — | مثل بقیهٔ فیلدها |\n| fix-on-blur | بولی | — | متن: اصلاح خودکار هنگام خروج از فیلد |\n| fix-digits | false | — | متن: ارقام انگلیسی اصلاح نشوند |\n| rows | عدد | 4 | متن: تعداد خط |\n| suggest | always | auto | صفحه‌کلید: پیشنهاد برای هر متن لاتین |\n\nرویدادها و JavaScript:\n\n| نام | توضیح |\n|---|---|\n| kashi-change | detail: { value } |"},{"slug":"persian/utilities","url":"https://kashi.sepantapouya.com/docs/persian/utilities/","title":"ابزارهای فارسی","description":"تاریخ شمسی، ارقام فارسی، قالب‌بندی اعداد و اعتبارسنجی؛ بدون وابستگی و برای هر فریم‌ورکی.","group":"فارسی","components":[],"markdown":"پکیج `@kashi-ui/persian` هیچ وابستگی‌ای ندارد و در مرورگر، Node.js و هر فریم‌ورکی (React، Vue، Svelte، Angular) کار می‌کند. کامپوننت‌های فارسی کاشی هم روی همین پکیج ساخته شده‌اند.\n\n```bash\nnpm i @kashi-ui/persian\n```\n\n## تاریخ شمسی\n\n```ts\nimport { toJalali, toGregorian, formatJalali, parseJalali } from \"@kashi-ui/persian\";\n\ntoJalali(new Date(2024, 7, 2)); // { jy: 1403, jm: 5, jd: 12 }\ntoGregorian(1403, 5, 12); // Date: 2024-08-02\n\nformatJalali(date); // \"۱۴۰۳/۰۵/۱۲\"\nformatJalali(date, { style: \"long\" }); // \"۱۲ مرداد ۱۴۰۳\"\nformatJalali(date, { style: \"full\" }); // \"جمعه ۱۲ مرداد ۱۴۰۳\"\n\nparseJalali(\"۱۴۰۳/۵/۱۲\"); // Date\nparseJalali(\"1404/12/30\"); // null (۱۴۰۴ کبیسه نیست)\n```\n\n| تابع | کاربرد |\n|---|---|\n| `toJalali(date)` | میلادی به شمسی |\n| `toGregorian(jy, jm, jd)` | شمسی به `Date` |\n| `formatJalali(date, options)` | نمایش؛ سبک `numeric`، `long` یا `full` |\n| `parseJalali(text)` | خواندن متن کاربر؛ ارقام فارسی و جداکننده‌های `/`، `-` و `.` |\n| `isLeapJalaliYear(jy)` | سال کبیسه |\n| `jalaliMonthLength(jy, jm)` | تعداد روزهای ماه |\n| `isValidJalaliDate(jy, jm, jd)` | وجود تاریخ |\n| `jalaliWeekday(date)` | روز هفته؛ ۰ = شنبه |\n| `toISODate(date)` | `YYYY-MM-DD` بر اساس وقت محلی، برای ارسال به سرور |\n| `fromISODate(text)` | `YYYY-MM-DD` به `Date` محلی |\n| `JALALI_MONTHS`، `WEEKDAYS` | نام ماه‌ها و روزها |\n\n> **هشدار منطقهٔ زمانی:** برای ارسال تاریخ به سرور از `date.toISOString()` استفاده نکنید. این تابع زمان را به UTC تبدیل می‌کند و چون ایران ۳٫۵ ساعت جلوتر است، نیمه‌شب را روز قبل می‌کند. به‌جای آن از `toISODate(date)` استفاده کنید.\n\nدقت: تبدیل برای همهٔ ۷۲٬۹۷۰ روز از ۱۳۰۰ تا ۱۵۰۰ با تقویم persian کتابخانهٔ ICU (همان تقویم مرورگرها) مقایسه و تست شده است.\n\n## زمان نسبی\n\n```ts\nimport { formatRelative } from \"@kashi-ui/persian\";\n\nformatRelative(date); // «همین الان»، «۵ دقیقه پیش»، «دیروز»، «۳ روز دیگر»، «۲ سال پیش»\n```\n\n«دیروز» و «فردا» بر اساس روز تقویمی محاسبه می‌شوند، نه ۲۴ ساعت. در React از `<RelativeTime date={date} />` استفاده کنید که هر دقیقه به‌روز می‌شود و تاریخ کامل شمسی را در `title` دارد.\n\n## ارقام و اعداد\n\n```ts\nimport { toFaDigits, toEnDigits, onlyDigits, formatNumber } from \"@kashi-ui/persian\";\n\ntoFaDigits(1403); // \"۱۴۰۳\"\ntoEnDigits(\"۰۹۱۲ ٣٤٥\"); // \"0912 345\" (ارقام عربی هم)\nonlyDigits(\"۰۹۱۲-۳۴۵ 67\"); // \"091234567\"\nformatNumber(1250000); // \"۱٬۲۵۰٬۰۰۰\"\n```\n\n`toEnDigits` را همیشه پیش از ذخیرهٔ ورودی کاربر استفاده کنید؛ کاربر ایرانی ممکن است با هر دو صفحه‌کلید تایپ کند.\n\n## عدد به حروف\n\n```ts\nimport { numberToWords } from \"@kashi-ui/persian\";\n\nnumberToWords(1250000); // \"یک میلیون و دویست و پنجاه هزار\"\nnumberToWords(1001); // \"هزار و یک\"\nnumberToWords(-45); // \"منفی چهل و پنج\"\n```\n\n## یکدست‌سازی متن\n\nکاربران با صفحه‌کلید عربی «ي» و «ك» تایپ می‌کنند و جستجوی «علی» دیگر «علي» را پیدا نمی‌کند. پیش از ذخیره و جستجو، متن را یکدست کنید:\n\n```ts\nimport { normalizePersian } from \"@kashi-ui/persian\";\n\nnormalizePersian(\"علي كريمي\"); // \"علی کریمی\"\nnormalizePersian(\"مـــدرسه\"); // \"مدرسه\" (حذف کشیده)\nnormalizePersian(\"كتاب ١٢\", { digits: \"fa\" }); // \"کتاب ۱۲\"\n```\n\nبرای متنی که کاربر می‌نویسد (توضیحات، نظرها، مقاله‌ها)، `fixPersianText` ویرایش نگارشی هم انجام می‌دهد: نیم‌فاصله، ویرگول و علامت سؤال فارسی و ارقام. تعداد هر نوع اصلاح را هم برمی‌گرداند. کامپوننت آن [ویرایش نگارشی](/docs/persian/text-fix/) است.\n\n```ts\nimport { fixPersianText, fixKeyboardLayout } from \"@kashi-ui/persian\";\n\nfixPersianText(\"كتاب ها را مي خوانم ?\").text; // \"کتاب‌ها را می‌خوانم؟\"\nfixKeyboardLayout(\"sghl\"); // \"سلام\" (صفحه‌کلید انگلیسی به فارسی)\n```\n\n## مبلغ\n\n```ts\nimport { formatMoney, rialToToman } from \"@kashi-ui/persian\";\n\nformatMoney(1250000); // \"۱٬۲۵۰٬۰۰۰ تومان\"\nformatMoney(1250000, { compact: true }); // \"۱٫۲۵ میلیون تومان\"\nformatMoney(125000000, { from: \"rial\", compact: true }); // \"۱۲٫۵ میلیون تومان\"\nrialToToman(50000); // 5000\n```\n\n## اعتبارسنجی\n\n```ts\nimport { isValidNationalId, isValidCardNumber, getBankByCard, formatCardNumber } from \"@kashi-ui/persian\";\n\nisValidNationalId(\"0013542419\"); // true\nisValidLegalId(\"10380284790\"); // true (شناسهٔ ملی شرکت)\nisValidIban(\"IR062960000000100324200001\");\nisValidMobile(\"+98 912 345 6789\"); // true\ngetMobileOperator(\"0935\")?.name; // \"ایرانسل\"\nisValidCardNumber(\"4111111111111111\"); // true\ngetBankByCard(\"610433\"); // { name: \"بانک ملت\", slug: \"mellat\" }\nformatCardNumber(\"6104331234\"); // \"6104 3312 34\"\nisValidPostalCode(\"1193653471\"); // true\nformatPostalCode(\"1193653471\"); // \"11936-53471\"\nisValidPlate({ first: \"12\", letter: \"ب\", middle: \"345\", region: \"67\" }); // true\nformatPlate({ first: \"12\", letter: \"ب\", middle: \"345\", region: \"67\" }); // \"۱۲ ب ۳۴۵ ایران ۶۷\"\nplateType(\"ت\"); // \"taxi\"\nisValidLandline(\"+98 21 8877 6655\"); // true\ngetLandlineProvince(\"031\"); // \"اصفهان\"\nformatLandline(\"02188776655\"); // \"021 8877 6655\"\n```\n\n## ساعت\n\n```ts\nimport { parseTime, formatTime } from \"@kashi-ui/persian\";\n\nparseTime(\"۹:۳۰\"); // { hours: 9, minutes: 30 }\nparseTime(\"2145\"); // { hours: 21, minutes: 45 }\nparseTime(\"25:00\"); // null\nformatTime({ hours: 9, minutes: 5 }); // \"09:05\"\n```\n\n## راهنمای استفاده\n\nکِی استفاده کنیم:\n\n- قالب‌بندی تاریخ، عدد و مبلغ و اعتبارسنجی کد ملی، کارت و شبا در هر جای برنامه، حتی سمت سرور.\n- اعتبارسنجی دوباره در سرور با همان قاعده‌های فرم.\n\nکِی استفاده نکنیم:\n\n- ساختن فیلد ورودی با این تابع‌ها از صفر؛ فیلدهای آماده رفتار کیبورد و صفحه‌خوان را هم دارند. (فیلد فارسی: /docs/persian/input/)\n\nدرست و نادرست:\n\n- ✓ درست: در سرور هم `isValidNationalId` و `isValidIban` را صدا بزنید؛ اعتبارسنجی مرورگر دورزدنی است.\n- ✗ نادرست: فقط به بررسی سمت کاربر اعتماد کنید.\n\n## وب کامپوننت\n\nهمین کامپوننت بدون React، برای HTML، وردپرس، Vue، Svelte و Angular: `<kashi-date>`، `<kashi-relative-time>` در `@kashi-ui/elements`.\n\nنصب بدون ابزار ساخت (CDN):\n\n```html\n<link rel=\"stylesheet\" href=\"https://cdn.jsdelivr.net/npm/@kashi-ui/css/dist/kashi.css\">\n<script type=\"module\" src=\"https://cdn.jsdelivr.net/npm/@kashi-ui/elements/dist/cdn/kashi-elements.js\"></script>\n```\n\nیا با npm، فقط همین بخش:\n\n```bash\nnpm i @kashi-ui/elements @kashi-ui/css\n```\n\n```js\nimport \"@kashi-ui/css\";\nimport \"@kashi-ui/elements/display.js\";\n```\n\n```html\n<kashi-date datetime=\"2026-03-21\" format=\"full\"></kashi-date>\n<kashi-relative-time datetime=\"2026-01-01T09:30:00Z\"></kashi-relative-time>\n```\n\n| عنصر | کاربرد |\n|---|---|\n| <kashi-date> | تاریخ شمسی در یک <time> |\n| <kashi-relative-time> | «۵ دقیقه پیش» که خودش به‌روز می‌شود |\n\nویژگی‌ها:\n\n| نام | مقدار | پیش‌فرض | توضیح |\n|---|---|---|---|\n| datetime | تاریخ ISO | — | تاریخ یا زمان |\n| format | numeric \\| long \\| full | long | kashi-date: قالب تاریخ |\n| refresh | میلی‌ثانیه | 60000 | kashi-relative-time: فاصلهٔ به‌روزرسانی |\n| digits | fa \\| latin | fa | خط ارقام |"},{"slug":"security","url":"https://kashi.sepantapouya.com/docs/security/","title":"امنیت","description":"چطور آسیب‌پذیری را گزارش کنید، در چه مدت جواب می‌گیرید و کاشی برای امنیت زنجیرهٔ تأمین چه می‌کند.","group":"پایداری و امنیت","components":[],"markdown":"## گزارش آسیب‌پذیری\n\nاگر در کاشی مشکل امنیتی پیدا کرده‌اید، **آن را در Issue عمومی ننویسید.** از گزارش خصوصی GitHub استفاده کنید تا تا زمان رفع، فقط نگهدارندهٔ کاشی آن را ببیند:\n\n**[گزارش خصوصی آسیب‌پذیری](https://github.com/Sepantapouya/Kashi/security/advisories/new)**\n\nلطفاً بنویسید کدام پکیج و نسخه، مشکل چیست، چطور می‌شود آن را تکرار کرد و چه اثری دارد. اگر راه رفعی به ذهنتان رسیده، آن را هم بنویسید.\n\n## زمان پاسخ\n\n| مرحله | زمان |\n|---|---|\n| تأیید دریافت گزارش | تا ۳ روز کاری |\n| بررسی و تعیین شدت | تا ۷ روز کاری |\n| رفع آسیب‌پذیری بحرانی و شدید | تا ۱۴ روز |\n| رفع آسیب‌پذیری متوسط و کم | در نسخهٔ بعدی |\n\nپس از انتشار نسخهٔ رفع‌شده، یک GitHub Security Advisory با شمارهٔ CVE (در صورت نیاز) منتشر و از گزارش‌دهنده، اگر بخواهد، نام برده می‌شود. لطفاً پیش از انتشار رفع، جزئیات را جایی منتشر نکنید.\n\n## نسخه‌های پشتیبانی‌شده\n\n| نسخه | رفع امنیتی |\n|---|---|\n| ۱.x | ✓ |\n| ۰.x | ✗ (به ۱.۰ مهاجرت کنید؛ تغییر کدی لازم ندارد) |\n\nپس از انتشار نسخهٔ ۲.۰، رفع‌های امنیتی ۱.x تا ۱۲ ماه ادامه دارد. [سیاست تغییرات](/docs/versioning/)\n\n## کاشی برای امنیت چه می‌کند\n\n- **وابستگی کم:** `@kashi-ui/persian`، `@kashi-ui/tokens`، `@kashi-ui/icons` و `@kashi-ui/tailwind` هیچ وابستگی‌ای ندارند. `@kashi-ui/react` و `@kashi-ui/elements` فقط به پکیج‌های خود کاشی وابسته‌اند (و React). تنها استثنا سرور MCP است که از SDK رسمی MCP استفاده می‌کند.\n- **بدون اجرای کد هنگام نصب:** هیچ پکیج کاشی اسکریپت `install` یا `postinstall` ندارد.\n- **بدون ارسال داده:** کامپوننت‌ها هیچ درخواستی به اینترنت نمی‌فرستند. لوگوی بانک‌ها و فونت داخل پکیج‌اند.\n- **متن کاربر به‌شکل متن:** کامپوننت‌های React متن را هرگز به‌شکل HTML خام نمی‌نویسند و وب کامپوننت‌ها مقدارها و ویژگی‌ها را پیش از ساختن HTML امن (escape) می‌کنند.\n- **بررسی خودکار وابستگی‌ها:** در هر تغییر، `npm audit` اجرا می‌شود و آسیب‌پذیری شدید جلوی ادغام را می‌گیرد.\n- **حساب انتشار** با ورود دومرحله‌ای محافظت می‌شود.\n\n## اگر از کاشی در محصولتان استفاده می‌کنید\n\n- پکیج‌ها را با `^1` نصب کنید تا رفع‌های امنیتی با `npm update` برسد.\n- برای خبر شدن از نسخه‌های امنیتی، در GitHub روی **Watch ← Custom ← Security alerts** بزنید."},{"slug":"tailwind","url":"https://kashi.sepantapouya.com/docs/tailwind/","title":"Tailwind","description":"رنگ‌ها، فونت، گردی گوشه و سایه‌های کاشی به‌شکل کلاس‌های Tailwind؛ همگام با تم تیره و روشن و رنگ برند شما.","group":"شروع","components":[],"markdown":"با پریست `@kashi-ui/tailwind`، توکن‌های کاشی در پروژهٔ Tailwind شما به‌شکل کلاس‌های آشنا در دسترس‌اند. کامپوننت‌های کاشی همان‌طور که هستند کار می‌کنند و برای بخش‌های دلخواه صفحه، به‌جای `bg-[var(--k-bg-secondary)]` می‌نویسید `bg-secondary`:\n\n```tsx\n<div className=\"bg-secondary border border-brand rounded-lg shadow-md p-4\">\n <h3 className=\"text-primary text-lg font-semibold\">سفارش‌های امروز</h3>\n <p className=\"text-tertiary text-sm\">۱۲ سفارش تازه</p>\n <Button variant=\"primary\">مشاهده</Button>\n</div>\n```\n\nکلاس‌ها کپی رنگ‌ها نیستند؛ همه به همان متغیرهای کاشی (`var(--k-…)`) اشاره می‌کنند. پس تم تیره و روشن، رنگ برندی که با `createTheme` ساخته‌اید و هر نسخهٔ جدید کاشی، بدون هیچ کاری روی کلاس‌های Tailwind هم اعمال می‌شود. ساختار کاشی هم تغییری نمی‌کند؛ این پکیج فقط یک پل است.\n\n## نصب\n\n```bash\nnpm i @kashi-ui/react @kashi-ui/css @kashi-ui/tailwind\n```\n\n### Tailwind 4\n\nدر فایل CSS اصلی پروژه (مثلاً `app/globals.css`) این سه خط را **به همین ترتیب** بگذارید:\n\n```css\n@import \"@kashi-ui/tailwind\";\n@import \"tailwindcss\";\n@import \"@kashi-ui/css\";\n```\n\n> **ترتیب مهم است:** `@kashi-ui/tailwind` باید اول بیاید تا ترتیب لایه‌های CSS را تعیین کند و کلاس‌های Tailwind همیشه بر استایل کامپوننت‌های کاشی برنده باشند. با ترتیب دیگر، استایل کاشی برنده می‌شود و مثلاً `rounded-none` روی دکمه اثر نمی‌کند.\n\n### Tailwind 3\n\nدر `tailwind.config.js` پریست را اضافه کنید و CSS کاشی را مثل همیشه وارد کنید. در Tailwind 3 کلاس‌ها بیرون از لایه‌ها ساخته می‌شوند و خودبه‌خود بر کاشی برنده‌اند:\n\n```js\nmodule.exports = {\n presets: [require(\"@kashi-ui/tailwind/preset\")],\n content: [\"./src/**/*.{js,ts,jsx,tsx}\"],\n};\n```\n\n```tsx\nimport \"@kashi-ui/css\";\n```\n\n## کلاس‌ها\n\n### رنگ‌های معنایی\n\nهمان نام‌های توکن‌های کاشی، بدون پیشوند. این‌ها با تم تیره و روشن عوض می‌شوند و برای بیشتر رابط کاربری کافی‌اند.\n\n<details class=\"tw-more\">\n<summary>فهرست متنی همهٔ رنگ‌های معنایی</summary>\n\nپس‌زمینه `bg-primary`، `bg-secondary`، `bg-tertiary`، `bg-elevated`، `bg-hover`، `bg-active`، `bg-brand`، `bg-brand-hover`، `bg-brand-subtle`، `bg-error`، `bg-error-hover`، `bg-error-subtle`، `bg-warning-subtle`، `bg-success-subtle` و `bg-info-subtle`؛ متن `text-primary`، `text-secondary`، `text-tertiary`، `text-quaternary`، `text-on-brand`، `text-on-error`، `text-brand`، `text-error`، `text-warning`، `text-success` و `text-info`؛ حاشیه `border-primary`، `border-secondary`، `border-brand`، `border-input` و `border-error`. همهٔ حالت‌های Tailwind هم کار می‌کنند: `hover:bg-hover`، `focus:border-brand`، `md:bg-secondary`.\n\n</details>\n\n### طیف رنگ‌ها\n\nطیف برند و رنگ‌های ایرانی کاشی، از ۵۰ تا ۹۵۰، با پشتیبانی از شفافیت مثل `bg-brand-500/50`. روی هر خانه بزنید تا کلاسش کپی شود:\n\nنام طیف‌ها: `brand` (همان رنگی که `createTheme` می‌سازد)، `firoozeh` (فیروزه)، `lajvard` (لاجورد)، `zafaran` (زعفران)، `anar` (انار)، `pesteh` (پسته) و `shab` (شب)؛ مثل `bg-firoozeh-100`، `text-anar-600` یا `border-lajvard-300`.\n\n### گردی و سایه\n\nضریب گردی کاشی (`--k-radius-factor`) روی همهٔ `rounded-*` ها اعمال می‌شود.\n\n### متن، لایه و حرکت\n\n| گروه | کلاس‌ها |\n|---|---|\n| فونت | `font-sans` (وزیرمتن یا فونت شما) · `font-display` · `font-mono` |\n| اندازهٔ متن | `text-xs` تا `text-6xl`، با ضریب `--k-text-scale` |\n| وزن | `font-light` · `font-regular` · `font-medium` · `font-semibold` · `font-bold` · `font-black` |\n| ارتفاع خط | `leading-tight` · `leading-normal` · `leading-relaxed` |\n| لایه | `z-dropdown` · `z-sticky` · `z-overlay` · `z-modal` · `z-toast` |\n| حرکت | `ease-standard` · `ease-emphasized` |\n\nفاصله‌ها (`p-4`، `gap-6`، `mt-8`) همان مقیاس پیش‌فرض Tailwind‌اند که با مقیاس کاشی (پله‌های ۴ پیکسلی) یکی است؛ `p-4` در هر دو ۱۶ پیکسل است.\n\n## تم تیره و روشن\n\nکاشی به‌طور پیش‌فرض تیره است و با `data-theme=\"light\"` روی `<html>` روشن می‌شود. حالت‌های `dark:` و `light:` هم از همین پیروی می‌کنند:\n\n```tsx\n<div className=\"shadow-none light:shadow-sm\">…</div>\n```\n\nبرای بیشتر کارها به این حالت‌ها نیازی ندارید؛ رنگ‌های معنایی مثل `bg-secondary` خودشان با تم عوض می‌شوند.\n\n## رنگ برند\n\nتمی که با `createTheme` یا [تم‌ساز](/docs/theming/) می‌سازید، روی کلاس‌های Tailwind هم اعمال می‌شود:\n\n```ts\nimport { createTheme } from \"@kashi-ui/tokens\";\n\nconst css = createTheme({ brand: \"#3d5ef8\", radius: 1.5 });\n// حالا bg-brand، text-brand و rounded-lg هم لاجوردی و گردتر شده‌اند\n```\n\n## راست‌به‌چپ\n\nTailwind کلاس‌های منطقی دارد که در RTL و LTR درست کار می‌کنند؛ مثل کامپوننت‌های کاشی، به‌جای `ml-*` و `pr-*` از این‌ها استفاده کنید:\n\n| به‌جای | بنویسید |\n|---|---|\n| `ml-4` / `mr-4` | `ms-4` / `me-4` |\n| `pl-4` / `pr-4` | `ps-4` / `pe-4` |\n| `left-0` / `right-0` | `start-0` / `end-0` |\n| `text-left` | `text-start` |\n\nبرای جاهایی که واقعاً به جهت وابسته‌اند، حالت‌های `rtl:` و `ltr:` هم هست، مثل `rtl:rotate-180` برای فلش."},{"slug":"theming","url":"https://kashi.sepantapouya.com/docs/theming/","title":"سفارشی‌سازی و تم","description":"کاشی را با برند خود هماهنگ کنید؛ از یک رنگ تا جزئی‌ترین متغیر هر کامپوننت.","group":"شروع","components":[],"markdown":"## تم‌ساز\n\nتغییرات روی کل همین صفحه اعمال می‌شوند. کد تولیدشده را کپی کنید و در CSS پروژه بگذارید.\n\n## چهار لایهٔ سفارشی‌سازی\n\n### ۱. رنگ برند\n\nهمهٔ کامپوننت‌ها رنگ برند را از طیف `--k-brand-50` تا `--k-brand-950` می‌خوانند. تابع `createTheme()` این طیف را از یک رنگ می‌سازد:\n\n```ts\nimport { createTheme } from \"@kashi-ui/tokens\";\n\nconst css = createTheme({ brand: \"#7c3aed\" });\n// css را در یک تگ <style> یا فایل CSS قرار دهید\n```\n\nاگر طیف دقیق برندتان را دارید، آن را مستقیم بدهید:\n\n```ts\ncreateTheme({ brand: { 50: \"#f5f3ff\", 100: \"#ede9fe\", /* … */ 950: \"#2e1065\" } });\n```\n\n### ۲. پیچ‌های سراسری\n\n| متغیر | پیش‌فرض | اثر |\n|---|---|---|\n| `--k-radius-factor` | `1` | ضریب همهٔ گوشه‌ها؛ `0` تیز، `2` بسیار گرد |\n| `--k-density` | `1` | ضریب ارتفاع و فاصلهٔ داخلی کنترل‌ها؛ `0.875` فشرده |\n| `--k-font-sans` | وزیرمتن | فونت اصلی؛ راهنمای کامل در [فونت و تایپوگرافی](/docs/typography/) |\n| `--k-text-scale` | `1` | ضریب اندازهٔ همهٔ متن‌ها |\n\n### ۳. متغیرهای هر کامپوننت\n\nهر کامپوننت متغیرهای عمومی با الگوی `--k-{کامپوننت}-{ویژگی}` دارد. فهرست کامل در صفحهٔ هر کامپوننت آمده است.\n\n```css\n:root {\n --k-btn-radius: 999px; /* دکمه‌های کاملاً گرد */\n --k-input-height-md: 3rem; /* فیلدهای بلندتر */\n}\n\n/* فقط در یک بخش خاص */\n.checkout {\n --k-card-padding: 2rem;\n}\n```\n\n### ۴. کلاس برای هر بخش داخلی\n\nکامپوننت‌های چندبخشی `classNames` می‌پذیرند تا بدون دستکاری CSS کاشی، هر بخش را جدا استایل دهید (مثلاً با Tailwind):\n\n```tsx\n<Input\n label=\"ایمیل\"\n classNames={{ label: \"text-lg\", box: \"rounded-full\", control: \"font-mono\" }}\n/>\n<DataTable classNames={{ row: \"h-14\", header: \"uppercase\" }} … />\n```\n\nهمچنین ریشهٔ کامپوننت‌ها ویژگی‌های `data-variant`، `data-size`، `data-tone`، `data-state` و `data-placement` دارند:\n\n```css\n.k-btn[data-variant=\"primary\"][data-size=\"lg\"] { letter-spacing: 0; }\n.k-input[data-state=\"invalid\"] { background: var(--k-bg-error-subtle); }\n```\n\n### ۵. بازنویسی مستقیم\n\nهمهٔ استایل‌های کاشی در Cascade Layer با نام `kashi` قرار دارند. هر CSS معمولی شما، با هر میزان اختصاصی بودن، روی آن‌ها اولویت دارد:\n\n```css\n/* بدون !important کار می‌کند */\n.k-btn { letter-spacing: 0; }\n```\n\n> **دام رایج: استایل‌های سراسری عناصر.** چون CSS شما همیشه بر کاشی اولویت دارد، قاعده‌ای مثل `a { color: blue }` رنگ دکمه‌ای را هم که با `<a class=\"k-btn\">` ساخته شده عوض می‌کند. دو راه حل:\n>\n> ```css\n> /* ۱. دکمه‌ها را مستثنا کنید و اختصاصی بودن را صفر نگه دارید */\n> :where(a:not(.k-btn)) { color: blue; }\n>\n> /* ۲. یا ریست‌ها و استایل‌های پایهٔ خودتان را در لایه‌ای پیش از کاشی بگذارید */\n> @layer my-reset, kashi;\n> @layer my-reset { a { color: blue; } }\n> ```\n\n## تم روشن و تیره\n\nتم پیش‌فرض تیره است. ویژگی `data-theme` را روی `<html>` یا هر عنصری بگذارید:\n\n```html\n<html data-theme=\"light\">\n<section data-theme=\"dark\">…</section>\n```\n\nبرای پیروی از تنظیم سیستم کاربر:\n\n```js\nconst dark = matchMedia(\"(prefers-color-scheme: dark)\").matches;\ndocument.documentElement.dataset.theme = dark ? \"dark\" : \"light\";\n```\n\n> طیفی که `createTheme()` می‌سازد خودکار از نظر کنتراست بررسی نمی‌شود. پس از انتخاب رنگ برند، کنتراست دکمهٔ اصلی را بررسی کنید."},{"slug":"typography","url":"https://kashi.sepantapouya.com/docs/typography/","title":"فونت و تایپوگرافی","description":"وزیرمتن پیش‌فرض است، ولی هر فونتی، رایگان یا خریداری‌شده، با یک خط قابل‌جایگزینی است.","group":"شروع","components":[],"markdown":"## پیش‌نمایش زنده\n\nفونت‌ها فقط هنگام انتخاب بارگذاری می‌شوند. کد تولیدشده را کپی کنید.\n\n## سه متغیر برای فونت\n\n| متغیر | پیش‌فرض | کاربرد |\n|---|---|---|\n| `--k-font-sans` | وزیرمتن | متن و همهٔ کامپوننت‌ها |\n| `--k-font-display` | همان `--k-font-sans` | عنوان‌ها (h1 تا h6) |\n| `--k-font-mono` | فونت سیستم + وزیرمتن | کد |\n| `--k-text-scale` | `1` | ضریب اندازهٔ همهٔ متن‌ها |\n\n## فونت رایگان\n\nفونت را با npm نصب و CSS آن را وارد کنید، سپس نامش را به کاشی بدهید:\n\n```ts\nimport \"@fontsource-variable/estedad\"; // npm i @fontsource-variable/estedad\nimport { createTheme } from \"@kashi-ui/tokens\";\n\nconst css = createTheme({ fontFamily: \"Estedad Variable\" });\n```\n\nیا فقط با CSS:\n\n```css\n@import \"@fontsource-variable/estedad\";\n:root { --k-font-sans: \"Estedad Variable\", \"Vazirmatn\", Tahoma, sans-serif; }\n```\n\n<div class=\"table-wrap\" role=\"region\" aria-label=\"فونت‌های رایگان پیشنهادی\" tabindex=\"0\">\n <table class=\"ref-table\">\n <thead><tr><th scope=\"col\">فونت</th><th scope=\"col\">پکیج npm</th><th scope=\"col\">کاربرد</th><th scope=\"col\">مجوز</th></tr></thead>\n <tbody>\n {fonts.map((f) => (\n <tr>\n <th scope=\"row\">{f.name}</th>\n <td><code class=\"type\">{f.pkg}</code></td>\n <td>{f.kind}؛ {f.note}</td>\n <td>{f.license}</td>\n </tr>\n ))}\n </tbody>\n </table>\n</div>\n\n## فونت خریداری‌شده\n\nفونت‌های تجاری مثل ایران‌سنس، یکان بخ، دانا یا پیدا مجوز جداگانه برای هر وب‌سایت دارند و نمی‌توان آن‌ها را در پکیج‌های عمومی منتشر کرد؛ فایل‌هایی را که خریده‌اید روی سرور خودتان بگذارید و به کاشی معرفی کنید:\n\n```ts\nimport { createTheme } from \"@kashi-ui/tokens\";\n\nconst css = createTheme({\n fonts: [\n // فونت متغیر: یک فایل، همهٔ وزن‌ها\n { family: \"IRANSansX\", src: \"/fonts/IRANSansXV.woff2\", weight: \"100 900\" },\n // یا فونت ثابت: هر وزن یک فایل\n { family: \"Yekan Bakh\", src: \"/fonts/YekanBakh-Regular.woff2\", weight: 400 },\n { family: \"Yekan Bakh\", src: \"/fonts/YekanBakh-Bold.woff2\", weight: 700 },\n ],\n fontFamily: \"IRANSansX\",\n displayFontFamily: \"Yekan Bakh\",\n});\n// css شامل قواعد @font-face و متغیرهاست؛ آن را در یک <style> یا فایل CSS بگذارید\n```\n\nنام تنها (مثل `\"IRANSansX\"`) خودکار با وزیرمتن و فونت‌های سیستم کامل می‌شود تا تا زمان بارگذاری فونت اصلی، متن نامرئی نباشد.\n\n> **حجم:** اگر از وزیرمتن استفاده نمی‌کنید، به‌جای `@kashi-ui/css` فایل `@kashi-ui/css/kashi.nofont.css` را وارد کنید تا فایل فونت (حدود ۱۱۰ کیلوبایت) در خروجی شما نیاید. مرورگر به‌هرحال فقط فونتی را دانلود می‌کند که واقعاً استفاده شود.\n\n## حروف لاتین با فونت جدا\n\nبیشتر فونت‌های فارسی حروف لاتین هم دارند. اگر برای متن انگلیسی فونت دیگری می‌خواهید، آن را **اول** بیاورید؛ مرورگر هر حرف را از اولین فونتی که آن را دارد می‌گیرد و چون Inter حروف فارسی ندارد، فارسی از فونت دوم خوانده می‌شود:\n\n```css\n:root { --k-font-sans: \"Inter\", \"Vazirmatn\", Tahoma, sans-serif; }\n```\n\n## اندازه و ارتفاع خط\n\nفونت‌ها در یک اندازهٔ یکسان، درشتی متفاوتی دارند. با `textScale` (یا `--k-text-scale`) همهٔ اندازه‌ها را با هم کوچک یا بزرگ کنید، مثلاً `0.95` برای فونتی که درشت‌تر دیده می‌شود. ارتفاع خط با `--k-leading-normal` (پیش‌فرض ۱٫۷۵) قابل‌تغییر است؛ حروف فارسی به‌خاطر نقطه‌ها و دندانه‌ها فضای عمودی بیشتری از لاتین لازم دارند.\n\n## ویژگی‌های فونت در createTheme\n\n| ویژگی | نوع | پیش‌فرض | توضیح |\n|---|---|---|---|\n| fontFamily | string | — | فونت متن؛ نام تنها یا فهرست کامل |\n| displayFontFamily | string | — | فونت عنوان‌ها |\n| monoFontFamily | string | — | فونت کد |\n| textScale | number | 1 | ضریب اندازهٔ متن‌ها |\n| fonts | { family, src, weight?, style?, display?, unicodeRange? }[] | — | فایل‌های فونت برای ساخت @font-face |\n\n`fontFace()` و `fontStack()` هم جداگانه از `@kashi-ui/tokens` صادر می‌شوند."},{"slug":"versioning","url":"https://kashi.sepantapouya.com/docs/versioning/","title":"نسخه‌ها و سیاست تغییرات","description":"کاشی از نسخهٔ ۱.۰ قول می‌دهد که API کامپوننت‌ها بی‌خبر نشکند؛ هر حذفی دست‌کم یک نسخه پیش‌تر «منسوخ» اعلام می‌شود و هر نسخهٔ اصلی راهنمای مهاجرت دارد.","group":"پایداری و امنیت","components":[],"markdown":"از نسخهٔ ۱.۰، کاشی برای استفاده در محصولات سازمانی پایدار است. این صفحه می‌گوید چه چیزی ثابت می‌ماند، چه چیزی چطور عوض می‌شود و هر نسخه تا کی پشتیبانی می‌شود.\n\n## شمارهٔ نسخه\n\nکاشی از [Semantic Versioning](https://semver.org/lang/fa/) پیروی می‌کند. همهٔ پکیج‌ها (`@kashi-ui/react`، `css`، `elements`، `persian`، `tokens`، `icons`، `bank-logos`، `tailwind` و `mcp`) با **یک شمارهٔ نسخه** منتشر می‌شوند تا هیچ‌وقت لازم نباشد بپرسید کدام با کدام سازگار است.\n\n| تغییر | مثال | نسخه |\n|---|---|---|\n| رفع باگ، بهتر شدن ظاهر بدون تغییر API | فاصلهٔ درست بین برچسب و فیلد | `1.0.0` ← `1.0.1` |\n| قابلیت یا کامپوننت تازه، prop اختیاری تازه | کامپوننت انتخاب نوبت | `1.0.1` ← `1.1.0` |\n| تغییر ناسازگار | حذف یا تغییر نام prop، تغییر مقدار پیش‌فرض، حذف کلاس CSS | `1.1.0` ← `2.0.0` |\n\n### چه چیزهایی API عمومی است\n\n- همهٔ کامپوننت‌ها، propها و نوع‌های TypeScript که از `@kashi-ui/react` و `@kashi-ui/elements` export شده‌اند.\n- نام تگ‌های وب کامپوننت (`kashi-*`)، ویژگی‌ها (attributes) و رویدادهای `kashi-change`.\n- کلاس‌های CSS با پیشوند `k-` و متغیرهای CSS با پیشوند `--k-`.\n- تابع‌های `@kashi-ui/persian` و توکن‌های `@kashi-ui/tokens`.\n- کلیدهای فرهنگ متن‌ها (`faMessages`، `enMessages`).\n\nساختار داخلی HTML، کلاس‌ها و متغیرهایی که با `_` شروع می‌شوند (مثل `--_p`) و متن پیش‌فرض برچسب‌ها جزو API نیستند. متن‌ها ممکن است در نسخهٔ کوچک بهتر شوند؛ اگر به متن دقیقی نیاز دارید، آن را با prop یا `messages` خودتان بدهید.\n\n## منسوخ کردن، پیش از حذف\n\nهیچ قابلیتی بی‌خبر حذف نمی‌شود:\n\n۱. **اعلام منسوخ شدن** در یک نسخهٔ کوچک (مثلاً `1.4.0`): قابلیت هنوز کار می‌کند، در حالت توسعه یک هشدار `[kashi] منسوخ: …` در کنسول می‌آید که جایگزین را می‌گوید، در صفحهٔ همان کامپوننت برچسب «منسوخ» می‌خورد و در [یادداشت نسخه‌ها](/docs/changelog/) نوشته می‌شود.\n۲. **دست‌کم یک نسخهٔ کوچک** با هر دو راه، قدیمی و جدید، منتشر می‌شود تا فرصت مهاجرت داشته باشید.\n۳. **حذف** فقط در نسخهٔ اصلی بعدی (مثلاً `2.0.0`)، همراه با راهنمای مهاجرت.\n\nهشدارها فقط در حالت توسعه نشان داده می‌شوند و در بیلد production هیچ هزینه‌ای ندارند.\n\n## راهنمای مهاجرت\n\nهر نسخهٔ اصلی صفحهٔ مهاجرت خودش را دارد: چه چیزی عوض شده، چرا، و کد قبل و بعد برای هر مورد. [مهاجرت به ۱.۰](/docs/migration/)\n\n## مدت پشتیبانی\n\n| نسخه | وضعیت | پشتیبانی |\n|---|---|---|\n| ۱.x | فعلی | قابلیت تازه، رفع باگ و رفع امنیتی |\n| ۰.x | قدیمی | به ۱.۰ مهاجرت کنید؛ مهاجرت تغییر کدی لازم ندارد |\n\nوقتی نسخهٔ ۲.۰ منتشر شود، ۱.x تا **۱۲ ماه** رفع‌های امنیتی و باگ‌های جدی را می‌گیرد. جزئیات گزارش و رفع آسیب‌پذیری‌ها در [امنیت](/docs/security/) آمده است.\n\n## سازگاری\n\n| | پشتیبانی‌شده |\n|---|---|\n| React | ۱۸ و ۱۹؛ در App Router نکست کامپوننت‌ها در صفحه‌های سرور هم کار می‌کنند |\n| فریم‌ورک‌ها | Next.js (در هر تغییر خودکار نصب و بیلد می‌شود)، Vite، Remix؛ وب کامپوننت‌ها برای Vue، Svelte، Angular و HTML |\n| مرورگرها | Chrome و Edge ۱۲۰+، Firefox ۱۲۱+، Safari ۱۶٫۴+ (iOS و macOS) |\n| Tailwind | ۳ و ۴ |\n| Node.js (برای ابزارها) | ۲۲ به بالا |\n\n## چطور کیفیت حفظ می‌شود\n\nدر هر تغییر، پیش از انتشار:\n\n- **تست رفتار** برای همهٔ کامپوننت‌ها، ابزارهای فارسی و وب کامپوننت‌ها.\n- **تست دوزبانه:** همهٔ کامپوننت‌ها به انگلیسی رندر می‌شوند و نباید حتی یک حرف فارسی در متن‌هایشان بماند.\n- **تست تصویری:** از همهٔ نمونه‌های این سایت و حالت‌های حساس (خطا کنار فیلد بی‌خطا، متن بلند، غیرفعال، انگلیسی) در تم روشن و تیره عکس گرفته و با عکس مرجع مقایسه می‌شود.\n- **سقف حجم:** هر کامپوننت سقف حجم دارد و بزرگ‌تر شدنش بی‌اجازه ممکن نیست. [حجم کامپوننت‌ها](/docs/sizes/)\n- **کنتراست رنگ** همهٔ جفت‌رنگ‌های تم با WCAG 2.2 AA.\n- **نصب واقعی** در یک پروژهٔ Next.js، با npm و با shadcn.\n- **کیت فیگما** در شبیه‌ساز اجرا و پیکسل‌به‌پیکسل با CSS مقایسه می‌شود."},{"slug":"web-components","url":"https://kashi.sepantapouya.com/docs/web-components/","title":"وب کامپوننت‌ها","description":"کامپوننت‌های کاشی بدون React؛ با یک خط script در HTML، وردپرس، Vue، Svelte و Angular. فیلدهای فارسی، تقویم شمسی، فرم نشانی، نقشهٔ ایران، جدول داده، منو، پنجره و اعلان.","group":"شروع","components":[],"markdown":"پکیج `@kashi-ui/elements` کامپوننت‌های تعاملی کاشی را به‌شکل **وب کامپوننت** (Custom Elements استاندارد مرورگر) دارد. با هر فریم‌ورکی کار می‌کنند، یا بدون فریم‌ورک:\n\n- **HTML ساده و وردپرس:** یک خط CSS و یک خط script، بدون ابزار ساخت.\n- **Vue، Svelte و Angular:** مثل هر تگ HTML دیگری.\n- **فرم معمولی HTML:** مقدار هر فیلد با `name` ارسال می‌شود و فرم با مقدار نامعتبر ارسال نمی‌شود.\n\nظاهر از همان `@kashi-ui/css` می‌آید و عنصرها Shadow DOM ندارند؛ پس تم، رنگ برند، حالت روشن و تیره و CSS خودتان بی‌تغییر روی آن‌ها اعمال می‌شود. در پروژهٔ React همان [`@kashi-ui/react`](/docs/installation/) را به کار ببرید.\n\nاین صفحه راه‌اندازی کلی است. نصب، نمونهٔ زنده، ویژگی‌ها و رویدادهای هر کامپوننت در بخش **«وب کامپوننت»** صفحهٔ همان کامپوننت آمده است ([فهرست](#کامپوننتها)).\n\n## نصب\n\nبدون نصب، از CDN، در `<head>` صفحه:\n\n```html\n<link rel=\"stylesheet\" href=\"https://cdn.jsdelivr.net/npm/@kashi-ui/css/dist/kashi.css\">\n<script type=\"module\" src=\"https://cdn.jsdelivr.net/npm/@kashi-ui/elements/dist/cdn/kashi-elements.js\"></script>\n```\n\nیا با npm، در پروژه‌ای که ابزار ساخت دارد (Vite، Nuxt، SvelteKit، Angular):\n\n```bash\nnpm i @kashi-ui/elements @kashi-ui/css\n```\n\n```js\nimport \"@kashi-ui/css\";\nimport \"@kashi-ui/elements\"; // همه؛ یا فقط یک بخش، مثل \"@kashi-ui/elements/inputs.js\"\n```\n\nصفحه را راست‌به‌چپ کنید: `<html lang=\"fa\" dir=\"rtl\">`.\n\n## نمونه\n\nیک فرم معمولی HTML با چند کامپوننت کاشی:\n\n```html\n<form action=\"/checkout\" method=\"post\">\n <kashi-national-id name=\"national_id\" required></kashi-national-id>\n <kashi-mobile name=\"mobile\"></kashi-mobile>\n <kashi-date-picker label=\"تاریخ تحویل\" name=\"delivery\" holidays></kashi-date-picker>\n <button class=\"k-btn k-btn--primary\">ثبت سفارش</button>\n</form>\n```\n\n## کامپوننت‌ها\n\n۶۳ عنصر؛ روی نام هر کامپوننت بزنید تا نصب، نمونه، ویژگی‌ها و رویدادهایش را ببینید.\n\n| کامپوننت | عنصرها | بخش npm |\n|---|---|---|\n| [فیلدهای فارسی](/docs/persian/input/#وب-کامپوننت) | `<kashi-national-id>`، `<kashi-legal-id>`، `<kashi-mobile>`، `<kashi-landline>`، `<kashi-card-number>`، `<kashi-iban>`، `<kashi-postal-code>`، `<kashi-price>` | `inputs.js` |\n| [ساعت و پلاک خودرو](/docs/persian/input/#وب-کامپوننت) | `<kashi-time-input>`، `<kashi-plate-input>` | `forms.js` |\n| [ویرایش نگارشی](/docs/persian/text-fix/#وب-کامپوننت) | `<kashi-persian-textarea>`، `<kashi-keyboard-fix>` | `forms.js` |\n| [بازهٔ تاریخ شمسی](/docs/persian/date-range/#وب-کامپوننت) | `<kashi-date-range-picker>` | `pickers.js` |\n| [انتخاب ماه](/docs/persian/month-picker/#وب-کامپوننت) | `<kashi-month-picker>` | `pickers.js` |\n| [انتخاب نوبت](/docs/persian/appointment/#وب-کامپوننت) | `<kashi-appointment-picker>` | `appointment.js` |\n| [هیت‌مپ فعالیت](/docs/persian/activity-heatmap/#وب-کامپوننت) | `<kashi-activity-heatmap>` | `charts.js` |\n| [دیت‌پیکر شمسی](/docs/persian/date-picker/#وب-کامپوننت) | `<kashi-date-picker>`، `<kashi-calendar>` | `calendar.js` |\n| [کد تأیید](/docs/persian/otp/#وب-کامپوننت) | `<kashi-otp>` | `fields.js` |\n| [انتخاب استان و شهر](/docs/persian/province/#وب-کامپوننت) | `<kashi-province-select>`، `<kashi-city-select>` | `combobox.js` |\n| [نقشهٔ ایران](/docs/persian/iran-map/#وب-کامپوننت) | `<kashi-iran-map>` | `map.js` |\n| [فرم نشانی](/docs/persian/address/#وب-کامپوننت) | `<kashi-address-form>` | `map.js` |\n| [نمایش مبلغ](/docs/persian/money/#وب-کامپوننت) | `<kashi-money>` | `display.js` |\n| [شمارش معکوس](/docs/persian/countdown/#وب-کامپوننت) | `<kashi-countdown>` | `display.js` |\n| [تاریخ و زمان نسبی](/docs/persian/utilities/#وب-کامپوننت) | `<kashi-date>`، `<kashi-relative-time>` | `display.js` |\n| [فیلد متنی](/docs/components/input/#وب-کامپوننت) | `<kashi-input>`، `<kashi-textarea>` | `basic.js` |\n| [بنر](/docs/components/banner/#وب-کامپوننت) | `<kashi-banner>` | `basic.js` |\n| [Combobox](/docs/components/combobox/#وب-کامپوننت) | `<kashi-combobox>` | `combobox.js` |\n| [انتخاب چندتایی](/docs/components/multi-select/#وب-کامپوننت) | `<kashi-multi-select>` | `forms.js` |\n| [اسلایدر](/docs/components/slider/#وب-کامپوننت) | `<kashi-slider>` | `forms.js` |\n| [چیپس](/docs/components/chips/#وب-کامپوننت) | `<kashi-chip-group>` | `forms.js` |\n| [قدرت رمز](/docs/components/password-strength/#وب-کامپوننت) | `<kashi-password-strength>` | `forms.js` |\n| [ویرایش درجا](/docs/components/inline-edit/#وب-کامپوننت) | `<kashi-inline-edit>` | `forms.js` |\n| [بارگذاری فایل](/docs/components/file-upload/#وب-کامپوننت) | `<kashi-file-upload>` | `pickers.js` |\n| [انتخاب رنگ](/docs/components/color-picker/#وب-کامپوننت) | `<kashi-color-picker>` | `pickers.js` |\n| [نمودار](/docs/components/charts/#وب-کامپوننت) | `<kashi-bar-chart>`، `<kashi-line-chart>`، `<kashi-pie-chart>`، `<kashi-bar-list>`، `<kashi-proportion-bar>`، `<kashi-sparkline>` | `charts.js` |\n| [جدول داده](/docs/components/table/#وب-کامپوننت) | `<kashi-data-table>` | `table.js` |\n| [صفحه‌بندی](/docs/components/pagination/#وب-کامپوننت) | `<kashi-pagination>` | `controls.js` |\n| [فیلد برچسب](/docs/components/tag-input/#وب-کامپوننت) | `<kashi-tag-input>` | `controls.js` |\n| [فیلد عدد](/docs/components/number-input/#وب-کامپوننت) | `<kashi-number-input>` | `controls.js` |\n| [امتیاز](/docs/components/rating/#وب-کامپوننت) | `<kashi-rating>` | `controls.js` |\n| [پنجرهٔ گفتگو](/docs/components/dialog/#وب-کامپوننت) | `<kashi-dialog>` | `overlay.js` |\n| [زبانه‌ها](/docs/components/tabs/#وب-کامپوننت) | `<kashi-tabs>` | `overlay.js` |\n| [راهنما](/docs/components/tooltip/#وب-کامپوننت) | `<kashi-tooltip>` | `overlay.js` |\n| [دکمهٔ کپی](/docs/components/copy-button/#وب-کامپوننت) | `<kashi-copy-button>` | `overlay.js` |\n| [منوی کشویی](/docs/components/dropdown-menu/#وب-کامپوننت) | `<kashi-dropdown-menu>` | `menu.js` |\n| [پاپ‌اور](/docs/components/popover/#وب-کامپوننت) | `<kashi-popover>` | `menu.js` |\n| [اعلان](/docs/components/toast/#وب-کامپوننت) | `<kashi-toaster>` و تابع `toast()` | `toast.js` |\n| [پالت فرمان](/docs/components/command-palette/#وب-کامپوننت) | `<kashi-command-palette>` | `nav.js` |\n| [منوی راست‌کلیک](/docs/components/context-menu/#وب-کامپوننت) | `<kashi-context-menu>` | `nav.js` |\n| [کاروسل](/docs/components/carousel/#وب-کامپوننت) | `<kashi-carousel>` | `nav.js` |\n| [نمای درختی](/docs/components/tree-view/#وب-کامپوننت) | `<kashi-tree-view>` | `nav.js` |\n| [تور معرفی](/docs/components/tour/#وب-کامپوننت) | `<kashi-tour>` | `nav.js` |\n| [اسپلیتر](/docs/components/splitter/#وب-کامپوننت) | `<kashi-splitter>` | `nav.js` |\n| [منوی کناری](/docs/components/side-nav/#وب-کامپوننت) | `<kashi-side-nav>` | `nav.js` |\n\nفیلدها ویژگی‌های مشترک دارند: `label`، `name`، `value`، `hint`، `error` (خطای دلخواه، مثلاً از سرور)، `required`، `disabled` و `aria-label` (به‌جای برچسب دیداری).\n\n## ارسال فرم و اعتبارسنجی\n\nهر فیلد با `name` مقدار **خام** خود را با ارقام انگلیسی ارسال می‌کند: کد ملی `0013542419`، موبایل `09123456789`، شبا `IR…`، مبلغ `2500000` و تاریخ `2026-03-21`. قالب نمایشی (فاصله‌ها و ارقام فارسی) به سرور نمی‌رسد.\n\nمقدار نامعتبر جلوی ارسال فرم را می‌گیرد و مرورگر پیام خطای فارسی همان فیلد را نشان می‌دهد؛ `required` هم مثل input معمولی کار می‌کند.\n\n```html\n<form action=\"/checkout\" method=\"post\">\n <kashi-national-id name=\"national_id\" required></kashi-national-id>\n <kashi-postal-code name=\"postal_code\" required></kashi-postal-code>\n <button class=\"k-btn k-btn--primary\">ثبت سفارش</button>\n</form>\n```\n\n## رویدادها و JavaScript\n\nهر فیلد با هر تغییر رویداد `kashi-change` می‌فرستد. در `detail` مقدار، معتبر بودن و اطلاعات اضافه می‌آید، مثل نام بانک یا اپراتور. ویژگی‌های `value` و `valid` را هم مستقیم می‌شود خواند:\n\n```js\nconst card = document.querySelector(\"kashi-card-number\");\ncard.addEventListener(\"kashi-change\", (e) => {\n console.log(e.detail); // { value: \"6037…\", valid: true, bank: \"بانک ملی\" }\n});\n\ncard.value = \"6037991234567893\"; // مقدار از بیرون\ncard.valid; // true\n```\n\nرویدادها و ویژگی‌های JavaScript هر عنصر در بخش «وب کامپوننت» صفحهٔ همان کامپوننت آمده است.\n\n## زبان انگلیسی\n\nهمهٔ متن‌های داخلی عنصرها (دکمه‌ها، متن صفحه‌خوان، پیام‌های خطا، ارقام و نام ماه‌ها) با یک خط انگلیسی می‌شوند؛ پیش از ساخته شدن عنصرها صدا بزنید:\n\n```ts\nimport { setLocale } from \"@kashi-ui/elements\";\nsetLocale(\"en\");\n```\n\nجزئیات و عوض کردن متن‌های دلخواه: [دوزبانه، فارسی و انگلیسی](/docs/i18n/)\n\n## Vue، Svelte و Angular\n\n**Vue:** به Vue بگویید تگ‌های `kashi-` کامپوننت Vue نیستند:\n\n```js\n// vite.config.js\nimport vue from \"@vitejs/plugin-vue\";\n\nexport default {\n plugins: [vue({ template: { compilerOptions: { isCustomElement: (tag) => tag.startsWith(\"kashi-\") } } })],\n};\n```\n\n```html\n<kashi-mobile :value=\"mobile\" @kashi-change=\"mobile = $event.detail.value\"></kashi-mobile>\n```\n\n**Svelte:** تنظیمی لازم ندارد:\n\n```html\n<kashi-mobile value={mobile} on:kashi-change={(e) => (mobile = e.detail.value)}></kashi-mobile>\n```\n\n**Angular:** `CUSTOM_ELEMENTS_SCHEMA` را به کامپوننت اضافه کنید:\n\n```ts\n@Component({\n schemas: [CUSTOM_ELEMENTS_SCHEMA],\n template: `<kashi-mobile [value]=\"mobile\" (kashi-change)=\"mobile = $event.detail.value\"></kashi-mobile>`,\n})\n```\n\nدر TypeScript نوع همهٔ عنصرها ثبت شده است؛ `document.querySelector(\"kashi-iban\")` نوع `KashiIban` را برمی‌گرداند.\n\n## کامپوننت‌های دیگر\n\nبیشتر کامپوننت‌های دیگر کاشی (دکمه، کارت، نشان، هشدار، آکاردئون، مراحل، پیشرفت، فیلد متنی و…) به JavaScript نیاز ندارند و با کلاس‌های `@kashi-ui/css` ساخته می‌شوند؛ کد HTML هر کدام در تب «HTML» صفحهٔ همان کامپوننت آمده است.\n\nهمهٔ کامپوننت‌های تعاملی کاشی نسخهٔ وب کامپوننت دارند.\n\n## حجم\n\nفایل CDN حدود ۳ کیلوبایت (gzip) است و هر بخش را فقط وقتی یکی از تگ‌هایش در صفحه پیدا شود دانلود می‌کند؛ صفحه‌ای که فقط فیلد کد ملی دارد، نمودار و نقشه را دانلود نمی‌کند. لوگوی بانک‌ها هم فقط هنگام وارد کردن شمارهٔ کارت یا شبا می‌آید. اگر عنصرها را با JavaScript می‌سازید و می‌خواهید از همان اول آماده باشند، `loadAll()` را صدا بزنید:\n\n```js\nimport { loadAll } from \"https://cdn.jsdelivr.net/npm/@kashi-ui/elements/dist/cdn/kashi-elements.js\";\nawait loadAll();\n```\n\nبا npm فقط بخشی را که لازم دارید وارد کنید (ستون «بخش npm» جدول بالا)؛ مثلاً فقط فیلدهای فارسی:\n\n```js\nimport \"@kashi-ui/elements/inputs.js\";\n```"},{"slug":"writing","url":"https://kashi.sepantapouya.com/docs/writing/","title":"نوشتن متن رابط","description":"راهنمای جامع فارسی‌نویسی در محصول؛ از لحن و برچسب دکمه و پیام خطا تا نیم‌فاصله، نشانه‌ها، عدد، واژه‌گزینی، دسترس‌پذیری و تایپوگرافی متن فارسی.","group":"شروع","components":[],"markdown":"متن رابط بخشی از طراحی است. دکمه‌ای که «تأیید» نوشته و معلوم نیست چه می‌کند، یا خطایی که فقط می‌گوید «خطا رخ داد»، به اندازهٔ یک دکمهٔ کج به محصول آسیب می‌زند؛ و «میخواهید» بی‌نیم‌فاصله یا «ي» عربی، حس بی‌دقتی می‌دهد، حتا اگر خواننده نداند چرا. این راهنما در دو بخش است: **چه بنویسیم** (لحن، ساختار و هر جزء رابط) و **چطور بنویسیم** (دستور خط، نشانه‌ها، عدد و واژه‌گزینی). متن‌های داخلی خود کاشی با همین قاعده‌ها نوشته شده‌اند.\n\n> **مبنا:** برای درست‌نویسی، کاشی از [دستور خط فارسی فرهنگستان زبان و ادب فارسی](https://virastaran.net/khat/) پیروی می‌کند. جاهایی که شیوه‌نامه‌های معتبر با هم فرق دارند (مثلاً «حتی» یا «حتا»، «خانهٔ» یا «خانه‌ی»)، هر دو را آورده‌ایم؛ یکی را برای محصولتان انتخاب کنید و **همه‌جا یکدست** بنویسید. یکدستی از هر انتخابی مهم‌تر است.\n\n## بخش اول: چه بنویسیم\n\n## اصول\n\n۱. **روشن:** کاربر بی‌فکر بفهمد. واژهٔ آشنا، جملهٔ کوتاه، فعل معلوم.\n۲. **کوتاه:** هر واژه‌ای که بی‌آن معنا عوض نمی‌شود، حذف شود. کاربر رابط را می‌خواند تا کارش را انجام دهد، نه برای لذت خواندن.\n۳. **مفید:** هر متن به یک پرسش کاربر جواب می‌دهد: این چیست؟ چه کنم؟ چه شد؟ حالا چه؟\n۴. **انسانی:** مثل آدمِ مؤدب حرف بزنید، نه مثل نامهٔ اداری و نه مثل ماشین.\n۵. **یکدست:** برای یک مفهوم در کل محصول یک واژه. اگر جایی «سبد خرید» نوشتید، جای دیگر «کارت» یا «سبد» ننویسید.\n\n## صدا و لحن\n\n**صدا** شخصیت ثابت محصول است؛ **لحن** با موقعیت عوض می‌شود. یک آدم با همان شخصیت، در جشن تولد و در بیمارستان یک‌جور حرف نمی‌زند.\n\n| موقعیت | لحن | نمونه |\n|---|---|---|\n| خوشامد و معرفی | گرم و تشویق‌کننده | به کاشی خوش آمدید. اولین پروژه‌تان را بسازید. |\n| کار عادی | خنثی و کوتاه | نشانی ذخیره شد. |\n| خطا | آرام و راهنما، بدون شوخی | پرداخت انجام نشد. مبلغی از حساب شما کم نشده است. |\n| پول، حریم خصوصی، امنیت | جدی و دقیق | برای برداشت، رمز دوم کارت لازم است. |\n| حذف و کار برگشت‌ناپذیر | صریح | این سفارش برای همیشه حذف می‌شود. |\n| موفقیت بزرگ | شاد، ولی کوتاه | تبریک! اولین فروشتان ثبت شد. |\n\n### رسمیت\n\n- در رابط، **فارسی معیارِ ساده** بنویسید: نه اداری («مبذول فرمایید»، «حسب‌الدستور») و نه شکسته («میشه»، «رو»).\n- **شکسته‌نویسی** را، اگر برندتان می‌خواهد، فقط در بازاریابی، شبکه‌های اجتماعی یا گفتگوی پشتیبانی به کار ببرید؛ هرگز در پیام خطا، پرداخت، قرارداد و متن‌های امنیتی. دستور خط شکسته هنوز مرجع واحد ندارد و خواندنش برای همه یکسان نیست.\n- کاربر را **«شما»** خطاب کنید و فعل را جمع بیاورید («وارد کنید»). نه «تو»، و نه سوم‌شخص اداری («کاربر گرامی، … نمایید»).\n- **«لطفاً»** را فقط وقتی بنویسید که کار اضافه‌ای می‌خواهید («لطفاً چند لحظه صبر کنید»)، نه برای هر دستوری.\n- **فرض جنسیتی نکنید:** «آقا/خانم» را فقط وقتی بنویسید که کاربر خودش آن را انتخاب کرده است. فارسی خوشبختانه ضمیر جنسیتی ندارد؛ با «کاربر گرامی» و «دوست عزیز» هم آن را خراب نکنید.\n\n| درست | نادرست |\n|---|---|\n| رمز خود را وارد کنید. | رمزت را وارد کن. / کاربر گرامی، رمز را وارد نمایید. |\n| پرداخت انجام شد. | پرداخت با موفقیت صورت پذیرفت. |\n| نشانی را ذخیره کردیم. | نشانی ذخیره گردید. |\n| کد پستی ده رقم است. | لطفاً توجه داشته باشید که کد پستی باید ده رقم باشد. |\n| پرداخت انجام نشد. | اوه! انگار پولت گم شد 😅 |\n\n## ساختار متن\n\nکاربر متن رابط را **نمی‌خواند، می‌پیماید**: چشمش اول عنوان و چند واژهٔ اول هر خط را می‌گیرد.\n\n- **مهم‌ترین چیز اول:** «کد پستی ده رقم است»، نه «با توجه به استاندارد پست، تعداد ارقام کد پستی ده است».\n- **واژهٔ کلیدی در ابتدای جمله و فهرست:** «سفارش‌ها»، «پرداخت‌ها»، «تنظیمات»؛ نه «مشاهدهٔ سفارش‌ها»، «مشاهدهٔ پرداخت‌ها».\n- **یک ایده در هر جمله** و حداکثر دو سه جمله در هر بلوک. متن بلندتر را فهرست کنید یا به صفحهٔ راهنما ببرید.\n- **فعل امر برای کار، اسم برای جا:** دکمه «ذخیرهٔ تغییرات»، منو «تنظیمات».\n- **«شما» و «ما» را درست جدا کنید:** «سفارش‌های من» یا «سفارش‌های شما»؛ یکی را برگزینید و در کل محصول همان را بنویسید.\n\n## جزء به جزء رابط\n\nپیش از نوشتن، جای پیام را درست انتخاب کنید؛ متن خوب در جای نادرست هم دیده نمی‌شود.\n\n### عنوان صفحه و بخش\n\n- اسم آنچه در صفحه هست: «سفارش‌ها»، «نشانی تحویل». بدون نقطه در پایان.\n- کوتاه، و همان واژه‌ای که در منو و پیوند رسیدن به این صفحه آمده است.\n\n### برچسب دکمه\n\nدکمه کاری انجام می‌دهد، پس برچسبش **فعل** است و می‌گوید بعد از کلیک چه می‌شود.\n\n| درست | نادرست | چرا |\n|---|---|---|\n| ذخیرهٔ تغییرات | تأیید | «تأیید» نمی‌گوید چه اتفاقی می‌افتد |\n| حذف سفارش | بله | دکمه بدون خواندن پرسش هم باید معنا داشته باشد |\n| پرداخت ۲۵۰٬۰۰۰ تومان | ادامه | مبلغ همان لحظهٔ تصمیم دیده می‌شود |\n| ارسال دوبارهٔ کد | کلیک کنید | کار را نمی‌گوید؛ روی گوشی هم کلیکی نیست |\n| افزودن به سبد | اقدام به خرید | «اقدام به … کردن» درازنویسی است |\n\n- دو تا چهار واژه. «دکمهٔ»، «اینجا» و «لطفاً» ننویسید.\n- در گفتگوی تأیید، دکمهٔ اصلی همان فعلِ عنوان است: عنوان «حذف این سفارش؟»، دکمه‌ها «انصراف» و «حذف سفارش».\n- دکمهٔ خطرناک را با `variant=\"danger\"` بسازید، نه فقط با متن قرمز.\n- دکمهٔ فقط‌آیکون `aria-label` می‌خواهد: «حذف سفارش ۴۸۱۲۷»، نه «آیکون سطل».\n\n### پیوند\n\n- بگوید به کجا می‌رود: «راهنمای بازگشت کالا»، نه «اینجا»، «بیشتر» یا «کلیک کنید». صفحه‌خوان‌ها فهرست پیوندهای صفحه را جدا از متن می‌خوانند.\n\n### برچسب فیلد، placeholder و راهنما\n\n- **برچسب** اسم داده است و همیشه دیده می‌شود: «ایمیل»، نه «ایمیل خود را وارد کنید».\n- **placeholder** فقط نمونهٔ قالب است («۰۹۱۲ ۳۴۵ ۶۷۸۹») و جای برچسب نیست؛ با شروع تایپ ناپدید می‌شود.\n- **راهنما** زیر فیلد، آنچه پیش از نوشتن باید بداند: «ده رقم، بدون خط تیره». با نقطه، چون جملهٔ کامل است.\n- فیلدهای اختیاری را با «اختیاری» نشان دهید، نه همهٔ اجباری‌ها را با ستاره (اگر بیشترشان اجباری‌اند).\n\n### پیام خطا\n\n**هر پیام خطا دو چیز می‌گوید: چه شد، و کاربر حالا چه کند.**\n\n| نوع | درست | نادرست |\n|---|---|---|\n| قالب فیلد | کد ملی باید ده رقم باشد. | ورودی نامعتبر است. |\n| داده‌ای که قبلاً هست | این ایمیل قبلاً ثبت شده است. وارد شوید یا رمز را بازیابی کنید. | خطای ۴۰۹ |\n| شبکه | اتصال اینترنت قطع است. دوباره امتحان کنید. | Network Error |\n| محدودیت | مبلغ باید حداقل ۱۰٬۰۰۰ تومان باشد. | مبلغ اشتباه است! |\n| دسترسی | برای دیدن این صفحه باید مدیر پروژه باشید. از مدیر بخواهید شما را اضافه کند. | دسترسی غیرمجاز |\n| خطای سرور | مشکلی از سمت ما پیش آمد و پرداخت انجام نشد. چند دقیقهٔ دیگر امتحان کنید. کد پیگیری: ۴۸۱۲ | Internal Server Error |\n\n- **کاربر را مقصر نکنید:** «شمارهٔ کارت کامل نیست»، نه «شمارهٔ کارت را اشتباه وارد کردید».\n- **مسئولیت اشکال خودتان را بپذیرید:** «مشکلی از سمت ما پیش آمد».\n- **کد فنی** را فقط اگر برای پشتیبانی لازم است و در خط دوم بنویسید.\n- **علامت تعجب نگذارید.** خطا داد زدن نمی‌خواهد.\n- **کنار همان فیلد** نشانش دهید. پیام کلی بالای فرم فقط وقتی لازم است که چند فیلد مشکل دارند، و آن هم بگوید کدام‌ها.\n- **پیش از پایان تایپ خطا نشان ندهید.** فیلدهای فارسی کاشی خطا را بعد از خروج از فیلد یا کامل شدن رقم‌ها نشان می‌دهند.\n- **متن کاربر را پاک نکنید.** پس از خطای ارسال، هر چه نوشته بود باید سر جایش بماند.\n\n### حالت خالی، بارگذاری و موفقیت\n\n| حالت | درست | نادرست |\n|---|---|---|\n| خالی برای اولین بار | هنوز سفارشی ثبت نکرده‌اید. اولین محصول را از فروشگاه انتخاب کنید. | داده‌ای وجود ندارد. |\n| خالی پس از جستجو | نتیجه‌ای برای «کتاب قدیمی» پیدا نشد. املای واژه را بررسی کنید. | ۰ نتیجه |\n| خالی پس از فیلتر | با این فیلترها سفارشی نیست. فیلترها را کم کنید. | نتیجه‌ای یافت نشد |\n| بارگذاری | در حال دریافت سفارش‌ها… | لطفاً صبر کنید |\n| موفقیت | سفارش ثبت شد. کد پیگیری: ۴۸۱۲۷ | عملیات با موفقیت انجام شد. |\n\n- «با موفقیت» را ننویسید؛ «ذخیره شد» خودش یعنی موفق.\n- پیام موفقیت اگر اطلاعات مهمی دارد (کد پیگیری، زمان تحویل) همان را می‌گوید و جای ماندگار می‌خواهد، نه توستی که ناپدید شود.\n\n### گفتگوی تأیید\n\n- **عنوان پرسش است** و کار را نام می‌برد: «حذف این سفارش؟».\n- **متن می‌گوید دقیقاً چه از بین می‌رود** و آیا برگشت دارد: «سفارش ۴۸۱۲۷ و رسیدهایش برای همیشه حذف می‌شوند.»\n- **دکمه‌ها فعل‌اند:** «انصراف» و «حذف سفارش»، نه «خیر» و «بله».\n- برای کارهای برگشت‌پذیر، به‌جای گفتگو کار را انجام دهید و «واگرد» بدهید.\n\n### اعلان، ایمیل و پیامک\n\n- **پیامک:** نام برند در ابتدا، کار اصلی بعد و کوتاه. پیامک فارسی در هر بخش حدود ۷۰ نویسه جا دارد و بیشتر از آن چند پیامک حساب می‌شود.\n- **کد یک‌بارمصرف:** «کد ورود شما به کاشی: ۴۸۱۲۷. این کد را به کسی ندهید.» کد را اول بیاورید تا در پیش‌نمایش اعلان دیده شود.\n- **عنوان ایمیل** موضوع را بگوید («سفارش ۴۸۱۲۷ ارسال شد»)، نه «اطلاعیه» یا «خبر مهم».\n- **اعلان** یک خبر؛ اگر کاری لازم است، همان را بگوید: «سفارش شما رسید. امتیاز بدهید.»\n\n### معرفی و آموزش\n\n- هر قدم یک جمله دربارهٔ اینکه این بخش **برای کاربر** چه می‌کند، نه اینکه نام دکمه چیست.\n- بیش از سه تا پنج قدم نشود؛ آموزش کامل را در راهنمای جدا بنویسید.\n\n### متن حقوقی و رضایت\n\n- چک‌باکس رضایت مثبت و روشن: «شرایط استفاده را خوانده‌ام و می‌پذیرم»، با پیوند به همان شرایط.\n- رضایت‌های جدا (خبرنامه، پیامک تبلیغاتی) چک‌باکس جدا و **بدون تیک پیش‌فرض** می‌خواهند.\n- خلاصهٔ ساده بالای متن حقوقی بلند، زحمت کاربر را کم و اعتمادش را بیشتر می‌کند.\n\n## بخش دوم: چطور بنویسیم\n\n## نیم‌فاصله و فاصله\n\n**نیم‌فاصله** (فاصلهٔ مجازی، ZWNJ) جزءهای یک واژه را جدا نشان می‌دهد بی‌آنکه واژه بشکند. بی‌آن یا «میخواهم» می‌شود و خواندنش کند، یا با فاصلهٔ کامل «می خواهم» می‌شود و در شکستن خط، «می» تنها سر یک خط می‌ماند.\n\n**قاعدهٔ کلی:** بین دو واژه یک فاصلهٔ کامل؛ داخل یک واژه (پیشوند، پسوند، ترکیب) نیم‌فاصله؛ هیچ‌جا دو فاصله.\n\n| جزء | درست | نادرست |\n|---|---|---|\n| «می» و «نمی» | می‌خواهم، نمی‌شود | میخواهم، می خواهم |\n| «ها»ی جمع | کتاب‌ها، سفارش‌ها | کتابها، کتاب ها |\n| «تر» و «ترین» | سریع‌تر، بزرگ‌ترین | سریعتر، بزرگ ترین |\n| «بی»، «نا»، «هم» | بی‌نام، ناامن، هم‌کار | بینام، نا امن، همکار* |\n| ضمیر و فعل ربطی پس از «ه» و «ی» | خانه‌ام، زندگی‌اش، آماده‌اید | خانه ام، زندگیش |\n| واژهٔ مرکب | کسب‌وکار، جست‌وجو، گفت‌وگو، پیش‌نمایش، به‌روزرسانی | کسب و کار، جستجو، پیشنمایش |\n| مصدر مرکب | ذخیره‌کردن، به‌دست‌آوردن | (در فعل صرف‌شده جدا: ذخیره می‌کنیم) |\n| «به» قیدساز | به‌تدریج، به‌سرعت، به‌طور کلی | بتدریج |\n\n\\* برخی ترکیب‌های جاافتاده سرهم هم درست‌اند (همکار، بیمار)؛ در شک، [فرهنگ املایی](https://virastaran.net/khat/) را ببینید.\n\n**جدا، همیشه:**\n\n- «به» حرف اضافه: «به تهران»، «به شما»؛ نه «بشما» و نه «بتهران».\n- «که»، «را»، «این»، «آن»، «هر»، «هیچ»، «عدم»، «بدون»: «آنچه را»، «هر روز».\n- حرف اضافهٔ پسین: «نسبت به»، «با توجه به»، «مربوط به».\n\n**اختلاف شیوه‌نامه‌ها؛ یکی را انتخاب کنید:**\n\n| مورد | فرهنگستان (شیوهٔ کاشی) | شیوهٔ رایج دیگر |\n|---|---|---|\n| «بیشتر»، «کمتر»، «بهتر» | سرهم: بیشتر، کمتر | جدا: بیش‌تر، کم‌تر |\n| اضافه پس از «ه» | خانهٔ ما، صفحهٔ اصلی | خانه‌ی ما، صفحه‌ی اصلی |\n| «آنچه»، «اینکه» | سرهم: آنچه، اینکه | جدا: آن‌چه، این‌که |\n\n«ـهٔ» در رابط کوچک‌تر و تمیزتر است و کاشی همین را به کار می‌برد؛ «ـه‌ی» برای خوانندهٔ کم‌تجربه آشکارتر است. هر کدام را برگزیدید، در کل محصول همان باشد.\n\n## حروف و املا\n\n| | درست | نادرست | توضیح |\n|---|---|---|---|\n| ی و ک | ی، ک | ي، ك | ي و ك عربی جستجو را خراب می‌کنند: «كتاب» با «کتاب» پیدا نمی‌شود |\n| «ة» و «ۀ» | ت، ـهٔ | ة، ۀ (نویسهٔ جدا) | ت گرد در فارسی نیست؛ همزهٔ «ـهٔ» را با نشانهٔ همزه روی «ه» بنویسید |\n| همزهٔ پایانی | املا، امضا، انشا | املاء، امضاء | به گفتهٔ فرهنگستان در واژه‌های رایج همزهٔ پایانی نمی‌آید |\n| همزهٔ میانی | تأیید، مسئول، هیئت، رئیس | تائید، مسوول (در شیوهٔ فرهنگستان) | شیوهٔ دیگری همزه را برمی‌دارد (مسوول، هییت)؛ یکی را برگزینید |\n| تنوین | کاملاً، دقیقاً | کاملا، دقیقا | بهتر: معادل فارسی (به‌کلی، به‌دقت)؛ شیوهٔ دیگری «کاملن» می‌نویسد |\n| ت و ط | اتاق، تهران، بلیت، باتری، اتو | اطاق، طهران، بلیط، باطری، اطو | املای فارسیِ واژه‌های غیرعربی |\n| «ناهار» | ناهار | نهار | |\n| «گزاردن» و «گذاشتن» | خدمت‌گزار، نمازگزار، سپاس‌گزار | خدمت‌گذار، سپاسگذار | «گزاردن» یعنی به‌جا آوردن |\n| «حتی» | حتی (فرهنگستان) | | شیوهٔ دیگر: «حتا»، همان‌طور که خوانده می‌شود |\n\n[`fixPersianText`](/docs/persian/utilities/) و [ویرایش نگارشی](/docs/persian/text-fix/) ی و ک عربی، نیم‌فاصلهٔ «می» و «ها»، نشانه‌ها و ارقام را خودکار درست می‌کنند؛ متن‌هایی که کاربر از جای دیگر می‌چسباند هم.\n\n## نشانه‌ها\n\n**قاعدهٔ فاصله:** نشانه‌های درنگ (. ، ؛ : ؟ ! …) **بی‌فاصله** به واژهٔ پیش و **با یک فاصله** از واژهٔ بعد. نشانه‌های دربرگیر (« » ( ) [ ]) **بی‌فاصله** با درون و **با فاصله** با بیرون.\n\n| | درست | نادرست |\n|---|---|---|\n| ویرگول | سلام، خوبید؟ | سلام , خوبید ? |\n| نقطه‌ویرگول | ارسال رایگان است؛ ولی… | ارسال رایگان است ; ولی… |\n| علامت پرسش | حذف این سفارش؟ | حذف این سفارش ? |\n| گیومه | «کاشی» | \"کاشی\" ، \" کاشی \" |\n| پرانتز | (اختیاری) | ( اختیاری ) |\n| سه‌نقطه | در حال بارگذاری… | در حال بارگذاری... |\n\n- **نقطه** پایان جملهٔ کامل (راهنما، پیام خطا، متن گفتگو) می‌آید؛ **پس از عنوان، برچسب، دکمه و آیتم کوتاه فهرست نه.**\n- **گیومه** برای نقل‌قول و نام چیزی که کاربر باید آن را تشخیص دهد («دکمهٔ «ذخیره» را بزنید»). برای تأکید از گیومه استفاده نکنید؛ پررنگ کنید.\n- **علامت تعجب** در رابط تقریباً هرگز؛ فقط برای تبریک واقعی، و تنها یکی.\n- **خط تیره** برای جملهٔ معترضه در رابط کم‌کاربرد و در RTL دردسرساز است؛ ویرگول یا جملهٔ جدا بهتر است.\n- **نقطه در پایان نقل‌قولِ کامل** داخل گیومه می‌آید: «همه‌چیز آماده است.»\n- **نشانه‌های ریاضی** (+ − = × /) در متن فارسی با یک فاصله از دو طرف: ۲ + ۲ = ۴.\n\n## عدد، تاریخ، واحد و پول\n\n| | درست | نادرست |\n|---|---|---|\n| ارقام در متن فارسی | ۱۴۰۵ | 1405 |\n| جداکنندهٔ هزارگان | ۱٬۲۵۰٬۰۰۰ | ۱,۲۵۰,۰۰۰ یا 1,250,000 |\n| ممیز | ۲٫۵ | ۲.۵ یا 2.5 |\n| درصد | ۲۵٪ | ۲۵ درصد٪ یا %25 |\n| بازه | ۱۰ تا ۲۰ نفر | ۱۰-۲۰ نفر |\n| عدد و معدود | ۲۵ تومان، ۳ کالا | ۲۵تومان |\n| ساعت | ساعت ۱۴:۳۰ | 14:30 در متن فارسی |\n| تاریخ در متن | ۱۶ آبان ۱۴۰۵ | ۱۴۰۵/۸/۱۶ در جمله |\n| تاریخ در جدول و فیلد | ۱۴۰۵/۰۸/۱۶ | ۱۶ آبان ۱۴۰۵ در ستون باریک |\n\n- **ارقام فارسی** در همهٔ متن‌های فارسی؛ کامپوننت‌های کاشی خودشان این کار را می‌کنند. ارقام لاتین فقط کنار واژهٔ لاتین («iPhone 16») و در کد.\n- **شماره‌ها** (موبایل، کارت، شبا، کد پستی) چپ‌به‌راست نوشته می‌شوند تا ترتیب رقم‌ها به‌هم نریزد، ولی ارقامشان در رابط فارسی همچنان فارسی است.\n- **عدد کوچک** در متن روان را می‌شود با حروف نوشت («دو روز دیگر»)؛ در رابط، رقم سریع‌تر خوانده می‌شود («۲ روز»). در یک محصول یکی را انتخاب کنید.\n- **واحد پول** را همیشه بنویسید و یکی را برگزینید: تومان یا ریال، نه هر دو. مبلغ بزرگ را کوتاه کنید: «۱٫۲ میلیون تومان» ([نمایش مبلغ](/docs/persian/money/)).\n- **زمان نسبی** («۵ دقیقه پیش») برای رویداد تازه، و تاریخ کامل در `title` برای کسی که دقیقش را می‌خواهد.\n- **تقویم:** شمسی پیش‌فرض است. اگر میلادی هم لازم است، کنارش و کم‌رنگ‌تر: «۱۶ آبان ۱۴۰۵ (۷ نوامبر ۲۰۲۶)».\n\n## واژه‌گزینی\n\n### ساختارهای اداری و ترجمه‌ای\n\nاین‌ها رایج‌ترین ساختارهایی‌اند که متن را اداری، دراز یا ترجمه‌زده می‌کنند. بسیاری از آن‌ها را [«آروانی نوشتن»](https://www.arvancloud.ir/fa/culture/orthography) هم با توضیح ریشه‌شان گرد آورده است.\n\n| به‌جای | بنویسید | چرا |\n|---|---|---|\n| می‌باشد، نمی‌باشد | است، نیست | |\n| می‌گردد، گردید | می‌شود، شد | «گردیدن» یعنی چرخیدن |\n| … نمایید، … نمود | … کنید، … کرد | «نمودن» یعنی نشان دادن |\n| صورت گرفت، صورت پذیرفت | انجام شد | |\n| اقدام به پرداخت نمایید | پرداخت کنید | درازنویسی |\n| سفارش توسط شما ثبت شد | سفارش را ثبت کردید / سفارش ثبت شد | مجهولِ همراه فاعل ترجمه‌زده است |\n| به‌عنوان مدیر انتخاب شد | مدیر شد | «به‌عنوان» (as) اغلب زائد است |\n| در رابطه با، در ارتباط با | دربارهٔ، برای | ترجمهٔ لفظ‌به‌لفظ |\n| در مورد | دربارهٔ | |\n| عدم پرداخت | پرداخت نکردن، نپرداختن | |\n| جهت ثبت | برای ثبت | «جهت» یعنی سو |\n| لذا | پس، برای همین | |\n| آنچه که | آنچه | «آنچه» خودش «آن چیزی که» است |\n| بر روی | روی | حشو |\n| کلیهٔ کاربران | همهٔ کاربران | |\n| قابل قبول، غیرقابل استفاده | پذیرفتنی، بی‌استفاده | ترجمهٔ able |\n| لازم به ذکر است که | (حذف) | جمله بی‌آن کامل است |\n| به اندازهٔ کافی | (بسته به جمله) بس، کافی | ترجمهٔ enough |\n| با موفقیت انجام شد | انجام شد | |\n| مبادرت ورزید | (فعل اصلی) | |\n\n### واژه‌های رابط\n\nبرای مفهوم‌های تکراری رابط، این واژه‌ها را پیشنهاد می‌کنیم. اگر محصولتان واژهٔ دیگری را جا انداخته (و کاربرانتان آن را بهتر می‌فهمند)، همان را نگه دارید؛ ولی همه‌جا همان.\n\n| مفهوم | پیشنهاد | نه |\n|---|---|---|\n| Save | ذخیره | سیو |\n| Login، Sign up | ورود، ثبت‌نام | لاگین، ساین‌آپ |\n| Account | حساب کاربری | اکانت |\n| Username، Password | نام کاربری، رمز عبور | یوزرنیم، پسورد |\n| Settings | تنظیمات | ستینگ |\n| Search | جستجو (یا جست‌وجو؛ یکدست) | سرچ |\n| Upload، Download | بارگذاری، دانلود | آپلود (برای مخاطب عمومی) |\n| Link | پیوند | لینک (مگر مخاطب فنی است) |\n| Dashboard | پیشخوان یا داشبورد | (هر دو رایج؛ یکی را برگزینید) |\n| Delete، Remove | حذف | پاک‌سازی (برای یک مورد) |\n| Cancel | انصراف | کنسل |\n| Back | بازگشت | برگشت به عقب |\n| Email | ایمیل | (رایانامه درست است ولی کم‌آشنا) |\n\n- **نام‌های خاص** (Google، iPhone، API) را به خط اصلی بنویسید؛ «اپل» و «گوگل» را هم اگر برای مخاطب عمومی است فارسی.\n- **کوته‌نوشت انگلیسی** (API، PDF، SMS) را با حروف بزرگ و بدون نقطه بنویسید و در اولین بار، اگر مخاطب عمومی است، توضیحش را بدهید.\n- **جمع واژهٔ لاتین** با «ها»: «PDFها»؛ کاشی آن را در `<bdi>` نگه می‌دارد تا جای «ها» عوض نشود.\n\n## راست‌به‌چپ و متن دوزبانه\n\n- **واژهٔ لاتین در جملهٔ فارسی** ترتیب نمایش را به‌هم می‌ریزد، به‌ویژه کنار عدد و نشانه. نام محصول و کد را در `<bdi>` بگذارید (کامپوننت‌های کاشی این کار را خودشان می‌کنند).\n- **دو واژهٔ لاتین با ویرگول فارسی** بینشان، جایشان عوض می‌شود («Claude، Cursor»). جمله را طوری بنویسید که پشت‌سرهم نیایند: «Claude و همچنین Cursor».\n- **جملهٔ فارسی را با واژهٔ لاتین شروع نکنید**؛ تراز خط و جای نشانهٔ پایان به‌هم می‌ریزد. «برنامهٔ Figma» بهتر از «Figma برنامه‌ای…» است.\n- **فلش و آیکون جهت‌دار** در RTL آینه می‌شوند؛ «بعدی» به چپ اشاره می‌کند. در متن «دکمهٔ سمت چپ» ننویسید؛ نام دکمه را بنویسید.\n- **نسخهٔ انگلیسی** را جدا و درست بنویسید، نه ترجمهٔ واژه‌به‌واژه: Sentence case در دکمه‌ها («Delete order»، نه «Delete Order»). [دوزبانه](/docs/i18n/)\n\n## تایپوگرافی متن فارسی\n\n- **فاصلهٔ حروف (letter-spacing) را روی متن فارسی نگذارید.** خط فارسی پیوسته است؛ فاصله‌گذاری اتصال حروف را می‌شکند. کاشی فقط روی عددها و متن لاتین فاصله می‌گذارد.\n- **ایتالیک ندارد.** کج کردن ساختگی خط فارسی زشت و ناخواناست؛ برای تأکید پررنگ یا رنگ.\n- **تراز دوطرفه (justify) در وب** فاصلهٔ ناهموار بین واژه‌ها می‌سازد و فارسی واژه‌شکن (hyphenation) ندارد؛ متن رابط را راست‌چین بگذارید. [W3C: Arabic & Persian Layout Requirements](https://www.w3.org/TR/alreq/)\n- **کشیده (ـ)** را برای پر کردن خط یا تزئین به کار نبرید؛ جستجو و صفحه‌خوان را خراب می‌کند.\n- **طول خط** متن خواندنی ۶۰ تا ۸۰ نویسه؛ برای متن بلندتر ستون را باریک کنید.\n- **حروف کوچک ریز** را با وزن کم ننویسید؛ دندانه‌ها و نقطه‌های فارسی در وزن نازک و اندازهٔ کوچک گم می‌شوند. [فونت و تایپوگرافی](/docs/typography/)\n\n## متن برای صفحه‌خوان\n\n- **دکمهٔ فقط‌آیکون** `aria-label` می‌خواهد که کار را بگوید.\n- **تصویر تزئینی** `alt=\"\"` می‌گیرد؛ تصویری که اطلاعات دارد همان را در `alt` بگوید، نه «تصویر».\n- **ایموجی** را صفحه‌خوان با نامش می‌خواند («چهرهٔ خندان با عرق»)؛ در متن مهم به کار نبرید و هرگز به‌جای واژه.\n- **فقط با رنگ معنا ندهید:** «فیلدهای قرمز» نه؛ «فیلدهایی که خطا دارند».\n- **عدد و تاریخ** را برای صفحه‌خوان کامل بگذارید؛ کامپوننت‌های کاشی متن صفحه‌خوانِ خودشان را دارند (مثلاً «شنبه ۱۶ آبان ۱۴۰۵، تعطیل»)، آن‌ها را با متن کوتاه‌تر عوض نکنید.\n\n## واژه‌نامهٔ محصول\n\nبرای هر محصول یک فهرست کوتاه نگه دارید: هر مفهوم، واژهٔ انتخابی، واژه‌هایی که **نباید** به کار بروند و یک مثال. همین فهرست جلوی «سبد» در یک صفحه و «کارت» در صفحهٔ دیگر را می‌گیرد، و نویسندهٔ تازه را سریع هم‌صدا می‌کند. اگر محصولتان دوزبانه است، معادل انگلیسی را هم کنارش بنویسید و متن‌های [فرهنگ کاشی](/docs/i18n/) را با همان واژه‌ها هم‌خوان کنید.\n\n## فهرست بازبینی\n\nپیش از انتشار هر صفحه:\n\n- همهٔ دکمه‌ها فعل‌اند و بدون خواندن بقیهٔ صفحه هم معنا دارند.\n- هر پیام خطا می‌گوید چه شد و حالا چه باید کرد، و کاربر را سرزنش نمی‌کند.\n- حالت خالی، بارگذاری، خطای اینترنت و موفقیت متن دارند.\n- لحن با موقعیت جور است؛ در پول، خطا و امنیت شوخی نیست.\n- ی و ک فارسی، نیم‌فاصلهٔ «می»، «ها» و «تر»، ارقام فارسی و نشانه‌های فارسی درست‌اند.\n- «می‌باشد»، «نمودن»، «توسط» و «در رابطه با» در متن نیست.\n- دکمه‌های فقط‌آیکون، تصویرها و پیوندها برای صفحه‌خوان نام درست دارند.\n- برای یک مفهوم در کل محصول یک واژه به کار رفته است.\n\n## منابع\n\n- [دستور خط فارسی فرهنگستان؛ خلاصه و فرهنگ املایی «ویراستاران»](https://virastaran.net/khat/)\n- [آروانی نوشتن؛ شیوه‌نامهٔ نگارش ابر آروان](https://www.arvancloud.ir/fa/culture/orthography)\n- [W3C: Arabic & Persian Layout Requirements](https://www.w3.org/TR/alreq/)\n- [لحن برند در راهنمای برند دیجی‌کالا](https://about.digikala.com/guidelines/brand/verbal/brandtone/)\n- [بهبود پیغام‌های خطا در طراحی محصول؛ آکادمی آمانج](https://amanjacademy.com/blog/%D9%85%D9%82%D8%A7%D9%84%D8%A7%D8%AA-ux-writing/improvement-of-error-messages-in-product-design/)\n- [Google Material: مقدمه‌ای بر نوشتن تجربهٔ کاربری](https://codelabs.developers.google.com/codelabs/material-communication-guidance?hl=fa)"}]}
1
+ {"version":"1.2.1","generated":"2026-11-08","docs":[{"slug":"components/accordion","url":"https://kashi.sepantapouya.com/docs/components/accordion/","title":"آکاردئون","description":"بخش‌های جمع‌شونده برای محتوای طولانی، مثل پرسش‌های متداول؛ روی details بومی.","group":"کامپوننت‌ها","components":["Accordion","AccordionItem"],"markdown":"```tsx\nimport { Accordion, AccordionItem } from \"@kashi-ui/react\";\n```\n\n## نمونه\n\nبا `type=\"single\"` باز شدن هر بخش بقیه را می‌بندد.\n\n```tsx\n<Accordion type=\"single\">\n <AccordionItem title=\"کاشی رایگان است؟\" defaultOpen>بله. …</AccordionItem>\n <AccordionItem title=\"با Next.js کار می‌کند؟\">بله؛ …</AccordionItem>\n <AccordionItem title=\"می‌توانم فونت خودم را استفاده کنم؟\">بله، …</AccordionItem>\n</Accordion>\n```\n\n| ویژگی | نوع | پیش‌فرض | توضیح |\n|---|---|---|---|\n| type | \"single\" \\| \"multiple\" | \"multiple\" | فقط یک بخش باز یا چند بخش (Accordion) |\n| flush | boolean | false | بدون حاشیه (Accordion) |\n| title | ReactNode | — | عنوان بخش (AccordionItem) |\n| defaultOpen | boolean | false | باز در ابتدا (AccordionItem) |\n\n## دسترس‌پذیری\n\nروی `<details>` و `<summary>` بومی ساخته شده: بدون JavaScript کار می‌کند، با <kbd>Enter</kbd> و <kbd>Space</kbd> باز و بسته می‌شود و صفحه‌خوان وضعیت باز یا بسته را اعلام می‌کند. جستجوی صفحه (<kbd>Ctrl</kbd>+<kbd>F</kbd>) محتوای بخش‌های بسته را هم پیدا و باز می‌کند.\n\n## راهنمای استفاده\n\nکِی استفاده کنیم:\n\n- پرسش‌های متداول و راهنماهایی که کاربر فقط بخشی از آن‌ها را می‌خواند.\n- تنظیمات پیشرفته‌ای که بیشتر کاربران لازم ندارند.\n\nکِی استفاده نکنیم:\n\n- محتوایی که همه باید ببینند، مثل شرایط مهم خرید؛ پنهانش نکنید.\n- جابه‌جایی بین چند نمای هم‌سطح یک محتوا. (زبانه‌ها: /docs/components/tabs/)\n- مراحل پشت‌سرهم یک فرایند. (استپر: /docs/components/stepper/)\n\nدرست و نادرست:\n\n- ✓ درست: عنوان هر بخش خودش پرسش یا موضوع را کامل بگوید: «هزینهٔ ارسال چقدر است؟»\n- ✗ نادرست: عنوان‌های مبهم مثل «بیشتر» یا «اطلاعات» که باید باز شوند تا معلوم شود چه دارند.\n- ✓ درست: همهٔ بخش‌ها بسته شروع شوند، مگر یکی که بیشتر کاربران دنبالش هستند.\n- ✗ نادرست: همه را باز نگه دارید؛ آن وقت آکاردئون فقط شلوغی اضافه است."},{"slug":"components/alert","url":"https://kashi.sepantapouya.com/docs/components/alert/","title":"هشدار","description":"پیامی مهم دربارهٔ وضعیت سیستم یا نتیجهٔ یک عمل، با آیکون شاخص و عمل‌های اختیاری.","group":"کامپوننت‌ها","components":["Alert"],"markdown":"```tsx\nimport { Alert } from \"@kashi-ui/react\";\n```\n\n## رنگ‌ها\n\nسطح هشدار خنثی است و رنگ فقط در آیکون شاخص دیده می‌شود؛ این کار صفحه را آرام نگه می‌دارد.\n\n```tsx\n<Alert tone=\"brand\" title=\"نسخهٔ جدید منتشر شد\">کاشی ۰.۲ با هفت کامپوننت تازه در دسترس است.</Alert>\n<Alert tone=\"success\" title=\"تغییرات ذخیره شد\" />\n<Alert tone=\"warning\" title=\"اشتراک شما ۳ روز دیگر تمام می‌شود\">برای جلوگیری از قطع سرویس، اشتراک را تمدید کنید.</Alert>\n<Alert tone=\"error\" title=\"پرداخت ناموفق بود\">موجودی کارت کافی نیست. کارت دیگری را امتحان کنید.</Alert>\n```\n\n## عمل‌ها و بستن\n\n```tsx\n<Alert\n tone=\"info\"\n title=\"به‌روزرسانی در دسترس است\"\n actions={<>\n <Button variant=\"link\">به‌روزرسانی</Button>\n <Button variant=\"link\">بعداً</Button>\n </>}\n onClose={() => setOpen(false)}\n>\n نسخهٔ جدید بهبودهای امنیتی دارد.\n</Alert>\n```\n\n## پس‌زمینهٔ رنگی\n\nبرای پیام‌هایی که باید بیشتر دیده شوند، از `tinted` استفاده کنید.\n\n```tsx\n<Alert tone=\"warning\" tinted title=\"حالت نگهداری\">سرویس امشب از ساعت ۲ تا ۴ در دسترس نیست.</Alert>\n```\n\n## نوار بالای صفحه و فشرده\n\n```tsx\n<Alert tone=\"brand\" layout=\"banner\" title=\"نسخهٔ ۰.۸ منتشر شد\" actions={…} onClose={…}>…</Alert>\n<Alert tone=\"warning\" size=\"sm\" title=\"این ایمیل قبلاً ثبت شده است.\" />\n```\n\n**ویژگی‌های جدید**\n\n| ویژگی | نوع | پیش‌فرض | توضیح |\n|---|---|---|---|\n| layout | \"card\" \\| \"banner\" | \"card\" | نوار تمام‌عرض بالای صفحه |\n| size | \"sm\" \\| \"md\" | \"md\" | نسخهٔ فشرده برای داخل فرم |\n| classNames | { root, icon, title, description, actions } | — | کلاس دلخواه برای هر بخش |\n\n## ویژگی‌ها\n\n| ویژگی | نوع | پیش‌فرض | توضیح |\n|---|---|---|---|\n| tone | \"neutral\" \\| \"brand\" \\| \"info\" \\| \"success\" \\| \"warning\" \\| \"error\" | \"neutral\" | رنگ و آیکون پیش‌فرض |\n| title | ReactNode | — | عنوان |\n| children | ReactNode | — | توضیح |\n| icon | ReactNode \\| false | — | آیکون سفارشی؛ false برای حذف |\n| actions | ReactNode | — | دکمه‌ها یا لینک‌های زیر متن |\n| onClose | () => void | — | با دادن این تابع دکمهٔ بستن نمایش داده می‌شود |\n| closeLabel | string | \"بستن\" | نام دسترس‌پذیر دکمهٔ بستن |\n| tinted | boolean | false | پس‌زمینهٔ رنگی کم‌رنگ |\n| role | string | خودکار | پیش‌فرض alert برای خطا و هشدار، status برای بقیه |\n\n## متغیرهای CSS\n\n| متغیر CSS | پیش‌فرض | توضیح |\n|---|---|---|\n| --k-alert-radius | var(--k-radius-xl) | گردی گوشه‌ها |\n| --k-alert-padding | 1rem × density | فاصلهٔ داخلی |\n| --k-alert-bg | var(--k-bg-secondary) | پس‌زمینه |\n\n## دسترس‌پذیری\n\n- هشدارهای `error` و `warning` نقش `alert` دارند و صفحه‌خوان آن‌ها را فوراً اعلام می‌کند؛ بقیه با نقش `status` بدون قطع کاربر خوانده می‌شوند.\n- فقط هشدارهایی را که پس از بارگذاری صفحه اضافه می‌شوند `alert` کنید؛ هشدار ثابت صفحه را با `role=\"note\"` نمایش دهید.\n- آیکون تزئینی است و از صفحه‌خوان پنهان است؛ عنوان باید معنا را کامل برساند.\n\n## راهنمای استفاده\n\nکِی استفاده کنیم:\n\n- پیامی مهم دربارهٔ همین بخش صفحه: نتیجهٔ یک عمل، هشدار پیش از تصمیم، یا محدودیتی که کاربر باید بداند.\n- خطایی که کل فرم را می‌گیرد، مثلاً قطع شدن اتصال هنگام ثبت.\n\nکِی استفاده نکنیم:\n\n- پیامی که به کل سایت مربوط است (نسخهٔ جدید، اختلال سرویس). (بنر: /docs/components/banner/)\n- تأیید کوتاه پس از یک عمل («ذخیره شد»). (توست: /docs/components/toast/)\n- خطای یک فیلد؛ آن را کنار همان فیلد با `error` نشان دهید. (ورودی: /docs/components/input/)\n\nدرست و نادرست:\n\n- ✓ درست: عنوان بگوید چه شده و متن بگوید حالا چه کنید.\n- ✗ نادرست: پیام کلی که نه علت را می‌گوید نه راه را.\n- ✓ درست: رنگ با معنا: `error` برای شکست، `warning` برای هشدار، `success` برای نتیجهٔ خوب.\n- ✗ نادرست: رنگ قرمز برای جلب توجه به پیامی که خطا نیست؛ کاربر یاد می‌گیرد قرمز را جدی نگیرد.\n\nمتن:\n\n- عنوان کوتاه و بدون علامت تعجب؛ متن یک یا دو جمله.\n- اگر عملی ممکن است (تلاش دوباره، تماس با پشتیبانی)، با `actions` دکمه‌اش را بگذارید."},{"slug":"components/avatar","url":"https://kashi.sepantapouya.com/docs/components/avatar/","title":"آواتار","description":"نمایش تصویر یا حروف اول یک شخص، با وضعیت حضور و گروه‌بندی.","group":"کامپوننت‌ها","components":["Avatar","AvatarGroup"],"markdown":"```tsx\nimport { Avatar, AvatarGroup } from \"@kashi-ui/react\";\n```\n\n## تصویر و حروف اول\n\nاگر تصویر نباشد یا بارگذاری نشود، حروف اول نام نمایش داده می‌شود. بین حروف فارسی نیم‌فاصله قرار می‌گیرد تا به هم نچسبند. در اندازه‌های `xs` و `sm` فقط حرف اول نمایش داده می‌شود تا خوانا بماند.\n\n```tsx\n<Avatar name=\"سپنتا پویا\" src=\"/images/sepanta-pouya.jpg\" size=\"lg\" />\n<Avatar name=\"سارا احمدی\" size=\"lg\" />\n<Avatar name=\"علی رضایی\" size=\"lg\" brand />\n<Avatar name=\"کاشی\" size=\"lg\" square />\n```\n\n## اندازه‌ها\n\n```tsx\n<Avatar name=\"سارا احمدی\" size=\"xs\" />\n<Avatar name=\"سارا احمدی\" size=\"sm\" />\n<Avatar name=\"سارا احمدی\" />\n<Avatar name=\"سارا احمدی\" size=\"lg\" />\n<Avatar name=\"سارا احمدی\" size=\"xl\" />\n<Avatar name=\"سارا احمدی\" size=\"2xl\" />\n```\n\n## وضعیت\n\n```tsx\n<Avatar name=\"سارا احمدی\" status=\"online\" size=\"lg\" />\n<Avatar name=\"علی رضایی\" status=\"busy\" size=\"lg\" />\n<Avatar name=\"مریم کریمی\" status=\"away\" size=\"lg\" />\n<Avatar name=\"رضا نوری\" status=\"offline\" size=\"lg\" />\n```\n\n## گروه\n\n```tsx\n<AvatarGroup max={4} aria-label=\"اعضای تیم\">\n <Avatar name=\"سپنتا پویا\" src=\"/images/sepanta-pouya.jpg\" />\n <Avatar name=\"سارا احمدی\" brand />\n <Avatar name=\"علی رضایی\" />\n <Avatar name=\"مریم کریمی\" brand />\n <Avatar name=\"رضا نوری\" />\n <Avatar name=\"نگار صادقی\" />\n</AvatarGroup>\n```\n\n## رنگ خودکار\n\nبا `color=\"auto\"` هر نام همیشه یک رنگ ثابت می‌گیرد (در هر دستگاه یکسان)؛ فهرست اعضا خواناتر و رنگارنگ‌تر می‌شود.\n\n```tsx\n{members.map((m) => <Avatar key={m.id} name={m.name} color=\"auto\" size=\"lg\" />)}\n```\n\n**ویژگی‌های جدید**\n\n| ویژگی | نوع | پیش‌فرض | توضیح |\n|---|---|---|---|\n| color | \"auto\" \\| \"neutral\" \\| \"brand\" \\| \"info\" \\| \"success\" \\| \"warning\" \\| \"error\" | \"neutral\" | رنگ حروف اول؛ همهٔ رنگ‌ها کنتراست AA دارند |\n\n## ویژگی‌ها\n\n| ویژگی | نوع | پیش‌فرض | توضیح |\n|---|---|---|---|\n| name | string | — | نام شخص (الزامی)؛ برای نام دسترس‌پذیر و حروف اول |\n| src | string | — | آدرس تصویر |\n| size | \"xs\" \\| \"sm\" \\| \"md\" \\| \"lg\" \\| \"xl\" \\| \"2xl\" | \"md\" | ۲۴ تا ۶۴ پیکسل |\n| status | \"online\" \\| \"offline\" \\| \"busy\" \\| \"away\" | — | نشانگر وضعیت حضور |\n| statusLabels | Partial<Record<AvatarStatus, string>> | فارسی | متن وضعیت برای صفحه‌خوان |\n| square | boolean | false | شکل مربعی برای سازمان‌ها و پروژه‌ها |\n| brand | boolean | false | رنگ برند برای حالت حروف اول |\n\n**ویژگی‌های AvatarGroup**\n\n| ویژگی | نوع | پیش‌فرض | توضیح |\n|---|---|---|---|\n| max | number | — | حداکثر تعداد نمایش؛ بقیه به شکل «+۲» |\n| size | AvatarSize | \"md\" | اندازهٔ آواتار باقی‌مانده |\n| moreLabel | (count: number) => string | \"۲ نفر دیگر\" | متن صفحه‌خوان برای باقی‌مانده |\n\n## متغیرهای CSS\n\n| متغیر CSS | پیش‌فرض | توضیح |\n|---|---|---|\n| --k-avatar-size | 2.5rem | اندازه |\n| --k-avatar-radius | var(--k-radius-full) | گردی گوشه‌ها |\n| --k-avatar-bg | var(--k-bg-tertiary) | پس‌زمینهٔ حروف اول |\n| --k-avatar-fg | var(--k-text-secondary) | رنگ حروف اول |\n\n## دسترس‌پذیری\n\n- آواتار نقش `img` دارد و نام شخص به‌همراه وضعیت («سارا احمدی، آنلاین») اعلام می‌شود. تصویر داخلی تزئینی است.\n- وضعیت فقط با رنگ نمایش داده نمی‌شود؛ متن آن برای صفحه‌خوان در دسترس است.\n\n## راهنمای استفاده\n\nکِی استفاده کنیم:\n\n- نشان دادن شخص در کامنت، فهرست اعضا، منوی کاربر و تخصیص کار.\n- گروهی از افراد با `AvatarGroup`، مثل اعضای یک پروژه.\n\nکِی استفاده نکنیم:\n\n- لوگوی شرکت یا محصول؛ تصویر معمولی یا کارت بگذارید.\n- تنها نشانهٔ هویت در جایی که نام لازم است؛ آواتار را همیشه با نام کنارش بیاورید، مگر فضا خیلی کم باشد.\n\nدرست و نادرست:\n\n- ✓ درست: `name` را همیشه بدهید تا وقتی عکس بارگذاری نشد حروف اول نشان داده شود و صفحه‌خوان نام را بخواند.\n- ✗ نادرست: فقط `src` بدون نام؛ با خرابی عکس، دایرهٔ خالی و بی‌نام می‌ماند.\n- ✓ درست: در گروه شلوغ `max` بدهید تا «+۴ نفر دیگر» نمایش داده شود.\n- ✗ نادرست: بیست آواتار در یک ردیف که از کادر بیرون می‌زند."},{"slug":"components/badge","url":"https://kashi.sepantapouya.com/docs/components/badge/","title":"بج","description":"برچسب کوچک برای وضعیت، دسته‌بندی یا شمارش.","group":"کامپوننت‌ها","components":["Badge"],"markdown":"```tsx\nimport { Badge } from \"@kashi-ui/react\";\n```\n\n## رنگ‌ها\n\nرنگ را بر اساس معنا انتخاب کنید، نه سلیقه؛ کاربر رنگ‌ها را در کل محصول یکسان تفسیر می‌کند.\n\n```tsx\n<Badge>پیش‌نویس</Badge>\n<Badge tone=\"brand\">جدید</Badge>\n<Badge tone=\"success\" dot>فعال</Badge>\n<Badge tone=\"warning\">در انتظار</Badge>\n<Badge tone=\"error\">رد شده</Badge>\n<Badge tone=\"info\">بتا</Badge>\n```\n\n## اندازه و شکل\n\n```tsx\n<Badge tone=\"brand\" size=\"sm\">کوچک</Badge>\n<Badge tone=\"brand\">متوسط</Badge>\n<Badge tone=\"brand\" size=\"lg\">بزرگ</Badge>\n<Badge tone=\"success\" square>مربعی</Badge>\n```\n\n## سبک‌ها و برچسب‌های حذف‌شدنی\n\n```tsx\n<Badge tone=\"success\" variant=\"solid\">پرداخت‌شده</Badge>\n<Badge tone=\"brand\" variant=\"outline\">بتا</Badge>\n<Badge variant=\"outline\" icon={<UserIcon />}>۱۲ عضو</Badge>\n\n// فیلترهای فعال\n{tags.map((t) => <Badge key={t} tone=\"brand\" onRemove={() => remove(t)}>{t}</Badge>)}\n```\n\n**ویژگی‌های جدید**\n\n| ویژگی | نوع | پیش‌فرض | توضیح |\n|---|---|---|---|\n| variant | \"soft\" \\| \"solid\" \\| \"outline\" | \"soft\" | سبک |\n| icon | ReactNode | — | آیکون ابتدای متن |\n| onRemove / removeLabel | () => void / string | «حذف {متن}» | دکمهٔ حذف با نام برای صفحه‌خوان |\n\n## ویژگی‌ها\n\n| ویژگی | نوع | پیش‌فرض | توضیح |\n|---|---|---|---|\n| tone | \"neutral\" \\| \"brand\" \\| \"error\" \\| \"warning\" \\| \"success\" \\| \"info\" | \"neutral\" | رنگ معنایی |\n| size | \"sm\" \\| \"md\" \\| \"lg\" | \"md\" | اندازه |\n| dot | boolean | false | نقطهٔ رنگی ابتدای متن |\n| square | boolean | false | گوشهٔ کم به‌جای گرد |\n\n## متغیرهای CSS\n\n| متغیر CSS | پیش‌فرض | توضیح |\n|---|---|---|\n| --k-badge-radius | var(--k-radius-full) | گردی گوشه‌ها |\n| --k-badge-font-weight | 500 | وزن متن |\n\n## دسترس‌پذیری\n\n- همهٔ ترکیب‌های رنگ متن و پس‌زمینه کنتراست حداقل ۴٫۵ به ۱ دارند.\n- رنگ نباید تنها حامل معنا باشد؛ متن نشان باید به‌تنهایی وضعیت را برساند.\n- اگر عدد نشان (مثلاً تعداد پیام‌ها) تغییر می‌کند، تغییر را با یک ناحیهٔ `aria-live` اعلام کنید.\n\n## راهنمای استفاده\n\nکِی استفاده کنیم:\n\n- وضعیت یک مورد در جدول یا کارت: «پرداخت شد»، «در انتظار»، «لغو شده».\n- شمارش کوچک کنار یک عنوان یا زبانه.\n\nکِی استفاده نکنیم:\n\n- فیلتری که کاربر روشن و خاموش می‌کند. (چیپس: /docs/components/chips/)\n- متن طولانی یا جمله؛ بج یک یا دو کلمه است.\n- دکمه؛ بج کلیک‌خور به نظر نمی‌رسد. (دکمه: /docs/components/button/)\n\nدرست و نادرست:\n\n- ✓ درست: یک رنگ برای یک معنا در کل محصول.\n- ✗ نادرست: رنگ‌های تزئینی که معنا ندارند و کاربر نمی‌فهمد کدام مهم است.\n- ✓ درست: متن کوتاه: «ارسال شد».\n- ✗ نادرست: جمله: «سفارش شما با موفقیت ارسال گردید»."},{"slug":"components/banner","url":"https://kashi.sepantapouya.com/docs/components/banner/","title":"بنر","description":"پیام سطح صفحه مثل نسخهٔ جدید، پایان اشتراک یا اختلال سرویس، با دکمهٔ عمل و بستن.","group":"کامپوننت‌ها","components":["Banner"],"markdown":"```tsx\nimport { Banner } from \"@kashi-ui/react\";\n```\n\nبرای پیامی که به کل صفحه یا حساب مربوط است. پیام مربوط به یک بخش یا فرم را با `Alert` و پیام گذرا را با `Toast` نشان دهید.\n\n## نمونه\n\n```tsx\n<Banner tone=\"brand\" icon={<InfoIcon />} onClose={dismiss}\n actions={<Button size=\"sm\" variant=\"primary\">مشاهدهٔ تغییرات</Button>}>\n <strong>نسخهٔ ۰٫۱۰ منتشر شد.</strong> هفت کامپوننت تازه…\n</Banner>\n\n<Banner tone=\"warning\" icon={<WarningIcon />} actions={<Button size=\"sm\">تمدید اشتراک</Button>}>\n <strong>اشتراک شما ۳ روز دیگر تمام می‌شود.</strong> …\n</Banner>\n```\n\nبرای چسباندن به بالای صفحه، `flush` را بدهید تا حاشیه و گوشهٔ گرد حذف شود.\n\n| ویژگی | نوع | پیش‌فرض | توضیح |\n|---|---|---|---|\n| tone | \"neutral\" \\| \"brand\" \\| \"warning\" \\| \"error\" | \"neutral\" | رنگ |\n| icon | ReactNode | — | آیکون شاخص |\n| actions | ReactNode | — | دکمه یا لینک‌ها |\n| onClose | () => void | — | با آن دکمهٔ بستن نمایش داده می‌شود |\n| flush | boolean | false | بدون حاشیه، برای بالای صفحه |\n| label / closeLabel | string | «اطلاعیه» / «بستن اطلاعیه» | نام ناحیه و دکمه |\n\n## دسترس‌پذیری\n\nناحیهٔ `role=\"region\"` با نام «اطلاعیه»، تا کاربر صفحه‌خوان بتواند به آن پرش کند. نوار به‌عمد `aria-live` ندارد، چون هنگام بارگذاری صفحه نمایش داده می‌شود؛ برای پیام‌هایی که بعداً ظاهر می‌شوند از `Toast` استفاده کنید.\n\n## راهنمای استفاده\n\nکِی استفاده کنیم:\n\n- پیام سطح سایت یا حساب: اختلال سرویس، پایان نزدیک اشتراک، نسخهٔ تازه.\n- پیامی که تا کاربر کاری نکند یا آن را نبندد می‌ماند.\n\nکِی استفاده نکنیم:\n\n- پیام مربوط به یک بخش یا فرم. (هشدار: /docs/components/alert/)\n- تبلیغ و پیشنهاد فروش در هر صفحه؛ کاربر عادت می‌کند بنرها را نخواند.\n- تأیید یک عمل. (توست: /docs/components/toast/)\n\nدرست و نادرست:\n\n- ✓ درست: یک بنر در هر زمان، بالای صفحه، با یک دکمهٔ عمل روشن: «تمدید اشتراک».\n- ✗ نادرست: دو سه بنر روی هم که نیمی از صفحه را می‌گیرند.\n- ✓ درست: اگر پیام دانستنی است نه اجباری، `onClose` بدهید و بسته شدنش را نگه دارید تا برنگردد (در وب کامپوننت با ویژگی `remember`).\n- ✗ نادرست: بنری که با هر بار باز کردن صفحه دوباره برمی‌گردد.\n\n## وب کامپوننت\n\nهمین کامپوننت بدون React، برای HTML، وردپرس، Vue، Svelte و Angular: `<kashi-banner>` در `@kashi-ui/elements`. متن خود عنصر است؛ عنصرهای `slot=\"actions\"` کنار متن و `slot=\"icon\"` پیش از آن می‌آیند. با `remember` بستن اطلاعیه در همان مرورگر یادش می‌ماند.\n\nنصب بدون ابزار ساخت (CDN):\n\n```html\n<link rel=\"stylesheet\" href=\"https://cdn.jsdelivr.net/npm/@kashi-ui/css/dist/kashi.css\">\n<script type=\"module\" src=\"https://cdn.jsdelivr.net/npm/@kashi-ui/elements/dist/cdn/kashi-elements.js\"></script>\n```\n\nیا با npm، فقط همین بخش:\n\n```bash\nnpm i @kashi-ui/elements @kashi-ui/css\n```\n\n```js\nimport \"@kashi-ui/css\";\nimport \"@kashi-ui/elements/basic.js\";\n```\n\n```html\n<kashi-banner tone=\"brand\" dismissible remember=\"v25\">\n نسخهٔ <strong>۰.۲۵</strong> منتشر شد: همهٔ کامپوننت‌ها حالا بدون React هم هستند.\n <a slot=\"actions\" class=\"k-btn k-btn--secondary k-btn--sm\" href=\"/docs/web-components/\">ببینید</a>\n</kashi-banner>\n```\n\nویژگی‌ها:\n\n| نام | مقدار | پیش‌فرض | توضیح |\n|---|---|---|---|\n| tone | brand \\| warning \\| error | neutral | رنگ اطلاعیه |\n| dismissible | بولی | — | دکمهٔ بستن |\n| remember | کلید | — | پس از بستن، دیگر نمایش داده نشود (localStorage) |\n| flush | بولی | — | بدون حاشیه و گوشه، برای بالای صفحه |\n| label / close-label | متن | اطلاعیه / بستن اطلاعیه | نام‌ها برای صفحه‌خوان |\n\nرویدادها و JavaScript:\n\n| نام | توضیح |\n|---|---|\n| kashi-close | پیش از بستن؛ با event.preventDefault() بسته نمی‌شود |\n| element.close() | بستن با JavaScript |"},{"slug":"components/breadcrumb","url":"https://kashi.sepantapouya.com/docs/components/breadcrumb/","title":"بردکرامب","description":"نشان می‌دهد کاربر کجای ساختار سایت است.","group":"کامپوننت‌ها","components":["Breadcrumb"],"markdown":"```tsx\nimport { Breadcrumb } from \"@kashi-ui/react\";\n```\n\n## نمونه\n\nخانه فقط با آیکون نمایش داده می‌شود و برچسبش برای صفحه‌خوان می‌ماند.\n\n```tsx\n<Breadcrumb items={[\n { label: \"خانه\", href: \"/\", icon: <HomeIcon />, hideLabel: true },\n { label: \"تنظیمات\", href: \"/settings\" },\n { label: \"امنیت\" },\n]} />\n```\n\n## مسیر بلند\n\nبا `maxItems` وسط مسیر به «…» جمع می‌شود؛ با زدن آن، مسیر کامل باز می‌شود. نام‌های بلند هم کوتاه می‌شوند و متن کامل در `title` است.\n\n```tsx\n<Breadcrumb maxItems={3} items={[\n { label: \"خانه\", href: \"/\", icon: <HomeIcon />, hideLabel: true },\n { label: \"پروژه‌ها\", href: \"/projects\" },\n { label: \"فروشگاه آنلاین\", href: \"/projects/shop\" },\n { label: \"تنظیمات\", href: \"/projects/shop/settings\" },\n { label: \"امنیت و ورود دو مرحله‌ای\" },\n]} />\n```\n\n## جداکنندهٔ «/»\n\n```tsx\n<Breadcrumb separator=\"slash\" items={[…]} />\n```\n\n| ویژگی | نوع | پیش‌فرض | توضیح |\n|---|---|---|---|\n| items | { label, href?, icon?, hideLabel? }[] | — | آخرین مورد صفحهٔ فعلی است |\n| maxItems | number | — | جمع کردن وسط مسیرهای بلند |\n| separator | \"chevron\" \\| \"slash\" | \"chevron\" | شکل جداکننده |\n| label | string | \"مسیر\" | نام nav برای صفحه‌خوان |\n| expandLabel | string | \"نمایش مسیر کامل\" | نام دکمهٔ «…» |\n\n## متغیرهای CSS\n\n`--k-breadcrumb-max-item` (پیش‌فرض `14rem`): بیشترین عرض هر مورد پیش از کوتاه شدن.\n\n## دسترس‌پذیری\n\nداخل `<nav aria-label=\"مسیر\">` با فهرست `<ol>`؛ صفحهٔ فعلی `aria-current=\"page\"` دارد و لینک نیست. جداکننده‌ها تزئینی‌اند و در راست‌به‌چپ آینه می‌شوند. «…» یک دکمهٔ واقعی با نام «نمایش مسیر کامل» است.\n\n## راهنمای استفاده\n\nکِی استفاده کنیم:\n\n- سایت یا پنلی با چند سطح، مثل فروشگاه (خانه ← پوشاک ← کفش) یا تنظیمات تودرتو.\n- صفحه‌هایی که کاربر از جستجو یا پیوند مستقیم به آن‌ها می‌رسد و باید بداند کجاست.\n\nکِی استفاده نکنیم:\n\n- سایت یک‌سطحی یا صفحهٔ اصلی.\n- مراحل یک فرایند؛ بردکرامب مکان را می‌گوید، نه پیشرفت را. (استپر: /docs/components/stepper/)\n\nدرست و نادرست:\n\n- ✓ درست: آخرین مورد صفحهٔ فعلی است و پیوند ندارد.\n- ✗ نادرست: پیوند صفحهٔ فعلی به خودش.\n- ✓ درست: در مسیرهای بلند `maxItems` بدهید تا وسطشان «…» شود.\n- ✗ نادرست: مسیری که در موبایل به چند خط می‌شکند."},{"slug":"components/button-group","url":"https://kashi.sepantapouya.com/docs/components/button-group/","title":"گروه دکمه","description":"دکمه‌های چسبیده به هم برای عمل‌های مرتبط، مثل «روز / هفته / ماه» یا «قبلی / امروز / بعدی».","group":"کامپوننت‌ها","components":["ButtonGroup","Button"],"markdown":"```tsx\nimport { ButtonGroup, Button } from \"@kashi-ui/react\";\n```\n\n## نمونه\n\n```tsx\n<ButtonGroup aria-label=\"بازهٔ تقویم\">\n <Button aria-pressed={view === \"day\"} onClick={() => setView(\"day\")}>روز</Button>\n <Button aria-pressed={view === \"week\"} onClick={() => setView(\"week\")}>هفته</Button>\n <Button aria-pressed={view === \"month\"} onClick={() => setView(\"month\")}>ماه</Button>\n</ButtonGroup>\n\n<ButtonGroup aria-label=\"پیمایش\">\n <Button iconOnly aria-label=\"قبلی\">…</Button>\n <Button>امروز</Button>\n <Button iconOnly aria-label=\"بعدی\">…</Button>\n</ButtonGroup>\n```\n\nبرای انتخاب یک گزینه از چند گزینه، `SegmentedControl` معمولاً مناسب‌تر است؛ `ButtonGroup` برای عمل‌هاست. دکمهٔ روشن با `aria-pressed` یا `aria-current` برجسته می‌شود.\n\n| ویژگی | نوع | پیش‌فرض | توضیح |\n|---|---|---|---|\n| aria-label | string | — | نام گروه برای صفحه‌خوان |\n| children | Button[] | — | دکمه‌ها؛ همهٔ گونه‌ها و اندازه‌های Button |\n\n## دسترس‌پذیری\n\nظرف `role=\"group\"` با نام دارد. هر دکمه جدا در ترتیب Tab است و حلقهٔ فوکوس روی دکمهٔ کناری نمی‌رود.\n\n## راهنمای استفاده\n\nکِی استفاده کنیم:\n\n- چند عمل هم‌خانواده کنار هم: «قبلی / امروز / بعدی» یا ابزارهای ویرایشگر.\n- دکمهٔ اصلی با فهرست گزینه‌های کناری (Split button).\n\nکِی استفاده نکنیم:\n\n- انتخاب یک حالت از چند حالت، مثل «روز / هفته / ماه». (سگمنت: /docs/components/segmented-control/)\n- عمل‌های بی‌ربط که فقط کنار هم افتاده‌اند؛ آن‌ها را جدا بگذارید.\n\nدرست و نادرست:\n\n- ✓ درست: همهٔ دکمه‌های گروه یک گونه و یک اندازه.\n- ✗ نادرست: یک دکمهٔ اصلی و یک دکمهٔ فرعی چسبیده به هم؛ سلسله‌مراتب گم می‌شود."},{"slug":"components/button","url":"https://kashi.sepantapouya.com/docs/components/button/","title":"دکمه","description":"برای اجرای یک عمل. شش گونه برای سلسله‌مراتب بصری، چهار اندازه و حالت بارگذاری دسترس‌پذیر.","group":"کامپوننت‌ها","components":["Button"],"markdown":"```tsx\nimport { Button } from \"@kashi-ui/react\";\n```\n\n## گونه‌ها\n\nدر هر بخش از صفحه فقط یک دکمهٔ `primary` بگذارید. بقیهٔ عمل‌ها با `secondary` یا `tertiary` نمایش داده شوند.\n\n```tsx\n<Button variant=\"primary\">ذخیره</Button>\n<Button variant=\"secondary\">پیش‌نمایش</Button>\n<Button variant=\"tertiary\">انصراف</Button>\n<Button variant=\"link\">اطلاعات بیشتر</Button>\n<Button variant=\"danger\">حذف</Button>\n<Button variant=\"danger-secondary\">بایگانی</Button>\n```\n\n## اندازه‌ها\n\n```tsx\n<Button variant=\"primary\" size=\"sm\">کوچک</Button>\n<Button variant=\"primary\">متوسط</Button>\n<Button variant=\"primary\" size=\"lg\">بزرگ</Button>\n<Button variant=\"primary\" size=\"xl\">خیلی بزرگ</Button>\n```\n\n## با آیکون\n\n`iconStart` در RTL سمت راست و `iconEnd` سمت چپ قرار می‌گیرد. آیکون‌های جهت‌دار مثل فلش را با `data-k-mirror` علامت بزنید تا در RTL آینه شوند.\n\n```tsx\n<Button variant=\"primary\" iconStart={<PlusIcon />}>پروژهٔ جدید</Button>\n<Button iconEnd={<ArrowIcon />}>ادامه</Button>\n<Button iconOnly aria-label=\"جستجو\"><SearchIcon /></Button>\n<Button variant=\"danger-secondary\" iconOnly aria-label=\"حذف\"><TrashIcon /></Button>\n```\n\n## حالت‌ها\n\n```tsx\n<Button variant=\"primary\" loading>پرداخت</Button>\n<Button disabled>غیرفعال</Button>\n```\n\n## شکل، اندازه و لینک\n\nبا `href` دکمه یک لینک واقعی (`<a>`) می‌شود؛ با `as` می‌توانید کامپوننت لینک روتر خود را بدهید، مثل `as={Link}` در Next.js.\n\n```tsx\n<Button variant=\"primary\" shape=\"pill\">کاملاً گرد</Button>\n<Button shape=\"square\">گوشهٔ کم</Button>\n<Button size=\"xs\">خیلی کوچک</Button>\n<Button href=\"/docs\" variant=\"link\">دکمهٔ لینکی</Button>\n<Button as={Link} href=\"/pricing\">با Link در Next.js</Button>\n```\n\n**ویژگی‌های جدید**\n\n| ویژگی | نوع | پیش‌فرض | توضیح |\n|---|---|---|---|\n| shape | \"default\" \\| \"pill\" \\| \"square\" | \"default\" | شکل گوشه‌ها |\n| size | \"xs\" \\| \"sm\" \\| \"md\" \\| \"lg\" \\| \"xl\" | \"md\" | اندازهٔ xs (۲۸ پیکسل) اضافه شد |\n| href / target / rel | string | — | رندر به‌صورت لینک؛ با disabled، href حذف و aria-disabled گذاشته می‌شود |\n| as | ElementType | — | عنصر یا کامپوننت دلخواه |\n\n## ویژگی‌ها\n\n| ویژگی | نوع | پیش‌فرض | توضیح |\n|---|---|---|---|\n| variant | \"primary\" \\| \"secondary\" \\| \"tertiary\" \\| \"link\" \\| \"danger\" \\| \"danger-secondary\" | \"secondary\" | سبک بصری و اهمیت عمل |\n| size | \"sm\" \\| \"md\" \\| \"lg\" \\| \"xl\" | \"md\" | اندازه؛ ارتفاع‌ها ۳۶، ۴۰، ۴۴ و ۴۸ پیکسل |\n| loading | boolean | false | نمایش اسپینر؛ دکمه فوکوس‌پذیر می‌ماند ولی کلیک نمی‌گیرد |\n| loadingLabel | string | \"در حال بارگذاری\" | متنی که صفحه‌خوان هنگام بارگذاری اعلام می‌کند |\n| iconStart | ReactNode | — | آیکون ابتدای متن |\n| iconEnd | ReactNode | — | آیکون انتهای متن |\n| iconOnly | boolean | false | دکمهٔ مربعی فقط‌آیکون؛ aria-label الزامی است |\n| block | boolean | false | تمام‌عرض |\n\nسایر ویژگی‌های `<button>` مثل `onClick`، `type` و `disabled` هم پشتیبانی می‌شوند. مقدار پیش‌فرض `type` برابر `\"button\"` است تا فرم‌ها ناخواسته ارسال نشوند.\n\n## متغیرهای CSS\n\n| متغیر CSS | پیش‌فرض | توضیح |\n|---|---|---|\n| --k-btn-height-{sm,md,lg,xl} | 2.25rem تا 3rem × density | ارتفاع هر اندازه |\n| --k-btn-padding-{sm,md,lg,xl} | 0.75rem تا 1.25rem × density | فاصلهٔ افقی داخلی |\n| --k-btn-radius | var(--k-radius-md) | گردی گوشه‌ها |\n| --k-btn-font-weight | 600 | وزن متن |\n| --k-btn-gap | var(--k-space-1_5) | فاصلهٔ آیکون و متن |\n\n## دسترس‌پذیری\n\n- در حالت `loading` از `aria-disabled` به‌جای `disabled` استفاده می‌شود تا فوکوس کیبورد از دست نرود، و متن «در حال بارگذاری» با `role=\"status\"` اعلام می‌شود.\n- دکمهٔ `iconOnly` بدون `aria-label` در حالت توسعه هشدار می‌دهد.\n- حداقل ناحیهٔ لمس ۲۴×۲۴ پیکسل است و حلقهٔ فوکوس در حالت کنتراست بالای ویندوز هم دیده می‌شود.\n- برای رفتن به صفحهٔ دیگر از لینک (`<a class=\"k-btn\">`) استفاده کنید، نه دکمه.\n\n## راهنمای استفاده\n\nکِی استفاده کنیم:\n\n- اجرای یک عمل: ثبت، ذخیره، پرداخت، حذف، باز کردن گفتگو.\n- مهم‌ترین عمل صفحه با `variant=\"primary\"`؛ عمل‌های دیگر `secondary` یا `tertiary`.\n\nکِی استفاده نکنیم:\n\n- رفتن به صفحهٔ دیگر بدون انجام کاری؛ پیوند بگذارید، یا دکمه با `href` که خودش `<a>` می‌سازد.\n- انتخاب یک حالت از چند حالت. (سگمنت: /docs/components/segmented-control/)\n- روشن و خاموش کردن یک تنظیم. (سوییچ: /docs/components/switch/)\n\nدرست و نادرست:\n\n- ✓ درست: یک دکمهٔ اصلی در هر بخش؛ بقیه فرعی.\n- ✗ نادرست: چند دکمهٔ اصلی کنار هم؛ کاربر نمی‌داند کار اصلی کدام است.\n- ✓ درست: برچسب فعل است و می‌گوید چه می‌شود.\n- ✗ نادرست: «تأیید» و «بله» که بدون خواندن پرسش معنا ندارند.\n- ✓ درست: هنگام ارسال `loading` بدهید تا دوبار کلیک نشود و صفحه‌خوان «در حال بارگذاری» بخواند.\n- ✗ نادرست: دکمه را غیرفعال و بی‌توضیح رها کنید؛ کاربر نمی‌فهمد چرا کار نمی‌کند.\n\nمتن:\n\n- دو تا چهار کلمه، با فعل: «افزودن به سبد»، «ارسال دوبارهٔ کد».\n- دکمهٔ فقط‌آیکون `aria-label` می‌خواهد که کار را بگوید: «حذف سفارش ۴۸۱۲۷»."},{"slug":"components/card","url":"https://kashi.sepantapouya.com/docs/components/card/","title":"کارت","description":"ظرفی برای گروه‌بندی محتوای مرتبط، با سربرگ، بدنه و پانویس.","group":"کامپوننت‌ها","components":["Card","CardHeader","CardTitle","CardDescription","CardBody","CardFooter"],"markdown":"```tsx\nimport { Card, CardHeader, CardTitle, CardDescription, CardBody, CardFooter } from \"@kashi-ui/react\";\n```\n\n## پایه\n\n```tsx\n<Card>\n <CardHeader>\n <CardTitle>پلن حرفه‌ای</CardTitle>\n <CardDescription>برای تیم‌های در حال رشد</CardDescription>\n </CardHeader>\n <CardBody>۴۹۰ هزار تومان در ماه، تا ۲۰ کاربر.</CardBody>\n <CardFooter>\n <Button variant=\"tertiary\">مقایسه</Button>\n <Button variant=\"primary\">انتخاب پلن</Button>\n </CardFooter>\n</Card>\n```\n\n## کارت قابل‌کلیک\n\nکل سطح کارت قابل‌کلیک است، ولی فقط یک لینک با کلاس `k-card__link` دارد؛ کاربر کیبورد با یک Tab از آن عبور می‌کند.\n\n```tsx\n<Card interactive>\n <CardHeader>\n <Badge tone=\"brand\">راهنما</Badge>\n <CardTitle>\n <a className=\"k-card__link\" href=\"/docs/accessibility/\">دسترس‌پذیری در کاشی</a>\n </CardTitle>\n <CardDescription>آنچه خودکار تضمین می‌شود و آنچه بر عهدهٔ شماست.</CardDescription>\n </CardHeader>\n</Card>\n```\n\n## سبک‌ها، تصویر و منوی سربرگ\n\n```tsx\n<Card variant=\"elevated\" padding=\"sm\">\n <CardMedia src=\"/cover.jpg\" alt=\"\" ratio=\"16 / 9\" />\n <CardHeader actions={<DropdownMenu trigger={…}>…</DropdownMenu>}>\n <CardTitle>سبک برجسته</CardTitle>\n <CardDescription>با تصویر و منوی سربرگ</CardDescription>\n </CardHeader>\n</Card>\n\n<Card variant=\"filled\">…</Card>\n```\n\n**ویژگی‌های جدید**\n\n| ویژگی | نوع | پیش‌فرض | توضیح |\n|---|---|---|---|\n| variant | \"outlined\" \\| \"elevated\" \\| \"filled\" | \"outlined\" | سبک (Card) |\n| padding | \"none\" \\| \"sm\" \\| \"md\" \\| \"lg\" | \"md\" | فاصلهٔ داخلی همهٔ بخش‌ها (Card) |\n| actions | ReactNode | — | دکمه یا منو در انتهای سربرگ (CardHeader) |\n| ratio | string | \"16 / 9\" | نسبت ابعاد تصویر (CardMedia) |\n\n## ویژگی‌ها\n\n| ویژگی | نوع | پیش‌فرض | توضیح |\n|---|---|---|---|\n| as | ElementType | \"article\" | عنصر HTML؛ برای کارت‌های غیرمستقل div یا section |\n| flat | boolean | false | بدون سایه |\n| interactive | boolean | false | حالت hover و فوکوس برای کارت قابل‌کلیک |\n\n`CardTitle` پیش‌فرض `h3` است. با `as` سطح عنوان را با ساختار صفحه هماهنگ کنید؛ برای مثال `<CardTitle as=\"h2\">`.\n\n## متغیرهای CSS\n\n| متغیر CSS | پیش‌فرض | توضیح |\n|---|---|---|\n| --k-card-padding | 1.5rem × density | فاصلهٔ داخلی بخش‌ها |\n| --k-card-radius | var(--k-radius-xl) | گردی گوشه‌ها |\n| --k-card-bg | var(--k-bg-elevated) | پس‌زمینه |\n| --k-card-border | var(--k-border-secondary) | رنگ حاشیه |\n| --k-card-shadow | var(--k-shadow-xs) | سایه |\n\n## دسترس‌پذیری\n\n- از کارت‌هایی که کل آن‌ها یک لینک بزرگ است (`<a>` دور همهٔ محتوا) پرهیز کنید؛ صفحه‌خوان همهٔ متن را به‌عنوان نام لینک می‌خواند.\n- در کارت قابل‌کلیک، دکمه‌های داخلی دیگر را بالای لایهٔ لینک (`position: relative; z-index: 1`) قرار دهید.\n\n## راهنمای استفاده\n\nکِی استفاده کنیم:\n\n- گروه کردن محتوای یک موضوع: یک محصول، یک سفارش، یک بخش تنظیمات.\n- شبکه‌ای از موردهای هم‌شکل که هر کدام به صفحهٔ خودش می‌رود (`interactive`).\n\nکِی استفاده نکنیم:\n\n- کارت داخل کارت؛ سطح‌ها را با فاصله و جداکننده نشان دهید. (جداکننده: /docs/components/divider/)\n- داده‌های ردیفی که مقایسه می‌شوند. (جدول: /docs/components/table/)\n\nدرست و نادرست:\n\n- ✓ درست: کارت قابل‌کلیک با `interactive` و یک پیوند اصلی که کل کارت را می‌گیرد.\n- ✗ نادرست: کارت کلیک‌خور که داخلش چند دکمه هم هست؛ کاربر نمی‌داند کجا را بزند."},{"slug":"components/carousel","url":"https://kashi.sepantapouya.com/docs/components/carousel/","title":"کاروسل","description":"نمایش چند کارت یا تصویر در یک ردیف قابل‌پیمایش، مثل محصولات پیشنهادی.","group":"کامپوننت‌ها","components":["Carousel"],"markdown":"```tsx\nimport { Carousel } from \"@kashi-ui/react\";\n```\n\n## نمونه\n\n```tsx\n<Carousel label=\"محصولات پیشنهادی\" perView={2}>\n {products.map((p) => <ProductCard key={p.id} {...p} />)}\n</Carousel>\n```\n\nروی scroll-snap بومی مرورگر ساخته شده: با کشیدن انگشت، چرخ موس، دکمه‌ها و کلیدهای جهت کار می‌کند و در RTL خودکار درست است. چرخش خودکار ندارد، چون خواندن را برای همه، به‌خصوص کاربران صفحه‌خوان و کم‌بینا، سخت می‌کند.\n\n| ویژگی | نوع | پیش‌فرض | توضیح |\n|---|---|---|---|\n| children | ReactNode | — | هر فرزند یک اسلاید |\n| label | string | — | نام کاروسل (الزامی) |\n| perView | number | 1 | تعداد اسلاید در هر نما؛ یا متغیر --k-carousel-per-view |\n| dots | boolean | true | نقطه‌های پیمایش |\n| labels | { prev?, next?, slide? } | — | متن دکمه‌ها و نام اسلایدها |\n\n## دسترس‌پذیری\n\nالگوی Carousel در WAI-ARIA: ظرف با `aria-roledescription=\"اسلایدر\"` و هر اسلاید با نام «۲ از ۵». ریل اسلایدها فوکوس‌پذیر است و ←→ بین آن‌ها حرکت می‌کند؛ اسلاید فعال با `aria-live` اعلام می‌شود.\n\n## راهنمای استفاده\n\nکِی استفاده کنیم:\n\n- چند کارت هم‌شکل که همه‌شان لازم نیست دیده شوند: محصولات پیشنهادی، مقاله‌های مرتبط.\n- گالری عکس یک محصول.\n\nکِی استفاده نکنیم:\n\n- محتوای مهمی که همه باید ببینند؛ اسلاید دوم و سوم را کمتر کسی می‌بیند.\n- بنر اصلی صفحه که خودکار می‌چرخد؛ کاشی چرخش خودکار ندارد چون خواندن را سخت می‌کند.\n- مقایسهٔ چند مورد کنار هم. (جدول: /docs/components/table/)\n\nدرست و نادرست:\n\n- ✓ درست: بخشی از کارت بعدی دیده شود تا معلوم باشد ردیف ادامه دارد.\n- ✗ نادرست: یک اسلاید تمام‌عرض بدون نشانهٔ ادامه، که فقط با نقطه‌های ریز می‌شود فهمید.\n\n## وب کامپوننت\n\nهمین کامپوننت بدون React، برای HTML، وردپرس، Vue، Svelte و Angular: `<kashi-carousel>` در `@kashi-ui/elements`.\n\nنصب بدون ابزار ساخت (CDN):\n\n```html\n<link rel=\"stylesheet\" href=\"https://cdn.jsdelivr.net/npm/@kashi-ui/css/dist/kashi.css\">\n<script type=\"module\" src=\"https://cdn.jsdelivr.net/npm/@kashi-ui/elements/dist/cdn/kashi-elements.js\"></script>\n```\n\nیا با npm، فقط همین بخش:\n\n```bash\nnpm i @kashi-ui/elements @kashi-ui/css\n```\n\n```js\nimport \"@kashi-ui/css\";\nimport \"@kashi-ui/elements/nav.js\";\n```\n\n```html\n<kashi-carousel label=\"محصولات\" per-view=\"2\">\n <div class=\"k-card\" style=\"padding: 2rem\">محصول ۱</div>\n <div class=\"k-card\" style=\"padding: 2rem\">محصول ۲</div>\n <div class=\"k-card\" style=\"padding: 2rem\">محصول ۳</div>\n <div class=\"k-card\" style=\"padding: 2rem\">محصول ۴</div>\n</kashi-carousel>\n```\n\nویژگی‌ها:\n\n| نام | مقدار | پیش‌فرض | توضیح |\n|---|---|---|---|\n| label | متن | — | نام اسلایدر |\n| per-view | عدد | 1 | تعداد اسلاید در هر نما |\n| dots | false | — | بدون نقطه‌ها |\n\nرویدادها و JavaScript:\n\n| نام | توضیح |\n|---|---|\n| element.go(n) | رفتن به اسلاید n (از صفر) |\n| kashi-change | detail: { index } |"},{"slug":"components/charts","url":"https://kashi.sepantapouya.com/docs/components/charts/","title":"نمودار","description":"نمودار ستونی، انباشته، خطی، دایره‌ای و دونات، میله‌ای افقی، سهم از کل و خط روند؛ با ارقام فارسی و جدول هم‌ارز برای صفحه‌خوان.","group":"کامپوننت‌ها","components":["BarChart","BarList","LineChart","PieChart","ProportionBar","Sparkline"],"markdown":"```tsx\nimport { BarChart, BarList, LineChart, PieChart, ProportionBar, Sparkline } from \"@kashi-ui/react\";\n```\n\nنمودارهای کاشی برای داشبورد و پنل مدیریت ساخته شده‌اند، نه برای تحلیل سنگین داده. اگر نمودار تعاملی پیچیده لازم دارید، از کتابخانهٔ تخصصی استفاده کنید و رنگ‌ها را از متغیرهای `--k-chart-1` تا `--k-chart-4` بگیرید.\n\n## ستونی\n\nبرای مقایسهٔ مقدار در چند دسته یا دوره. فقط بلندترین ستون برچسب دارد؛ مقدار بقیه با موس یا Tab نمایش داده می‌شود.\n\n```tsx\n<BarChart\n title=\"فروش ماهانه (میلیون تومان)\"\n data={[{ label: \"فروردین\", value: 82 }, { label: \"اردیبهشت\", value: 96 }, …]}\n/>\n```\n\n## ستونی انباشته\n\nبرای ترکیب یک کل در طول زمان، تا چهار سری. بخش‌ها با فاصلهٔ ۲ پیکسلی از هم جدا شده‌اند و راهنما همیشه نمایش داده می‌شود.\n\n```tsx\n<BarChart title=\"سفارش‌ها بر اساس کانال\" labels={months} series={[\n { name: \"وب\", values: [420, 460, …] },\n { name: \"اپ\", values: [260, 300, …] },\n { name: \"حضوری\", values: [120, 110, …] },\n]} />\n```\n\n## خطی\n\nبرای روند در طول زمان، تا چهار سری. محور زمان در جهت خواندن صفحه است: در RTL اولین ماه سمت راست. نام هر سری کنار انتهای خطش آمده تا تشخیص سری فقط به رنگ وابسته نباشد.\n\n```tsx\n<LineChart\n title=\"کاربران فعال روزانه\"\n labels={[\"فروردین\", \"اردیبهشت\", …]}\n series={[\n { name: \"وب\", values: [3200, 3600, …] },\n { name: \"اپ\", values: [1800, 2300, …] },\n { name: \"API\", values: [600, 700, …] },\n ]}\n/>\n```\n\n## میله‌ای افقی\n\nبرای رتبه‌بندی، به‌خصوص وقتی نام‌ها بلندند؛ نام داخل میله و مقدار در انتهای ردیف.\n\n```tsx\n<BarList title=\"پربازدیدترین صفحه‌ها\" data={[\n { label: \"صفحهٔ اصلی\", value: 48210 },\n { label: \"محصولات / مبل راحتی\", value: 21840, href: \"/products/sofa\" },\n …\n]} />\n```\n\n## سهم از کل\n\nجایگزین خواناتر نمودار دایره‌ای: مقایسهٔ طول در یک میله از مقایسهٔ زاویه دقیق‌تر است. حداکثر چهار بخش؛ بقیه را در «سایر» جمع کنید.\n\n```tsx\n<ProportionBar title=\"منبع ورود کاربران\" data={[\n { label: \"جستجوی گوگل\", value: 46200 },\n { label: \"ورود مستقیم\", value: 21800 },\n { label: \"اینستاگرام\", value: 15400 },\n { label: \"سایر\", value: 6100 },\n]} />\n```\n\n## دایره‌ای و دونات\n\nبرای نشان دادن سهم چند بخش از کل، وقتی تعداد بخش‌ها کم است. بخش‌ها از بزرگ به کوچک چیده می‌شوند و بیش از پنج بخش در «سایر» جمع می‌شود. با بردن موس روی هر بخش، درصد آن وسط دونات نمایش داده می‌شود. چشم زاویه را دقیق مقایسه نمی‌کند؛ برای مقایسهٔ دقیق از «سهم از کل» یا نمودار میله‌ای استفاده کنید.\n\n```tsx\n<PieChart title=\"سفارش‌ها بر اساس استان\" data={[\n { label: \"تهران\", value: 48200 },\n { label: \"اصفهان\", value: 18400 },\n …\n]} />\n\n<PieChart title=\"روش پرداخت\" variant=\"pie\" data={[…]} />\n```\n\n## خط روند\n\nشکل روند بدون محور، برای کارت آمار و سلول جدول. نام دسترس‌پذیر را با جهت روند بدهید.\n\n```tsx\n<Sparkline values={[4, 6, 5, 8, 7, 9, 12]} tone=\"success\" aria-label=\"روند فروش: افزایشی\" />\n```\n\n| ویژگی | نوع | پیش‌فرض | توضیح |\n|---|---|---|---|\n| title | string | — | عنوان و نام دسترس‌پذیر (الزامی) |\n| data (BarChart) | { label, value }[] | — | یک سری |\n| labels / series (BarChart) | string[] / { name, values }[] | — | ستون انباشته، حداکثر چهار سری |\n| data (BarList) | { label, value, href? }[] | — | ردیف‌ها به همان ترتیب |\n| data (ProportionBar) | { label, value }[] | — | حداکثر چهار بخش؛ percent برای درصد |\n| data / variant (PieChart) | { label, value }[] / \"donut\" \\| \"pie\" | \"donut\" | حداکثر پنج بخش؛ totalLabel برای متن وسط |\n| values / tone (Sparkline) | number[] / brand \\| success \\| error \\| neutral | — | بدون محور؛ aria-label بدهید |\n| labels / series (LineChart) | string[] / { name, values }[] | — | حداکثر چهار سری؛ بیشتر نادیده گرفته می‌شود |\n| format | (n: number) => string | ارقام فارسی | قالب مقدار در راهنما و جدول |\n| area (LineChart) | boolean | false | سطح کم‌رنگ زیر خط، فقط برای یک سری |\n| labelMax (BarChart) | boolean | true | برچسب مقدار روی بلندترین ستون |\n| height | string | \"14rem\" | یا متغیر --k-chart-height |\n\n## رنگ‌ها\n\nچهار رنگ دسته‌ای با ترتیب ثابت، برای تم تیره و روشن جداگانه انتخاب و با شبیه‌سازی کوررنگی بررسی شده‌اند (فاصلهٔ رنگی دست‌کم ۸). رنگ همیشه به نام سری وابسته است، نه به جایگاهش. نمودار دومحوره نداریم؛ دو مقیاس متفاوت را در دو نمودار جدا نشان دهید.\n\n## دسترس‌پذیری\n\nهر نمودار یک جدول هم‌ارز پنهان دارد که صفحه‌خوان کامل می‌خواند. در نمودار ستونی هر ستون با Tab فوکوس می‌گیرد («آبان: ۱۸۴»)؛ در نمودار خطی، با فوکوس روی نمودار کلیدهای ←→ بین نقطه‌ها حرکت می‌کنند و مقدار همهٔ سری‌ها اعلام می‌شود.\n\n## راهنمای استفاده\n\nکِی استفاده کنیم:\n\n- روند در طول زمان (خطی)، مقایسهٔ دسته‌ها (ستونی)، سهم از کل (دونات یا سهم از کل) و رتبه‌بندی (میله‌ای افقی).\n- داشبورد و گزارش که خواننده باید الگو را با یک نگاه ببیند.\n\nکِی استفاده نکنیم:\n\n- عدد دقیقی که مهم است؛ جدول یا کارت آمار بگذارید. (کارت آمار: /docs/components/stat-card/)\n- دونات با بیش از پنج بخش؛ کاشی بقیه را «سایر» می‌کند، ولی میله‌ای افقی خواناتر است.\n- دو داده با واحدهای مختلف روی یک محور.\n\nدرست و نادرست:\n\n- ✓ درست: عنوان بگوید نمودار چه نشان می‌دهد: «فروش ماهانهٔ ۱۴۰۵ (میلیون تومان)».\n- ✗ نادرست: عنوان «نمودار ۱» یا بدون عنوان؛ صفحه‌خوان هم چیزی برای خواندن ندارد.\n- ✓ درست: برای اعداد بزرگ `format` با واحد کوتاه بدهید تا محور شلوغ نشود.\n- ✗ نادرست: ۱۲۵۰۰۰۰۰۰ روی محور؛ چشم باید صفرها را بشمارد.\n\n## وب کامپوننت\n\nهمین کامپوننت بدون React، برای HTML، وردپرس، Vue، Svelte و Angular: `<kashi-bar-chart>`، `<kashi-line-chart>`، `<kashi-pie-chart>`، `<kashi-bar-list>`، `<kashi-proportion-bar>`، `<kashi-sparkline>` در `@kashi-ui/elements`. عنوان با `label` و داده با ویژگی JSON (`data`، `labels`، `series`) یا با JavaScript (`element.data = …`). هر نمودار جدول هم‌ارز برای صفحه‌خوان دارد.\n\nنصب بدون ابزار ساخت (CDN):\n\n```html\n<link rel=\"stylesheet\" href=\"https://cdn.jsdelivr.net/npm/@kashi-ui/css/dist/kashi.css\">\n<script type=\"module\" src=\"https://cdn.jsdelivr.net/npm/@kashi-ui/elements/dist/cdn/kashi-elements.js\"></script>\n```\n\nیا با npm، فقط همین بخش:\n\n```bash\nnpm i @kashi-ui/elements @kashi-ui/css\n```\n\n```js\nimport \"@kashi-ui/css\";\nimport \"@kashi-ui/elements/charts.js\";\n```\n\n```html\n<kashi-bar-chart label=\"فروش ماهانه\" unit=\"سفارش\" data='[{\"label\": \"فروردین\", \"value\": 120}, {\"label\": \"اردیبهشت\", \"value\": 180}, {\"label\": \"خرداد\", \"value\": 150}]'></kashi-bar-chart>\n<kashi-line-chart label=\"بازدید\" labels='[\"شنبه\", \"یکشنبه\", \"دوشنبه\", \"سه‌شنبه\"]' series='[{\"name\": \"وب\", \"values\": [40, 65, 58, 80]}, {\"name\": \"اپ\", \"values\": [20, 30, 45, 50]}]'></kashi-line-chart>\n<kashi-pie-chart label=\"منبع ورود\" data='[{\"label\": \"جستجو\", \"value\": 520}, {\"label\": \"مستقیم\", \"value\": 310}, {\"label\": \"شبکه‌های اجتماعی\", \"value\": 170}]'></kashi-pie-chart>\n```\n\n| عنصر | کاربرد |\n|---|---|\n| <kashi-bar-chart> | ستونی؛ data، یا انباشته با labels و series |\n| <kashi-line-chart> | خطی تا چهار سری؛ labels و series |\n| <kashi-pie-chart> | دونات یا دایره‌ای (variant=\"pie\")؛ data |\n| <kashi-bar-list> | میله‌های افقی برای رتبه‌بندی؛ data با href اختیاری |\n| <kashi-proportion-bar> | سهم هر بخش در یک میله؛ data |\n| <kashi-sparkline> | خط روند کوچک؛ values=\"12, 18, 15\" |\n\nویژگی‌ها:\n\n| نام | مقدار | پیش‌فرض | توضیح |\n|---|---|---|---|\n| label | متن | — | عنوان نمودار و نام دسترس‌پذیر |\n| data / labels / series | JSON | — | داده |\n| unit | متن | — | واحد کنار اعداد، مثل «سفارش» |\n| height | طول CSS | — | ارتفاع نمودار ستونی و خطی |\n| variant / total-label / size | pie / متن / پیکسل | donut / جمع / 176 | نمودار دایره‌ای |\n| tone / area | brand \\| success \\| error \\| neutral / false | brand | خط روند |\n\nرویدادها و JavaScript:\n\n| نام | توضیح |\n|---|---|\n| element.data / series / labels / values | داده با JavaScript |\n| element.format | قالب عدد: (n) => \"…\" |"},{"slug":"components/checkbox","url":"https://kashi.sepantapouya.com/docs/components/checkbox/","title":"چک‌باکس","description":"برای انتخاب یک یا چند گزینه، یا تأیید یک مورد. روی input بومی ساخته شده است.","group":"کامپوننت‌ها","components":["Checkbox"],"markdown":"```tsx\nimport { Checkbox } from \"@kashi-ui/react\";\n```\n\n## پایه\n\n```tsx\n<Checkbox label=\"مرا به خاطر بسپار\" defaultChecked />\n<Checkbox label=\"خبرنامه\" description=\"ماهی یک ایمیل، بدون تبلیغات.\" />\n```\n\n## حالت‌ها\n\n`indeterminate` برای «انتخاب همه» وقتی فقط بخشی از گزینه‌ها انتخاب شده‌اند.\n\n```tsx\n<Checkbox label=\"انتخاب همه\" indeterminate />\n<Checkbox label=\"قوانین را می‌پذیرم\" invalid />\n<Checkbox label=\"غیرفعال\" disabled />\n<Checkbox label=\"بزرگ\" size=\"lg\" defaultChecked />\n```\n\n## ویژگی‌ها\n\n| ویژگی | نوع | پیش‌فرض | توضیح |\n|---|---|---|---|\n| label | ReactNode | — | برچسب کنار چک‌باکس؛ کل برچسب قابل‌کلیک است |\n| description | ReactNode | — | توضیح زیر برچسب؛ با aria-describedby متصل می‌شود |\n| indeterminate | boolean | false | حالت نیمه‌انتخاب |\n| invalid | boolean | false | حالت خطا |\n| size | \"md\" \\| \"lg\" | \"md\" | ۱۸ یا ۲۰ پیکسل |\n\n## متغیرهای CSS\n\n| متغیر CSS | پیش‌فرض | توضیح |\n|---|---|---|\n| --k-checkbox-size | 1.125rem | اندازه |\n| --k-checkbox-radius | 0.3125rem × radius-factor | گردی گوشه‌ها |\n\n## دسترس‌پذیری\n\n- روی `<input type=\"checkbox\">` بومی ساخته شده؛ کلید Space، فرم‌ها و صفحه‌خوان‌ها بدون کد اضافه کار می‌کنند.\n- در حالت کنتراست بالای ویندوز، ظاهر بومی سیستم نمایش داده می‌شود.\n- برای گروه چک‌باکس‌ها از `<fieldset>` و `<legend>` استفاده کنید.\n\n## راهنمای استفاده\n\nکِی استفاده کنیم:\n\n- انتخاب چند گزینه از یک فهرست کوتاه.\n- پذیرفتن یک مورد در فرم: «شرایط استفاده را خواندم».\n\nکِی استفاده نکنیم:\n\n- تنظیمی که همان لحظه اعمال می‌شود. (سوییچ: /docs/components/switch/)\n- انتخاب فقط یکی از چند گزینه. (رادیو: /docs/components/radio/)\n- فهرست بلند که جا نمی‌شود. (انتخاب چندتایی: /docs/components/multi-select/)\n\nدرست و نادرست:\n\n- ✓ درست: برچسب مثبت بگوید تیک یعنی چه.\n- ✗ نادرست: برچسب منفی که تیک‌زدنش ذهن را می‌پیچاند.\n- ✓ درست: برچسب قابل‌کلیک است؛ کل متن را داخل `label` بدهید.\n- ✗ نادرست: متن جدا از چک‌باکس که کلیک رویش کار نمی‌کند."},{"slug":"components/chips","url":"https://kashi.sepantapouya.com/docs/components/chips/","title":"چیپس","description":"فیلترهای روشن و خاموش‌شونده با تعداد نتیجه، بالای فهرست یا جدول.","group":"کامپوننت‌ها","components":["ChipGroup"],"markdown":"```tsx\nimport { ChipGroup } from \"@kashi-ui/react\";\n```\n\n## نمونه\n\n```tsx\n<ChipGroup aria-label=\"وضعیت سفارش\" value={v} onChange={setV} options={[\n { value: \"paid\", label: \"پرداخت‌شده\", count: 128 },\n { value: \"pending\", label: \"در انتظار\", count: 14 },\n { value: \"shipped\", label: \"ارسال‌شده\", count: 96 },\n]} />\n```\n\n| ویژگی | نوع | پیش‌فرض | توضیح |\n|---|---|---|---|\n| options | { value, label, icon?, count?, disabled? }[] | — | چیپ‌ها |\n| value / defaultValue | string[] | [] | چیپ‌های روشن |\n| onChange | (value: string[]) => void | — | — |\n| single | boolean | false | فقط یکی روشن؛ کلیک دوباره خاموشش می‌کند |\n| aria-label | string | — | نام گروه |\n\n## دسترس‌پذیری\n\nهر چیپ یک دکمه با `aria-pressed` است. حالت روشن با تیک، رنگ و حاشیه نشان داده می‌شود، نه فقط رنگ.\n\n## راهنمای استفاده\n\nکِی استفاده کنیم:\n\n- فیلترهای سریع بالای فهرست یا جدول: «فقط موجود»، «ارسال رایگان».\n- انتخاب یک یا چند دسته با تعداد نتیجهٔ هر کدام.\n\nکِی استفاده نکنیم:\n\n- نمایش وضعیت که کاربر عوضش نمی‌کند. (بج: /docs/components/badge/)\n- گزینه‌های یک فرم که ثبت می‌شوند؛ چک‌باکس یا رادیو روشن‌تر است. (چک‌باکس: /docs/components/checkbox/)\n\nدرست و نادرست:\n\n- ✓ درست: تعداد نتیجهٔ هر فیلتر را با `count` نشان دهید تا کاربر پیش از کلیک بداند.\n- ✗ نادرست: فیلتری که کلیک می‌کنید و فهرست خالی می‌شود.\n\n## وب کامپوننت\n\nهمین کامپوننت بدون React، برای HTML، وردپرس، Vue، Svelte و Angular: `<kashi-chip-group>` در `@kashi-ui/elements`. هر `<button>` یک چیپ است (`data-pressed` برای روشن بودن اولیه، `data-count` برای عدد).\n\nنصب بدون ابزار ساخت (CDN):\n\n```html\n<link rel=\"stylesheet\" href=\"https://cdn.jsdelivr.net/npm/@kashi-ui/css/dist/kashi.css\">\n<script type=\"module\" src=\"https://cdn.jsdelivr.net/npm/@kashi-ui/elements/dist/cdn/kashi-elements.js\"></script>\n```\n\nیا با npm، فقط همین بخش:\n\n```bash\nnpm i @kashi-ui/elements @kashi-ui/css\n```\n\n```js\nimport \"@kashi-ui/css\";\nimport \"@kashi-ui/elements/forms.js\";\n```\n\n```html\n<kashi-chip-group label=\"دسته‌بندی\" name=\"category\">\n <button data-value=\"all\" data-pressed>همه</button>\n <button data-value=\"new\" data-count=\"12\">تازه‌ها</button>\n <button data-value=\"sale\" data-count=\"5\">تخفیف‌دار</button>\n</kashi-chip-group>\n```\n\nویژگی‌ها:\n\n| نام | مقدار | پیش‌فرض | توضیح |\n|---|---|---|---|\n| label | متن | — | نام گروه برای صفحه‌خوان |\n| single | بولی | — | فقط یک چیپ روشن |\n| name | متن | — | مقدار چیپ‌های روشن در فرم |\n\nرویدادها و JavaScript:\n\n| نام | توضیح |\n|---|---|\n| kashi-change | detail: { value: [...] } |"},{"slug":"components/color-picker","url":"https://kashi.sepantapouya.com/docs/components/color-picker/","title":"انتخاب رنگ","description":"انتخاب رنگ برند یا برچسب با نمونه‌رنگ‌ها، پنل انتخاب دلخواه و کد hex، همراه کنتراست متن.","group":"کامپوننت‌ها","components":["ColorPicker"],"markdown":"```tsx\nimport { ColorPicker } from \"@kashi-ui/react\";\n```\n\n## نمونه\n\n```tsx\n<ColorPicker label=\"رنگ برند\" value={color} onChange={setColor} name=\"brand\" />\n```\n\nبا زدن روی مربع رنگ، پنل انتخاب دلخواه باز می‌شود: سطح اشباع و روشنایی، نوار رنگ‌مایه، فیلدهای R، G و B، و در مرورگرهایی که پشتیبانی می‌کنند قطره‌چکان برای برداشتن رنگ از صفحه. در سطح رنگ، کلیدهای جهت یک درصد و با Shift ده درصد جابه‌جا می‌کنند.\n\nزیر انتخابگر، کنتراست رنگ با متن سفید یا مشکی (هر کدام بهتر است) نمایش داده می‌شود تا رنگی انتخاب نشود که متن روی آن خوانا نباشد. تابع `contrastRatio` هم صادر شده است.\n\n| ویژگی | نوع | پیش‌فرض | توضیح |\n|---|---|---|---|\n| value / defaultValue | string | \"#0b8077\" | رنگ hex |\n| onChange | (hex: string) => void | — | — |\n| swatches | { name, value }[] | رنگ‌های کاشی | نمونه‌رنگ‌ها؛ [] برای حذف |\n| showContrast | boolean | true | نمایش کنتراست متن |\n| name | string | — | فیلد پنهان برای فرم |\n\n## دسترس‌پذیری\n\nنمونه‌رنگ‌ها رادیوهای بومی با نام فارسی هستند («فیروزه‌ای»)، نه فقط یک خانهٔ رنگی. کد hex یک فیلد متنی معمولی است، پس کاربر صفحه‌خوان هم می‌تواند رنگ دقیق را وارد کند. سطح رنگ یک slider با مقدار متنی است («اشباع ۹۱٪، روشنایی ۷۰٪») و Esc پنل را می‌بندد و فوکوس را به مربع رنگ برمی‌گرداند.\n\n## راهنمای استفاده\n\nکِی استفاده کنیم:\n\n- انتخاب رنگ برند، رنگ برچسب یا رنگ تم توسط کاربر.\n- جایی که چند رنگ پیشنهادی کافی است و رنگ دلخواه هم ممکن باشد.\n\nکِی استفاده نکنیم:\n\n- انتخاب از چند رنگ ثابت که رنگ دلخواه لازم ندارد؛ `swatches` بدهید یا رادیوی کارتی بسازید. (رادیو: /docs/components/radio/)\n\nدرست و نادرست:\n\n- ✓ درست: کنتراست با متن را نشان دهید (`showContrast`) تا کاربر رنگی نسازد که متن رویش خوانده نشود.\n- ✗ نادرست: هر رنگی بپذیرید و بعد متن سفید روی زرد کم‌رنگ بیاید.\n\n## وب کامپوننت\n\nهمین کامپوننت بدون React، برای HTML، وردپرس، Vue، Svelte و Angular: `<kashi-color-picker>` در `@kashi-ui/elements`.\n\nنصب بدون ابزار ساخت (CDN):\n\n```html\n<link rel=\"stylesheet\" href=\"https://cdn.jsdelivr.net/npm/@kashi-ui/css/dist/kashi.css\">\n<script type=\"module\" src=\"https://cdn.jsdelivr.net/npm/@kashi-ui/elements/dist/cdn/kashi-elements.js\"></script>\n```\n\nیا با npm، فقط همین بخش:\n\n```bash\nnpm i @kashi-ui/elements @kashi-ui/css\n```\n\n```js\nimport \"@kashi-ui/css\";\nimport \"@kashi-ui/elements/pickers.js\";\n```\n\n```html\n<kashi-color-picker label=\"رنگ برند\" value=\"#0b8077\" name=\"brand\"></kashi-color-picker>\n```\n\nویژگی‌ها:\n\n| نام | مقدار | پیش‌فرض | توضیح |\n|---|---|---|---|\n| value | #0b8077 | #0b8077 | رنگ HEX |\n| swatches | «#0b8077 فیروزه‌ای, …» | رنگ‌های کاشی | رنگ‌های پیشنهادی؛ خالی برای هیچ |\n| show-contrast | false | — | بدون کنتراست با متن |\n| label / hint / name | — | — | مثل بقیهٔ فیلدها |\n\nرویدادها و JavaScript:\n\n| نام | توضیح |\n|---|---|\n| kashi-change | detail: { value: \"#0b8077\" } |"},{"slug":"components/combobox","url":"https://kashi.sepantapouya.com/docs/components/combobox/","title":"کامبوباکس","description":"فیلد جستجو با فهرست پیشنهادها (Combobox)؛ برای فهرست‌های بلند مثل شهر، کاربر یا محصول.","group":"کامپوننت‌ها","components":["Combobox"],"markdown":"```tsx\nimport { Combobox } from \"@kashi-ui/react\";\n```\n\nوقتی گزینه‌ها بیشتر از ده‌تاست، به‌جای `Select` از `Combobox` استفاده کنید. جستجو متن فارسی را یکدست می‌کند: «ي» و «ی»، «ك» و «ک» و ارقام فارسی و انگلیسی یکی حساب می‌شوند.\n\n## نمونه\n\n```tsx\nconst [city, setCity] = useState<string | null>(null);\n\n<Combobox label=\"استان\" placeholder=\"جستجو کنید…\"\n options={provinces.map((p) => ({ value: p, label: p }))}\n value={city} onChange={setCity} />\n```\n\n## گروه، توضیح و آیکون\n\n```tsx\n<Combobox label=\"مسئول\" options={[\n { value: \"sara\", label: \"سارا محمدی\", description: \"طراح محصول\", group: \"طراحی\", icon: <Avatar name=\"سارا محمدی\" size=\"xs\" /> },\n { value: \"ali\", label: \"علی رضایی\", group: \"فنی\", keywords: [\"ali\"] },\n { value: \"mina\", label: \"مینا احمدی\", group: \"فنی\", disabled: true },\n]} />\n```\n\nبرای جستجوی سمت سرور، `onInputChange` را بگیرید، `options` را از پاسخ سرور بسازید، `loading` را در زمان درخواست روشن کنید و `filter={() => true}` بدهید.\n\n| ویژگی | نوع | پیش‌فرض | توضیح |\n|---|---|---|---|\n| options | { value, label, description?, icon?, group?, keywords?, disabled? }[] | — | گزینه‌ها؛ گزینه‌های هم‌گروه باید پشت سر هم باشند |\n| value / defaultValue | string \\| null | null | مقدار انتخاب‌شده |\n| onChange | (value: string \\| null) => void | — | پس از انتخاب یا پاک کردن |\n| onInputChange | (query: string) => void | — | تغییر متن جستجو |\n| filter | (option, query) => boolean | defaultFilter | جستجوی سفارشی |\n| loading | boolean | false | اسپینر و متن «در حال جستجو…» |\n| emptyMessage | ReactNode | «نتیجه‌ای پیدا نشد.» | متن فهرست خالی |\n| name | string | — | فیلد پنهان برای ارسال فرم |\n| label / hint / error | ReactNode | — | مثل ورودی |\n\nابزارهای `defaultFilter`، `searchText` و `highlight` هم صادر شده‌اند تا در جستجوهای خودتان استفاده کنید.\n\n## کیبورد\n\n| کلید | عمل |\n|---|---|\n| <kbd>↓</kbd> / <kbd>↑</kbd> | باز کردن فهرست و رفتن به گزینهٔ بعد / قبل |\n| <kbd>Enter</kbd> | انتخاب گزینهٔ فعال |\n| <kbd>Esc</kbd> | بستن فهرست؛ اگر بسته است، پاک کردن مقدار |\n| <kbd>Home</kbd> / <kbd>End</kbd> | اولین / آخرین گزینه (وقتی فهرست باز است) |\n\n## دسترس‌پذیری\n\nالگوی Combobox در WAI-ARIA با `aria-activedescendant`: فوکوس همیشه در فیلد می‌ماند و صفحه‌خوان گزینهٔ فعال را می‌خواند. تعداد نتیجه‌ها پس از هر تایپ با یک ناحیهٔ `aria-live` اعلام می‌شود («۳ نتیجه») و گزینه‌های غیرفعال `aria-disabled` دارند.\n\n## راهنمای استفاده\n\nکِی استفاده کنیم:\n\n- فهرست بلند که کاربر نام گزینه را می‌داند: شهر، کاربر، محصول.\n- جستجو با پیشنهاد از سرور (`onInputChange` و `loading`).\n\nکِی استفاده نکنیم:\n\n- کمتر از هفت گزینه. (رادیو: /docs/components/radio/)\n- فهرست متوسط که کاربر باید مرورش کند و نامش را نمی‌داند. (سلکت: /docs/components/select/)\n- انتخاب چند گزینه. (انتخاب چندتایی: /docs/components/multi-select/)\n\nدرست و نادرست:\n\n- ✓ درست: `keywords` بدهید تا «مشهد» استان خراسان رضوی را پیدا کند و «tea» هم «چای» را.\n- ✗ نادرست: فقط تطبیق دقیق اول متن؛ کاربر باید املای دقیق را بداند.\n- ✓ درست: پیام نتیجهٔ خالی بگوید چه کند: «شهری پیدا نشد؛ نام را کوتاه‌تر بنویسید».\n- ✗ نادرست: فهرست خالی بدون هیچ متنی.\n\n## وب کامپوننت\n\nهمین کامپوننت بدون React، برای HTML، وردپرس، Vue، Svelte و Angular: `<kashi-combobox>` در `@kashi-ui/elements`. گزینه‌ها همان `<option>` و `<optgroup>` معمولی‌اند (`data-description` و `data-keywords` برای جستجو).\n\nنصب بدون ابزار ساخت (CDN):\n\n```html\n<link rel=\"stylesheet\" href=\"https://cdn.jsdelivr.net/npm/@kashi-ui/css/dist/kashi.css\">\n<script type=\"module\" src=\"https://cdn.jsdelivr.net/npm/@kashi-ui/elements/dist/cdn/kashi-elements.js\"></script>\n```\n\nیا با npm، فقط همین بخش:\n\n```bash\nnpm i @kashi-ui/elements @kashi-ui/css\n```\n\n```js\nimport \"@kashi-ui/css\";\nimport \"@kashi-ui/elements/combobox.js\";\n```\n\n```html\n<kashi-combobox label=\"زبان برنامه‌نویسی\" name=\"lang\" placeholder=\"جستجو…\">\n <optgroup label=\"وب\">\n <option value=\"ts\" data-description=\"TypeScript\">تایپ‌اسکریپت</option>\n <option value=\"js\">جاوااسکریپت</option>\n </optgroup>\n <option value=\"py\">پایتون</option>\n <option value=\"go\">گو</option>\n</kashi-combobox>\n```\n\nویژگی‌ها:\n\n| نام | مقدار | پیش‌فرض | توضیح |\n|---|---|---|---|\n| label | متن | نام فارسی فیلد | برچسب بالای فیلد؛ بدون آن aria-label بدهید |\n| name | متن | — | نام فیلد در فرم HTML؛ مقدار خام ارسال می‌شود |\n| value | متن | — | مقدار اولیه |\n| hint | متن | — | راهنمای زیر فیلد |\n| error | متن | — | خطای دلخواه، مثلاً از اعتبارسنجی سرور |\n| required / disabled | بولی | — | مثل input معمولی |\n| placeholder | متن | — | متن خالی فیلد |\n| empty-message | متن | نتیجه‌ای پیدا نشد. | وقتی جستجو نتیجه ندارد |\n\nرویدادها و JavaScript:\n\n| نام | توضیح |\n|---|---|\n| kashi-change | detail: { value, option } |\n| kashi-input | با هر تایپ؛ detail: { query }، برای جستجوی سمت سرور |\n| element.options | گزینه‌ها با JavaScript: [{ value, label, description, group }] |"},{"slug":"components/command-palette","url":"https://kashi.sepantapouya.com/docs/components/command-palette/","title":"کامند پالت","description":"جستجوی سریع صفحه‌ها و عملیات با Ctrl+K، به سبک ابزارهای حرفه‌ای.","group":"کامپوننت‌ها","components":["CommandPalette"],"markdown":"```tsx\nimport { CommandPalette, type CommandItem } from \"@kashi-ui/react\";\n```\n\n## نمونه\n\n```tsx\nconst [open, setOpen] = useState(false);\nconst items: CommandItem[] = [\n { id: \"home\", label: \"داشبورد\", group: \"صفحه‌ها\", icon: <HomeIcon />, shortcut: \"G D\", href: \"/\" },\n { id: \"users\", label: \"کاربران\", group: \"صفحه‌ها\", keywords: [\"users\"], href: \"/users\" },\n { id: \"new\", label: \"پروژهٔ جدید\", group: \"عملیات\", shortcut: \"N\", onSelect: createProject },\n];\n\n<Button onClick={() => setOpen(true)}>جستجوی دستور <Kbd keys={[\"Ctrl\", \"K\"]} /></Button>\n<CommandPalette open={open} onOpenChange={setOpen} items={items} />\n```\n\nپالت را یک بار در ریشهٔ برنامه بگذارید. با `shortcut` (پیش‌فرض روشن) میان‌بر <kbd>Ctrl</kbd>+<kbd>K</kbd> (و <kbd>⌘</kbd>+<kbd>K</kbd> در مک) در کل صفحه فعال است.\n\n| ویژگی | نوع | پیش‌فرض | توضیح |\n|---|---|---|---|\n| open / onOpenChange | boolean / (open) => void | — | کنترل باز و بسته بودن |\n| items | { id, label, group?, icon?, shortcut?, description?, keywords?, href?, onSelect? }[] | — | فرمان‌ها؛ با href به آن آدرس می‌رود |\n| shortcut | boolean | true | میان‌بر سراسری Ctrl/⌘+K |\n| placeholder | string | «جستجوی صفحه یا فرمان…» | — |\n| emptyMessage | ReactNode | «نتیجه‌ای پیدا نشد.» | — |\n| labels | { title?, navigate?, select?, close?, results? } | — | متن‌های راهنما و صفحه‌خوان |\n\n## دسترس‌پذیری\n\nروی `<dialog>` بومی ساخته شده، پس فوکوس داخل پنجره می‌ماند و پشت آن غیرفعال است. فیلد جستجو `role=\"combobox\"` است و فهرست `listbox`؛ با <kbd>↑</kbd>/<kbd>↓</kbd> حرکت، <kbd>Enter</kbd> اجرا و <kbd>Esc</kbd> بسته می‌شود و فوکوس به دکمه‌ای که پالت را باز کرد برمی‌گردد.\n\n## راهنمای استفاده\n\nکِی استفاده کنیم:\n\n- پنل‌ها و ابزارهای پرکار که کاربر حرفه‌ای می‌خواهد بدون موس جابه‌جا شود.\n- جستجوی یک‌جای صفحه‌ها، تنظیمات و عمل‌ها.\n\nکِی استفاده نکنیم:\n\n- تنها راه رسیدن به یک عمل؛ همه‌چیز باید از منو و دکمه هم در دسترس باشد.\n- سایت محتوایی ساده؛ جستجوی معمولی کافی است.\n\nدرست و نادرست:\n\n- ✓ درست: عمل‌ها را با فعل بنویسید و گروه‌بندی کنید: «ساخت پروژهٔ تازه» در گروه «پروژه‌ها».\n- ✗ نادرست: فهرست بلند و بی‌گروه با نام‌های فنی.\n\n## وب کامپوننت\n\nهمین کامپوننت بدون React، برای HTML، وردپرس، Vue، Svelte و Angular: `<kashi-command-palette>` در `@kashi-ui/elements`. با Ctrl+K یا ⌘K، یا دکمه‌ای با `data-kashi-command`؛ لینک‌ها باز می‌شوند و دکمه‌ها رویداد `kashi-select` می‌فرستند.\n\nنصب بدون ابزار ساخت (CDN):\n\n```html\n<link rel=\"stylesheet\" href=\"https://cdn.jsdelivr.net/npm/@kashi-ui/css/dist/kashi.css\">\n<script type=\"module\" src=\"https://cdn.jsdelivr.net/npm/@kashi-ui/elements/dist/cdn/kashi-elements.js\"></script>\n```\n\nیا با npm، فقط همین بخش:\n\n```bash\nnpm i @kashi-ui/elements @kashi-ui/css\n```\n\n```js\nimport \"@kashi-ui/css\";\nimport \"@kashi-ui/elements/nav.js\";\n```\n\n```html\n<button class=\"k-btn k-btn--secondary\" data-kashi-command=\"palette\">جستجو <kbd class=\"k-kbd\">Ctrl K</kbd></button>\n<kashi-command-palette id=\"palette\">\n <a href=\"/docs/\" data-group=\"صفحه‌ها\">مستندات</a>\n <a href=\"/docs/web-components/\" data-group=\"صفحه‌ها\" data-keywords=\"html, vue\">وب کامپوننت‌ها</a>\n <button data-value=\"theme\" data-group=\"دستورها\" data-shortcut=\"⌘D\">تغییر تم</button>\n</kashi-command-palette>\n```\n\nویژگی‌ها:\n\n| نام | مقدار | پیش‌فرض | توضیح |\n|---|---|---|---|\n| label / placeholder | متن | پالت فرمان | عنوان و متن فیلد جستجو |\n| shortcut | false | — | بدون Ctrl+K |\n| data-group / data-description / data-shortcut / data-keywords | روی هر دستور | — | گروه، توضیح، میان‌بر و کلمه‌های جستجو |\n\nرویدادها و JavaScript:\n\n| نام | توضیح |\n|---|---|\n| kashi-select | detail: { value, item } |\n| element.show() / close() | باز و بستن |\n| element.items | دستورها با JavaScript |"},{"slug":"components/context-menu","url":"https://kashi.sepantapouya.com/docs/components/context-menu/","title":"منوی راست‌کلیک","description":"منوی میان‌بر روی فایل، ردیف جدول یا کارت؛ با موس و کیبورد.","group":"کامپوننت‌ها","components":["ContextMenu","MenuItem","MenuSeparator"],"markdown":"```tsx\nimport { ContextMenu, MenuItem, MenuSeparator } from \"@kashi-ui/react\";\n```\n\n## نمونه\n\n```tsx\n<ContextMenu label=\"عملیات فایل\" menu={\n <>\n <MenuItem onSelect={open}>باز کردن</MenuItem>\n <MenuItem shortcut=\"Ctrl+D\" onSelect={copy}>کپی</MenuItem>\n <MenuSeparator />\n <MenuItem tone=\"danger\" onSelect={remove}>حذف</MenuItem>\n </>\n}>\n <FileCard tabIndex={0} />\n</ContextMenu>\n```\n\nگزینه‌ها همان `MenuItem`، `MenuSeparator` و `MenuLabel` دراپ‌داون‌اند. منو کنار نشانگر باز می‌شود و از لبهٔ صفحه بیرون نمی‌رود.\n\n| ویژگی | نوع | پیش‌فرض | توضیح |\n|---|---|---|---|\n| children | ReactNode | — | ناحیهٔ راست‌کلیک |\n| menu | ReactNode | — | گزینه‌ها |\n| label | string | «گزینه‌ها» | نام منو |\n\n## دسترس‌پذیری\n\nراست‌کلیک برای کاربر کیبورد و صفحه‌خوان در دسترس نیست، پس منو با کلید ContextMenu یا <kbd>Shift</kbd>+<kbd>F10</kbd> روی عنصر فوکوس‌شده هم باز می‌شود و این راهنما به ناحیه وصل است. همهٔ عمل‌های منو باید از راه دیگری (دکمه یا منوی کشویی) هم در دسترس باشند.\n\n## راهنمای استفاده\n\nکِی استفاده کنیم:\n\n- عمل‌های یک مورد مشخص (فایل، ردیف جدول، کارت) برای کاربرانی که به راست‌کلیک عادت دارند.\n- ابزارهای حرفه‌ای مثل مدیر فایل یا ویرایشگر.\n\nکِی استفاده نکنیم:\n\n- تنها راه انجام یک عمل؛ راست‌کلیک در موبایل نیست و خیلی‌ها نمی‌دانندش. همان عمل‌ها را در منوی سه‌نقطه هم بگذارید. (دراپ‌داون: /docs/components/dropdown-menu/)\n\nدرست و نادرست:\n\n- ✓ درست: همان گزینه‌ها و همان ترتیب منوی سه‌نقطهٔ همان مورد.\n- ✗ نادرست: عمل‌هایی که فقط با راست‌کلیک پیدا می‌شوند.\n\n## وب کامپوننت\n\nهمین کامپوننت بدون React، برای HTML، وردپرس، Vue، Svelte و Angular: `<kashi-context-menu>` در `@kashi-ui/elements`. محتوا ناحیهٔ کلیک است و گزینه‌ها داخل `<div data-menu>`؛ با کیبورد، کلید ContextMenu یا Shift+F10.\n\nنصب بدون ابزار ساخت (CDN):\n\n```html\n<link rel=\"stylesheet\" href=\"https://cdn.jsdelivr.net/npm/@kashi-ui/css/dist/kashi.css\">\n<script type=\"module\" src=\"https://cdn.jsdelivr.net/npm/@kashi-ui/elements/dist/cdn/kashi-elements.js\"></script>\n```\n\nیا با npm، فقط همین بخش:\n\n```bash\nnpm i @kashi-ui/elements @kashi-ui/css\n```\n\n```js\nimport \"@kashi-ui/css\";\nimport \"@kashi-ui/elements/nav.js\";\n```\n\n```html\n<kashi-context-menu label=\"گزینه‌های فایل\">\n <div class=\"k-card\" tabindex=\"0\" style=\"padding: 2rem; text-align: center\">روی این کارت راست‌کلیک کنید</div>\n <div data-menu>\n <button data-value=\"rename\">تغییر نام</button>\n <button data-value=\"copy\" data-shortcut=\"⌘C\">کپی</button>\n <hr>\n <button data-value=\"delete\" data-tone=\"danger\">حذف</button>\n </div>\n</kashi-context-menu>\n```\n\nویژگی‌ها:\n\n| نام | مقدار | پیش‌فرض | توضیح |\n|---|---|---|---|\n| label | متن | گزینه‌ها | نام منو |\n\nرویدادها و JavaScript:\n\n| نام | توضیح |\n|---|---|\n| kashi-select | detail: { value } |"},{"slug":"components/copy-button","url":"https://kashi.sepantapouya.com/docs/components/copy-button/","title":"دکمهٔ کپی","description":"کپی متن در کلیپ‌بورد با بازخورد «کپی شد»، برای کلید API، شماره شبا یا کد تخفیف.","group":"کامپوننت‌ها","components":["CopyButton"],"markdown":"```tsx\nimport { CopyButton } from \"@kashi-ui/react\";\n```\n\n## نمونه\n\n```tsx\n<Input aria-label=\"کلید API\" readOnly value=\"sk_live_4f9a…b21c\" />\n<CopyButton value={apiKey} />\n<CopyButton value={iban} iconOnly labels={{ copy: \"کپی شماره شبا\" }} />\n```\n\n| ویژگی | نوع | پیش‌فرض | توضیح |\n|---|---|---|---|\n| value | string | — | متنی که کپی می‌شود |\n| onCopy | (value: string) => void | — | پس از کپی موفق |\n| iconOnly | boolean | false | فقط آیکون؛ نام از labels.copy |\n| timeout | number | 2000 | مدت نمایش «کپی شد» به میلی‌ثانیه |\n| labels | { copy?, copied?, failed? } | «کپی»، «کپی شد»، «کپی نشد» | — |\n| variant / size | ButtonProps | \"secondary\" / \"sm\" | همهٔ props دکمه |\n\n## دسترس‌پذیری\n\nنتیجه («کپی شد» یا «کپی نشد») با `aria-live` اعلام می‌شود، پس کاربر صفحه‌خوان هم بازخورد می‌گیرد. در حالت فقط‌آیکون، نام دکمه هم به «کپی شد» تغییر می‌کند. کلیپ‌بورد فقط در HTTPS یا localhost در دسترس است.\n\n## راهنمای استفاده\n\nکِی استفاده کنیم:\n\n- متنی که کاربر احتمالاً جای دیگری می‌چسباند: شمارهٔ شبا، کد تخفیف، کد پیگیری، کلید API.\n\nکِی استفاده نکنیم:\n\n- متن طولانی یا پاراگراف؛ کاربر خودش انتخابش می‌کند.\n\nدرست و نادرست:\n\n- ✓ درست: دکمه کنار همان متن، با بازخورد «کپی شد».\n- ✗ نادرست: دکمهٔ کپی دور از متن که معلوم نیست چه را کپی می‌کند.\n\n## وب کامپوننت\n\nهمین کامپوننت بدون React، برای HTML، وردپرس، Vue، Svelte و Angular: `<kashi-copy-button>` در `@kashi-ui/elements`.\n\nنصب بدون ابزار ساخت (CDN):\n\n```html\n<link rel=\"stylesheet\" href=\"https://cdn.jsdelivr.net/npm/@kashi-ui/css/dist/kashi.css\">\n<script type=\"module\" src=\"https://cdn.jsdelivr.net/npm/@kashi-ui/elements/dist/cdn/kashi-elements.js\"></script>\n```\n\nیا با npm، فقط همین بخش:\n\n```bash\nnpm i @kashi-ui/elements @kashi-ui/css\n```\n\n```js\nimport \"@kashi-ui/css\";\nimport \"@kashi-ui/elements/overlay.js\";\n```\n\n```html\n<kashi-copy-button value=\"IR820540102680020817909002\"></kashi-copy-button>\n<kashi-copy-button value=\"6037991234567893\" icon-only label=\"کپی شمارهٔ کارت\"></kashi-copy-button>\n```\n\nویژگی‌ها:\n\n| نام | مقدار | پیش‌فرض | توضیح |\n|---|---|---|---|\n| value | متن | — | متنی که کپی می‌شود |\n| target | انتخابگر CSS | — | یا کپی مقدار یا متن عنصری دیگر |\n| label / copied-label | متن | کپی / کپی شد | متن دکمه |\n| icon-only | بولی | — | فقط آیکون |\n| variant / size | — | secondary / sm | ظاهر دکمه |\n\nرویدادها و JavaScript:\n\n| نام | توضیح |\n|---|---|\n| kashi-copy | پس از کپی موفق؛ detail: { value } |"},{"slug":"components/dialog","url":"https://kashi.sepantapouya.com/docs/components/dialog/","title":"دیالوگ","description":"پنجره‌ای روی صفحه برای کاری کوتاه یا تأیید یک تصمیم، روی dialog بومی مرورگر.","group":"کامپوننت‌ها","components":["Dialog"],"markdown":"```tsx\nimport { Dialog } from \"@kashi-ui/react\";\n```\n\n## نمونه\n\n```tsx\nconst [open, setOpen] = useState(false);\n\n<Button onClick={() => setOpen(true)}>دعوت هم‌تیمی</Button>\n<Dialog\n open={open}\n onOpenChange={setOpen}\n title=\"دعوت به تیم\"\n description=\"ایمیل هم‌تیمی را وارد کنید تا دعوت‌نامه برایش ارسال شود.\"\n icon={<UserIcon />}\n footer={<>\n <Button variant=\"secondary\" onClick={() => setOpen(false)}>انصراف</Button>\n <Button variant=\"primary\">ارسال دعوت</Button>\n </>}\n>\n <Input label=\"ایمیل\" type=\"email\" autoFocus />\n</Dialog>\n\n// تأیید عملیات مخرب\n<Dialog tone=\"danger\" size=\"sm\" title=\"پروژه حذف شود؟\" … />\n```\n\n## کشوی کناری و برگهٔ پایین\n\nهمان دسترس‌پذیری دیالوگ (تلهٔ فوکوس، Esc، برگشت فوکوس) با چیدمان کشو؛ برگهٔ پایین برای گوشی مناسب است.\n\n```tsx\n<Dialog placement=\"end\" open={open} onOpenChange={setOpen} title=\"فیلترها\" footer={…}>\n …\n</Dialog>\n\n<Dialog placement=\"bottom\" …>…</Dialog>\n```\n\n**ویژگی‌های جدید**\n\n| ویژگی | نوع | پیش‌فرض | توضیح |\n|---|---|---|---|\n| placement | \"center\" \\| \"start\" \\| \"end\" \\| \"bottom\" | \"center\" | جایگاه؛ در RTL «start» سمت راست است |\n| size | \"sm\" \\| \"md\" \\| \"lg\" \\| \"full\" | \"md\" | اندازهٔ full اضافه شد |\n\nمتغیرهای CSS: `--k-drawer-width` (پیش‌فرض ۲۶rem) و `--k-dialog-backdrop`.\n\n## ویژگی‌ها\n\n| ویژگی | نوع | پیش‌فرض | توضیح |\n|---|---|---|---|\n| open | boolean | — | باز بودن (کنترل‌شده) |\n| onOpenChange | (open: boolean) => void | — | با Esc، دکمهٔ بستن یا کلیک روی پس‌زمینه |\n| title | ReactNode | — | عنوان — الزامی |\n| description | ReactNode | — | توضیح زیر عنوان |\n| icon | ReactNode | — | آیکون شاخص |\n| tone | \"default\" \\| \"danger\" | \"default\" | رنگ آیکون برای عملیات مخرب |\n| size | \"sm\" \\| \"md\" \\| \"lg\" | \"md\" | عرض: ۲۴، ۳۰ یا ۴۲ rem |\n| footer | ReactNode | — | دکمه‌ها |\n| dismissible | boolean | true | بستن با کلیک روی پس‌زمینه |\n| closeLabel | string | \"بستن\" | نام دکمهٔ بستن |\n\n## دسترس‌پذیری\n\n- روی `<dialog>` بومی با `showModal()` ساخته شده: فوکوس داخل پنجره می‌ماند، بقیهٔ صفحه برای کیبورد و صفحه‌خوان غیرفعال می‌شود و <kbd>Esc</kbd> پنجره را می‌بندد.\n- پس از بستن، فوکوس به دکمه‌ای که پنجره را باز کرده برمی‌گردد.\n- عنوان و توضیح با `aria-labelledby` و `aria-describedby` به پنجره متصل‌اند.\n- برای تأیید عملیات مخرب، دکمهٔ «انصراف» را اول بگذارید و نتیجهٔ کار را در توضیح صریح بنویسید.\n\n## راهنمای استفاده\n\nکِی استفاده کنیم:\n\n- تأیید یک تصمیم مهم یا برگشت‌ناپذیر: حذف، خروج بدون ذخیره.\n- کاری کوتاه که نباید کاربر را از صفحه دور کند: ویرایش یک مورد، افزودن نشانی.\n\nکِی استفاده نکنیم:\n\n- فرم یا محتوای طولانی؛ صفحهٔ جدا بسازید یا کشوی کناری (`placement`).\n- پیام اطلاع‌رسانی که تصمیمی نمی‌خواهد. (توست: /docs/components/toast/)\n- گفتگو روی گفتگو.\n\nدرست و نادرست:\n\n- ✓ درست: عنوان پرسش است و دکمهٔ اصلی همان فعل: «حذف این سفارش؟» با دکمه‌های «انصراف» و «حذف سفارش».\n- ✗ نادرست: «آیا مطمئن هستید؟» با «بله» و «خیر»؛ دکمه‌ها بدون خواندن پرسش معنا ندارند.\n\nمتن:\n\n- در متن گفتگوی حذف بگویید دقیقاً چه چیزی از بین می‌رود و آیا برگشت دارد.\n\n## وب کامپوننت\n\nهمین کامپوننت بدون React، برای HTML، وردپرس، Vue، Svelte و Angular: `<kashi-dialog>` در `@kashi-ui/elements`. دکمه با `data-kashi-open` پنجره را باز می‌کند و `data-kashi-close` داخل پنجره آن را می‌بندد؛ بدون JavaScript.\n\nنصب بدون ابزار ساخت (CDN):\n\n```html\n<link rel=\"stylesheet\" href=\"https://cdn.jsdelivr.net/npm/@kashi-ui/css/dist/kashi.css\">\n<script type=\"module\" src=\"https://cdn.jsdelivr.net/npm/@kashi-ui/elements/dist/cdn/kashi-elements.js\"></script>\n```\n\nیا با npm، فقط همین بخش:\n\n```bash\nnpm i @kashi-ui/elements @kashi-ui/css\n```\n\n```js\nimport \"@kashi-ui/css\";\nimport \"@kashi-ui/elements/overlay.js\";\n```\n\n```html\n<button class=\"k-btn k-btn--danger\" data-kashi-open=\"page-dialog\">حذف فایل</button>\n<kashi-dialog id=\"page-dialog\" heading=\"فایل حذف شود؟\" description=\"این کار قابل بازگشت نیست.\" tone=\"danger\" size=\"sm\">\n <div slot=\"footer\">\n <button class=\"k-btn k-btn--secondary\" data-kashi-close>انصراف</button>\n <button class=\"k-btn k-btn--danger\" data-kashi-close=\"delete\">حذف</button>\n </div>\n</kashi-dialog>\n```\n\nویژگی‌ها:\n\n| نام | مقدار | پیش‌فرض | توضیح |\n|---|---|---|---|\n| heading / description | متن | — | عنوان و توضیح پنجره |\n| tone | danger | — | برای عملیات مخرب |\n| size | sm \\| md \\| lg \\| full | md | اندازه |\n| placement | start \\| end \\| bottom | center | کشوی کناری یا برگهٔ پایین |\n| open | بولی | — | باز بودن |\n| dismissible | false | — | بسته نشدن با کلیک روی پس‌زمینه |\n\nرویدادها و JavaScript:\n\n| نام | توضیح |\n|---|---|\n| element.show() / close() | باز و بستن |\n| kashi-open / kashi-close | detail.returnValue: مقدار data-kashi-close دکمه‌ای که پنجره را بست |"},{"slug":"components/divider","url":"https://kashi.sepantapouya.com/docs/components/divider/","title":"جداکننده و کلید میان‌بر","description":"خط جداکننده با متن اختیاری، و نمایش کلیدهای صفحه‌کلید.","group":"کامپوننت‌ها","components":["Divider","Kbd"],"markdown":"```tsx\nimport { Divider, Kbd } from \"@kashi-ui/react\";\n```\n\n## جداکننده\n\n```tsx\n<Divider />\n<Divider label=\"یا\" />\n```\n\n## کلید صفحه‌کلید\n\n```tsx\n<Kbd keys={[\"Ctrl\", \"K\"]} />\n<Kbd>Esc</Kbd>\n```\n\n**ویژگی‌های Divider و Kbd**\n\n| ویژگی | نوع | پیش‌فرض | توضیح |\n|---|---|---|---|\n| label | ReactNode | — | متن وسط خط (Divider) |\n| vertical | boolean | false | جداکنندهٔ عمودی (Divider) |\n| keys | string[] | — | ترکیب کلیدها (Kbd) |\n\nکلیدها همیشه چپ‌به‌راست نمایش داده می‌شوند تا ترتیب «Ctrl + K» در متن فارسی برعکس نشود.\n\n## راهنمای استفاده\n\nکِی استفاده کنیم:\n\n- جدا کردن بخش‌های یک کارت یا منو.\n- «یا» بین دو راه، مثل ورود با رمز یا با کد یک‌بارمصرف.\n- نمایش میان‌بر صفحه‌کلید با `Kbd`.\n\nکِی استفاده نکنیم:\n\n- جدا کردن هر دو خط محتوا؛ فاصلهٔ خالی معمولاً کافی و آرام‌تر است.\n\nدرست و نادرست:\n\n- ✓ درست: یک جداکننده بین گروه‌های واقعاً جدا.\n- ✗ نادرست: خط زیر هر ردیف فرم که صفحه را خط‌خطی می‌کند."},{"slug":"components/dropdown-menu","url":"https://kashi.sepantapouya.com/docs/components/dropdown-menu/","title":"دراپ‌داون","description":"فهرستی از عمل‌ها که با یک دکمه باز می‌شود؛ با الگوی Menu Button و پیمایش کامل کیبورد.","group":"کامپوننت‌ها","components":["DropdownMenu","MenuItem","MenuSeparator","MenuLabel"],"markdown":"```tsx\nimport { DropdownMenu, MenuItem, MenuSeparator, MenuLabel } from \"@kashi-ui/react\";\n```\n\n## نمونه\n\n```tsx\n<DropdownMenu trigger={<Button>گزینه‌ها</Button>}>\n <MenuLabel>پروژه</MenuLabel>\n <MenuItem icon={<EditIcon />} shortcut=\"Ctrl+E\" onSelect={edit}>ویرایش</MenuItem>\n <MenuItem icon={<CopyIcon />} shortcut=\"Ctrl+D\" onSelect={copy}>کپی</MenuItem>\n <MenuItem icon={<ArchiveIcon />} disabled>بایگانی</MenuItem>\n <MenuSeparator />\n <MenuItem icon={<TrashIcon />} tone=\"danger\" onSelect={remove}>حذف</MenuItem>\n</DropdownMenu>\n\n<DropdownMenu align=\"end\" label=\"عملیات ردیف\"\n trigger={<Button variant=\"tertiary\" iconOnly aria-label=\"عملیات بیشتر\"><DotsIcon /></Button>}>\n …\n</DropdownMenu>\n```\n\n## کیبورد\n\n| کلید | عمل |\n|---|---|\n| <kbd>Enter</kbd> / <kbd>Space</kbd> / <kbd>↓</kbd> | باز کردن و رفتن به اولین گزینه |\n| <kbd>↑</kbd> روی دکمه | باز کردن و رفتن به آخرین گزینه |\n| <kbd>↑</kbd> / <kbd>↓</kbd> | گزینهٔ قبل / بعد (گزینه‌های غیرفعال رد می‌شوند) |\n| <kbd>Home</kbd> / <kbd>End</kbd> | اولین / آخرین گزینه |\n| حرف | پرش به گزینه‌ای که با آن حرف شروع می‌شود |\n| <kbd>Esc</kbd> | بستن و برگشت به دکمه |\n| <kbd>Tab</kbd> | بستن و رفتن به عنصر بعدی صفحه |\n\n## گزینه‌های تیک‌دار\n\nبرای تنظیماتی مثل نمایش ستون‌ها؛ منو پس از انتخاب باز می‌ماند تا چند گزینه پشت سر هم عوض شوند.\n\n```tsx\n<MenuItem checked={cols.date} onSelect={() => toggle(\"date\")}>تاریخ</MenuItem>\n```\n\n`checked` نقش گزینه را `menuitemcheckbox` با `aria-checked` می‌کند. متغیرهای CSS: `--k-menu-min-width`، `--k-menu-radius`، `--k-menu-item-height`، `--k-menu-bg`.\n\n## ویژگی‌ها\n\n| ویژگی | نوع | پیش‌فرض | توضیح |\n|---|---|---|---|\n| trigger | ReactElement | — | دکمهٔ بازکننده (DropdownMenu) |\n| label | string | متن دکمه | نام منو برای صفحه‌خوان |\n| align | \"start\" \\| \"end\" | \"start\" | هم‌ترازی با دکمه |\n| onSelect | () => void | — | با انتخاب گزینه؛ منو بسته می‌شود (MenuItem) |\n| icon / shortcut | ReactNode / string | — | آیکون و میان‌بر نمایشی |\n| tone | \"default\" \\| \"danger\" | \"default\" | رنگ قرمز برای عملیات مخرب |\n| href | string | — | گزینهٔ لینکی |\n| disabled | boolean | false | غیرفعال؛ در پیمایش رد می‌شود |\n\n## دسترس‌پذیری\n\n- دکمه `aria-haspopup=\"menu\"` و `aria-expanded` دارد؛ منو `role=\"menu\"` با گزینه‌های `menuitem`.\n- فوکوس داخل منو با کلیدهای جهت‌دار جابه‌جا می‌شود، نه Tab؛ پس از انتخاب یا Esc، فوکوس به دکمه برمی‌گردد.\n- منو برای **عمل‌ها** است. برای انتخاب یک مقدار در فرم از [سلکت](/docs/components/select/) استفاده کنید.\n\n## راهنمای استفاده\n\nکِی استفاده کنیم:\n\n- عمل‌های کم‌کاربرد یک مورد، پشت دکمهٔ سه‌نقطه: ویرایش، کپی، حذف.\n- منوی حساب کاربری.\n\nکِی استفاده نکنیم:\n\n- انتخاب یک مقدار برای فرم. (سلکت: /docs/components/select/)\n- عمل اصلی صفحه؛ آن را به‌شکل دکمه نشان دهید. (دکمه: /docs/components/button/)\n- پیوندهای ناوبری اصلی سایت. (سایدبار: /docs/components/side-nav/)\n\nدرست و نادرست:\n\n- ✓ درست: گزینهٔ خطرناک آخر فهرست، جدا با خط و به رنگ خطر.\n- ✗ نادرست: «حذف» بین «ویرایش» و «کپی»، جایی که با یک لغزش زده می‌شود.\n\n## وب کامپوننت\n\nهمین کامپوننت بدون React، برای HTML، وردپرس، Vue، Svelte و Angular: `<kashi-dropdown-menu>` در `@kashi-ui/elements`. اولین فرزند دکمهٔ بازکننده است؛ بقیه گزینه‌ها: `<button>`، `<a href>`، `<hr>` برای جداکننده و عنصر `data-label` برای عنوان گروه.\n\nنصب بدون ابزار ساخت (CDN):\n\n```html\n<link rel=\"stylesheet\" href=\"https://cdn.jsdelivr.net/npm/@kashi-ui/css/dist/kashi.css\">\n<script type=\"module\" src=\"https://cdn.jsdelivr.net/npm/@kashi-ui/elements/dist/cdn/kashi-elements.js\"></script>\n```\n\nیا با npm، فقط همین بخش:\n\n```bash\nnpm i @kashi-ui/elements @kashi-ui/css\n```\n\n```js\nimport \"@kashi-ui/css\";\nimport \"@kashi-ui/elements/menu.js\";\n```\n\n```html\n<kashi-dropdown-menu label=\"عملیات\">\n <button class=\"k-btn k-btn--secondary\">بیشتر</button>\n <button data-value=\"edit\" data-shortcut=\"⌘E\">ویرایش</button>\n <button data-value=\"pin\" data-checked=\"false\">سنجاق کردن</button>\n <hr>\n <button data-value=\"delete\" data-tone=\"danger\">حذف</button>\n</kashi-dropdown-menu>\n```\n\nویژگی‌ها:\n\n| نام | مقدار | پیش‌فرض | توضیح |\n|---|---|---|---|\n| label | متن | — | نام منو برای صفحه‌خوان |\n| align | end | — | چسبیدن منو به سمت دیگر دکمه |\n| data-value / data-shortcut | روی گزینه | — | مقدار و میان‌بر نمایشی |\n| data-tone / data-checked / disabled | danger / true \\| false | — | گزینهٔ خطرناک، تیک‌دار یا غیرفعال |\n\nرویدادها و JavaScript:\n\n| نام | توضیح |\n|---|---|\n| kashi-select | detail: { value, checked } |"},{"slug":"components/empty-state","url":"https://kashi.sepantapouya.com/docs/components/empty-state/","title":"حالت خالی","description":"وقتی داده‌ای برای نمایش نیست؛ توضیح بدهید چرا و قدم بعدی را پیشنهاد کنید.","group":"کامپوننت‌ها","components":["EmptyState"],"markdown":"```tsx\nimport { EmptyState } from \"@kashi-ui/react\";\n```\n\n## نمونه\n\n```tsx\n<EmptyState\n icon={<FolderIcon />}\n title=\"هنوز پروژه‌ای ندارید\"\n description=\"اولین پروژه را بسازید یا از یکی از قالب‌های آماده شروع کنید.\"\n actions={<>\n <Button variant=\"secondary\">مشاهدهٔ قالب‌ها</Button>\n <Button variant=\"primary\">پروژهٔ جدید</Button>\n </>}\n/>\n```\n\n| ویژگی | نوع | پیش‌فرض | توضیح |\n|---|---|---|---|\n| title | ReactNode | — | عنوان — الزامی |\n| description | ReactNode | — | توضیح و راهنمایی |\n| icon | ReactNode | — | آیکون |\n| actions | ReactNode | — | دکمه‌ها |\n\nداخل [جدول داده](/docs/components/table/) هم می‌توانید آن را به‌عنوان `empty` بدهید.\n\n## راهنمای استفاده\n\nکِی استفاده کنیم:\n\n- فهرست، جدول یا صفحه‌ای که هنوز داده‌ای ندارد.\n- نتیجهٔ خالی جستجو یا فیلتر.\n\nکِی استفاده نکنیم:\n\n- حالت بارگذاری. (اسکلتون: /docs/components/skeleton/)\n- خطای دریافت داده؛ آن را با هشدار و دکمهٔ تلاش دوباره نشان دهید. (هشدار: /docs/components/alert/)\n\nدرست و نادرست:\n\n- ✓ درست: بگویید چرا خالی است و قدم بعدی را پیشنهاد کنید.\n- ✗ نادرست: پیام فنی بدون راه بعدی."},{"slug":"components/file-upload","url":"https://kashi.sepantapouya.com/docs/components/file-upload/","title":"آپلود فایل","description":"کشیدن و رها کردن یا انتخاب فایل، با بررسی نوع و حجم و نوار پیشرفت هر فایل.","group":"کامپوننت‌ها","components":["FileUpload"],"markdown":"```tsx\nimport { FileUpload } from \"@kashi-ui/react\";\n```\n\n## نمونه\n\nفایلی که نامش «خطا» دارد، در این نمونه با شکست روبه‌رو می‌شود.\n\n```tsx\n<FileUpload\n accept=\"image/png,image/jpeg,application/pdf\"\n maxSize={5 * 1024 * 1024}\n maxFiles={4}\n hint=\"PNG، JPG یا PDF؛ هر فایل تا ۵ مگابایت\"\n uploader={(file, onProgress) => upload(file, { onProgress })}\n/>\n```\n\n`uploader` هر فایل را می‌گیرد و یک Promise برمی‌گرداند؛ با `onProgress(درصد)` نوار پیشرفت به‌روز می‌شود. بدون `uploader`، فایل‌ها فقط فهرست می‌شوند و با `onFilesChange` به شما می‌رسند (یا با `name` در فرم ارسال می‌شوند).\n\n| ویژگی | نوع | پیش‌فرض | توضیح |\n|---|---|---|---|\n| accept | string | — | مثل input بومی |\n| multiple | boolean | true | — |\n| maxSize | number | — | بیشترین حجم هر فایل به بایت |\n| maxFiles | number | — | بیشترین تعداد |\n| uploader | (file, onProgress) => Promise<void> | — | بارگذاری هر فایل |\n| onFilesChange | (items: FileItem[]) => void | — | وضعیت همهٔ فایل‌ها |\n| hint | ReactNode | — | متن راهنما داخل ناحیه |\n| labels | { browse?, drop?, tooBig?, wrongType?, tooMany?, … } | — | متن‌ها |\n\nتابع `formatBytes` حجم را فارسی می‌نویسد: `formatBytes(1536)` ← «۱٫۵ کیلوبایت».\n\n## دسترس‌پذیری\n\nناحیهٔ رها کردن یک دکمهٔ واقعی «انتخاب فایل» دارد، پس بدون موس هم کار می‌کند. هر فایل نام، حجم و وضعیتش را با متن نشان می‌دهد (نه فقط رنگ) و دکمهٔ حذف آن «حذف نام‌فایل» نام دارد. پایان بارگذاری و خطاها با `aria-live` اعلام می‌شوند.\n\n## راهنمای استفاده\n\nکِی استفاده کنیم:\n\n- بارگذاری مدرک، عکس محصول، فایل اکسل یا رزومه.\n- چند فایل با نوار پیشرفت هر کدام.\n\nکِی استفاده نکنیم:\n\n- متنی که کاربر می‌تواند تایپ کند؛ فرم را با فایل سخت‌تر نکنید.\n\nدرست و نادرست:\n\n- ✓ درست: نوع و حجم مجاز را از قبل بگویید و با `accept` و `maxSize` بررسی کنید: «PDF یا JPG، حداکثر ۵ مگابایت».\n- ✗ نادرست: بعد از بارگذاری کامل یک فایل ۴۰ مگابایتی بگویید «فرمت پشتیبانی نمی‌شود».\n\n## وب کامپوننت\n\nهمین کامپوننت بدون React، برای HTML، وردپرس، Vue، Svelte و Angular: `<kashi-file-upload>` در `@kashi-ui/elements`. با `name` فایل‌ها همراه فرم HTML ارسال می‌شوند (فرم با `enctype=\"multipart/form-data\"`). برای بارگذاری فوری `element.uploader` بدهید.\n\nنصب بدون ابزار ساخت (CDN):\n\n```html\n<link rel=\"stylesheet\" href=\"https://cdn.jsdelivr.net/npm/@kashi-ui/css/dist/kashi.css\">\n<script type=\"module\" src=\"https://cdn.jsdelivr.net/npm/@kashi-ui/elements/dist/cdn/kashi-elements.js\"></script>\n```\n\nیا با npm، فقط همین بخش:\n\n```bash\nnpm i @kashi-ui/elements @kashi-ui/css\n```\n\n```js\nimport \"@kashi-ui/css\";\nimport \"@kashi-ui/elements/pickers.js\";\n```\n\n```html\n<kashi-file-upload name=\"documents\" accept=\".pdf,image/*\" max-size=\"5242880\" hint=\"PDF یا تصویر، حداکثر ۵ مگابایت\"></kashi-file-upload>\n```\n\nویژگی‌ها:\n\n| نام | مقدار | پیش‌فرض | توضیح |\n|---|---|---|---|\n| accept | .pdf,image/* | — | نوع فایل‌های پذیرفته |\n| multiple | false | — | فقط یک فایل |\n| max-size / max-files | بایت / عدد | — | بیشترین حجم و تعداد |\n| name / hint / label / disabled | — | — | نام فرم، راهنما و متن دکمه |\n\nرویدادها و JavaScript:\n\n| نام | توضیح |\n|---|---|\n| element.uploader | (file, onProgress) => Promise؛ درصد پیشرفت را با onProgress بدهید |\n| kashi-change | detail: { files } |\n| element.files | فایل‌های پذیرفته‌شده |"},{"slug":"components/inline-edit","url":"https://kashi.sepantapouya.com/docs/components/inline-edit/","title":"ویرایش درجا","description":"ویرایش متن در همان جا، مثل نام پروژه یا عنوان کارت، بدون فرم جداگانه.","group":"کامپوننت‌ها","components":["InlineEdit"],"markdown":"```tsx\nimport { InlineEdit } from \"@kashi-ui/react\";\n```\n\n## نمونه\n\n```tsx\n<InlineEdit label=\"نام پروژه\" defaultValue=\"بازطراحی فروشگاه\"\n validate={(v) => (v ? undefined : \"نام پروژه نمی‌تواند خالی باشد.\")}\n onSave={(v) => api.rename(v)} />\n```\n\nاگر `onSave` یک Promise برگرداند، دکمهٔ ثبت تا پایانش در حالت بارگذاری می‌ماند و در صورت خطا متن قبلی برمی‌گردد.\n\n| ویژگی | نوع | پیش‌فرض | توضیح |\n|---|---|---|---|\n| label | string | — | نام فیلد برای صفحه‌خوان (الزامی) |\n| value / defaultValue | string | \"\" | — |\n| onSave | (value) => void \\| Promise | — | پس از ثبت |\n| validate | (value) => string \\| undefined | — | پیام خطا |\n| renderView | (value) => ReactNode | — | ظاهر حالت نمایش، مثلاً عنوان |\n| placeholder | string | «خالی» | — |\n\n## کیبورد\n\n| کلید | عمل |\n|---|---|\n| <kbd>Enter</kbd> روی متن | شروع ویرایش |\n| <kbd>Enter</kbd> در فیلد | ثبت |\n| <kbd>Esc</kbd> | انصراف و برگشت به متن |\n\n## دسترس‌پذیری\n\nحالت نمایش یک دکمه با نام کامل است («نام پروژه: بازطراحی فروشگاه، ویرایش»). پس از ثبت یا انصراف، فوکوس به همان دکمه برمی‌گردد.\n\n## راهنمای استفاده\n\nکِی استفاده کنیم:\n\n- تغییر یک مقدار کوتاه در همان جا: نام پروژه، عنوان کارت، برچسب.\n- جایی که ویرایش گاه‌به‌گاه است و فرم جدا سنگین است.\n\nکِی استفاده نکنیم:\n\n- چند فیلد که با هم ثبت می‌شوند؛ فرم یا گفتگو بسازید. (دیالوگ: /docs/components/dialog/)\n- مقدار مهم یا حساس مثل مبلغ پرداخت.\n\nدرست و نادرست:\n\n- ✓ درست: ذخیره با Enter یا دکمهٔ تیک، انصراف با Escape؛ اگر ذخیره شکست خورد پیام بدهید و متن کاربر را نگه دارید.\n- ✗ نادرست: ذخیرهٔ بی‌صدا که اگر نشد، متن کاربر هم از دست برود.\n\n## وب کامپوننت\n\nهمین کامپوننت بدون React، برای HTML، وردپرس، Vue، Svelte و Angular: `<kashi-inline-edit>` در `@kashi-ui/elements`.\n\nنصب بدون ابزار ساخت (CDN):\n\n```html\n<link rel=\"stylesheet\" href=\"https://cdn.jsdelivr.net/npm/@kashi-ui/css/dist/kashi.css\">\n<script type=\"module\" src=\"https://cdn.jsdelivr.net/npm/@kashi-ui/elements/dist/cdn/kashi-elements.js\"></script>\n```\n\nیا با npm، فقط همین بخش:\n\n```bash\nnpm i @kashi-ui/elements @kashi-ui/css\n```\n\n```js\nimport \"@kashi-ui/css\";\nimport \"@kashi-ui/elements/forms.js\";\n```\n\n```html\n<kashi-inline-edit label=\"نام پروژه\" value=\"فروشگاه کاشی\"></kashi-inline-edit>\n```\n\nویژگی‌ها:\n\n| نام | مقدار | پیش‌فرض | توضیح |\n|---|---|---|---|\n| value | متن | — | متن فعلی |\n| label | متن | متن | نام برای صفحه‌خوان |\n| placeholder | متن | خالی | وقتی متن خالی است |\n\nرویدادها و JavaScript:\n\n| نام | توضیح |\n|---|---|\n| kashi-save | پیش از ثبت؛ detail: { value, wait }. برای ذخیرهٔ سمت سرور detail.wait(fetch(…)) را صدا بزنید؛ اگر رد شود، خطا نشان داده می‌شود |\n| kashi-change | پس از ثبت؛ detail: { value } |\n| element.validate | تابع اعتبارسنجی؛ پیام خطا برمی‌گرداند |"},{"slug":"components/input","url":"https://kashi.sepantapouya.com/docs/components/input/","title":"ورودی","description":"فیلد متن تک‌خطی و چندخطی، با برچسب، متن راهنما، پیام خطا و افزونه.","group":"کامپوننت‌ها","components":["Input","Textarea"],"markdown":"```tsx\nimport { Input, Textarea } from \"@kashi-ui/react\";\n```\n\n## پایه\n\nبرچسب، متن راهنما و فیلد به‌صورت خودکار با `id` و `aria-describedby` به هم وصل می‌شوند.\n\n```tsx\n<Input label=\"نام و نام خانوادگی\" placeholder=\"مثلاً سارا احمدی\" hint=\"همان‌طور که در کارت ملی آمده است.\" required />\n```\n\n## خطا\n\nبا دادن `error`، فیلد `aria-invalid` می‌شود و پیام خطا پیش از متن راهنما خوانده می‌شود.\n\n```tsx\n<Input label=\"ایمیل\" type=\"email\" defaultValue=\"sara@example\" error=\"آدرس ایمیل معتبر نیست.\" />\n```\n\n## افزونه‌ها\n\nایمیل، تلفن، آدرس اینترنتی، عدد و رمز عبور به‌صورت خودکار چپ‌به‌راست تایپ می‌شوند، ولی متن راهنمای داخل فیلد راست‌چین می‌ماند.\n\n```tsx\n<Input label=\"ایمیل\" type=\"email\" placeholder=\"you@example.com\" addonStart={<MailIcon />} />\n<Input label=\"مبلغ\" inputMode=\"numeric\" placeholder=\"۰\" addonEnd=\"تومان\" />\n<Input aria-label=\"جستجو\" placeholder=\"جستجو…\" addonStart={<SearchIcon />} size=\"sm\" />\n```\n\n## چندخطی\n\n```tsx\n<Textarea label=\"توضیحات\" placeholder=\"چند خط بنویسید…\" hint=\"حداکثر ۵۰۰ نویسه.\" />\n```\n\n## غیرفعال\n\n```tsx\n<Input label=\"کد ملی\" defaultValue=\"۰۰۱۲۳۴۵۶۷۸\" disabled />\n```\n\n## رمز، پاک کردن، شمارنده و بیشتر\n\n```tsx\n<Input label=\"رمز عبور\" type=\"password\" labelEnd={<a href=\"/forgot\">فراموشی رمز؟</a>} />\n<Input aria-label=\"جستجو\" addonStart={<SearchIcon />} clearable />\n<Input label=\"نام کاربری\" loading hint=\"در حال بررسی…\" />\n<Input label=\"نام شرکت\" optional />\n<Textarea label=\"دربارهٔ شما\" autoResize showCount maxLength={280} />\n```\n\n**ویژگی‌های جدید**\n\n| ویژگی | نوع | پیش‌فرض | توضیح |\n|---|---|---|---|\n| type=\"password\" | — | — | دکمهٔ نمایش/پنهان کردن رمز خودکار اضافه می‌شود؛ با revealable={false} خاموش |\n| clearable / onClear | boolean / () => void | — | دکمهٔ پاک کردن وقتی فیلد مقدار دارد |\n| loading | boolean | false | اسپینر در انتهای فیلد |\n| showCount | boolean | false | شمارندهٔ نویسه؛ با maxLength به شکل «۱۲ / ۲۸۰» |\n| optional | boolean \\| string | — | نشان «(اختیاری)» کنار برچسب |\n| labelEnd | ReactNode | — | محتوای انتهای ردیف برچسب |\n| autoResize / maxRows | boolean / number | — | ارتفاع خودکار Textarea |\n| classNames | { root, label, box, control, addon, hint, error } | — | کلاس دلخواه برای هر بخش داخلی |\n| labels | { clear, show, hide, loading } | فارسی | متن‌های صفحه‌خوان |\n\n## ویژگی‌ها\n\n| ویژگی | نوع | پیش‌فرض | توضیح |\n|---|---|---|---|\n| label | ReactNode | — | برچسب بالای فیلد؛ اگر نمی‌دهید aria-label بدهید |\n| hint | ReactNode | — | متن راهنمای زیر فیلد |\n| error | ReactNode | — | پیام خطا؛ فیلد را aria-invalid می‌کند |\n| size | \"sm\" \\| \"md\" \\| \"lg\" | \"md\" | ارتفاع‌ها ۴۰، ۴۴ و ۴۸ پیکسل (فقط Input) |\n| addonStart | ReactNode | — | آیکون یا متن ابتدای فیلد (فقط Input) |\n| addonEnd | ReactNode | — | آیکون یا متن انتهای فیلد (فقط Input) |\n| fieldClassName | string | — | کلاس ظرف بیرونی k-field |\n| required | boolean | false | ستاره کنار برچسب و ویژگی required |\n\nسایر ویژگی‌های `<input>` و `<textarea>` به خود فیلد منتقل می‌شوند. `ref` به عنصر فیلد اشاره می‌کند.\n\n## متغیرهای CSS\n\n| متغیر CSS | پیش‌فرض | توضیح |\n|---|---|---|\n| --k-input-height-{sm,md,lg} | 2.25rem تا 2.75rem × density | ارتفاع؛ هم‌قد دکمه‌های sm، md و lg |\n| --k-input-radius | var(--k-radius-md) | گردی گوشه‌ها |\n| --k-input-bg | var(--k-bg-primary) | پس‌زمینه |\n| --k-input-border | var(--k-border-input) | رنگ حاشیه در حالت عادی |\n| --k-field-gap | var(--k-space-1_5) | فاصلهٔ برچسب، فیلد و راهنما |\n\n## دسترس‌پذیری\n\n- حاشیهٔ فیلد کنتراست حداقل ۳ به ۱ دارد تا مرز آن برای کاربران کم‌بینا مشخص باشد.\n- متن راهنمای داخل فیلد (placeholder) جایگزین برچسب نیست؛ با تایپ کاربر ناپدید می‌شود.\n- پیام خطا باید بگوید چه اشتباهی رخ داده و چطور درست شود، نه فقط «نامعتبر».\n\n## راهنمای استفاده\n\nکِی استفاده کنیم:\n\n- متن کوتاه یک‌خطی: نام، ایمیل، عنوان. با `Textarea` برای متن چندخطی.\n- هر ورودی فرم که برچسب، راهنما و خطای خودش را لازم دارد.\n\nکِی استفاده نکنیم:\n\n- کد ملی، موبایل، کارت، شبا، مبلغ و کد پستی. (فیلد فارسی: /docs/persian/input/)\n- تاریخ. (دیت‌پیکر شمسی: /docs/persian/date-picker/)\n- عدد با گام مشخص. (ورودی عدد: /docs/components/number-input/)\n\nدرست و نادرست:\n\n- ✓ درست: برچسب بالای فیلد و همیشه دیدنی؛ مثال در `placeholder` یا راهنما.\n- ✗ نادرست: فقط `placeholder` به‌جای برچسب؛ با شروع تایپ ناپدید می‌شود و کاربر یادش می‌رود فیلد چه بود.\n- ✓ درست: خطا کنار همان فیلد، با علت و راه درست کردن.\n- ✗ نادرست: خطای کلی و سرزنش‌گر.\n\nمتن:\n\n- برچسب اسم است، نه دستور: «ایمیل»، نه «ایمیل خود را وارد کنید».\n- فیلد اختیاری را با `optional` نشان دهید، نه همهٔ فیلدهای اجباری را با ستاره، اگر بیشترشان اجباری‌اند.\n\n## وب کامپوننت\n\nهمین کامپوننت بدون React، برای HTML، وردپرس، Vue، Svelte و Angular: `<kashi-input>`، `<kashi-textarea>` در `@kashi-ui/elements`. `input` و `textarea` واقعی داخل عنصرند و با `name` مستقیم در فرم ارسال می‌شوند؛ ویژگی‌هایی مثل `type`، `maxlength` و `autocomplete` همان‌جا می‌روند.\n\nنصب بدون ابزار ساخت (CDN):\n\n```html\n<link rel=\"stylesheet\" href=\"https://cdn.jsdelivr.net/npm/@kashi-ui/css/dist/kashi.css\">\n<script type=\"module\" src=\"https://cdn.jsdelivr.net/npm/@kashi-ui/elements/dist/cdn/kashi-elements.js\"></script>\n```\n\nیا با npm، فقط همین بخش:\n\n```bash\nnpm i @kashi-ui/elements @kashi-ui/css\n```\n\n```js\nimport \"@kashi-ui/css\";\nimport \"@kashi-ui/elements/basic.js\";\n```\n\n```html\n<kashi-input label=\"ایمیل\" name=\"email\" type=\"email\" placeholder=\"you@example.com\" clearable></kashi-input>\n<kashi-input label=\"رمز عبور\" name=\"password\" type=\"password\" hint=\"حداقل ۸ نویسه\"></kashi-input>\n<kashi-textarea label=\"توضیحات\" name=\"bio\" maxlength=\"200\" show-count optional></kashi-textarea>\n```\n\n| عنصر | کاربرد |\n|---|---|\n| <kashi-input> | فیلد یک‌خطی؛ با type=\"password\" دکمهٔ نمایش رمز |\n| <kashi-textarea> | متن چندخطی؛ rows و autoresize |\n\nویژگی‌ها:\n\n| نام | مقدار | پیش‌فرض | توضیح |\n|---|---|---|---|\n| label / hint / error / placeholder | متن | — | برچسب، راهنما، خطا و متن خالی |\n| type / name / value / autocomplete / inputmode | — | text | مثل input معمولی |\n| clearable | بولی | — | دکمهٔ پاک کردن وقتی متن دارد |\n| revealable | false | — | رمز بدون دکمهٔ نمایش |\n| show-count / maxlength | بولی / عدد | — | شمارندهٔ نویسه، مثل «۱۲ / ۲۰۰» |\n| loading | بولی | — | اسپینر در انتهای فیلد، مثلاً هنگام بررسی نام کاربری |\n| optional | متن | اختیاری | نشان «اختیاری» کنار برچسب |\n| size / required / disabled / readonly | sm \\| lg / بولی | — | اندازه و حالت‌ها |\n\nرویدادها و JavaScript:\n\n| نام | توضیح |\n|---|---|\n| kashi-change | با هر تایپ؛ detail: { value } |\n| kashi-clear | پس از پاک کردن با دکمه |"},{"slug":"components/meter","url":"https://kashi.sepantapouya.com/docs/components/meter/","title":"گیج","description":"نمایش یک مقدار در برابر سقف، مثل فضای ذخیره، سهمیهٔ پیامک یا ظرفیت پلن.","group":"کامپوننت‌ها","components":["Meter"],"markdown":"```tsx\nimport { Meter } from \"@kashi-ui/react\";\n```\n\n## نمونه\n\n```tsx\n<Meter label=\"فضای ذخیره‌سازی\" value={4.2} max={10} format={(n) => `${n} گیگابایت`} />\n<Meter label=\"سهمیهٔ پیامک این ماه\" value={8600} max={10000} />\n<Meter label=\"کاربران پلن تیمی\" value={49} max={50} />\n```\n\nفرق آن با `Progress`: نوار پیشرفت برای کاری است که در حال انجام است؛ گیج برای اندازه‌ای است که ثابت است، مثل میزان مصرف. از ۸۰٪ رنگ هشدار و از ۹۵٪ رنگ بحرانی می‌گیرد و وضعیت با متن و آیکون هم گفته می‌شود.\n\n| ویژگی | نوع | پیش‌فرض | توضیح |\n|---|---|---|---|\n| label | ReactNode | — | نام گیج |\n| value / max | number | — / 100 | — |\n| warning / critical | number | ۸۰٪ / ۹۵٪ max | آستانهٔ هشدار و بحرانی |\n| format | (n: number) => string | ارقام فارسی | قالب مقدار |\n| labels | { ok?, warning?, critical?, of? } | — | متن وضعیت‌ها |\n\n## دسترس‌پذیری\n\n`role=\"meter\"` با `aria-valuetext` کامل («۸٬۶۰۰ از ۱۰٬۰۰۰، ۸۶٪، نزدیک به سقف»). وضعیت فقط با رنگ نشان داده نمی‌شود.\n\n## راهنمای استفاده\n\nکِی استفاده کنیم:\n\n- مقدار مصرف‌شده از یک سقف: فضای ذخیره، سهمیهٔ پیامک، ظرفیت پلن.\n- هشدار نزدیک شدن به سقف با `warning` و `critical`.\n\nکِی استفاده نکنیم:\n\n- پیشرفت یک کار که تمام می‌شود. (نوار پیشرفت: /docs/components/progress/)\n- مقدار بدون سقف مشخص. (کارت آمار: /docs/components/stat-card/)\n\nدرست و نادرست:\n\n- ✓ درست: عدد را هم بنویسید: «۸٫۲ از ۱۰ گیگابایت».\n- ✗ نادرست: فقط نوار رنگی که مقدار دقیق را نمی‌گوید."},{"slug":"components/multi-select","url":"https://kashi.sepantapouya.com/docs/components/multi-select/","title":"انتخاب چندتایی","description":"انتخاب چند گزینه از یک فهرست بلند با جستجو، مثل مسئولان پروژه یا برچسب‌های یک محصول.","group":"کامپوننت‌ها","components":["MultiSelect"],"markdown":"```tsx\nimport { MultiSelect } from \"@kashi-ui/react\";\n```\n\n## نمونه\n\n```tsx\n<MultiSelect label=\"مسئولان پروژه\" options={team} value={owners} onChange={setOwners}\n max={3} placeholder=\"جستجوی همکار\" name=\"owners\" />\n```\n\nهمان گزینه‌ها و جستجوی `Combobox` را دارد (یکدست‌سازی «ي» و «ی»، توضیح و آیکون). انتخاب‌ها به‌شکل برچسب داخل فیلد نمایش داده می‌شوند و با رسیدن به `max`، بقیهٔ گزینه‌ها غیرفعال می‌شوند. برای وارد کردن مقدار آزاد، که در فهرست نیست، `TagInput` مناسب‌تر است.\n\n| ویژگی | نوع | پیش‌فرض | توضیح |\n|---|---|---|---|\n| options | ComboboxOption[] | — | مثل Combobox |\n| value / defaultValue | string[] | [] | مقدارهای انتخاب‌شده |\n| onChange | (value: string[]) => void | — | — |\n| max | number | — | بیشترین تعداد انتخاب |\n| name | string | — | یک فیلد پنهان برای هر مقدار |\n| labels | { remove?, selected?, max?, clear? } | — | متن‌های صفحه‌خوان |\n\n## کیبورد\n\n| کلید | عمل |\n|---|---|\n| <kbd>↓</kbd> / <kbd>↑</kbd> | گزینهٔ بعد / قبل |\n| <kbd>Enter</kbd> | انتخاب یا لغو انتخاب گزینهٔ فعال |\n| <kbd>Backspace</kbd> در فیلد خالی | حذف آخرین برچسب |\n| <kbd>Esc</kbd> | بستن فهرست |\n\n## دسترس‌پذیری\n\nالگوی Combobox با `aria-multiselectable`؛ هر گزینه `aria-selected` دارد و تعداد انتخاب‌ها به فیلد وصل است («۲ مورد انتخاب شده»). افزودن و حذف با `aria-live` اعلام می‌شود.\n\n## راهنمای استفاده\n\nکِی استفاده کنیم:\n\n- انتخاب چند گزینه از فهرست بلند با جستجو: مسئولان پروژه، برچسب‌های محصول، شهرهای ارسال.\n\nکِی استفاده نکنیم:\n\n- کمتر از هشت گزینه؛ همه را با چک‌باکس نشان دهید. (چک‌باکس: /docs/components/checkbox/)\n- مقدارهای آزاد که در فهرست نیستند. (ورودی تگ: /docs/components/tag-input/)\n\nدرست و نادرست:\n\n- ✓ درست: اگر سقف دارد، `max` بدهید تا کاربر بداند و پیام «حداکثر ۳ مورد» بگیرد.\n- ✗ نادرست: بعد از ثبت فرم بگویید بیش از سه مورد مجاز نیست.\n\n## وب کامپوننت\n\nهمین کامپوننت بدون React، برای HTML، وردپرس، Vue، Svelte و Angular: `<kashi-multi-select>` در `@kashi-ui/elements`. گزینه‌ها همان `<option>` معمولی‌اند و `selected` انتخاب اولیه است؛ با `name` هر انتخاب یک فیلد هم‌نام در فرم است.\n\nنصب بدون ابزار ساخت (CDN):\n\n```html\n<link rel=\"stylesheet\" href=\"https://cdn.jsdelivr.net/npm/@kashi-ui/css/dist/kashi.css\">\n<script type=\"module\" src=\"https://cdn.jsdelivr.net/npm/@kashi-ui/elements/dist/cdn/kashi-elements.js\"></script>\n```\n\nیا با npm، فقط همین بخش:\n\n```bash\nnpm i @kashi-ui/elements @kashi-ui/css\n```\n\n```js\nimport \"@kashi-ui/css\";\nimport \"@kashi-ui/elements/forms.js\";\n```\n\n```html\n<kashi-multi-select label=\"مهارت‌ها\" name=\"skills\" max=\"4\" placeholder=\"جستجو…\">\n <option value=\"figma\" selected>فیگما</option>\n <option value=\"react\">ری‌اکت</option>\n <option value=\"css\" data-description=\"Cascade Layers\">CSS</option>\n <option value=\"a11y\">دسترس‌پذیری</option>\n</kashi-multi-select>\n```\n\nویژگی‌ها:\n\n| نام | مقدار | پیش‌فرض | توضیح |\n|---|---|---|---|\n| label / hint / error / placeholder | متن | — | مثل بقیهٔ فیلدها |\n| name | متن | — | یک فیلد پنهان برای هر انتخاب |\n| max | عدد | — | بیشترین تعداد انتخاب |\n| disabled | بولی | — | غیرفعال |\n\nرویدادها و JavaScript:\n\n| نام | توضیح |\n|---|---|\n| kashi-change | detail: { value: [...] } |\n| element.options / element.value | گزینه‌ها و انتخاب‌ها با JavaScript |"},{"slug":"components/number-input","url":"https://kashi.sepantapouya.com/docs/components/number-input/","title":"ورودی عدد","description":"فیلد عدد با دکمه‌های افزایش و کاهش، محدودهٔ مجاز و ارقام فارسی.","group":"کامپوننت‌ها","components":["NumberInput"],"markdown":"```tsx\nimport { NumberInput } from \"@kashi-ui/react\";\n```\n\n## نمونه\n\n```tsx\n<NumberInput label=\"تعداد مسافر\" min={1} max={9} value={n} onChange={setN} unit=\"نفر\" />\n<NumberInput label=\"بودجه\" min={0} step={100_000} defaultValue={1_500_000} thousands unit=\"تومان\" />\n```\n\nکاربر می‌تواند با ارقام فارسی یا انگلیسی تایپ کند. هنگام تایپ عدد محدود نمی‌شود تا عدد نیمه‌کاره پاک نشود؛ با خروج از فیلد به محدودهٔ `min` و `max` برمی‌گردد.\n\n| ویژگی | نوع | پیش‌فرض | توضیح |\n|---|---|---|---|\n| value / defaultValue | number \\| null | null | — |\n| onChange | (value: number \\| null) => void | — | — |\n| min / max | number | — | محدودهٔ مجاز |\n| step | number | 1 | گام دکمه‌ها و کلیدهای جهت |\n| thousands | boolean | false | جداکنندهٔ هزارگان |\n| digits | \"fa\" \\| \"latin\" | \"fa\" | ارقام نمایش |\n| unit | ReactNode | — | واحد کنار عدد |\n| labels | { increment?, decrement? } | «افزایش»، «کاهش» | نام دکمه‌ها |\n\n## کیبورد\n\n| کلید | عمل |\n|---|---|\n| <kbd>↑</kbd> / <kbd>↓</kbd> | یک گام بیشتر / کمتر |\n| <kbd>PageUp</kbd> / <kbd>PageDown</kbd> | ده گام |\n| <kbd>Home</kbd> / <kbd>End</kbd> | کمینه / بیشینه |\n\n## دسترس‌پذیری\n\nفیلد `role=\"spinbutton\"` با `aria-valuenow`، `aria-valuemin` و `aria-valuemax` است. دکمه‌های + و − از ترتیب Tab بیرون‌اند (کلیدهای جهت همان کار را می‌کنند) و در مرز محدوده غیرفعال می‌شوند.\n\n## راهنمای استفاده\n\nکِی استفاده کنیم:\n\n- عدد کوچک با گام مشخص: تعداد کالا، تعداد مسافر، سن.\n- جایی که دکمهٔ افزایش و کاهش از تایپ راحت‌تر است.\n\nکِی استفاده نکنیم:\n\n- مبلغ. (فیلد فارسی (مبلغ): /docs/persian/input/)\n- شمارهٔ تلفن، کد پستی یا کد ملی؛ این‌ها عدد نیستند و صفر اولشان مهم است. (فیلد فارسی: /docs/persian/input/)\n- محدودهٔ پیوسته مثل صدا. (اسلایدر: /docs/components/slider/)\n\nدرست و نادرست:\n\n- ✓ درست: `min` و `max` بدهید تا دکمه‌ها در مرز غیرفعال شوند.\n- ✗ نادرست: عدد منفی برای تعداد کالا بپذیرید و بعد خطا بدهید.\n\n## وب کامپوننت\n\nهمین کامپوننت بدون React، برای HTML، وردپرس، Vue، Svelte و Angular: `<kashi-number-input>` در `@kashi-ui/elements`.\n\nنصب بدون ابزار ساخت (CDN):\n\n```html\n<link rel=\"stylesheet\" href=\"https://cdn.jsdelivr.net/npm/@kashi-ui/css/dist/kashi.css\">\n<script type=\"module\" src=\"https://cdn.jsdelivr.net/npm/@kashi-ui/elements/dist/cdn/kashi-elements.js\"></script>\n```\n\nیا با npm، فقط همین بخش:\n\n```bash\nnpm i @kashi-ui/elements @kashi-ui/css\n```\n\n```js\nimport \"@kashi-ui/css\";\nimport \"@kashi-ui/elements/controls.js\";\n```\n\n```html\n<kashi-number-input label=\"تعداد\" name=\"qty\" value=\"1\" min=\"1\" max=\"10\"></kashi-number-input>\n```\n\nویژگی‌ها:\n\n| نام | مقدار | پیش‌فرض | توضیح |\n|---|---|---|---|\n| value / min / max / step | عدد | step: 1 | مقدار و محدوده |\n| thousands | بولی | — | جداکنندهٔ هزارگان |\n| unit | متن | — | واحد کنار فیلد |\n| label / name / hint / error / disabled | — | — | مثل بقیهٔ فیلدها |\n| digits | fa \\| latin | fa | خط ارقام |\n\nرویدادها و JavaScript:\n\n| نام | توضیح |\n|---|---|\n| kashi-change | detail: { value } |"},{"slug":"components/pagination","url":"https://kashi.sepantapouya.com/docs/components/pagination/","title":"صفحه‌بندی","description":"پیمایش بین صفحه‌های یک فهرست، با ارقام فارسی و «…» برای صفحه‌های زیاد.","group":"کامپوننت‌ها","components":["Pagination"],"markdown":"```tsx\nimport { Pagination } from \"@kashi-ui/react\";\n```\n\n## نمونه\n\n```tsx\nconst [page, setPage] = useState(4);\n\n<Pagination page={page} total={12} onChange={setPage} summary=\"صفحهٔ ۴ از ۱۲\" />\n\n// با لینک (بهتر برای موتورهای جستجو)\n<Pagination page={page} total={12} getHref={(p) => `?page=${p}`} />\n```\n\n## ویژگی‌ها\n\n| ویژگی | نوع | پیش‌فرض | توضیح |\n|---|---|---|---|\n| page / total | number | — | صفحهٔ فعلی و تعداد کل |\n| onChange | (page) => void | — | برای صفحه‌بندی با دکمه |\n| getHref | (page) => string | — | برای صفحه‌بندی با لینک |\n| siblings | number | 1 | تعداد صفحه‌های کنار صفحهٔ فعلی |\n| summary | ReactNode | — | متن خلاصه |\n| digits | \"fa\" \\| \"latin\" | \"fa\" | خط ارقام |\n| pageSize / pageSizes / onPageSizeChange | number / number[] / (size) => void | — / [10, 20, 50, 100] | فهرست «در هر صفحه» |\n| jumper | boolean | false | فیلد «برو به صفحه» (با onChange) |\n\n## تعداد در هر صفحه و پرش به صفحه\n\nبرای جدول‌های بلند: با `onPageSizeChange` فهرست «در هر صفحه» و با `jumper` فیلد «برو به صفحه» نمایش داده می‌شود. فیلد پرش ارقام فارسی و انگلیسی را می‌پذیرد.\n\n```tsx\n<Pagination page={page} total={total} onChange={setPage}\n pageSize={size} onPageSizeChange={setSize} pageSizes={[10, 20, 50, 100]}\n jumper summary=\"۴۱ تا ۶۰ از ۴۸۷\" />\n```\n\n## دسترس‌پذیری\n\n- داخل `<nav aria-label=\"صفحه‌بندی\">`؛ صفحهٔ فعلی `aria-current=\"page\"` و هر شماره نام کامل «صفحهٔ ۵» دارد.\n- «قبلی» در صفحهٔ اول و «بعدی» در صفحهٔ آخر غیرفعال‌اند. فوکوس کیبورد پس از تغییر صفحه حفظ می‌شود.\n\n## راهنمای استفاده\n\nکِی استفاده کنیم:\n\n- فهرست‌ها و جدول‌های بلند که کاربر می‌خواهد بداند کجای آن‌هاست: سفارش‌ها، تراکنش‌ها، نتیجهٔ جستجو.\n\nکِی استفاده نکنیم:\n\n- خبرنامه و شبکهٔ اجتماعی که کاربر فقط مرور می‌کند؛ بارگذاری بیشتر با دکمهٔ «نمایش بیشتر» ساده‌تر است.\n- کمتر از دو صفحه؛ صفحه‌بندی را پنهان کنید.\n\nدرست و نادرست:\n\n- ✓ درست: با `getHref` نشانی هر صفحه را بدهید تا قابل‌اشتراک و بازگشت با دکمهٔ مرورگر باشد.\n- ✗ نادرست: صفحه‌بندی فقط با state که با برگشتن به صفحه، کاربر را به صفحهٔ اول می‌برد.\n\n## وب کامپوننت\n\nهمین کامپوننت بدون React، برای HTML، وردپرس، Vue، Svelte و Angular: `<kashi-pagination>` در `@kashi-ui/elements`.\n\nنصب بدون ابزار ساخت (CDN):\n\n```html\n<link rel=\"stylesheet\" href=\"https://cdn.jsdelivr.net/npm/@kashi-ui/css/dist/kashi.css\">\n<script type=\"module\" src=\"https://cdn.jsdelivr.net/npm/@kashi-ui/elements/dist/cdn/kashi-elements.js\"></script>\n```\n\nیا با npm، فقط همین بخش:\n\n```bash\nnpm i @kashi-ui/elements @kashi-ui/css\n```\n\n```js\nimport \"@kashi-ui/css\";\nimport \"@kashi-ui/elements/controls.js\";\n```\n\n```html\n<kashi-pagination page=\"5\" total=\"20\"></kashi-pagination>\n```\n\nویژگی‌ها:\n\n| نام | مقدار | پیش‌فرض | توضیح |\n|---|---|---|---|\n| page / total | عدد | — | صفحهٔ فعلی و تعداد صفحه‌ها |\n| href | ?page={page} | — | لینک به‌جای دکمه |\n| siblings | عدد | 1 | تعداد صفحهٔ کنار صفحهٔ فعلی |\n| summary / label | متن | — | خلاصه کنار دکمه‌ها و نام برای صفحه‌خوان |\n\nرویدادها و JavaScript:\n\n| نام | توضیح |\n|---|---|\n| kashi-change | بدون href؛ detail: { page } |"},{"slug":"components/password-strength","url":"https://kashi.sepantapouya.com/docs/components/password-strength/","title":"قدرت رمز عبور","description":"نوار قدرت رمز و فهرست شرط‌ها زیر فیلد رمز، با هشدار صفحه‌کلید فارسی.","group":"کامپوننت‌ها","components":["PasswordStrength"],"markdown":"```tsx\nimport { PasswordStrength } from \"@kashi-ui/react\";\n```\n\n## نمونه\n\nاگر کاربر با صفحه‌کلید فارسی تایپ کند (مثلاً «سلام» به‌جای «sghl»)، هشدار داده می‌شود؛ اشتباه رایجی که کاربر فارسی‌زبان را پشت در ورود نگه می‌دارد.\n\n```tsx\n<Input label=\"رمز عبور\" type=\"password\" value={p} onChange={…} aria-describedby=\"pw-strength\" />\n<PasswordStrength id=\"pw-strength\" password={p} />\n```\n\n| ویژگی | نوع | پیش‌فرض | توضیح |\n|---|---|---|---|\n| password | string | — | رمز فعلی |\n| rules | { label, test }[] | defaultPasswordRules | شرط‌ها؛ امتیاز به نسبت شرط‌های برآورده‌شده |\n| showRules | boolean | true | نمایش فهرست شرط‌ها |\n| warnPersian | boolean | true | هشدار حروف فارسی در رمز |\n| id | string | — | برای aria-describedby فیلد رمز |\n| labels | { title?, levels?, persian? } | «ضعیف، متوسط، خوب، قوی» | — |\n\nاین کامپوننت فقط راهنمای کاربر است و جایگزین بررسی رمز در سرور نیست.\n\n## دسترس‌پذیری\n\nسطح قدرت با `aria-live` اعلام می‌شود و هر شرط برای صفحه‌خوان «رعایت شده» یا «رعایت نشده» دارد. با `id` و `aria-describedby`، صفحه‌خوان این راهنما را همراه فیلد رمز می‌خواند. نوار رنگی تزئینی است و متن سطح همیشه نمایش داده می‌شود.\n\n## راهنمای استفاده\n\nکِی استفاده کنیم:\n\n- زیر فیلد رمز در ثبت‌نام و تغییر رمز.\n\nکِی استفاده نکنیم:\n\n- فیلد ورود؛ آنجا کاربر رمز قبلی‌اش را می‌زند و قدرتش مهم نیست.\n\nدرست و نادرست:\n\n- ✓ درست: شرط‌ها را از اول نشان دهید و با هر تایپ تیک بخورند.\n- ✗ نادرست: بعد از ارسال فرم بگویید رمز ضعیف است.\n- ✓ درست: هشدار صفحه‌کلید فارسی (`warnPersian`) را روشن بگذارید؛ رمزی که با صفحه‌کلید فارسی تایپ شده، بعداً وارد نمی‌شود.\n- ✗ نادرست: شرط‌های عجیب مثل «حداکثر ۱۲ نویسه» که رمز امن‌تر را رد می‌کند.\n\n## وب کامپوننت\n\nهمین کامپوننت بدون React، برای HTML، وردپرس، Vue، Svelte و Angular: `<kashi-password-strength>` در `@kashi-ui/elements`. با `for` به فیلد رمز وصل می‌شود و با هر تایپ به‌روز می‌شود؛ اگر صفحه‌کلید روی فارسی مانده باشد هشدار می‌دهد.\n\nنصب بدون ابزار ساخت (CDN):\n\n```html\n<link rel=\"stylesheet\" href=\"https://cdn.jsdelivr.net/npm/@kashi-ui/css/dist/kashi.css\">\n<script type=\"module\" src=\"https://cdn.jsdelivr.net/npm/@kashi-ui/elements/dist/cdn/kashi-elements.js\"></script>\n```\n\nیا با npm، فقط همین بخش:\n\n```bash\nnpm i @kashi-ui/elements @kashi-ui/css\n```\n\n```js\nimport \"@kashi-ui/css\";\nimport \"@kashi-ui/elements/forms.js\";\n```\n\n```html\n<label class=\"k-label\" for=\"new-password\">رمز تازه</label>\n<div class=\"k-input\"><input id=\"new-password\" class=\"k-input__control\" type=\"password\" autocomplete=\"new-password\"></div>\n<kashi-password-strength for=\"new-password\"></kashi-password-strength>\n```\n\nویژگی‌ها:\n\n| نام | مقدار | پیش‌فرض | توضیح |\n|---|---|---|---|\n| for | شناسهٔ فیلد | — | فیلد رمز |\n| show-rules | false | — | بدون فهرست شرط‌ها |\n\nرویدادها و JavaScript:\n\n| نام | توضیح |\n|---|---|\n| element.score(password) | امتیاز ۰ تا ۴ |"},{"slug":"components/popover","url":"https://kashi.sepantapouya.com/docs/components/popover/","title":"پاپ‌اور","description":"محتوای تعاملی کوتاه کنار یک دکمه، مثل فیلتر، فرم کوچک یا توضیح.","group":"کامپوننت‌ها","components":["Popover"],"markdown":"```tsx\nimport { Popover } from \"@kashi-ui/react\";\n```\n\nبرخلاف `Tooltip`، داخل `Popover` می‌توان دکمه و فیلد گذاشت؛ برخلاف `Dialog`، بقیهٔ صفحه را مسدود نمی‌کند.\n\n## نمونه\n\n```tsx\n<Popover title=\"فیلتر سفارش‌ها\" trigger={<Button iconStart={<FilterIcon />}>فیلتر</Button>}>\n <Checkbox label=\"پرداخت‌شده\" />\n …\n</Popover>\n\n<Popover placement=\"bottom-end\" aria-label=\"دربارهٔ کارمزد\"\n trigger={<Button variant=\"tertiary\">کارمزد چطور محاسبه می‌شود؟</Button>}>\n کارمزد هر تراکنش ۱٪ مبلغ است.\n</Popover>\n```\n\n| ویژگی | نوع | پیش‌فرض | توضیح |\n|---|---|---|---|\n| trigger | ReactElement | — | دکمهٔ باز کردن |\n| title | ReactNode | — | عنوان و نام پنجره |\n| open / onOpenChange | boolean / (open) => void | — | حالت کنترل‌شده |\n| placement | \"bottom-start\" \\| \"bottom-end\" \\| \"top-start\" \\| \"top-end\" | \"bottom-start\" | جای پنجره |\n| aria-label | string | — | نام پنجره وقتی title ندارد |\n\n## دسترس‌پذیری\n\nدکمه `aria-haspopup=\"dialog\"` و `aria-expanded` می‌گیرد. با باز شدن، فوکوس به اولین عنصر داخل می‌رود؛ <kbd>Esc</kbd> می‌بندد و فوکوس را به دکمه برمی‌گرداند. کلیک بیرون یا رفتن فوکوس به بیرون هم پنجره را می‌بندد.\n\n## راهنمای استفاده\n\nکِی استفاده کنیم:\n\n- محتوای تعاملی کوتاه کنار یک دکمه: فیلتر، فرم دو سه فیلدی، انتخاب سریع.\n\nکِی استفاده نکنیم:\n\n- توضیح کوتاه بدون دکمه و فیلد. (تولتیپ: /docs/components/tooltip/)\n- فهرست عمل‌ها. (دراپ‌داون: /docs/components/dropdown-menu/)\n- کاری که تمرکز کامل می‌خواهد. (دیالوگ: /docs/components/dialog/)\n\nدرست و نادرست:\n\n- ✓ درست: محتوای کوتاه که با کلیک بیرون یا Escape بسته شود و فوکوس به دکمه برگردد.\n- ✗ نادرست: فرم بلند با اسکرول داخل پاپ‌اور.\n\n## وب کامپوننت\n\nهمین کامپوننت بدون React، برای HTML، وردپرس، Vue، Svelte و Angular: `<kashi-popover>` در `@kashi-ui/elements`. اولین فرزند دکمهٔ بازکننده است و بقیه محتوای پنجره.\n\nنصب بدون ابزار ساخت (CDN):\n\n```html\n<link rel=\"stylesheet\" href=\"https://cdn.jsdelivr.net/npm/@kashi-ui/css/dist/kashi.css\">\n<script type=\"module\" src=\"https://cdn.jsdelivr.net/npm/@kashi-ui/elements/dist/cdn/kashi-elements.js\"></script>\n```\n\nیا با npm، فقط همین بخش:\n\n```bash\nnpm i @kashi-ui/elements @kashi-ui/css\n```\n\n```js\nimport \"@kashi-ui/css\";\nimport \"@kashi-ui/elements/menu.js\";\n```\n\n```html\n<kashi-popover heading=\"فیلتر قیمت\">\n <button class=\"k-btn k-btn--secondary\">فیلتر</button>\n <kashi-price label=\"بیشترین قیمت\"></kashi-price>\n</kashi-popover>\n```\n\nویژگی‌ها:\n\n| نام | مقدار | پیش‌فرض | توضیح |\n|---|---|---|---|\n| heading | متن | — | عنوان پنجره |\n| placement | bottom-start \\| bottom-end \\| top-start \\| top-end | bottom-start | جای پنجره |\n| label | متن | — | نام برای صفحه‌خوان وقتی عنوان ندارد |\n\nرویدادها و JavaScript:\n\n| نام | توضیح |\n|---|---|\n| element.show() / close() | باز و بستن |\n| kashi-open / kashi-close | باز و بسته شدن |"},{"slug":"components/progress","url":"https://kashi.sepantapouya.com/docs/components/progress/","title":"نوار پیشرفت","description":"نمایش پیشرفت یک کار طولانی، به‌همراه اسپینر برای انتظارهای کوتاه.","group":"کامپوننت‌ها","components":["Progress","Spinner"],"markdown":"```tsx\nimport { Progress, Spinner } from \"@kashi-ui/react\";\n```\n\n## نمونه\n\n```tsx\n<Progress value={35} label=\"آپلود گزارش.pdf\" showValue />\n<Progress value={100} tone=\"success\" label=\"پشتیبان‌گیری\" showValue size=\"sm\" />\n<Progress aria-label=\"در حال پردازش\" size=\"sm\" /> // بدون value: نامعین\n<Spinner size=\"sm\" />\n```\n\n## دایره‌ای\n\n```tsx\n<Progress variant=\"circular\" value={72} showValue aria-label=\"پیشرفت پروژه\" />\n<Progress variant=\"circular\" size=\"sm\" aria-label=\"در حال پردازش\" />\n```\n\n`variant=\"circular\"` با همان `value`، `size`، `tone` و `showValue`. متغیرهای CSS: `--k-progress-height`، `--k-progress-color`، `--k-progress-ring-size`.\n\n## ویژگی‌ها\n\n| ویژگی | نوع | پیش‌فرض | توضیح |\n|---|---|---|---|\n| value | number | — | مقدار؛ بدون آن نوار نامعین است |\n| max | number | 100 | حداکثر |\n| label | ReactNode | — | برچسب؛ اگر نمی‌دهید aria-label بدهید |\n| showValue | boolean | false | نمایش درصد |\n| size | \"sm\" \\| \"md\" \\| \"lg\" | \"md\" | ضخامت |\n| tone | \"brand\" \\| \"success\" \\| \"warning\" \\| \"error\" | \"brand\" | رنگ |\n\n## دسترس‌پذیری\n\n- `role=\"progressbar\"` با `aria-valuenow` و `aria-valuetext` فارسی («۳۵٪»)؛ در حالت نامعین `aria-valuenow` حذف می‌شود.\n- اسپینر `role=\"status\"` با متن «در حال بارگذاری» برای صفحه‌خوان دارد. با `prefers-reduced-motion` انیمیشن‌ها کند یا متوقف می‌شوند.\n\n## راهنمای استفاده\n\nکِی استفاده کنیم:\n\n- کار طولانی با پیشرفت مشخص: بارگذاری فایل، پردازش، درون‌ریزی.\n- اسپینر برای انتظار کوتاه و نامعلوم، مثل ارسال فرم.\n\nکِی استفاده نکنیم:\n\n- مقدار مصرف از سقف. (گیج: /docs/components/meter/)\n- بارگذاری محتوای صفحه؛ اسکلتون پرش کمتری دارد. (اسکلتون: /docs/components/skeleton/)\n- مراحل یک فرایند. (استپر: /docs/components/stepper/)\n\nدرست و نادرست:\n\n- ✓ درست: اگر درصد را می‌دانید، `value` بدهید؛ نوار نامعلوم فقط وقتی که واقعاً نمی‌دانید.\n- ✗ نادرست: نوار نامعلومی که سه دقیقه بی‌پایان می‌چرخد."},{"slug":"components/radio","url":"https://kashi.sepantapouya.com/docs/components/radio/","title":"رادیو","description":"انتخاب دقیقاً یک گزینه از چند گزینه؛ به شکل فهرست، افقی یا کارت‌های قابل‌انتخاب.","group":"کامپوننت‌ها","components":[],"markdown":"```tsx\n```\n\n## فهرست\n\n```tsx\n<RadioGroup label=\"روش ارسال\" name=\"shipping\" defaultValue=\"post\">\n <Radio value=\"post\" label=\"پست پیشتاز\" description=\"۳ تا ۵ روز کاری\" />\n <Radio value=\"courier\" label=\"پیک\" description=\"همان روز، فقط تهران\" />\n <Radio value=\"pickup\" label=\"تحویل حضوری\" disabled />\n</RadioGroup>\n```\n\n## افقی\n\n```tsx\n<RadioGroup label=\"اندازه\" variant=\"inline\" name=\"size\" defaultValue=\"m\">\n <Radio value=\"s\" label=\"کوچک\" />\n <Radio value=\"m\" label=\"متوسط\" />\n <Radio value=\"l\" label=\"بزرگ\" />\n</RadioGroup>\n```\n\n## کارت‌های قابل‌انتخاب\n\nبرای گزینه‌هایی که توضیح بیشتری لازم دارند، مثل انتخاب پلن.\n\n```tsx\n<RadioGroup label=\"پلن اشتراک\" variant=\"cards\" value={plan} onChange={setPlan}>\n <Radio value=\"basic\" label=\"پایه\" description=\"۱ کاربر · ۹۰ هزار تومان در ماه\" />\n <Radio value=\"pro\" label=\"حرفه‌ای\" description=\"تا ۲۰ کاربر · ۴۹۰ هزار تومان در ماه\" />\n <Radio value=\"org\" label=\"سازمانی\" description=\"کاربر نامحدود · تماس بگیرید\" />\n</RadioGroup>\n```\n\n## ویژگی‌های RadioGroup\n\n| ویژگی | نوع | پیش‌فرض | توضیح |\n|---|---|---|---|\n| label | ReactNode | — | عنوان گروه (legend) — الزامی |\n| name | string | خودکار | نام فیلد فرم |\n| value / defaultValue | string | — | گزینهٔ انتخاب‌شده |\n| onChange | (value: string) => void | — | با تغییر انتخاب |\n| variant | \"list\" \\| \"inline\" \\| \"cards\" | \"list\" | چیدمان |\n| hint / error | ReactNode | — | راهنما و خطای گروه |\n| required / disabled | boolean | false | الزامی یا غیرفعال کردن کل گروه |\n\n## دسترس‌پذیری\n\n- گروه در `<fieldset>` با `<legend>` است؛ صفحه‌خوان عنوان گروه را پیش از هر گزینه می‌خواند.\n- رادیوها بومی‌اند: <kbd>Tab</kbd> وارد گروه می‌شود و کلیدهای جهت‌دار بین گزینه‌ها جابه‌جا می‌کنند.\n- اگر کاربر باید چند گزینه انتخاب کند، از [چک‌باکس](/docs/components/checkbox/) استفاده کنید.\n\n## راهنمای استفاده\n\nکِی استفاده کنیم:\n\n- انتخاب دقیقاً یکی از دو تا هفت گزینه که کاربر باید همه را ببیند: روش ارسال، روش پرداخت.\n- `variant=\"cards\"` وقتی هر گزینه توضیح یا قیمت دارد.\n\nکِی استفاده نکنیم:\n\n- بیش از هفت گزینه. (سلکت: /docs/components/select/)\n- روشن و خاموش یک تنظیم. (سوییچ: /docs/components/switch/)\n- انتخاب چند گزینه. (چک‌باکس: /docs/components/checkbox/)\n\nدرست و نادرست:\n\n- ✓ درست: گزینهٔ پیش‌فرضِ رایج را از اول انتخاب کنید تا کاربر کمتر کار کند.\n- ✗ نادرست: پیش‌فرضی که به نفع کسب‌وکار است نه کاربر، مثل بیمهٔ اختیاری از پیش انتخاب‌شده."},{"slug":"components/rating","url":"https://kashi.sepantapouya.com/docs/components/rating/","title":"امتیاز ستاره‌ای","description":"دریافت یا نمایش امتیاز ۱ تا ۵، با نیم‌ستاره در حالت نمایش.","group":"کامپوننت‌ها","components":["Rating"],"markdown":"```tsx\nimport { Rating } from \"@kashi-ui/react\";\n```\n\n## نمونه\n\n```tsx\n<Rating label=\"امتیاز شما به این محصول\" value={v} onChange={setV} showValue />\n\n<Rating value={4.5} readOnly size=\"sm\" showValue />\n```\n\n| ویژگی | نوع | پیش‌فرض | توضیح |\n|---|---|---|---|\n| value / defaultValue | number | 0 | امتیاز؛ در readOnly اعشار هم می‌پذیرد |\n| onChange | (value: number) => void | — | — |\n| max | number | 5 | تعداد ستاره‌ها |\n| readOnly | boolean | false | فقط نمایش |\n| showValue | boolean | false | عدد کنار ستاره‌ها |\n| size | \"sm\" \\| \"md\" \\| \"lg\" | \"md\" | — |\n| label | string | «امتیاز» | نام گروه |\n| starLabel | (n, max) => string | «n از max» | نام هر ستاره |\n\n## دسترس‌پذیری\n\nحالت قابل‌انتخاب یک `fieldset` با رادیوهای بومی است: <kbd>←</kbd>/<kbd>→</kbd> امتیاز را عوض می‌کند و هر ستاره نام «۳ از ۵» دارد. حالت فقط‌خواندنی یک تصویر با نام کامل است («امتیاز: ۴٫۵ از ۵») تا صفحه‌خوان ستاره‌ها را یکی‌یکی نخواند.\n\n## راهنمای استفاده\n\nکِی استفاده کنیم:\n\n- امتیاز دادن به محصول، سفارش یا پشتیبانی.\n- نمایش میانگین امتیاز با `readOnly` و نیم‌ستاره.\n\nکِی استفاده نکنیم:\n\n- نظرسنجی دقیق با معیارهای مختلف؛ پرسش‌های جدا با رادیو روشن‌ترند. (رادیو: /docs/components/radio/)\n\nدرست و نادرست:\n\n- ✓ درست: کنار میانگین تعداد امتیازها را هم بنویسید: «۴٫۵ از ۱۲۸ نظر».\n- ✗ نادرست: پنج ستاره بدون اینکه بگویید از چند نظر آمده.\n\n## وب کامپوننت\n\nهمین کامپوننت بدون React، برای HTML، وردپرس، Vue، Svelte و Angular: `<kashi-rating>` در `@kashi-ui/elements`.\n\nنصب بدون ابزار ساخت (CDN):\n\n```html\n<link rel=\"stylesheet\" href=\"https://cdn.jsdelivr.net/npm/@kashi-ui/css/dist/kashi.css\">\n<script type=\"module\" src=\"https://cdn.jsdelivr.net/npm/@kashi-ui/elements/dist/cdn/kashi-elements.js\"></script>\n```\n\nیا با npm، فقط همین بخش:\n\n```bash\nnpm i @kashi-ui/elements @kashi-ui/css\n```\n\n```js\nimport \"@kashi-ui/css\";\nimport \"@kashi-ui/elements/controls.js\";\n```\n\n```html\n<kashi-rating name=\"stars\" value=\"4\" show-value></kashi-rating>\n<kashi-rating value=\"3.5\" readonly></kashi-rating>\n```\n\nویژگی‌ها:\n\n| نام | مقدار | پیش‌فرض | توضیح |\n|---|---|---|---|\n| value / max | عدد | 0 / 5 | امتیاز و تعداد ستاره |\n| readonly | بولی | — | فقط نمایش، با نیم‌ستاره |\n| show-value | بولی | — | عدد امتیاز کنار ستاره‌ها |\n| name / label / size / disabled | — | — | نام فرم، نام صفحه‌خوان و اندازه |\n\nرویدادها و JavaScript:\n\n| نام | توضیح |\n|---|---|\n| kashi-change | detail: { value } |"},{"slug":"components/segmented-control","url":"https://kashi.sepantapouya.com/docs/components/segmented-control/","title":"سگمنت","description":"انتخاب یکی از چند گزینهٔ کوتاه کنار هم، مثل حالت نمایش یا بازهٔ زمانی گزارش.","group":"کامپوننت‌ها","components":["SegmentedControl"],"markdown":"```tsx\nimport { SegmentedControl } from \"@kashi-ui/react\";\n```\n\nبرای دو تا پنج گزینهٔ کوتاه که نتیجه‌شان بلافاصله دیده می‌شود. برای گزینه‌های بیشتر یا فرم‌ها از `RadioGroup` یا `Select` استفاده کنید؛ برای جابه‌جایی بین محتواهای مختلف از `Tabs`.\n\n## نمونه\n\n```tsx\n<SegmentedControl aria-label=\"نمایش\" value={view} onChange={setView} options={[\n { value: \"list\", label: \"فهرست\", icon: <ListIcon /> },\n { value: \"grid\", label: \"کارت\", icon: <GridIcon /> },\n]} />\n\n<SegmentedControl aria-label=\"بازهٔ زمانی\" size=\"sm\" defaultValue=\"30\" options={[\n { value: \"1\", label: \"امروز\" }, { value: \"7\", label: \"۷ روز\" },\n { value: \"30\", label: \"۳۰ روز\" }, { value: \"365\", label: \"یک سال\" },\n]} />\n```\n\n| ویژگی | نوع | پیش‌فرض | توضیح |\n|---|---|---|---|\n| options | { value, label, icon?, disabled?, aria-label? }[] | — | گزینه‌ها |\n| value / defaultValue | string | اولین گزینهٔ فعال | — |\n| onChange | (value: string) => void | — | — |\n| aria-label / aria-labelledby | string | — | نام گروه (الزامی) |\n| size | \"sm\" \\| \"md\" | \"md\" | هم‌قد دکمه‌ها: ۳۶ و ۴۰ پیکسل |\n| block | boolean | false | تمام‌عرض با بخش‌های هم‌اندازه |\n| name | string | خودکار | نام رادیوها برای ارسال فرم |\n\n## دسترس‌پذیری\n\nروی رادیوهای بومی ساخته شده: <kbd>Tab</kbd> وارد گروه می‌شود و <kbd>←</kbd>/<kbd>→</kbd> گزینه را عوض می‌کند. گروه `role=\"radiogroup\"` با نام دارد و گزینهٔ انتخاب‌شده با سطح برجسته و سایه، نه فقط رنگ، مشخص است. اگر گزینه فقط آیکون دارد، `aria-label` بدهید.\n\n## راهنمای استفاده\n\nکِی استفاده کنیم:\n\n- انتخاب یکی از دو تا پنج گزینهٔ کوتاه که نمای همان بخش را عوض می‌کند: «روز / هفته / ماه»، «جدول / کارت».\n\nکِی استفاده نکنیم:\n\n- عوض کردن کل محتوای پایین با عنوان‌های جدا. (زبانه‌ها: /docs/components/tabs/)\n- گزینه‌های فرم که ثبت می‌شوند. (رادیو: /docs/components/radio/)\n- عمل‌ها. (گروه دکمه: /docs/components/button-group/)\n\nدرست و نادرست:\n\n- ✓ درست: برچسب‌های یک یا دو کلمه‌ای هم‌اندازه.\n- ✗ نادرست: گزینه‌های بلند که در موبایل به دو خط می‌شکنند."},{"slug":"components/select","url":"https://kashi.sepantapouya.com/docs/components/select/","title":"سلکت","description":"انتخاب یک گزینه از فهرست بلند، روی select بومی مرورگر.","group":"کامپوننت‌ها","components":["Select"],"markdown":"```tsx\nimport { Select } from \"@kashi-ui/react\";\n```\n\n## نمونه\n\n```tsx\n<Select\n label=\"استان\"\n placeholder=\"انتخاب کنید\"\n hint=\"برای محاسبهٔ هزینهٔ ارسال\"\n options={[\n { value: \"thr\", label: \"تهران\" },\n { value: \"isf\", label: \"اصفهان\" },\n { value: \"frs\", label: \"فارس\" },\n { value: \"krm\", label: \"کرمان\" },\n ]}\n/>\n```\n\n## خطا و اندازه‌ها\n\n```tsx\n<Select label=\"نقش\" error=\"یک نقش انتخاب کنید.\" placeholder=\"انتخاب کنید\" required>\n <option value=\"admin\">مدیر</option>\n <option value=\"editor\">ویرایشگر</option>\n</Select>\n<Select label=\"مرتب‌سازی\" size=\"sm\" defaultValue=\"new\">\n <option value=\"new\">جدیدترین</option>\n <option value=\"price\">ارزان‌ترین</option>\n</Select>\n```\n\n## چرا select بومی؟\n\nفهرست بومی مرورگر در گوشی انتخاب‌گر سیستم‌عامل را باز می‌کند (که برای انگشت ساخته شده)، با صفحه‌خوان‌ها بی‌نقص کار می‌کند و در فرم‌ها بدون کد اضافه ارسال می‌شود. کاشی فقط ظاهر آن را هماهنگ می‌کند.\n\n## ویژگی‌ها\n\n| ویژگی | نوع | پیش‌فرض | توضیح |\n|---|---|---|---|\n| label / hint / error | ReactNode | — | مثل Input |\n| placeholder | string | — | گزینهٔ خالی اول که قابل‌انتخاب نیست |\n| options | { value, label, disabled? }[] | — | گزینه‌ها؛ یا <option> به‌عنوان children |\n| size | \"sm\" \\| \"md\" \\| \"lg\" | \"md\" | ارتفاع |\n\n## راهنمای استفاده\n\nکِی استفاده کنیم:\n\n- انتخاب یکی از هشت تا حدود سی گزینه که کاربر مرورشان می‌کند.\n- فرم‌هایی که روی موبایل باید انتخاب‌گر بومی گوشی باز شود.\n\nکِی استفاده نکنیم:\n\n- کمتر از هشت گزینه که بهتر است همه دیده شوند. (رادیو: /docs/components/radio/)\n- فهرست خیلی بلند که باید جستجو شود. (کامبوباکس: /docs/components/combobox/)\n- استان و شهر. (انتخاب استان: /docs/persian/province/)\n\nدرست و نادرست:\n\n- ✓ درست: گزینه‌ها به ترتیبی که کاربر انتظار دارد: الفبایی، یا پرکاربردها اول.\n- ✗ نادرست: ترتیب تصادفی یا ترتیب پایگاه داده."},{"slug":"components/side-nav","url":"https://kashi.sepantapouya.com/docs/components/side-nav/","title":"سایدبار","description":"منوی کناری پنل مدیریت با گروه‌ها، زیرمنو، نشان و حالت جمع‌شده.","group":"کامپوننت‌ها","components":["SideNav"],"markdown":"```tsx\nimport { SideNav } from \"@kashi-ui/react\";\n```\n\n## نمونه\n\n```tsx\n<SideNav\n collapsed={collapsed}\n header={<Logo />}\n sections={[\n { items: [\n { label: \"داشبورد\", href: \"/\", icon: <HomeIcon />, current: true },\n { label: \"صندوق ورودی\", href: \"/inbox\", icon: <InboxIcon />, badge: <Badge size=\"sm\" tone=\"brand\">۱۲</Badge> },\n { label: \"گزارش‌ها\", icon: <ChartIcon />, items: [\n { label: \"فروش\", href: \"/reports/sales\" },\n { label: \"کاربران\", href: \"/reports/users\" },\n ] },\n ] },\n { label: \"مدیریت\", items: [{ label: \"تیم\", href: \"/team\", icon: <UsersIcon /> }] },\n ]}\n footer={<UserMenu />}\n/>\n```\n\n| ویژگی | نوع | پیش‌فرض | توضیح |\n|---|---|---|---|\n| sections | { label?, items: SideNavItem[] }[] | — | گروه‌های منو |\n| SideNavItem | { label, href?, icon?, badge?, current?, onClick?, items? } | — | با items، زیرمنوی باز و بسته‌شونده |\n| collapsed | boolean | false | فقط آیکون‌ها |\n| header / footer | ReactNode | — | بالا و پایین منو |\n| aria-label | string | «اصلی» | نام ناحیهٔ ناوبری |\n\nعرض با متغیر `--k-sidenav-width` (پیش‌فرض ۱۷rem) عوض می‌شود.\n\n## دسترس‌پذیری\n\nناحیهٔ `<nav>` با نام «اصلی». صفحهٔ فعلی `aria-current=\"page\"` دارد و زیرمنویی که صفحهٔ فعلی در آن است از اول باز است. والد زیرمنو دکمه‌ای با `aria-expanded` است. در حالت جمع‌شده، نام هر مورد با `aria-label` و `title` حفظ می‌شود.\n\n## راهنمای استفاده\n\nکِی استفاده کنیم:\n\n- ناوبری اصلی پنل مدیریت و داشبورد با چند بخش.\n- گروه‌بندی صفحه‌ها با عنوان و زیرمنو.\n\nکِی استفاده نکنیم:\n\n- سایت محتوایی کم‌صفحه؛ منوی بالای صفحه کافی است.\n- زیرمنوهای بیش از دو سطح؛ ساختار را ساده‌تر کنید. (نمای درختی: /docs/components/tree-view/)\n\nدرست و نادرست:\n\n- ✓ درست: صفحهٔ فعلی با `current` مشخص شود و در حالت جمع‌شده نام‌ها در تولتیپ بیایند.\n- ✗ نادرست: حالت جمع‌شده با آیکون‌های بی‌نام که فقط کاربر قدیمی می‌شناسدشان.\n\n## وب کامپوننت\n\nهمین کامپوننت بدون React، برای HTML، وردپرس، Vue، Svelte و Angular: `<kashi-side-nav>` در `@kashi-ui/elements`. همان HTML منوی کناری (تب HTML بالای صفحه) را داخلش بگذارید؛ زیرمنوها با کلیک باز و بسته می‌شوند.\n\nنصب بدون ابزار ساخت (CDN):\n\n```html\n<link rel=\"stylesheet\" href=\"https://cdn.jsdelivr.net/npm/@kashi-ui/css/dist/kashi.css\">\n<script type=\"module\" src=\"https://cdn.jsdelivr.net/npm/@kashi-ui/elements/dist/cdn/kashi-elements.js\"></script>\n```\n\nیا با npm، فقط همین بخش:\n\n```bash\nnpm i @kashi-ui/elements @kashi-ui/css\n```\n\n```js\nimport \"@kashi-ui/css\";\nimport \"@kashi-ui/elements/nav.js\";\n```\n\n```html\n<kashi-side-nav>\n <nav class=\"k-sidenav\" aria-label=\"اصلی\">\n <ul class=\"k-sidenav__group\" role=\"list\">\n <li><a class=\"k-sidenav__item\" href=\"#\" aria-current=\"page\"><span class=\"k-sidenav__text\">داشبورد</span></a></li>\n <li><button type=\"button\" class=\"k-sidenav__item\" aria-expanded=\"false\" aria-controls=\"nav-orders\"><span class=\"k-sidenav__text\">سفارش‌ها</span></button>\n <ul class=\"k-sidenav__sub\" id=\"nav-orders\" role=\"list\" hidden>\n <li><a class=\"k-sidenav__item\" href=\"#\"><span class=\"k-sidenav__text\">همه</span></a></li>\n <li><a class=\"k-sidenav__item\" href=\"#\"><span class=\"k-sidenav__text\">در انتظار</span></a></li>\n </ul>\n </li>\n </ul>\n </nav>\n</kashi-side-nav>\n```\n\nویژگی‌ها:\n\n| نام | مقدار | پیش‌فرض | توضیح |\n|---|---|---|---|\n| collapsed | بولی | — | فقط آیکون‌ها، با نام در راهنما |\n\nرویدادها و JavaScript:\n\n| نام | توضیح |\n|---|---|\n| kashi-toggle | باز و بسته شدن زیرمنو؛ detail: { id, open } |"},{"slug":"components/skeleton","url":"https://kashi.sepantapouya.com/docs/components/skeleton/","title":"اسکلتون","description":"جای خالی محتوا هنگام بارگذاری؛ از پرش صفحه جلوگیری می‌کند.","group":"کامپوننت‌ها","components":["Skeleton"],"markdown":"```tsx\nimport { Skeleton } from \"@kashi-ui/react\";\n```\n\n## نمونه\n\n```tsx\n<div aria-busy=\"true\" aria-label=\"در حال بارگذاری\">\n <Skeleton circle width={40} />\n <Skeleton lines={2} />\n <Skeleton height={120} />\n <Skeleton lines={3} />\n</div>\n```\n\n## ویژگی‌ها\n\n| ویژگی | نوع | پیش‌فرض | توضیح |\n|---|---|---|---|\n| width / height | string \\| number | 100% / 1em | اندازه |\n| circle | boolean | false | شکل دایره |\n| lines | number | — | چند خط متن؛ خط آخر کوتاه‌تر است |\n\n## دسترس‌پذیری\n\nاسکلت تزئینی است (`aria-hidden`). روی ظرف بارگذاری `aria-busy=\"true\"` و یک `aria-label` بگذارید تا صفحه‌خوان بداند محتوا در راه است. با `prefers-reduced-motion` درخشش متوقف می‌شود.\n\n## راهنمای استفاده\n\nکِی استفاده کنیم:\n\n- محتوای صفحه یا کارت هنگام بارگذاری، به همان شکل محتوای نهایی.\n- جلوگیری از پرش صفحه وقتی داده می‌رسد.\n\nکِی استفاده نکنیم:\n\n- انتظار کوتاه یک عمل مثل ارسال فرم. (اسپینر: /docs/components/progress/)\n- بیش از چند ثانیه؛ اگر بارگذاری طول می‌کشد، بگویید چه خبر است.\n\nدرست و نادرست:\n\n- ✓ درست: اسکلتون همان چیدمان محتوای واقعی: عکس، عنوان، دو خط متن.\n- ✗ نادرست: یک مستطیل خاکستری بزرگ که هیچ شباهتی به صفحه ندارد."},{"slug":"components/slider","url":"https://kashi.sepantapouya.com/docs/components/slider/","title":"اسلایدر","description":"انتخاب یک عدد یا یک بازه با کشیدن، مثل صدا، روشنایی یا بازهٔ قیمت.","group":"کامپوننت‌ها","components":["Slider"],"markdown":"```tsx\nimport { Slider } from \"@kashi-ui/react\";\n```\n\n## مقدار تکی و بازه\n\nبرای بازه، به‌جای عدد یک آرایهٔ دوتایی بدهید؛ دستگیره‌ها از روی هم رد نمی‌شوند.\n\n```tsx\n<Slider label=\"روشنایی\" value={v} onChange={setV} format={(n) => `${toFaDigits(n)}٪`} />\n\n<Slider label=\"قیمت\" min={0} max={10_000_000} step={500_000}\n value={price} onChange={setPrice}\n format={(n) => `${formatNumber(n)} تومان`}\n marks={[0, 5_000_000, 10_000_000]} />\n```\n\n| ویژگی | نوع | پیش‌فرض | توضیح |\n|---|---|---|---|\n| value / defaultValue | number \\| [number, number] | — | آرایه برای بازه |\n| onChange | (value) => void | — | — |\n| min / max / step | number | 0 / 100 / 1 | — |\n| format | (n: number) => string | formatNumber | قالب نمایش و متن صفحه‌خوان |\n| showValue | boolean | true | مقدار کنار برچسب |\n| marks | number[] | — | برچسب‌های زیر نوار |\n| name | string | — | در بازه: name_min و name_max |\n| labels | { from?, to?, separator? } | «حداقل»، «حداکثر» | — |\n\n## دسترس‌پذیری\n\nروی `<input type=\"range\">` بومی ساخته شده، پس همهٔ کلیدهای استاندارد (<kbd>←</kbd>/<kbd>→</kbd>، <kbd>PageUp</kbd>، <kbd>Home</kbd>/<kbd>End</kbd>) کار می‌کنند. `aria-valuetext` مقدار قالب‌بندی‌شده را می‌خواند («۲٬۰۰۰٬۰۰۰ تومان») و در بازه، هر دستگیره نام جدا دارد: «حداقل قیمت» و «حداکثر قیمت».\n\n## راهنمای استفاده\n\nکِی استفاده کنیم:\n\n- مقدار تقریبی در یک محدوده: صدا، روشنایی، بازهٔ قیمت در فیلتر.\n\nکِی استفاده نکنیم:\n\n- عدد دقیق؛ کشیدن تا عدد دقیق سخت است. (ورودی عدد: /docs/components/number-input/)\n- مبلغ پرداخت. (فیلد فارسی (مبلغ): /docs/persian/input/)\n\nدرست و نادرست:\n\n- ✓ درست: مقدار فعلی را کنار برچسب بنویسید (`showValue`) تا کاربر بداند کجاست.\n- ✗ نادرست: اسلایدر بدون عدد که کاربر حدس بزند ۴۰ است یا ۶۰.\n\n## وب کامپوننت\n\nهمین کامپوننت بدون React، برای HTML، وردپرس، Vue، Svelte و Angular: `<kashi-slider>` در `@kashi-ui/elements`. روی `input type=range` بومی؛ با `range` دو دستگیره و دو فیلد `name_min` و `name_max` در فرم.\n\nنصب بدون ابزار ساخت (CDN):\n\n```html\n<link rel=\"stylesheet\" href=\"https://cdn.jsdelivr.net/npm/@kashi-ui/css/dist/kashi.css\">\n<script type=\"module\" src=\"https://cdn.jsdelivr.net/npm/@kashi-ui/elements/dist/cdn/kashi-elements.js\"></script>\n```\n\nیا با npm، فقط همین بخش:\n\n```bash\nnpm i @kashi-ui/elements @kashi-ui/css\n```\n\n```js\nimport \"@kashi-ui/css\";\nimport \"@kashi-ui/elements/forms.js\";\n```\n\n```html\n<kashi-slider label=\"حجم صدا\" value=\"40\" name=\"volume\"></kashi-slider>\n<kashi-slider label=\"قیمت\" range min=\"0\" max=\"1000000\" step=\"50000\" value=\"200000, 750000\" unit=\"تومان\" name=\"price\"></kashi-slider>\n```\n\nویژگی‌ها:\n\n| نام | مقدار | پیش‌فرض | توضیح |\n|---|---|---|---|\n| label | متن | — | برچسب |\n| min / max / step | عدد | 0 / 100 / 1 | محدوده و گام |\n| value | عدد یا «کمینه، بیشینه» | — | مقدار اولیه |\n| range | بولی | — | انتخاب بازه با دو دستگیره |\n| unit / marks | متن / «0, 50, 100» | — | واحد کنار عدد و علامت‌های زیر نوار |\n| show-value | false | — | پنهان کردن عدد بالای نوار |\n\nرویدادها و JavaScript:\n\n| نام | توضیح |\n|---|---|\n| kashi-change | detail: { value }؛ در range آرایهٔ [کمینه، بیشینه] |"},{"slug":"components/splitter","url":"https://kashi.sepantapouya.com/docs/components/splitter/","title":"اسپلیتر","description":"دو پنل کنار هم یا زیر هم که کاربر اندازه‌شان را با کشیدن مرز تغییر می‌دهد.","group":"کامپوننت‌ها","components":["Splitter"],"markdown":"```tsx\nimport { Splitter } from \"@kashi-ui/react\";\n```\n\n## نمونه\n\n```tsx\n<Splitter label=\"اندازهٔ فهرست فایل‌ها\" defaultSize={35} height=\"16rem\">\n <FileList />\n <Preview />\n</Splitter>\n```\n\n| ویژگی | نوع | پیش‌فرض | توضیح |\n|---|---|---|---|\n| children | [ReactNode, ReactNode] | — | پنل ابتدا و پنل انتها |\n| orientation | \"horizontal\" \\| \"vertical\" | \"horizontal\" | کنار هم یا زیر هم |\n| size / defaultSize | number | 40 | اندازهٔ پنل اول به درصد |\n| onSizeChange | (percent) => void | — | برای ذخیرهٔ اندازه |\n| min / max | number | 15 / 85 | محدودهٔ درصد |\n| label | string | «تغییر اندازهٔ پنل‌ها» | نام دستگیره |\n\n## دسترس‌پذیری\n\nدستگیره یک `role=\"separator\"` فوکوس‌پذیر با `aria-valuenow` است. با ←→ (یا ↑↓ در حالت عمودی) پنج درصد، و با <kbd>Home</kbd>/<kbd>End</kbd> به کمینه و بیشینه می‌رود. دوبار کلیک اندازهٔ اولیه را برمی‌گرداند.\n\n## راهنمای استفاده\n\nکِی استفاده کنیم:\n\n- دو پنل کاری کنار هم که کاربر نسبتشان را تنظیم می‌کند: فهرست و جزئیات، کد و پیش‌نمایش.\n\nکِی استفاده نکنیم:\n\n- صفحه‌های معمولی سایت؛ چیدمان ثابت ساده‌تر است.\n- موبایل؛ آنجا پنل‌ها را زیر هم بگذارید.\n\nدرست و نادرست:\n\n- ✓ درست: `min` و `max` بدهید تا هیچ پنلی آن‌قدر کوچک نشود که بی‌استفاده شود.\n- ✗ نادرست: پنلی که تا صفر کوچک می‌شود و کاربر نمی‌فهمد کجا رفت.\n\n## وب کامپوننت\n\nهمین کامپوننت بدون React، برای HTML، وردپرس، Vue، Svelte و Angular: `<kashi-splitter>` در `@kashi-ui/elements`.\n\nنصب بدون ابزار ساخت (CDN):\n\n```html\n<link rel=\"stylesheet\" href=\"https://cdn.jsdelivr.net/npm/@kashi-ui/css/dist/kashi.css\">\n<script type=\"module\" src=\"https://cdn.jsdelivr.net/npm/@kashi-ui/elements/dist/cdn/kashi-elements.js\"></script>\n```\n\nیا با npm، فقط همین بخش:\n\n```bash\nnpm i @kashi-ui/elements @kashi-ui/css\n```\n\n```js\nimport \"@kashi-ui/css\";\nimport \"@kashi-ui/elements/nav.js\";\n```\n\n```html\n<kashi-splitter size=\"35\" height=\"12rem\">\n <div style=\"padding: 1rem\">فهرست</div>\n <div style=\"padding: 1rem\">جزئیات</div>\n</kashi-splitter>\n```\n\nویژگی‌ها:\n\n| نام | مقدار | پیش‌فرض | توضیح |\n|---|---|---|---|\n| size | درصد | 40 | اندازهٔ اولیهٔ پنل اول |\n| min / max | درصد | 15 / 85 | محدودهٔ اندازه |\n| orientation | vertical | — | پنل‌ها زیر هم |\n| height / label | طول CSS / متن | — | ارتفاع و نام جداکننده |\n\nرویدادها و JavaScript:\n\n| نام | توضیح |\n|---|---|\n| kashi-change | detail: { value } |"},{"slug":"components/stat-card","url":"https://kashi.sepantapouya.com/docs/components/stat-card/","title":"کارت آمار","description":"نمایش شاخص‌های کلیدی (KPI) داشبورد با روند تغییر.","group":"کامپوننت‌ها","components":["StatCard","StatGrid"],"markdown":"```tsx\nimport { StatCard, StatGrid } from \"@kashi-ui/react\";\n```\n\n## نمونه\n\n```tsx\n<StatGrid>\n <StatCard label=\"فروش این ماه\" value=\"۱۸۴٬۵۰۰٬۰۰۰\" unit=\"تومان\" icon={<WalletIcon />}\n change={12.4} caption=\"نسبت به ماه قبل\">\n <Sparkline points={…} />\n </StatCard>\n <StatCard label=\"کاربران فعال\" value=\"۳٬۸۲۰\" unit=\"نفر\" change={-2} caption=\"نسبت به هفتهٔ قبل\" />\n <StatCard label=\"نرخ خطا\" value=\"۰٫۸٪\" change={-35} invertTrend />\n</StatGrid>\n```\n\n## ساده\n\n```tsx\n<StatCard label=\"سفارش‌های امروز\" value=\"۴۸\" />\n```\n\n| ویژگی | نوع | پیش‌فرض | توضیح |\n|---|---|---|---|\n| label | ReactNode | — | نام شاخص |\n| value | ReactNode | — | مقدار قالب‌بندی‌شده |\n| unit | ReactNode | — | واحد کوچک کنار عدد، مثل «تومان» |\n| icon | ReactNode | — | آیکون تزئینی |\n| change | number | — | درصد تغییر؛ منفی یعنی کاهش |\n| invertTrend | boolean | false | وقتی کاهش خوب است (رنگ سبز برای کاهش) |\n| caption | ReactNode | — | متن کنار روند |\n| children | ReactNode | — | نمودار کوچک یا محتوای اضافه |\n\n`StatGrid` کارت‌ها را در ستون‌های خودکار (حداقل ۱۴rem) می‌چیند.\n\nعدد با ارقام فارسی و عرض طبیعی نمایش داده می‌شود (نه `tabular-nums`)، چون ارقام فارسی با عرض ثابت از هم فاصله می‌گیرند. اندازهٔ عدد با `--k-stat-value-size` عوض می‌شود.\n\n## دسترس‌پذیری\n\nجهت روند فقط با رنگ و فلش نشان داده نمی‌شود: صفحه‌خوان متن کامل را می‌خواند («۱۲٫۴٪ افزایش»). رنگ روند بر اساس خوب یا بد بودن است، نه جهت.\n\n## راهنمای استفاده\n\nکِی استفاده کنیم:\n\n- شاخص‌های کلیدی بالای داشبورد: فروش امروز، سفارش‌ها، کاربران فعال.\n- عدد مهم با روند تغییر نسبت به دورهٔ قبل.\n\nکِی استفاده نکنیم:\n\n- بیش از چهار پنج کارت در یک ردیف؛ مهم‌ترین‌ها را انتخاب کنید.\n- روند در طول زمان. (نمودار: /docs/components/charts/)\n\nدرست و نادرست:\n\n- ✓ درست: بگویید تغییر نسبت به چه دوره‌ای است: «۱۲٪ بیشتر از ماه قبل».\n- ✗ نادرست: فقط «+۱۲٪» که معلوم نیست نسبت به دیروز است یا پارسال."},{"slug":"components/stepper","url":"https://kashi.sepantapouya.com/docs/components/stepper/","title":"استپر","description":"نشان دادن مراحل یک فرایند چندبخشی، مثل خرید یا ثبت‌نام.","group":"کامپوننت‌ها","components":["Stepper"],"markdown":"```tsx\nimport { Stepper } from \"@kashi-ui/react\";\n```\n\n## افقی\n\n```tsx\n<Stepper current={1} steps={[\n { title: \"سبد خرید\", description: \"۳ کالا\" },\n { title: \"اطلاعات ارسال\", description: \"آدرس و زمان\" },\n { title: \"پرداخت\" },\n { title: \"تأیید\" },\n]} />\n```\n\n## عمودی\n\n`} align=\"start\" code={`<Stepper orientation=\"vertical\" current={2} steps={[…]} />`}>\n\n| ویژگی | نوع | پیش‌فرض | توضیح |\n|---|---|---|---|\n| steps | { title, description? }[] | — | مراحل |\n| current | number | — | مرحلهٔ فعلی از صفر |\n| orientation | \"horizontal\" \\| \"vertical\" | \"horizontal\" | جهت |\n| label / labels | string / { complete?, current? } | «مراحل» | متن‌های صفحه‌خوان |\n\n## دسترس‌پذیری\n\nفهرست `<ol>` با نام «مراحل»؛ مرحلهٔ فعلی `aria-current=\"step\"` دارد و صفحه‌خوان وضعیت هر مرحله را می‌خواند («ارسال، مرحلهٔ فعلی»، «سبد خرید، انجام شده»). وضعیت فقط با رنگ نمایش داده نمی‌شود: مراحل انجام‌شده تیک دارند.\n\n## راهنمای استفاده\n\nکِی استفاده کنیم:\n\n- فرایند سه تا شش مرحله‌ای که کاربر باید بداند چقدر مانده: خرید، ثبت‌نام، احراز هویت.\n\nکِی استفاده نکنیم:\n\n- دو مرحله یا کمتر.\n- بخش‌های مستقل که ترتیب ندارند. (زبانه‌ها: /docs/components/tabs/)\n- مکان در ساختار سایت. (بردکرامب: /docs/components/breadcrumb/)\n\nدرست و نادرست:\n\n- ✓ درست: عنوان مرحله‌ها کوتاه و اسم: «سبد خرید»، «نشانی و ارسال»، «پرداخت».\n- ✗ نادرست: هشت مرحله که در موبایل جا نمی‌شود؛ مرحله‌ها را ادغام کنید."},{"slug":"components/switch","url":"https://kashi.sepantapouya.com/docs/components/switch/","title":"سوییچ","description":"برای روشن و خاموش کردن تنظیمی که بلافاصله اعمال می‌شود.","group":"کامپوننت‌ها","components":["Switch"],"markdown":"```tsx\nimport { Switch } from \"@kashi-ui/react\";\n```\n\n## پایه\n\nدر راست‌به‌چپ، دستگیرهٔ سوییچ در حالت روشن به سمت چپ می‌رود؛ این رفتار خودکار است.\n\n```tsx\n<Switch label=\"اعلان‌های ایمیلی\" description=\"خلاصهٔ هفتگی فعالیت‌ها\" defaultChecked />\n<Switch label=\"حالت تعطیلات\" />\n```\n\n## اندازه‌ها و حالت‌ها\n\n```tsx\n<Switch label=\"کوچک\" size=\"sm\" defaultChecked />\n<Switch label=\"متوسط\" defaultChecked />\n<Switch label=\"بزرگ\" size=\"lg\" defaultChecked />\n<Switch label=\"غیرفعال\" disabled />\n```\n\n## سوییچ یا چک‌باکس؟\n\n- **سوییچ:** تغییر فوراً اعمال می‌شود، مثل روشن کردن اعلان‌ها.\n- **چک‌باکس:** انتخاب پس از ارسال فرم اعمال می‌شود، مثل پذیرفتن قوانین.\n\n## فهرست تنظیمات\n\nبا `labelPosition=\"start\"` برچسب پیش از کنترل می‌آید و کنترل به انتهای ردیف می‌رود؛ چیدمان رایج صفحه‌های تنظیمات. برای Checkbox هم کار می‌کند.\n\n```tsx\n<Switch labelPosition=\"start\" label=\"اعلان‌های ایمیلی\" description=\"خلاصهٔ هفتگی فعالیت‌ها\" />\n```\n\n**ویژگی‌های جدید**\n\n| ویژگی | نوع | پیش‌فرض | توضیح |\n|---|---|---|---|\n| labelPosition | \"start\" \\| \"end\" | \"end\" | جای برچسب (Switch و Checkbox) |\n| classNames | { root, control, label, description } | — | کلاس دلخواه برای هر بخش |\n\n## ویژگی‌ها\n\n| ویژگی | نوع | پیش‌فرض | توضیح |\n|---|---|---|---|\n| label | ReactNode | — | برچسب کنار سوییچ |\n| description | ReactNode | — | توضیح زیر برچسب |\n| size | \"sm\" \\| \"md\" \\| \"lg\" | \"md\" | اندازه |\n| invalid | boolean | false | حالت خطا |\n\n## متغیرهای CSS\n\n| متغیر CSS | پیش‌فرض | توضیح |\n|---|---|---|\n| --k-switch-width | 2.25rem | عرض |\n| --k-switch-height | 1.25rem | ارتفاع |\n| --k-switch-track-on | var(--k-bg-brand) | رنگ مسیر در حالت روشن |\n| --k-switch-track-off | var(--k-border-input) | رنگ مسیر در حالت خاموش |\n| --k-switch-thumb | #fff | رنگ دستگیره |\n\n## دسترس‌پذیری\n\n- روی `<input type=\"checkbox\" role=\"switch\">` ساخته شده؛ صفحه‌خوان آن را «سوییچ، روشن» یا «خاموش» اعلام می‌کند.\n- مسیر خاموش کنتراست حداقل ۳ به ۱ با پس‌زمینه دارد.\n\n## راهنمای استفاده\n\nکِی استفاده کنیم:\n\n- تنظیمی که همان لحظه اعمال می‌شود: اعلان‌ها، حالت تیره، نمایش عمومی پروفایل.\n\nکِی استفاده نکنیم:\n\n- گزینه‌ای در فرم که با دکمهٔ ثبت ذخیره می‌شود. (چک‌باکس: /docs/components/checkbox/)\n- دو گزینه با نام‌های جدا، مثل «ماهانه / سالانه». (سگمنت: /docs/components/segmented-control/)\n\nدرست و نادرست:\n\n- ✓ درست: برچسب اسم تنظیم است و روشن یعنی فعال.\n- ✗ نادرست: برچسبی که وضعیت را می‌گوید و با هر تغییر عوض می‌شود."},{"slug":"components/table","url":"https://kashi.sepantapouya.com/docs/components/table/","title":"جدول داده","description":"جدول با مرتب‌سازی، انتخاب ردیف‌ها، ستون‌های عددی و حالت خالی.","group":"کامپوننت‌ها","components":["DataTable"],"markdown":"```tsx\nimport { DataTable } from \"@kashi-ui/react\";\n```\n\n## نمونه\n\nروی سرستون «مشتری» یا «مبلغ» بزنید تا مرتب شود. نام‌ها به ترتیب الفبای فارسی مرتب می‌شوند.\n\n```tsx\n<DataTable\n caption=\"تراکنش‌های اخیر\"\n description=\"آخرین پرداخت‌های مشتریان در این ماه\"\n rows={transactions}\n getRowId={(r) => r.id}\n selectable\n selected={selected}\n onSelectedChange={setSelected}\n defaultSort={{ key: \"amount\", direction: \"descending\" }}\n columns={[\n { key: \"customer\", header: \"مشتری\", sortable: true },\n { key: \"date\", header: \"تاریخ\" },\n { key: \"status\", header: \"وضعیت\", render: (r) => <Badge tone=\"success\" dot>…</Badge> },\n { key: \"amount\", header: \"مبلغ (تومان)\", sortable: true, numeric: true,\n render: (r) => formatNumber(r.amount) },\n ]}\n/>\n```\n\n## بارگذاری، راه‌راه، سرستون ثابت و صفحه‌بندی\n\nروی «بارگذاری دوباره» بزنید تا ردیف‌های اسکلت را ببینید؛ روی هر ردیف بزنید تا رویداد کلیک را ببینید. جدول را اسکرول کنید: سرستون ثابت می‌ماند.\n\n```tsx\n<DataTable\n …\n striped\n stickyHeader\n maxHeight=\"16rem\"\n loading={isLoading ? 4 : false}\n onRowClick={(row) => open(row)}\n columns={[\n { key: \"date\", header: \"تاریخ\", hideBelow: \"sm\" },\n { key: \"status\", header: \"وضعیت\", align: \"center\" },\n …\n ]}\n footer={<Pagination page={page} total={8} onChange={setPage} />}\n/>\n```\n\n**ویژگی‌های جدید**\n\n| ویژگی | نوع | پیش‌فرض | توضیح |\n|---|---|---|---|\n| striped | boolean | false | ردیف‌های راه‌راه |\n| stickyHeader / maxHeight | boolean / string | — | سرستون ثابت هنگام اسکرول |\n| loading | boolean \\| number | false | ردیف‌های اسکلت؛ عدد = تعداد ردیف |\n| onRowClick | (row) => void | — | کلیک روی ردیف (کلیک روی دکمه و لینک داخل ردیف را نمی‌گیرد) |\n| footer | ReactNode | — | محتوای پایین جدول |\n| align / hideBelow / className (ستون) | — | — | چینش، پنهان شدن در موبایل، کلاس خانه‌ها |\n| classNames | { root, table, row, cell, header, footer } | — | کلاس دلخواه برای هر بخش |\n\nمتغیرهای CSS: `--k-table-radius`، `--k-table-cell-px`، `--k-table-cell-py`، `--k-table-header-bg`، `--k-table-stripe`، `--k-table-row-hover`.\n\n## ویژگی‌ها\n\n| ویژگی | نوع | پیش‌فرض | توضیح |\n|---|---|---|---|\n| columns | Column[] | — | ستون‌ها: key، header، render، sortable، sortValue، numeric، width |\n| rows | T[] | — | ردیف‌ها |\n| getRowId | (row) => string | — | شناسهٔ یکتای هر ردیف |\n| caption | ReactNode | — | عنوان جدول — الزامی، برای صفحه‌خوان |\n| description / hideCaption | ReactNode / boolean | — | توضیح زیر عنوان / پنهان کردن عنوان از دید |\n| selectable | boolean | false | ستون انتخاب با «انتخاب همه» |\n| selected / onSelectedChange | string[] | — | ردیف‌های انتخاب‌شده |\n| sort / defaultSort / onSortChange | { key, direction } \\| null | — | مرتب‌سازی؛ کنترل‌شده برای مرتب‌سازی سمت سرور |\n| empty | ReactNode | — | محتوای جدول خالی، مثلاً یک EmptyState |\n| compact | boolean | false | ردیف‌های فشرده |\n\n## دسترس‌پذیری\n\n- جدول واقعی HTML با `<caption>` و سرستون‌های `scope=\"col\"`؛ صفحه‌خوان‌ها می‌توانند بین خانه‌ها پیمایش کنند.\n- سرستون مرتب‌شده `aria-sort` دارد و مرتب‌سازی با یک دکمهٔ واقعی انجام می‌شود.\n- چک‌باکس هر ردیف نام اختصاصی می‌گیرد (`labels.selectRow`)، مثل «انتخاب تراکنش سارا احمدی».\n- ظرف جدول در صفحه‌های باریک اسکرول افقی دارد و با کیبورد (Tab) قابل‌اسکرول است.\n\n## راهنمای استفاده\n\nکِی استفاده کنیم:\n\n- داده‌های ردیفی که مقایسه، مرتب و انتخاب می‌شوند: سفارش‌ها، تراکنش‌ها، کاربران.\n\nکِی استفاده نکنیم:\n\n- دو سه ستون که در موبایل بهتر است فهرست یا کارت باشند. (کارت: /docs/components/card/)\n- چیدمان صفحه؛ جدول برای داده است.\n\nدرست و نادرست:\n\n- ✓ درست: ستون‌های عددی و مبلغ `numeric` بگیرند تا هم‌تراز شوند و رقم‌های یکان و دهگان زیر هم بیایند.\n- ✗ نادرست: مبلغ‌ها با ارقام لاتین و چپ‌چین در جدول فارسی.\n- ✓ درست: حالت خالی (`empty`) که بگوید چرا خالی است.\n- ✗ نادرست: جدول بی‌ردیف با فقط سرستون‌ها.\n\n## وب کامپوننت\n\nهمین کامپوننت بدون React، برای HTML، وردپرس، Vue، Svelte و Angular: `<kashi-data-table>` در `@kashi-ui/elements`. یک `<table>` معمولی را بهتر می‌کند: `th[data-sort]` مرتب‌شدنی، `data-numeric` برای عدد و `tr[data-id]` شناسهٔ ردیف. بدون JavaScript همان جدول با ظاهر کاشی دیده می‌شود.\n\nنصب بدون ابزار ساخت (CDN):\n\n```html\n<link rel=\"stylesheet\" href=\"https://cdn.jsdelivr.net/npm/@kashi-ui/css/dist/kashi.css\">\n<script type=\"module\" src=\"https://cdn.jsdelivr.net/npm/@kashi-ui/elements/dist/cdn/kashi-elements.js\"></script>\n```\n\nیا با npm، فقط همین بخش:\n\n```bash\nnpm i @kashi-ui/elements @kashi-ui/css\n```\n\n```js\nimport \"@kashi-ui/css\";\nimport \"@kashi-ui/elements/table.js\";\n```\n\n```html\n<kashi-data-table selectable searchable name=\"orders\">\n <table>\n <caption>سفارش‌های امروز</caption>\n <thead><tr><th data-sort>مشتری</th><th data-sort>شهر</th><th data-sort data-numeric>مبلغ (تومان)</th></tr></thead>\n <tbody>\n <tr data-id=\"1\"><td>سارا احمدی</td><td>تهران</td><td>۱٬۲۰۰٬۰۰۰</td></tr>\n <tr data-id=\"2\"><td>علی رضایی</td><td>اصفهان</td><td>۹۸۰٬۰۰۰</td></tr>\n <tr data-id=\"3\"><td>مینا کریمی</td><td>شیراز</td><td>۳۵۰٬۰۰۰</td></tr>\n </tbody>\n </table>\n</kashi-data-table>\n```\n\nویژگی‌ها:\n\n| نام | مقدار | پیش‌فرض | توضیح |\n|---|---|---|---|\n| selectable | بولی | — | چک‌باکس انتخاب ردیف |\n| searchable | بولی | — | جستجو بالای جدول |\n| name | متن | — | شناسهٔ ردیف‌های انتخاب‌شده در فرم |\n| compact / striped / sticky-header | بولی | — | ظاهر جدول |\n\nرویدادها و JavaScript:\n\n| نام | توضیح |\n|---|---|\n| kashi-change | انتخاب ردیف؛ detail: { selected } |\n| kashi-sort | detail: { column, direction } |\n| element.columns / element.rows | ساخت جدول از داده: [{ key, header, sortable, numeric, format }] |\n| element.selected | شناسهٔ ردیف‌های انتخاب‌شده |"},{"slug":"components/tabs","url":"https://kashi.sepantapouya.com/docs/components/tabs/","title":"تب‌ها","description":"نمایش بخش‌های مرتبط یک محتوا در یک فضا، با سه سبک و پیمایش کامل کیبورد.","group":"کامپوننت‌ها","components":["Tabs","TabList","Tab","TabPanel"],"markdown":"```tsx\nimport { Tabs, TabList, Tab, TabPanel } from \"@kashi-ui/react\";\n```\n\n## خطی\n\n```tsx\n<Tabs defaultValue=\"account\">\n <TabList aria-label=\"تنظیمات\">\n <Tab value=\"account\">حساب کاربری</Tab>\n <Tab value=\"security\">امنیت</Tab>\n <Tab value=\"billing\">پرداخت <Badge tone=\"brand\" size=\"sm\">۲</Badge></Tab>\n <Tab value=\"api\" disabled>API</Tab>\n </TabList>\n <TabPanel value=\"account\">…</TabPanel>\n <TabPanel value=\"security\">…</TabPanel>\n <TabPanel value=\"billing\">…</TabPanel>\n</Tabs>\n```\n\n## قرصی\n\n```tsx\n<Tabs defaultValue=\"account\" variant=\"pill\">…</Tabs>\n```\n\n## چندبخشی\n\n```tsx\n<Tabs defaultValue=\"account\" variant=\"segmented\">…</Tabs>\n```\n\n## زبانه‌های زیاد در صفحهٔ باریک\n\nزبانه‌ها هیچ‌وقت ظرفشان را پهن‌تر نمی‌کنند: اگر در عرض جا نشوند، فهرست زبانه‌ها داخل کادر خودش افقی اسکرول می‌خورد و صفحه در موبایل اسکرول افقی نمی‌گیرد. محتوای پهن پنل (کد، جدول) هم باید اسکرول خودش را داشته باشد، مثل `overflow-x: auto`.\n\n## کیبورد\n\n| کلید | عمل |\n|---|---|\n| <kbd>Tab</kbd> | ورود به زبانهٔ فعال، سپس رفتن به محتوای پنل |\n| <kbd>←</kbd> / <kbd>→</kbd> | زبانهٔ بعد / قبل (در راست‌به‌چپ) و فعال‌سازی آن |\n| <kbd>Home</kbd> / <kbd>End</kbd> | اولین / آخرین زبانه |\n\n## عمودی\n\nبرای صفحه‌های تنظیمات؛ کلیدهای ↑ و ↓ بین زبانه‌ها جابه‌جا می‌کنند. در صفحه‌های باریک خودکار افقی می‌شود.\n\n```tsx\n<Tabs defaultValue=\"profile\" orientation=\"vertical\">\n <TabList aria-label=\"تنظیمات حساب\">…</TabList>\n …\n</Tabs>\n```\n\n**ویژگی‌های جدید**\n\n| ویژگی | نوع | پیش‌فرض | توضیح |\n|---|---|---|---|\n| orientation | \"horizontal\" \\| \"vertical\" | \"horizontal\" | جهت؛ aria-orientation و کلیدهای پیمایش هم تغییر می‌کنند |\n| size | \"sm\" \\| \"md\" | \"md\" | اندازه |\n\nمتغیرهای CSS: `--k-tab-height`، `--k-tab-radius`، `--k-tabs-gap`، `--k-tabs-indicator`، `--k-tabs-list-width`.\n\n## ویژگی‌ها\n\n| ویژگی | نوع | پیش‌فرض | توضیح |\n|---|---|---|---|\n| value / defaultValue | string | — | زبانهٔ فعال (Tabs) |\n| onChange | (value: string) => void | — | با تغییر زبانه |\n| variant | \"line\" \\| \"pill\" \\| \"segmented\" | \"line\" | سبک |\n| block | boolean | false | زبانه‌ها تمام عرض را پر کنند |\n| aria-label | string | — | نام فهرست زبانه‌ها (TabList) — الزامی |\n| value | string | — | شناسهٔ زبانه و پنل متناظر (Tab و TabPanel) |\n\nنشانگر زبانهٔ فعال (خط زیر زبانه، یا پس‌زمینه در گونه‌های قرصی و چندبخشی) با تغییر زبانه به‌نرمی جابه‌جا می‌شود؛ با تنظیم «کاهش حرکت» در سیستم، بدون انیمیشن.\n\n## دسترس‌پذیری\n\n- الگوی Tabs در WAI-ARIA: `tablist`، `tab` و `tabpanel` با اتصال `aria-controls` و `aria-labelledby`.\n- فقط زبانهٔ فعال در ترتیب Tab است؛ زبانه‌های غیرفعال (`disabled`) در پیمایش رد می‌شوند.\n- برای ناوبری بین صفحه‌ها از زبانه استفاده نکنید؛ لینک‌ها مناسب‌ترند.\n\n## راهنمای استفاده\n\nکِی استفاده کنیم:\n\n- چند بخش هم‌سطح یک محتوا در یک فضا: «مشخصات / نظرها / پرسش‌ها» در صفحهٔ محصول.\n\nکِی استفاده نکنیم:\n\n- مراحل پشت‌سرهم. (استپر: /docs/components/stepper/)\n- عوض کردن نمای همان داده (جدول یا کارت). (سگمنت: /docs/components/segmented-control/)\n- محتوایی که کاربر باید با هم مقایسه کند؛ زبانه‌ها فقط یکی را در هر زمان نشان می‌دهند.\n\nدرست و نادرست:\n\n- ✓ درست: دو تا شش زبانه با عنوان یکی دو کلمه‌ای.\n- ✗ نادرست: زبانه‌های زیاد که اسکرول افقی می‌خواهند.\n\n## وب کامپوننت\n\nهمین کامپوننت بدون React، برای HTML، وردپرس، Vue، Svelte و Angular: `<kashi-tabs>` در `@kashi-ui/elements`. هر فرزند با `data-tab` یک زبانه است (`data-value` برای مقدار، `data-disabled` برای غیرفعال). بدون JavaScript همهٔ بخش‌ها دیده می‌شوند.\n\nنصب بدون ابزار ساخت (CDN):\n\n```html\n<link rel=\"stylesheet\" href=\"https://cdn.jsdelivr.net/npm/@kashi-ui/css/dist/kashi.css\">\n<script type=\"module\" src=\"https://cdn.jsdelivr.net/npm/@kashi-ui/elements/dist/cdn/kashi-elements.js\"></script>\n```\n\nیا با npm، فقط همین بخش:\n\n```bash\nnpm i @kashi-ui/elements @kashi-ui/css\n```\n\n```js\nimport \"@kashi-ui/css\";\nimport \"@kashi-ui/elements/overlay.js\";\n```\n\n```html\n<kashi-tabs label=\"تنظیمات حساب\">\n <section data-tab=\"پروفایل\">نام، تصویر و نام کاربری.</section>\n <section data-tab=\"امنیت\">رمز عبور و ورود دومرحله‌ای.</section>\n <section data-tab=\"اعلان‌ها\">پیامک و ایمیل.</section>\n</kashi-tabs>\n```\n\nویژگی‌ها:\n\n| نام | مقدار | پیش‌فرض | توضیح |\n|---|---|---|---|\n| label | متن | — | نام فهرست زبانه‌ها برای صفحه‌خوان |\n| value | متن | اولین زبانه | زبانهٔ فعال |\n| variant | line \\| pill \\| segmented | line | ظاهر |\n| orientation | vertical | — | زبانه‌ها در ستون کناری |\n| size / block | sm / بولی | — | اندازه و تمام‌عرض |\n\nرویدادها و JavaScript:\n\n| نام | توضیح |\n|---|---|\n| kashi-change | detail: { value } |"},{"slug":"components/tag-input","url":"https://kashi.sepantapouya.com/docs/components/tag-input/","title":"ورودی تگ","description":"نوشتن چند مقدار در یک فیلد، مثل مهارت‌ها، برچسب‌ها یا گیرندگان ایمیل.","group":"کامپوننت‌ها","components":["TagInput"],"markdown":"```tsx\nimport { TagInput } from \"@kashi-ui/react\";\n```\n\n## نمونه\n\n```tsx\nconst [tags, setTags] = useState([\"طراحی\", \"React\"]);\n\n<TagInput label=\"مهارت‌ها\" value={tags} onChange={setTags} max={5}\n hint=\"با Enter یا ویرگول جدا کنید؛ حداکثر ۵ مورد.\" />\n```\n\nبا <kbd>Enter</kbd>، ویرگول انگلیسی و ویرگول فارسی «،» برچسب ساخته می‌شود؛ متن چسبانده‌شده هم جدا می‌شود. <kbd>Backspace</kbd> در فیلد خالی آخرین برچسب را حذف می‌کند.\n\n## اعتبارسنجی\n\n```tsx\n<TagInput label=\"گیرندگان\" tone=\"neutral\"\n validate={(t) => (isEmail(t) ? undefined : \"این ایمیل معتبر نیست.\")} />\n```\n\n| ویژگی | نوع | پیش‌فرض | توضیح |\n|---|---|---|---|\n| value / defaultValue | string[] | [] | برچسب‌ها |\n| onChange | (tags: string[]) => void | — | — |\n| separators | string[] | [\"Enter\", \",\", \"،\"] | کلیدهای جداکننده |\n| validate | (tag) => string \\| undefined | — | پیام خطا برای برچسب نامعتبر |\n| max | number | — | بیشترین تعداد |\n| allowDuplicates | boolean | false | — |\n| tone | BadgeTone | \"brand\" | رنگ برچسب‌ها |\n| name | string | — | یک فیلد پنهان برای هر برچسب |\n\n## دسترس‌پذیری\n\nهر برچسب دکمهٔ حذف با نام «حذف React» دارد. افزودن، حذف و خطاها (تکراری، بیش از حد مجاز) با `aria-live` اعلام می‌شوند و پیام خطا به فیلد وصل است.\n\n## راهنمای استفاده\n\nکِی استفاده کنیم:\n\n- چند مقدار آزاد در یک فیلد: مهارت‌ها، برچسب‌ها، ایمیل گیرندگان.\n\nکِی استفاده نکنیم:\n\n- انتخاب از فهرست مشخص. (انتخاب چندتایی: /docs/components/multi-select/)\n- یک مقدار. (ورودی: /docs/components/input/)\n\nدرست و نادرست:\n\n- ✓ درست: با `validate` همان لحظه بررسی کنید (مثلاً ایمیل درست) و برچسب تکراری نپذیرید.\n- ✗ نادرست: هر متنی بپذیرید و بعد از ارسال بگویید یکی از ایمیل‌ها اشتباه بود.\n\n## وب کامپوننت\n\nهمین کامپوننت بدون React، برای HTML، وردپرس، Vue، Svelte و Angular: `<kashi-tag-input>` در `@kashi-ui/elements`.\n\nنصب بدون ابزار ساخت (CDN):\n\n```html\n<link rel=\"stylesheet\" href=\"https://cdn.jsdelivr.net/npm/@kashi-ui/css/dist/kashi.css\">\n<script type=\"module\" src=\"https://cdn.jsdelivr.net/npm/@kashi-ui/elements/dist/cdn/kashi-elements.js\"></script>\n```\n\nیا با npm، فقط همین بخش:\n\n```bash\nnpm i @kashi-ui/elements @kashi-ui/css\n```\n\n```js\nimport \"@kashi-ui/css\";\nimport \"@kashi-ui/elements/controls.js\";\n```\n\n```html\n<kashi-tag-input label=\"مهارت‌ها\" name=\"skills\" value=\"طراحی، فیگما\" max=\"5\" placeholder=\"بنویسید و Enter بزنید\"></kashi-tag-input>\n```\n\nویژگی‌ها:\n\n| نام | مقدار | پیش‌فرض | توضیح |\n|---|---|---|---|\n| label / hint / error / placeholder | متن | — | مثل بقیهٔ فیلدها |\n| name | متن | — | هر برچسب یک فیلد هم‌نام در فرم |\n| value | جدا با «،» | — | برچسب‌های اولیه |\n| max | عدد | — | بیشترین تعداد |\n| allow-duplicates / tone | بولی / رنگ نشان | brand | تکراری و رنگ برچسب‌ها |\n\nرویدادها و JavaScript:\n\n| نام | توضیح |\n|---|---|\n| kashi-change | detail: { value: [...] } |\n| element.validate | تابع اعتبارسنجی هر برچسب؛ پیام خطا برمی‌گرداند |"},{"slug":"components/timeline","url":"https://kashi.sepantapouya.com/docs/components/timeline/","title":"تایم‌لاین","description":"نمایش رویدادها و فعالیت‌ها به ترتیب زمان، مثل تاریخچهٔ سفارش یا فعالیت تیم.","group":"کامپوننت‌ها","components":["Timeline"],"markdown":"```tsx\nimport { Timeline } from \"@kashi-ui/react\";\n```\n\n## نمونه\n\n```tsx\n<Timeline items={[\n { title: <><strong>سارا محمدی</strong> سفارش را تأیید کرد</>, time: new Date(…), tone: \"success\", icon: <CheckIcon /> },\n { title: <><strong>رضا کریمی</strong> نظر گذاشت</>, time: new Date(…), icon: <Avatar name=\"رضا کریمی\" size=\"sm\" />,\n content: \"بسته‌بندی را دوبل کنید؛ کالا شکستنی است.\" },\n { title: \"سفارش ثبت شد\", time: \"۱۲ آبان، ۱۰:۲۰\" },\n]} />\n```\n\nاگر `time` از نوع `Date` باشد، به شکل نسبی («۵ دقیقه پیش») با `RelativeTime` نمایش داده و خودکار به‌روز می‌شود؛ متن هم می‌توانید بدهید.\n\n| ویژگی | نوع | پیش‌فرض | توضیح |\n|---|---|---|---|\n| items | { id?, title, time?, icon?, tone?, content? }[] | — | رویدادها به ترتیب نمایش |\n| items[].tone | \"neutral\" \\| \"brand\" \\| \"success\" \\| \"error\" | \"neutral\" | رنگ نشانگر |\n| digits | \"fa\" \\| \"latin\" | \"fa\" | ارقام زمان نسبی |\n\n## دسترس‌پذیری\n\nفهرست مرتب `<ol>` است، پس صفحه‌خوان تعداد و ترتیب رویدادها را می‌گوید. نشانگرها تزئینی‌اند و زمان در عنصر `<time>` با تاریخ دقیق است.\n\n## راهنمای استفاده\n\nکِی استفاده کنیم:\n\n- رویدادهای یک مورد به ترتیب زمان: تاریخچهٔ سفارش، فعالیت تیم، تغییرات یک سند.\n\nکِی استفاده نکنیم:\n\n- مراحلی که هنوز انجام نشده‌اند و کاربر طی می‌کند. (استپر: /docs/components/stepper/)\n\nدرست و نادرست:\n\n- ✓ درست: زمان نسبی برای رویدادهای تازه («۵ دقیقه پیش») و تاریخ کامل در `title`.\n- ✗ نادرست: تاریخ و ساعت کامل برای همه، که خواندن را کند می‌کند."},{"slug":"components/toast","url":"https://kashi.sepantapouya.com/docs/components/toast/","title":"توست","description":"پیام کوتاه و موقت پس از یک عمل، مثل «ذخیره شد»؛ با توقف هنگام hover و امکان واگرد.","group":"کامپوننت‌ها","components":["Toaster"],"markdown":"```tsx\nimport { Toaster, toast } from \"@kashi-ui/react\";\n```\n\n`<Toaster />` را یک بار در ریشهٔ برنامه بگذارید؛ سپس از هر جای برنامه `toast()` را صدا بزنید.\n\n## نمونه\n\n```tsx\ntoast.success(\"تغییرات ذخیره شد\", { description: \"…\" });\ntoast.error(\"پرداخت ناموفق بود\");\ntoast.warning(\"اتصال اینترنت ناپایدار است\");\ntoast({\n title: \"پیام بایگانی شد\",\n action: { label: \"واگرد\", onClick: () => restore() },\n});\n\n// در ریشهٔ برنامه\n<Toaster />\n```\n\n## رفتار\n\n- **زمان:** پیش‌فرض ۵ ثانیه. نوار پایین زمان باقی‌مانده را نشان می‌دهد و با hover یا فوکوس متوقف می‌شود.\n- **با عمل:** اعلانی که دکمه دارد (مثل «واگرد») خودکار بسته نمی‌شود تا کاربر فرصت کافی داشته باشد.\n- **بستن:** با دکمهٔ بستن یا <kbd>Esc</kbd> وقتی فوکوس روی اعلان است.\n\n## کار زمان‌بر و جایگاه\n\n`toast.promise` اعلان «در حال ذخیره…» را نشان می‌دهد و پس از پایان کار، **همان اعلان** را به پیام موفقیت یا خطا تغییر می‌دهد. (در این نمونه از هر چهار بار، حدود یک بار خطا شبیه‌سازی می‌شود.)\n\n```tsx\ntoast.promise(saveFiles(), {\n loading: \"در حال ذخیره…\",\n success: (n) => `${n} فایل ذخیره شد`,\n error: (e) => `ذخیره نشد: ${e.message}`,\n});\n\n<Toaster placement=\"top-center\" max={3} />\n```\n\n**ویژگی‌های جدید**\n\n| ویژگی | نوع | پیش‌فرض | توضیح |\n|---|---|---|---|\n| placement (Toaster) | \"top-start\" \\| \"top-center\" \\| \"top-end\" \\| \"bottom-start\" \\| \"bottom-center\" \\| \"bottom-end\" | \"bottom-end\" | جایگاه؛ در RTL «end» سمت چپ است |\n| max (Toaster) | number | 5 | بیشترین اعلان هم‌زمان |\n| toast.promise | (promise, { loading, success, error }) | — | اعلان یک کار زمان‌بر |\n| id (toast) | number \\| string | — | با شناسهٔ تکراری، اعلان قبلی به‌روز می‌شود |\n\n## ویژگی‌های toast()\n\n| ویژگی | نوع | پیش‌فرض | توضیح |\n|---|---|---|---|\n| title | ReactNode | — | متن اصلی |\n| description | ReactNode | — | توضیح |\n| tone | \"neutral\" \\| \"success\" \\| \"error\" \\| \"warning\" \\| \"info\" | \"neutral\" | رنگ و آیکون |\n| duration | number | ۵۰۰۰، یا بی‌نهایت با action | میلی‌ثانیه |\n| action | { label, onClick } | — | دکمهٔ عمل |\n\n## دسترس‌پذیری\n\n- ناحیهٔ اعلان‌ها با `aria-live=\"polite\"` از ابتدا در صفحه است تا اعلان‌های جدید بدون جابه‌جایی فوکوس خوانده شوند؛ اعلان خطا با `role=\"alert\"` فوراً خوانده می‌شود.\n- توقف زمان هنگام hover و فوکوس و ماندن اعلان‌های دارای عمل، معیار WCAG 2.2.1 (زمان کافی) را برآورده می‌کنند.\n- اطلاعات حیاتی را فقط در توست نگذارید؛ ممکن است کاربر آن را نبیند.\n\n## راهنمای استفاده\n\nکِی استفاده کنیم:\n\n- تأیید کوتاه پس از یک عمل: «ذخیره شد»، «به سبد اضافه شد».\n- عمل برگشت‌پذیر با دکمهٔ «واگرد» (`action`).\n\nکِی استفاده نکنیم:\n\n- خطایی که کاربر باید درستش کند؛ کنار همان بخش نشانش دهید. (هشدار: /docs/components/alert/)\n- اطلاعات مهمی که نباید از دست برود، مثل کد پیگیری پرداخت. (رسید پرداخت: /docs/persian/receipt/)\n- تأیید پیش از عمل. (دیالوگ: /docs/components/dialog/)\n\nدرست و نادرست:\n\n- ✓ درست: یک جملهٔ کوتاه: «سفارش ذخیره شد».\n- ✗ نادرست: دو پاراگراف که پیش از خوانده شدن ناپدید می‌شود.\n- ✓ درست: برای حذف، به‌جای گفتگوی تأیید، حذف کنید و «واگرد» بدهید.\n- ✗ نادرست: چند توست پشت‌سرهم برای هر قدم یک کار.\n\n## وب کامپوننت\n\nهمین کامپوننت بدون React، برای HTML، وردپرس، Vue، Svelte و Angular: `<kashi-toaster>` در `@kashi-ui/elements`. دکمه با `data-kashi-toast` بدون JavaScript اعلان نشان می‌دهد. اگر `<kashi-toaster>` در صفحه نباشد، خودکار ساخته می‌شود.\n\nنصب بدون ابزار ساخت (CDN):\n\n```html\n<link rel=\"stylesheet\" href=\"https://cdn.jsdelivr.net/npm/@kashi-ui/css/dist/kashi.css\">\n<script type=\"module\" src=\"https://cdn.jsdelivr.net/npm/@kashi-ui/elements/dist/cdn/kashi-elements.js\"></script>\n```\n\nیا با npm، فقط همین بخش:\n\n```bash\nnpm i @kashi-ui/elements @kashi-ui/css\n```\n\n```js\nimport \"@kashi-ui/css\";\nimport \"@kashi-ui/elements/toast.js\";\n```\n\n```html\n<button class=\"k-btn k-btn--primary\" data-kashi-toast=\"ذخیره شد\" data-description=\"تغییرات شما اعمال شد.\" data-tone=\"success\">نمایش اعلان</button>\n```\n\nویژگی‌ها:\n\n| نام | مقدار | پیش‌فرض | توضیح |\n|---|---|---|---|\n| placement | top-start … bottom-end | bottom-end | kashi-toaster: جای اعلان‌ها |\n| max | عدد | 5 | kashi-toaster: بیشترین تعداد هم‌زمان |\n| data-kashi-toast / data-description / data-tone | متن | — | روی هر دکمه: عنوان، توضیح و رنگ اعلان |\n\nرویدادها و JavaScript:\n\n| نام | توضیح |\n|---|---|\n| toast(\"…\") / toast.success / error / warning / info | import { toast } from \"@kashi-ui/elements\" |\n| toast.promise(promise, { loading, success, error }) | اعلان برای کار زمان‌بر |\n| toast.dismiss(id) | بستن اعلان |"},{"slug":"components/tooltip","url":"https://kashi.sepantapouya.com/docs/components/tooltip/","title":"تولتیپ","description":"توضیح کوتاه یک کنترل هنگام hover یا فوکوس.","group":"کامپوننت‌ها","components":["Tooltip"],"markdown":"```tsx\nimport { Tooltip } from \"@kashi-ui/react\";\n```\n\n## نمونه\n\nنشانگر را روی دکمه‌ها ببرید یا با <kbd>Tab</kbd> روی آن‌ها بروید.\n\n```tsx\n<Tooltip content=\"جستجو در همهٔ پروژه‌ها\">\n <Button iconOnly aria-label=\"جستجو\"><SearchIcon /></Button>\n</Tooltip>\n\n<Tooltip content=\"ایمیل برای همه قابل‌مشاهده نیست\" placement=\"bottom\">\n …\n</Tooltip>\n```\n\n## چهار جایگاه\n\n```tsx\n<Tooltip content=\"…\" placement=\"start\">…</Tooltip>\n```\n\n`placement` حالا `\"start\"` و `\"end\"` هم می‌پذیرد. متغیرهای CSS: `--k-tooltip-bg`، `--k-tooltip-fg`، `--k-tooltip-max-width`، `--k-tooltip-radius`.\n\n## ویژگی‌ها\n\n| ویژگی | نوع | پیش‌فرض | توضیح |\n|---|---|---|---|\n| content | ReactNode | — | متن راهنما |\n| children | ReactElement | — | یک عنصر فوکوس‌پذیر، معمولاً دکمه |\n| placement | \"top\" \\| \"bottom\" | \"top\" | جایگاه |\n| delay | number | 300 | تأخیر نمایش با hover (میلی‌ثانیه)؛ با فوکوس فوری است |\n\n## دسترس‌پذیری\n\n- با `aria-describedby` به عنصر متصل است؛ صفحه‌خوان آن را پس از نام دکمه می‌خواند.\n- طبق WCAG 1.4.13: با <kbd>Esc</kbd> بسته می‌شود، نشانگر می‌تواند روی خود راهنما برود بدون اینکه بسته شود، و تا وقتی hover یا فوکوس ادامه دارد می‌ماند.\n- تولتیپ **جایگزین برچسب نیست**: دکمهٔ فقط‌آیکون همچنان `aria-label` لازم دارد. اطلاعات ضروری را در آن پنهان نکنید؛ کاربران لمسی آن را نمی‌بینند.\n\n## راهنمای استفاده\n\nکِی استفاده کنیم:\n\n- نام دکمهٔ فقط‌آیکون.\n- توضیح یک خط برای واژه یا نماد ناآشنا.\n\nکِی استفاده نکنیم:\n\n- اطلاعاتی که کاربر برای کارش لازم دارد؛ تولتیپ در موبایل دیده نمی‌شود. آن را در متن راهنمای فیلد بنویسید.\n- محتوای تعاملی مثل پیوند یا دکمه. (پاپ‌اور: /docs/components/popover/)\n\nدرست و نادرست:\n\n- ✓ درست: چند کلمه که همان برچسب صفحه‌خوان هم هست: «حذف سفارش».\n- ✗ نادرست: پاراگراف راهنما که روی محتوا می‌افتد.\n\n## وب کامپوننت\n\nهمین کامپوننت بدون React، برای HTML، وردپرس، Vue، Svelte و Angular: `<kashi-tooltip>` در `@kashi-ui/elements`.\n\nنصب بدون ابزار ساخت (CDN):\n\n```html\n<link rel=\"stylesheet\" href=\"https://cdn.jsdelivr.net/npm/@kashi-ui/css/dist/kashi.css\">\n<script type=\"module\" src=\"https://cdn.jsdelivr.net/npm/@kashi-ui/elements/dist/cdn/kashi-elements.js\"></script>\n```\n\nیا با npm، فقط همین بخش:\n\n```bash\nnpm i @kashi-ui/elements @kashi-ui/css\n```\n\n```js\nimport \"@kashi-ui/css\";\nimport \"@kashi-ui/elements/overlay.js\";\n```\n\n```html\n<kashi-tooltip content=\"تغییرات در پیش‌نویس ذخیره می‌شود\">\n <button class=\"k-btn k-btn--secondary\">ذخیره</button>\n</kashi-tooltip>\n```\n\nویژگی‌ها:\n\n| نام | مقدار | پیش‌فرض | توضیح |\n|---|---|---|---|\n| content | متن | — | متن راهنما |\n| placement | top \\| bottom \\| start \\| end | top | جای راهنما |\n| delay | میلی‌ثانیه | 300 | تأخیر نمایش با hover |"},{"slug":"components/tour","url":"https://kashi.sepantapouya.com/docs/components/tour/","title":"تور معرفی","description":"معرفی بخش‌های تازهٔ محصول به کاربر، مرحله به مرحله، با روشن کردن همان بخش.","group":"کامپوننت‌ها","components":["Tour"],"markdown":"```tsx\nimport { Tour } from \"@kashi-ui/react\";\n```\n\n## نمونه\n\n```tsx\n<Tour open={open} onOpenChange={setOpen} steps={[\n { target: \"#search\", title: \"جستجوی سریع\", content: \"نام مشتری یا شمارهٔ سفارش را بنویسید.\" },\n { target: \"#export\", title: \"خروجی اکسل\", content: \"فهرست فیلترشده را دانلود کنید.\" },\n]} />\n```\n\nراهنما را کوتاه نگه دارید (سه تا پنج مرحله) و همیشه امکان «رد کردن» بدهید. بهتر است فقط یک بار، برای کاربر تازه یا بخش تازه، نمایش داده شود.\n\n| ویژگی | نوع | پیش‌فرض | توضیح |\n|---|---|---|---|\n| steps | { target, title, content }[] | — | target یک CSS selector است |\n| open / onOpenChange | boolean / (open) => void | — | — |\n| onFinish | () => void | — | پس از آخرین مرحله |\n| labels | { next?, prev?, done?, skip?, step? } | — | متن دکمه‌ها |\n\n## دسترس‌پذیری\n\nهر مرحله یک `role=\"dialog\"` غیرمودال با عنوان و توضیح است و فوکوس به آن می‌رود. <kbd>Esc</kbd> می‌بندد، ←→ بین مرحله‌ها حرکت می‌کند و پس از پایان فوکوس به جای قبلی برمی‌گردد.\n\n## راهنمای استفاده\n\nکِی استفاده کنیم:\n\n- معرفی قابلیت تازه یا تغییر مهم رابط به کاربران فعلی، در سه تا پنج قدم.\n\nکِی استفاده نکنیم:\n\n- آموزش کامل محصول؛ کاربر قدم‌ها را رد می‌کند. راهنمای جدا بنویسید.\n- هر بار ورود؛ فقط یک بار و با امکان رد کردن نشانش دهید.\n\nدرست و نادرست:\n\n- ✓ درست: هر قدم یک جمله دربارهٔ اینکه این بخش برای کاربر چه می‌کند.\n- ✗ نادرست: قدم‌هایی که فقط نام دکمه‌ها را تکرار می‌کنند: «این دکمهٔ ذخیره است».\n\n## وب کامپوننت\n\nهمین کامپوننت بدون React، برای HTML، وردپرس، Vue، Svelte و Angular: `<kashi-tour>` در `@kashi-ui/elements`. هر مرحله یک `<div data-target data-title>` است؛ با دکمه‌ای با `data-kashi-tour` یا `element.start()` شروع می‌شود.\n\nنصب بدون ابزار ساخت (CDN):\n\n```html\n<link rel=\"stylesheet\" href=\"https://cdn.jsdelivr.net/npm/@kashi-ui/css/dist/kashi.css\">\n<script type=\"module\" src=\"https://cdn.jsdelivr.net/npm/@kashi-ui/elements/dist/cdn/kashi-elements.js\"></script>\n```\n\nیا با npm، فقط همین بخش:\n\n```bash\nnpm i @kashi-ui/elements @kashi-ui/css\n```\n\n```js\nimport \"@kashi-ui/css\";\nimport \"@kashi-ui/elements/nav.js\";\n```\n\n```html\n<button class=\"k-btn k-btn--primary\" id=\"tour-start\" data-kashi-tour=\"intro\">شروع تور</button>\n<kashi-tour id=\"intro\">\n <div data-target=\"#tour-start\" data-title=\"شروع از اینجا\">با این دکمه تور دوباره شروع می‌شود.</div>\n <div data-target=\".page-head h1\" data-title=\"عنوان صفحه\">نام کامپوننت و توضیح کوتاهش.</div>\n</kashi-tour>\n```\n\nویژگی‌ها:\n\n| نام | مقدار | پیش‌فرض | توضیح |\n|---|---|---|---|\n| data-target / data-title | روی هر مرحله | — | انتخابگر CSS بخش و عنوان مرحله |\n\nرویدادها و JavaScript:\n\n| نام | توضیح |\n|---|---|\n| element.start() / stop() | شروع و پایان |\n| kashi-finish | پس از آخرین مرحله |\n| element.steps | مراحل با JavaScript: [{ target, title, content }] |"},{"slug":"components/tree-view","url":"https://kashi.sepantapouya.com/docs/components/tree-view/","title":"نمای درختی","description":"نمایش ساختار سلسله‌مراتبی مثل پوشه‌ها، دسته‌بندی محصولات یا نمودار سازمانی.","group":"کامپوننت‌ها","components":["TreeView"],"markdown":"```tsx\nimport { TreeView, type TreeNode } from \"@kashi-ui/react\";\n```\n\n## نمونه\n\n```tsx\nconst items: TreeNode[] = [\n { id: \"docs\", label: \"اسناد\", icon: <FolderIcon />, meta: \"۳ پوشه\", children: [\n { id: \"contracts\", label: \"قراردادها\", icon: <FolderIcon />, children: [\n { id: \"c1\", label: \"قرارداد-۱۴۰۵.pdf\", icon: <FileIcon />, meta: \"۲٫۴ مگابایت\" },\n ] },\n { id: \"archive\", label: \"بایگانی\", disabled: true },\n ] },\n];\n\n<TreeView aria-label=\"فایل‌ها\" items={items} selected={sel} onSelect={setSel}\n defaultExpanded={[\"docs\", \"contracts\"]} />\n```\n\n| ویژگی | نوع | پیش‌فرض | توضیح |\n|---|---|---|---|\n| items | { id, label, icon?, meta?, disabled?, children? }[] | — | گره‌ها |\n| selected / defaultSelected | string \\| null | null | گره انتخاب‌شده |\n| onSelect | (id, node) => void | — | — |\n| expanded / defaultExpanded | string[] | [] | شناسهٔ گره‌های باز |\n| onExpandedChange | (ids: string[]) => void | — | — |\n| aria-label | string | — | نام درخت (الزامی) |\n\n## کیبورد\n\n| کلید | عمل |\n|---|---|\n| <kbd>↓</kbd> / <kbd>↑</kbd> | ردیف بعد / قبل |\n| <kbd>←</kbd> (در RTL) | باز کردن، یا رفتن به اولین زیرشاخه |\n| <kbd>→</kbd> (در RTL) | بستن، یا رفتن به والد |\n| <kbd>Home</kbd> / <kbd>End</kbd> | اولین / آخرین ردیف |\n| <kbd>Enter</kbd> / <kbd>Space</kbd> | انتخاب |\n\nدر صفحه‌های چپ‌به‌راست، جهت ← و → برعکس است.\n\n## دسترس‌پذیری\n\nالگوی Tree در WAI-ARIA با `role=\"tree\"`، `treeitem` و `group`؛ هر گره `aria-level` و `aria-expanded` دارد و فقط یک گره در ترتیب Tab است (roving tabindex). زیرشاخه‌های بسته رندر نمی‌شوند.\n\n## راهنمای استفاده\n\nکِی استفاده کنیم:\n\n- ساختار سلسله‌مراتبی که کاربر بازش می‌کند: پوشه‌ها، دسته‌بندی کالا، نمودار سازمانی.\n\nکِی استفاده نکنیم:\n\n- فقط یک سطح. (سایدبار: /docs/components/side-nav/)\n- انتخاب یک دسته در فرم؛ دو سلسله سلکت یا کامبوباکس ساده‌تر است. (کامبوباکس: /docs/components/combobox/)\n\nدرست و نادرست:\n\n- ✓ درست: مسیر مورد انتخاب‌شده از اول باز باشد (`expanded`).\n- ✗ نادرست: همه‌چیز بسته، و کاربر باید چهار سطح را باز کند تا جای فعلی‌اش را پیدا کند.\n\n## وب کامپوننت\n\nهمین کامپوننت بدون React، برای HTML، وردپرس، Vue، Svelte و Angular: `<kashi-tree-view>` در `@kashi-ui/elements`. ساختار همان فهرست تودرتوی `<ul>` و `<li>` است (`data-id`، `data-meta`، `data-disabled`) یا `element.items`.\n\nنصب بدون ابزار ساخت (CDN):\n\n```html\n<link rel=\"stylesheet\" href=\"https://cdn.jsdelivr.net/npm/@kashi-ui/css/dist/kashi.css\">\n<script type=\"module\" src=\"https://cdn.jsdelivr.net/npm/@kashi-ui/elements/dist/cdn/kashi-elements.js\"></script>\n```\n\nیا با npm، فقط همین بخش:\n\n```bash\nnpm i @kashi-ui/elements @kashi-ui/css\n```\n\n```js\nimport \"@kashi-ui/css\";\nimport \"@kashi-ui/elements/nav.js\";\n```\n\n```html\n<kashi-tree-view label=\"فایل‌های پروژه\" expanded=\"src\" selected=\"app\">\n <ul>\n <li data-id=\"src\">src\n <ul>\n <li data-id=\"app\" data-meta=\"۲ کیلوبایت\">App.tsx</li>\n <li data-id=\"main\">main.tsx</li>\n </ul>\n </li>\n <li data-id=\"readme\">README.md</li>\n </ul>\n</kashi-tree-view>\n```\n\nویژگی‌ها:\n\n| نام | مقدار | پیش‌فرض | توضیح |\n|---|---|---|---|\n| label | متن | — | نام درخت |\n| selected | شناسه | — | گرهٔ انتخاب‌شده |\n| expanded | شناسه‌ها با «,» | — | گره‌های باز |\n\nرویدادها و JavaScript:\n\n| نام | توضیح |\n|---|---|\n| kashi-change | detail: { value, node } |\n| kashi-expand | detail: { expanded: [...] } |\n| element.items | درخت با JavaScript: [{ id, label, children }] |"},{"slug":"i18n","url":"https://kashi.sepantapouya.com/docs/i18n/","title":"دوزبانه، فارسی و انگلیسی","description":"همهٔ متن‌های داخلی کامپوننت‌ها (دکمه‌ها، متن صفحه‌خوان، پیام‌های خطا، نام ماه‌ها و ارقام) با یک تنظیم انگلیسی می‌شوند؛ برای React و وب کامپوننت‌ها.","group":"شروع","components":[],"markdown":"کاشی به‌طور پیش‌فرض فارسی است. اگر محصولتان نسخهٔ انگلیسی هم دارد، لازم نیست برچسب هیچ دکمه‌ای را دستی عوض کنید: با یک تنظیم در ریشهٔ برنامه، همهٔ متن‌های داخلی کامپوننت‌ها انگلیسی می‌شوند. تقویم همچنان شمسی است، فقط نام ماه‌ها و روزها به انگلیسی نوشته می‌شود (Farvardin، Aban…).\n\n## نمونه\n\n```en\n<html lang=\"en\" dir=\"ltr\">\n …\n <KashiProvider locale=\"en\">\n <DatePicker label=\"Delivery date\" value={date} onChange={setDate} />\n <PriceInput label=\"Amount\" defaultValue={1250000} />\n <Pagination page={page} total={12} onChange={setPage} />\n </KashiProvider>\n```\n\n## چه چیزهایی عوض می‌شود\n\n| | فارسی | انگلیسی |\n|---|---|---|\n| دکمه‌ها و برچسب‌ها | بستن، قبلی، بعدی، تأیید | Close، Previous، Next، Apply |\n| متن صفحه‌خوان | «انتخاب تاریخ از تقویم» | «Choose a date from the calendar» |\n| پیام‌های خطا | کد ملی معتبر نیست. | Invalid national ID. |\n| ارقام | ۱٬۲۵۰٬۰۰۰ | 1,250,000 |\n| تاریخ | شنبه ۱۶ آبان ۱۴۰۵ | Saturday, 16 Aban 1405 |\n| مبلغ به حروف | یک میلیون و دویست و پنجاه هزار تومان | one million two hundred fifty thousand Toman |\n| نام بانک و اپراتور | بانک ملت، همراه اول | Bank Mellat، Hamrah-e Aval |\n| تعطیلی‌ها | عید نوروز | Nowruz |\n\nنام استان‌ها و شهرها، حروف پلاک خودرو و نوشتهٔ «ایران» روی پلاک فارسی می‌مانند، چون بخشی از خود داده‌اند. جهت صفحه را خودتان با `dir=\"ltr\"` روی `<html>` بگذارید. همهٔ استایل‌های کاشی با Logical Properties نوشته شده‌اند و در هر دو جهت درست می‌نشینند.\n\n## React\n\n```tsx\nimport { KashiProvider } from \"@kashi-ui/react\";\n\nexport default function App({ children }) {\n return <KashiProvider locale=\"en\">{children}</KashiProvider>;\n}\n```\n\nبدون `KashiProvider` همه‌چیز مثل قبل فارسی است. هر prop متنی که خودتان بدهید (`label`، `labels`، `invalidMessage`…) همچنان بر متن پیش‌فرض مقدم است.\n\n| ویژگی | نوع | پیش‌فرض | توضیح |\n|---|---|---|---|\n| locale | \"fa\" \\| \"en\" | \"fa\" | زبان متن‌های داخلی، نام ماه‌ها و روزها |\n| messages | KashiMessagesInput | — | عوض کردن هر متن دلخواه؛ بقیه از زبان پایه می‌آیند |\n| digits | \"fa\" \\| \"latin\" | \"fa\" برای فارسی، \"latin\" برای انگلیسی | ارقام پیش‌فرض؛ prop ‏digits هر کامپوننت بر آن مقدم است |\n\n### عوض کردن چند متن\n\n```tsx\n<KashiProvider locale=\"en\" messages={{ common: { close: \"Dismiss\" }, address: { plaque: \"Building no.\" } }}>\n```\n\nفرهنگ کامل متن‌ها با `faMessages` و `enMessages` در دسترس است تا کلیدها را ببینید یا زبان دیگری بسازید. در کامپوننت‌های خودتان هم با `useKashi()` به همان زبان، ارقام و نام ماه‌ها دسترسی دارید:\n\n```tsx\nimport { useKashi } from \"@kashi-ui/react\";\n\nfunction OrderCount({ count }: { count: number }) {\n const { n, locale } = useKashi();\n return <span>{locale === \"en\" ? `${n(count)} orders` : `${n(count)} سفارش`}</span>;\n}\n```\n\n## وب کامپوننت‌ها\n\n```html\n<script type=\"module\">\n import { setLocale } from \"https://cdn.jsdelivr.net/npm/@kashi-ui/elements/dist/cdn/kashi-elements.js\";\n setLocale(\"en\");\n</script>\n```\n\nبا npm:\n\n```ts\nimport { setLocale } from \"@kashi-ui/elements\";\nsetLocale(\"en\", { messages: { common: { close: \"Dismiss\" } } });\n```\n\n`setLocale` را پیش از ساخته شدن عنصرها، بالای صفحه صدا بزنید. هر ویژگی متنی عنصر (`label`، `placeholder`، `close-label`…) همچنان بر متن پیش‌فرض مقدم است.\n\n## ابزارهای فارسی\n\nتابع‌های `@kashi-ui/persian` هم گزینهٔ `locale` دارند:\n\n```ts\nformatJalali(date, { style: \"full\", locale: \"en\" }); // Saturday, 16 Aban 1405\nformatRelative(date, { locale: \"en\" }); // 5 minutes ago\nformatMoney(1250000, { locale: \"en\" }); // 1,250,000 Toman\nnumberToWords(1250000, { locale: \"en\" }); // one million two hundred fifty thousand\ngetIranHoliday(date, { locale: \"en\" }); // Nowruz\nbankName(getBankByCard(\"6104…\")!, \"en\"); // Bank Mellat\n```\n\n## تضمین\n\nتست خودکار هر کامپوننت React و هر وب کامپوننت را با زبان انگلیسی رندر می‌کند و اگر حتی یک حرف فارسی در متن یا متن صفحه‌خوانش بماند، شکست می‌خورد. کامپوننت تازه بدون ترجمهٔ انگلیسی منتشر نمی‌شود."},{"slug":"icons/usage","url":"https://kashi.sepantapouya.com/docs/icons/usage/","title":"راهنمای آیکون‌ها","description":"۴۸۰۰ آیکون نرم در شش سبک با گوشه‌های پیوسته، هم‌خانواده با کامپوننت‌های کاشی؛ برای React، HTML، هر فریم‌ورک دیگر و فیگما.","group":"شروع","components":[],"markdown":"آیکون‌های کاشی برای خود کاشی طراحی شده‌اند؛ همان سبکی که داخل دکمه‌ها، فیلدها و هشدارها می‌بینید. همه با `currentColor` رنگ می‌گیرند، پس رنگ متن اطراف را به ارث می‌برند و در تم تیره و روشن درست دیده می‌شوند. مجوز MIT است و در هر پروژه‌ای، حتی تجاری، آزادانه قابل استفاده‌اند.\n\n```bash\nnpm i @kashi-ui/icons\n```\n\nهمهٔ آیکون‌ها را دسته به دسته در [فهرست آیکون‌ها](/docs/icons/) ببینید.\n\n## شش سبک\n\nهر آیکون در شش سبک هست و همه از یک طرح خطی ساخته می‌شوند: `light` (خطی، پیش‌فرض)، `bold` (توپر)، `bulk` (حجمی)، `two-tone` (دورنگ)، `broken` (شکسته) و `curved` (منحنی).\n\n```tsx\n<IconWallet variant=\"bold\" />\n<Icon name=\"calendar\" variant=\"bulk\" />\n```\n\n```js\niconSvg(\"heart\", { variant: \"two-tone\" });\n```\n\nهر سبک اسپرایت جدای خودش را دارد (`@kashi-ui/icons/sprite-bold.svg` با شناسهٔ `#k-home--bold`) و فایل‌های جدا در `@kashi-ui/icons/svg/<variant>/<name>.svg` هستند. سبک `bold` جزئیات را با ماسک SVG از دل شکل سوراخ می‌کند؛ هر نمونه شناسهٔ ماسک یکتای خودش را می‌گیرد.\n\n## اصول طراحی\n\nهمهٔ آیکون‌ها از چند قاعدهٔ ساده پیروی می‌کنند تا کنار هم یکدست دیده شوند:\n\n[اسکیل طراحی آیکون‌های کاشی](https://github.com/Sepantapouya/Kashi/blob/main/packages/icons/DESIGN.md) اندازهٔ بصری، فضای منفی، نشان وضعیت و مراحل بازبینی طرح را توضیح می‌دهد.\n\n## React\n\nهر آیکون یک کامپوننت جداست و فقط طرح خودش را دارد؛ فقط آیکون‌هایی که import می‌کنید در باندل می‌آیند (سه آیکون حدود ۴ کیلوبایت gzip، همراه کد سبک‌ها).\n\n```tsx\nimport { IconSearch, IconTrash } from \"@kashi-ui/icons/react\";\n\n<IconSearch />\n<IconTrash size={24} strokeWidth={2} className=\"danger\" />\n```\n\nوقتی نام آیکون از داده می‌آید، از `Icon` استفاده کنید:\n\n```tsx\nimport { Icon } from \"@kashi-ui/icons/react\";\n\n<Icon name={item.icon} />\n```\n\n`Icon` چون هر نامی را می‌پذیرد، **همهٔ** آیکون‌ها را به باندل می‌آورد (چند صد کیلوبایت). اگر نام‌ها از پیش معلوم‌اند، یک نقشهٔ کوچک بسازید تا فقط همان‌ها بیایند:\n\n```tsx\nimport { IconHome, IconSearch, IconUser } from \"@kashi-ui/icons/react\";\n\nconst icons = { home: IconHome, search: IconSearch, user: IconUser };\nconst C = icons[item.icon];\n<C />\n```\n\n| ویژگی | نوع | پیش‌فرض | توضیح |\n|---|---|---|---|\n| `size` | `number \\| string` | `20` | عرض و ارتفاع؛ عدد یعنی پیکسل، یا هر واحد CSS مثل `\"1.25em\"` |\n| `strokeWidth` | `number` | `1.5` | ضخامت خط |\n| `title` | `string` | — | متن برای صفحه‌خوان؛ بدون آن آیکون تزئینی و پنهان است |\n| `mirror` | `boolean` | `false` | قرینهٔ افقی؛ برای فلش‌ها در طرح‌های دوجهته |\n| `name` | `IconName` | — | فقط در `Icon`؛ نام آیکون |\n\nبقیهٔ ویژگی‌های `<svg>` (مثل `className`، `style` و `onClick`) هم پذیرفته می‌شوند و `ref` به خود `<svg>` می‌رسد.\n\n## HTML و فریم‌ورک‌های دیگر\n\nبرای Vue، Svelte، Angular یا HTML ساده سه راه دارید.\n\n**۱. کد SVG با جاوااسکریپت.** `iconSvg` رشتهٔ SVG برمی‌گرداند:\n\n```js\nimport { iconSvg } from \"@kashi-ui/icons\";\n\nbutton.innerHTML = iconSvg(\"trash\", { size: 16 }) + \" حذف\";\n```\n\n**۲. اسپرایت.** فایل `@kashi-ui/icons/sprite.svg` را یک بار در صفحه بگذارید یا کنار سایتتان منتشر کنید و هر جا لازم شد صدا بزنید:\n\n```html\n<svg width=\"20\" height=\"20\" aria-hidden=\"true\">\n <use href=\"/sprite.svg#k-search\" />\n</svg>\n```\n\n**۳. فایل SVG جدا.** هر آیکون در `@kashi-ui/icons/svg/<name>.svg` هم هست؛ برای `<img>` یا ابزارهای ساخت که SVG را import می‌کنند.\n\n## داخل کامپوننت‌های کاشی\n\nآیکون‌ها برای کار کنار کامپوننت‌ها ساخته شده‌اند و اندازه‌شان با دکمه و فیلد هماهنگ است:\n\n```tsx\nimport { Button, Input } from \"@kashi-ui/react\";\nimport { IconPlus, IconSearch } from \"@kashi-ui/icons/react\";\n\n<Button variant=\"primary\" iconStart={<IconPlus />}>پروژهٔ جدید</Button>\n<Input addonStart={<IconSearch />} placeholder=\"جستجو\" />\n```\n\n## دسترس‌پذیری\n\n- آیکون بدون `title` با `aria-hidden` از صفحه‌خوان پنهان می‌شود؛ برای آیکون‌هایی که کنار متن می‌آیند همین درست است.\n- آیکونی که تنها می‌آید و معنا دارد (مثل دکمهٔ فقط‌آیکون) باید نام داشته باشد: یا `title` بدهید، یا بهتر، `aria-label` را روی خود دکمه بگذارید.\n- آیکون‌ها `focusable=\"false\"` دارند تا در اینترنت اکسپلورر و نسخه‌های قدیمی Edge در ترتیب Tab نیایند.\n\n## راست‌به‌چپ\n\nآیکون‌ها به‌طور پیش‌فرض قرینه نمی‌شوند؛ `arrow-left` همیشه به چپ اشاره می‌کند. در رابط فارسی، «بعدی» و «ادامه» یعنی فلش چپ و «بازگشت» یعنی فلش راست. شش آیکون جهت‌دار (فلش‌ها، شورون‌های چپ و راست، ارسال و خروج) در صفحهٔ هر آیکون با نشان «جهت‌دار» مشخص‌اند. اگر طرح شما هم راست‌به‌چپ و هم چپ‌به‌راست است، با `mirror` قرینه‌شان کنید.\n\n## فیگما\n\nهمهٔ آیکون‌ها در صفحهٔ «🧩 Icons» [پلاگین فیگمای کاشی](/docs/figma/) هم هستند، دسته‌به‌دسته، هر کدام به شکل کامپوننت با نام `Icon / <دسته> / <name>` تا در پنل Assets هم پوشه‌بندی شوند؛ با Instance swap داخل دکمه‌ها و فیلدها عوض می‌شوند. فیگما و کد از یک منبع ساخته می‌شوند، پس شکل آیکون‌ها در هر دو یکی است.\n\n## پیشنهاد آیکون تازه\n\nآیکونی کم دارید؟ در [GitHub](https://github.com/Sepantapouya/Kashi/issues) بنویسید. آیکون‌های تازه با همین شبکه و ضخامت کشیده می‌شوند تا مجموعه یکدست بماند."},{"slug":"intro","url":"https://kashi.sepantapouya.com/docs/","title":"معرفی","description":"کاشی چیست، برای چه کسانی است و از چه بخش‌هایی تشکیل شده.","group":"شروع","components":[],"markdown":"کاشی یک دیزاین‌سیستم متن‌باز برای محصولات فارسی است. نام آن از کاشی‌کاری ایرانی گرفته شده: قطعه‌های کوچک و دقیق که کنار هم یک کل یکپارچه می‌سازند.\n\n```bash\nnpm i @kashi-ui/react @kashi-ui/css\n```\n\n## اصول\n\n- **راست‌به‌چپ از پایه.** هیچ استایلی با `left` و `right` نوشته نشده؛ همه‌چیز با Logical Properties است. صفحه‌ای که با `dir=\"ltr\"` باز شود هم بدون تغییر درست نمایش داده می‌شود.\n- **دسترس‌پذیری به‌عنوان شرط انتشار.** هدف استاندارد WCAG 2.2 در سطح AA است. کنتراست رنگ‌ها در هر بیلد به‌صورت خودکار بررسی می‌شود و تست‌های خودکار ویژگی‌های ARIA را کنترل می‌کنند.\n- **سفارشی‌سازی بدون جنگ با CSS.** همهٔ استایل‌ها در Cascade Layer قرار دارند؛ CSS شما همیشه اولویت دارد، بدون `!important`.\n- **مستقل از فریم‌ورک.** ظاهر یک‌بار در CSS تعریف شده و هر فریم‌ورک لایهٔ نازکی روی آن است: کامپوننت‌های React، و برای بقیه [وب کامپوننت‌ها](/docs/web-components/).\n\n## پکیج‌ها\n\n| پکیج | کاربرد |\n|---|---|\n| `@kashi-ui/tokens` | توکن‌های طراحی به‌صورت CSS Variables، JSON و JavaScript، به‌همراه `createTheme()` |\n| `@kashi-ui/css` | استایل همهٔ کامپوننت‌ها با کلاس‌های `k-*`، به‌همراه فونت وزیرمتن |\n| `@kashi-ui/react` | کامپوننت‌های React با تایپ کامل TypeScript |\n| `@kashi-ui/elements` | [وب کامپوننت‌ها](/docs/web-components/) برای Vue، Svelte، Angular و HTML: فیلدهای فارسی، تقویم شمسی، فرم نشانی، نقشهٔ ایران، جدول داده، منو، پنجره و اعلان |\n\n## وضعیت پروژه\n\nکاشی در نسخهٔ `0.x` است؛ یعنی API ممکن است پیش از نسخهٔ `1.0` تغییر کند. تغییرات ناسازگار همیشه در یادداشت انتشار ذکر می‌شوند."},{"slug":"installation","url":"https://kashi.sepantapouya.com/docs/installation/","title":"نصب","description":"کاشی را با پکیج npm، رجیستری shadcn، Tailwind یا فقط CSS به پروژهٔ React یا هر پروژهٔ وب دیگری بیاورید.","group":"شروع","components":[],"markdown":"## پکیج npm\n\nراه پیشنهادی برای بیشتر پروژه‌ها. کامپوننت‌ها از پکیج می‌آیند و با به‌روز کردن پکیج، اصلاح‌ها و کامپوننت‌های تازه هم می‌رسند.\n\n```bash\nnpm i @kashi-ui/react @kashi-ui/css\n```\n\nفایل CSS را یک بار در ریشهٔ برنامه وارد کنید و جهت صفحه را راست‌به‌چپ قرار دهید:\n\n```tsx\n// main.tsx یا app/layout.tsx\nimport \"@kashi-ui/css\";\n```\n\n```html\n<html lang=\"fa\" dir=\"rtl\">\n```\n\nحالا کامپوننت‌ها آمادهٔ استفاده‌اند:\n\n```tsx\nimport { Button } from \"@kashi-ui/react\";\n\nexport function Example() {\n return <Button variant=\"primary\">ثبت‌نام</Button>;\n}\n```\n\nکاشی با React نسخهٔ ۱۸ و ۱۹ کار می‌کند و با Next.js، Vite و Remix سازگار است. در App Router نکست می‌شود کامپوننت‌ها را مستقیم در صفحه‌های سرور هم استفاده کرد.\n\n## رجیستری shadcn\n\nاگر می‌خواهید کد خودِ کامپوننت در پروژه‌تان باشد و هر تغییری خواستید در آن بدهید، از رجیستری کاشی با ابزار [shadcn](https://ui.shadcn.com/docs/cli) استفاده کنید. فایل‌ها در پوشهٔ `components/kashi` کپی می‌شوند و کامپوننت‌هایی که به آن‌ها وابسته‌اند، CSS کاشی و ابزارهای فارسی هم خودکار نصب می‌شوند.\n\nکاشی در [فهرست رسمی رجیستری‌های shadcn](https://ui.shadcn.com/docs/directory) با نام `@kashi` ثبت شده است، پس بدون هیچ تنظیمی کافی است نام کامپوننت را بدهید:\n\n```bash\nnpx shadcn@latest add @kashi/iran-map\n```\n\n```tsx\nimport { IranMap } from \"@/components/kashi/IranMap\";\n```\n\nروی هر کامپوننت بزنید تا دستور نصبش کپی شود:\n\nاگر نسخهٔ shadcn شما قدیمی است و پیام `Unknown registry \"@kashi\"` داد، یا آدرس کامل آیتم را بدهید:\n\n```bash\nnpx shadcn@latest add https://kashi.sepantapouya.com/r/iran-map.json\n```\n\nیا رجیستری کاشی را یک بار در `components.json` معرفی کنید تا `@kashi` همیشه شناخته شود:\n\n```json\n{\n \"registries\": {\n \"@kashi\": \"https://kashi.sepantapouya.com/r/{name}.json\"\n }\n}\n```\n\n```bash\nnpx shadcn@latest add @kashi/date-picker @kashi/iran-map\n```\n\nپروژه باید پیش‌تر با `npx shadcn@latest init` آماده شده باشد. فهرست کامل آیتم‌ها در [registry.json](/r/registry.json) است.\n\n### npm یا shadcn؟\n\n| | پکیج npm | رجیستری shadcn |\n|---|---|---|\n| کد کامپوننت | داخل `node_modules` | داخل پروژهٔ شما |\n| تغییر دادن کد | با props، کلاس و متغیرهای CSS | هر تغییری، مستقیم در فایل |\n| به‌روزرسانی | با `npm update` | با اجرای دوبارهٔ دستور (تغییرهای شما بازنویسی می‌شود) |\n| مناسب برای | بیشتر پروژه‌ها | تیم‌هایی که کد را از آنِ خود می‌خواهند |\n\n## Tailwind\n\nاگر پروژه‌تان Tailwind دارد، پریست کاشی را هم نصب کنید تا رنگ‌ها، فونت، گردی و سایه‌های کاشی به‌شکل کلاس‌های Tailwind (`bg-brand`، `rounded-lg`، `text-secondary`) در دسترس باشند و با تم تیره و روشن همگام بمانند:\n\n```bash\nnpm i @kashi-ui/tailwind\n```\n\n```css\n@import \"@kashi-ui/tailwind\";\n@import \"tailwindcss\";\n@import \"@kashi-ui/css\";\n```\n\nراهنمای کامل، فهرست کلاس‌ها و تنظیم Tailwind 3: [Tailwind](/docs/tailwind/)\n\n## HTML و فریم‌ورک‌های دیگر\n\nبرای Vue، Svelte، Angular، وردپرس یا HTML ساده دو پکیج دارید: `@kashi-ui/css` برای ظاهر همهٔ کامپوننت‌ها و `@kashi-ui/elements` برای کامپوننت‌هایی که JavaScript لازم دارند (فیلدهای فارسی، تقویم شمسی، کد تأیید، فرم نشانی، نقشهٔ ایران، Combobox، جدول داده، منو، پنجره، زبانه و اعلان) به‌شکل وب کامپوننت.\n\n```bash\nnpm i @kashi-ui/css @kashi-ui/elements\n```\n\nیا بدون نصب، از CDN:\n\n```html\n<link rel=\"stylesheet\" href=\"https://cdn.jsdelivr.net/npm/@kashi-ui/css/dist/kashi.css\">\n<script type=\"module\" src=\"https://cdn.jsdelivr.net/npm/@kashi-ui/elements/dist/cdn/kashi-elements.js\"></script>\n```\n\nسپس از کلاس‌ها و تگ‌ها استفاده کنید. نمونهٔ HTML هر کامپوننت در صفحهٔ همان کامپوننت آمده است.\n\n```html\n<button class=\"k-btn k-btn--primary\">ثبت‌نام</button>\n<kashi-national-id name=\"national_id\" required></kashi-national-id>\n<kashi-date-picker label=\"تاریخ تولد\" name=\"birth\"></kashi-date-picker>\n```\n\nفهرست همهٔ وب کامپوننت‌ها، رویدادها و تنظیم Vue و Angular: [وب کامپوننت‌ها](/docs/web-components/)\n\n## پکیج‌ها\n\nهمهٔ پکیج‌ها هم‌نسخه منتشر می‌شوند. معمولاً فقط `react` و `css` را خودتان نصب می‌کنید و بقیه یا خودکار می‌آیند یا اختیاری‌اند:\n\n## وارد کردن بخشی از CSS\n\nاگر فقط به چند کامپوننت نیاز دارید، فایل‌ها را جداگانه وارد کنید. ترتیب مهم است: توکن‌ها، فونت و پایه، سپس کامپوننت‌ها.\n\n```css\n@import \"@kashi-ui/css/tokens.css\";\n@import \"@kashi-ui/css/font.css\";\n@import \"@kashi-ui/css/base.css\";\n@import \"@kashi-ui/css/components/button.css\";\n@import \"@kashi-ui/css/components/input.css\";\n```\n\n> فایل‌های جداگانه در Cascade Layer قرار ندارند. اگر به اولویت لایه‌ها نیاز دارید، از فایل کامل `kashi.css` استفاده کنید.\n\n## تم روشن\n\nتم پیش‌فرض تیره است. برای تم روشن، ویژگی `data-theme` را روی هر عنصری بگذارید؛ زیرشاخهٔ آن عنصر روشن می‌شود.\n\n```html\n<html lang=\"fa\" dir=\"rtl\" data-theme=\"light\">\n```\n\n## قدم بعدی\n\n- [سفارشی‌سازی و تم](/docs/theming/): رنگ برند و تراکم را عوض کنید.\n- [کامپوننت‌ها](/docs/components/): همهٔ کامپوننت‌ها با پیش‌نمایش زنده و دکمهٔ StackBlitz.\n- [استفاده با هوش مصنوعی](/docs/ai/): مستندات کاشی را با سرور MCP به Claude یا Cursor بدهید."},{"slug":"migration","url":"https://kashi.sepantapouya.com/docs/migration/","title":"مهاجرت به ۱.۰","description":"کاشی ۱.۰ تغییر ناسازگاری ندارد؛ نسخه را بالا ببرید و تمام. قابلیت‌های تازه (دوزبانه، سقف حجم، تست تصویری) اختیاری‌اند.","group":"پایداری و امنیت","components":[],"markdown":"کاشی ۱.۰ همان کاشی ۰.۲۶ است با قولِ پایداری: از این نسخه، هر تغییر ناسازگار فقط در نسخهٔ اصلی بعدی و پس از دوره‌ای «منسوخ» می‌آید ([سیاست تغییرات](/docs/versioning/)).\n\n## به‌روزرسانی\n\n```bash\nnpm i @kashi-ui/react@^1 @kashi-ui/css@^1\n```\n\nهمهٔ پکیج‌های کاشی را با هم به ۱ ببرید (`@kashi-ui/elements`، `@kashi-ui/persian`، `@kashi-ui/tailwind` و…)؛ نسخه‌ها یکی‌اند.\n\n**تغییر کدی لازم نیست.** هیچ prop، کلاس CSS، تگ یا تابعی حذف یا تغییر نام نداده است و متن‌ها و ظاهر پیش‌فرض همان فارسیِ قبل‌اند.\n\n## اگر با shadcn نصب کرده‌اید\n\nکامپوننت‌ها حالا متن‌هایشان را از `KashiProvider` می‌خوانند. پس از به‌روزرسانی، کامپوننت‌هایی را که دارید دوباره اضافه کنید تا فایل تازهٔ `components/kashi/KashiProvider.tsx` هم بیاید:\n\n```bash\nnpx shadcn@latest add @kashi/kashi-provider @kashi/date-picker\n```\n\nاگر در کد کامپوننت‌ها تغییری داده‌اید، پیش از دوباره اضافه کردن آن را نگه دارید؛ shadcn فایل را بازنویسی می‌کند.\n\n## چه چیزهایی تازه است\n\n- **[دوزبانه](/docs/i18n/):** با `<KashiProvider locale=\"en\">` یا `setLocale(\"en\")` همهٔ متن‌های داخلی، ارقام و نام ماه‌ها انگلیسی می‌شوند.\n- **[سیاست تغییرات](/docs/versioning/)** و **[امنیت](/docs/security/):** قول‌های مکتوب برای پایداری و رفع آسیب‌پذیری.\n- **[حجم کامپوننت‌ها](/docs/sizes/):** حجم هر کامپوننت در صفحهٔ خودش و سقفی که تست نمی‌گذارد از آن بیشتر شود.\n- **تست تصویری** از همهٔ کامپوننت‌ها در دو تم.\n- **ابزارهای فارسی** گزینهٔ `locale: \"en\"` گرفته‌اند و `bankName` و `operatorName` برای نام انگلیسی بانک و اپراتور اضافه شده‌اند.\n\n## نکته‌های کوچک\n\n- `defaultPasswordRules` و `defaultRangePresets` همچنان فارسی‌اند. اگر `rules` یا `presets` ندهید، کامپوننت شرط‌ها و میان‌برها را به زبان `KashiProvider` می‌سازد.\n- `formatBytes` در React و وب کامپوننت‌ها با زبان انگلیسی واحدها را `KB` و `MB` می‌نویسد."},{"slug":"persian/activity-heatmap","url":"https://kashi.sepantapouya.com/docs/persian/activity-heatmap/","title":"هیت‌مپ فعالیت","description":"فعالیت روزانهٔ یک سال شمسی به سبک GitHub، با ماه‌های جلالی و هفتهٔ شنبه‌تا‌جمعه.","group":"فارسی","components":["ActivityHeatmap"],"markdown":"```tsx\nimport { ActivityHeatmap } from \"@kashi-ui/react\";\n```\n\n## نمونه\n\n```tsx\n<ActivityHeatmap year={1405} unit=\"کامیت\"\n data={{ \"2026-03-21\": 3, \"2026-03-22\": 7, … }} />\n```\n\nکلید داده تاریخ میلادی `YYYY-MM-DD` است (مثل خروجی `toISODate`) تا مستقیم از پایگاه‌داده بیاید. شدت رنگ نسبت به بیشترین مقدار سال در چهار پله است، با یک رنگ از کم‌رنگ به پررنگ.\n\n| ویژگی | نوع | پیش‌فرض | توضیح |\n|---|---|---|---|\n| data | Record<string, number> | — | تعداد در هر روز؛ کلید YYYY-MM-DD |\n| year | number | سال جاری | سال شمسی |\n| unit | string | «فعالیت» | واحد در راهنما |\n| label | string | — | نام دسترس‌پذیر |\n\n## دسترس‌پذیری\n\nبرای صفحه‌خوان، نقشه یک تصویر با جمع کل است و یک جدول پنهان جمع هر ماه را می‌گوید؛ ۳۶۵ خانه یکی‌یکی خوانده نمی‌شوند. مقدار هر روز با نگه‌داشتن موس نمایش داده می‌شود.\n\n## راهنمای استفاده\n\nکِی استفاده کنیم:\n\n- فعالیت روزانهٔ یک سال شمسی: ورود کاربر، سفارش‌ها، تمرین، مشارکت در پروژه.\n\nکِی استفاده نکنیم:\n\n- مقایسهٔ ماه‌ها یا روند کلی. (نمودار: /docs/components/charts/)\n- داده‌ای که بیشتر روزهایش خالی است؛ نقشه تقریباً سفید می‌ماند و چیزی نمی‌گوید.\n\nدرست و نادرست:\n\n- ✓ درست: `unit` بدهید تا راهنما بگوید «۱۲ سفارش»، نه فقط «۱۲».\n- ✗ نادرست: عدد بدون واحد که معلوم نیست چه شمرده شده.\n\n## وب کامپوننت\n\nهمین کامپوننت بدون React، برای HTML، وردپرس، Vue، Svelte و Angular: `<kashi-activity-heatmap>` در `@kashi-ui/elements`.\n\nنصب بدون ابزار ساخت (CDN):\n\n```html\n<link rel=\"stylesheet\" href=\"https://cdn.jsdelivr.net/npm/@kashi-ui/css/dist/kashi.css\">\n<script type=\"module\" src=\"https://cdn.jsdelivr.net/npm/@kashi-ui/elements/dist/cdn/kashi-elements.js\"></script>\n```\n\nیا با npm، فقط همین بخش:\n\n```bash\nnpm i @kashi-ui/elements @kashi-ui/css\n```\n\n```js\nimport \"@kashi-ui/css\";\nimport \"@kashi-ui/elements/charts.js\";\n```\n\n```html\n<kashi-activity-heatmap year=\"1405\" unit=\"سفارش\" data='{\"2026-03-21\": 4, \"2026-03-22\": 9, \"2026-04-02\": 2, \"2026-05-10\": 7}'></kashi-activity-heatmap>\n```\n\nویژگی‌ها:\n\n| نام | مقدار | پیش‌فرض | توضیح |\n|---|---|---|---|\n| data | JSON | — | تاریخ میلادی YYYY-MM-DD ← تعداد |\n| year | سال شمسی | سال جاری | سال نمایش |\n| unit / label | متن | فعالیت | واحد و نام دسترس‌پذیر |\n\nرویدادها و JavaScript:\n\n| نام | توضیح |\n|---|---|\n| element.data | داده با JavaScript |"},{"slug":"persian/address","url":"https://kashi.sepantapouya.com/docs/persian/address/","title":"فرم نشانی","description":"استان، شهرِ همان استان، نشانی، پلاک، واحد و کد پستی ده‌رقمی در یک فرم؛ با فهرست بیش از ۱۲۰۰ شهر ایران و نقشه برای انتخاب استان.","group":"فارسی","components":["AddressForm"],"markdown":"```tsx\nimport { AddressForm } from \"@kashi-ui/react\";\nimport { EMPTY_ADDRESS, formatAddress, type IranAddress } from \"@kashi-ui/persian\";\n```\n\n## فرم کامل\n\nبا انتخاب استان فقط شهرهای همان استان می‌آیند و با عوض شدن استان، شهرِ نامربوط پاک می‌شود. اگر کاربر اول شهر را بنویسد، استان خودکار پر می‌شود. کد پستی هنگام تایپ قالب می‌گیرد و با قواعد کد پستی ایران بررسی می‌شود.\n\n```tsx\nconst [address, setAddress] = useState<IranAddress>(EMPTY_ADDRESS);\n\n<AddressForm value={address} onChange={setAddress} name=\"shipping\" />\n<p>{formatAddress(address)}</p>\n```\n\nمقدار فرم یک شیء `IranAddress` است:\n\n```ts\ninterface IranAddress {\n province: string | null; // «اصفهان»\n city: string | null; // «کاشان»\n street: string; // «خیابان امیرکبیر، کوچهٔ ۱۲»\n plaque: string;\n unit: string;\n postalCode: string; // ده رقم لاتین، «8713714568»\n}\n```\n\n`formatAddress` همین شیء را در یک خط با ارقام فارسی برمی‌گرداند، مثل «اصفهان، کاشان، خیابان امیرکبیر، پلاک ۱۲، واحد ۳، کد پستی ۸۷۱۳۷-۱۴۵۶۸»؛ برای فاکتور، رسید یا صفحهٔ تأیید سفارش.\n\n## با نقشه\n\nبا `map` نقشهٔ ایران کنار فیلدها می‌آید و استان را می‌شود روی نقشه هم انتخاب کرد. در عرض کم، نقشه زیر فیلدها می‌رود.\n\n```tsx\n<AddressForm value={address} onChange={setAddress} map legend=\"نشانی تحویل\" />\n```\n\n## ارسال با فرم HTML\n\nبا `name` هر فیلد در فرم HTML هم فرستاده می‌شود: `name=\"shipping\"` می‌شود `shipping[province]`، `shipping[city]`، `shipping[street]`، `shipping[plaque]`، `shipping[unit]` و `shipping[postalCode]`. این همان قالبی است که Laravel و PHP به‌صورت آرایه می‌خوانند. خطاهای اعتبارسنجی سرور را با `errors` زیر هر فیلد نشان دهید:\n\n```tsx\n<AddressForm name=\"shipping\" errors={{ plaque: \"پلاک را بنویسید.\" }} />\n```\n\n## انتخاب شهر\n\n`CitySelect` جدا هم در دسترس است. با `province` فقط شهرهای همان استان را نشان می‌دهد و بدون آن همهٔ شهرها را با نام استانشان. جستجو «علی آباد»، «علی‌آباد» و «علیآباد» را یکی می‌داند. شهری که در فهرست نیست (مثلاً شهرهای تازه‌تأسیس) با همان نامی که کاربر نوشته پذیرفته می‌شود؛ برای بستن این امکان `allowCustom={false}` بدهید.\n\n```tsx\n<CitySelect value={city} onChange={(city, province) => { setCity(city); setProvince(province); }} />\n```\n\n| ویژگی | نوع | پیش‌فرض | توضیح |\n|---|---|---|---|\n| value / defaultValue | IranAddress | — | مقدار فرم |\n| onChange | (address: IranAddress) => void | — | با هر تغییر، کل نشانی |\n| errors | Partial<Record<keyof IranAddress, ReactNode>> | — | خطای هر فیلد |\n| name | string | — | پیشوند نام فیلدها برای فرم HTML |\n| legend | ReactNode | «نشانی» | عنوان گروه فیلدها |\n| labels | Partial<Record<keyof IranAddress, string>> | — | برچسب دلخواه هر فیلد |\n| map | boolean | false | نقشهٔ ایران برای انتخاب استان |\n| required | boolean | true | همه اجباری جز واحد |\n| disabled | boolean | false | غیرفعال کردن همهٔ فیلدها |\n\n## ابزارهای فهرست شهرها\n\nفهرست شهرها در `@kashi-ui/persian` است و بدون React هم کار می‌کند:\n\n```ts\nimport { CITIES, getCities, getCityProvinces, formatAddress } from \"@kashi-ui/persian\";\n\ngetCities(\"قم\"); // [\"جعفریه\", \"دستجرد\", \"سلفچگان\", \"قاهان\", \"قم\", \"قنوات\", \"کهک\"]\ngetCityProvinces(\"کاشان\"); // [\"اصفهان\"]\nObject.keys(CITIES).length; // ۳۱ استان\n```\n\n## دسترس‌پذیری\n\n- فیلدها در یک `fieldset` با `legend` هستند و صفحه‌خوان نام گروه را همراه هر فیلد می‌خواند.\n- هر فیلد `autocomplete` استاندارد دارد (`street-address`، `postal-code` و …) تا مرورگر نشانی ذخیره‌شده را پیشنهاد دهد.\n- فیلد اختیاری با «اختیاری» مشخص شده، نه فقط با نبود ستاره.\n- استان و شهر با الگوی Combobox در WAI-ARIA کار می‌کنند و نقشه با کلیدهای جهت‌دار.\n\nفهرست ۱۲۷۸ شهر در ۳۱ استان از [ویکی‌داده](https://www.wikidata.org) (مالکیت عمومی، CC0) گرفته شده است. شهرهای تازه‌تأسیس ممکن است هنوز در آن نباشند؛ برای همین شهر خارج از فهرست هم پذیرفته می‌شود.\n\n## راهنمای استفاده\n\nکِی استفاده کنیم:\n\n- گرفتن نشانی تحویل، صورت‌حساب یا محل خدمت در ایران.\n- فرم‌هایی که شهر باید با استان هم‌خوان باشد و کد پستی ده‌رقمی معتبر بخواهند.\n\nکِی استفاده نکنیم:\n\n- نشانی خارج از ایران.\n- فقط استان یا فقط شهر. (انتخاب استان: /docs/persian/province/)\n\nدرست و نادرست:\n\n- ✓ درست: خطای سرور را با `errors` کنار همان فیلد نشان دهید.\n- ✗ نادرست: یک پیام کلی «نشانی نامعتبر است» بالای فرم.\n- ✓ درست: واحد را اختیاری بگذارید (پیش‌فرض کاشی)؛ خیلی از نشانی‌ها واحد ندارند.\n- ✗ نادرست: همهٔ فیلدها اجباری، تا کاربر برای خانهٔ ویلایی «۰» بنویسد.\n\n## وب کامپوننت\n\nهمین کامپوننت بدون React، برای HTML، وردپرس، Vue، Svelte و Angular: `<kashi-address-form>` در `@kashi-ui/elements`. فیلدها به‌شکل `shipping[province]`، `shipping[city]`، … ارسال می‌شوند.\n\nنصب بدون ابزار ساخت (CDN):\n\n```html\n<link rel=\"stylesheet\" href=\"https://cdn.jsdelivr.net/npm/@kashi-ui/css/dist/kashi.css\">\n<script type=\"module\" src=\"https://cdn.jsdelivr.net/npm/@kashi-ui/elements/dist/cdn/kashi-elements.js\"></script>\n```\n\nیا با npm، فقط همین بخش:\n\n```bash\nnpm i @kashi-ui/elements @kashi-ui/css\n```\n\n```js\nimport \"@kashi-ui/css\";\nimport \"@kashi-ui/elements/map.js\";\n```\n\n```html\n<kashi-address-form name=\"shipping\" legend=\"نشانی تحویل\"></kashi-address-form>\n```\n\nویژگی‌ها:\n\n| نام | مقدار | پیش‌فرض | توضیح |\n|---|---|---|---|\n| name | متن | — | پیشوند نام فیلدها در فرم |\n| legend | متن | نشانی | عنوان گروه فیلدها |\n| map | بولی | — | نقشهٔ ایران کنار فرم |\n| required | false | — | همهٔ فیلدها اختیاری |\n| disabled | بولی | — | غیرفعال |\n| label-province، label-city، … | متن | — | برچسب دلخواه هر فیلد |\n\nرویدادها و JavaScript:\n\n| نام | توضیح |\n|---|---|\n| kashi-change | detail: { value: { province, city, street, plaque, unit, postalCode } } |\n| element.value | خواندن و پر کردن نشانی |\n| element.errors | خطای هر فیلد: { plaque: \"پلاک را بنویسید.\" } |"},{"slug":"persian/appointment","url":"https://kashi.sepantapouya.com/docs/persian/appointment/","title":"انتخاب نوبت","description":"رزرو نوبت با روزهای شمسی و ساعت‌های خالی هر روز؛ برای مطب، آرایشگاه، مشاوره و هر سرویس نوبت‌دهی. جمعه‌ها و تعطیلات رسمی بسته‌اند و نوبت‌های پر و گذشته غیرفعال.","group":"فارسی","components":["AppointmentPicker"],"markdown":"```tsx\nimport { AppointmentPicker } from \"@kashi-ui/react\";\n```\n\n## نمونه\n\nروزها با تاریخ شمسی و تعداد نوبت خالی‌شان در یک ردیف می‌آیند و ساعت‌های همان روز در گروه‌های صبح، بعدازظهر و عصر. جمعه‌ها و تعطیلات رسمی ثابت بسته‌اند، نوبت‌های پرشده خط خورده‌اند و ساعت‌هایی از امروز که گذشته‌اند قابل انتخاب نیستند.\n\n```tsx\nconst [visit, setVisit] = useState<AppointmentValue | null>(null);\n\n<AppointmentPicker\n value={visit}\n onChange={setVisit}\n from=\"09:00\"\n to=\"17:00\"\n step={30}\n booked={{ \"2026-11-04\": [\"09:30\", \"10:00\"] }}\n name=\"visit\"\n/>\n```\n\nبا `from`، `to` و `step` ساعت کاری و طول هر نوبت را بدهید و نوبت‌های پرشده را با `booked` (کلید تاریخ میلادی `YYYY-MM-DD`). با `name` دو فیلد `visit_date` و `visit_time` در فرم HTML ارسال می‌شوند.\n\n## ساعت‌های دلخواه هر روز\n\nاگر ساعت‌ها هر روز فرق می‌کنند (نیمه‌وقت پنجشنبه، استراحت ظهر، چند پزشک)، `getSlots` را بدهید. برای هر روز فهرست ساعت‌ها را برمی‌گرداند؛ هر ساعت یک رشته یا `{ time, available }` است.\n\n```tsx\n<AppointmentPicker\n label=\"نوبت مشاوره\"\n days={10}\n getSlots={(date) =>\n date.getDay() === 4\n ? [\"09:00\", \"10:00\", \"11:00\"]\n : [\"09:00\", \"10:00\", \"11:00\", \"17:00\", \"18:00\", { time: \"19:00\", available: false }]\n }\n/>\n```\n\nروزهای تعطیل را با `closed` عوض کنید: `false` برای باز بودن همهٔ روزها، یا تابعی مثل `(d) => d.getDay() === 4` برای تعطیلی پنجشنبه‌ها. تابع `appointmentSlots` هم جدا در دسترس است تا همان ساعت‌ها را در سرور حساب کنید.\n\n## ویژگی‌ها\n\n| ویژگی | نوع | پیش‌فرض | توضیح |\n|---|---|---|---|\n| value / defaultValue | { date: Date; time: string } \\| null | — | نوبت انتخاب‌شده |\n| onChange | (value) => void | — | با انتخاب هر ساعت |\n| start | Date | امروز | اولین روز |\n| days | number | 7 | تعداد روزهای قابل انتخاب |\n| from / to | string | \"09:00\" / \"17:00\" | ساعت شروع و پایان کار |\n| step | number | 30 | طول هر نوبت به دقیقه |\n| booked | Record<string, string[]> | — | نوبت‌های پرشده؛ کلید YYYY-MM-DD |\n| getSlots | (date: Date) => (string \\| AppointmentSlot)[] | — | ساعت‌های هر روز به‌جای from، to، step و booked |\n| closed | boolean \\| (date: Date) => boolean | true | روزهای تعطیل؛ true یعنی جمعه‌ها و تعطیلات رسمی ثابت |\n| label | string | \"انتخاب نوبت\" | برچسب و نام گروه |\n| name | string | — | فیلدهای name_date و name_time در فرم |\n| digits | \"fa\" \\| \"latin\" | \"fa\" | خط ارقام |\n\n## دسترس‌پذیری\n\n- روزها و ساعت‌ها گروه‌های رادیویی‌اند (`role=\"radiogroup\"`) با فوکوس گردشی: فقط یک گزینه در ترتیب Tab است و کلیدهای جهت بین بقیه حرکت می‌کنند؛ در راست‌به‌چپ، فلش چپ یعنی گزینهٔ بعد.\n- صفحه‌خوان هر روز را با تاریخ کامل و وضعیتش می‌خواند: «سه‌شنبه ۱۳ آبان ۱۴۰۵، ۶ نوبت خالی» یا «تعطیل».\n- نوبت انتخاب‌شده در یک ناحیهٔ زنده (`aria-live`) اعلام می‌شود.\n- نوبت پر فقط با خط‌خوردگی نشان داده نمی‌شود؛ غیرفعال است و صفحه‌خوان «پر» را می‌خواند.\n\n## راهنمای استفاده\n\nکِی استفاده کنیم:\n\n- رزرو نوبت با ساعت مشخص: مطب، آرایشگاه، مشاوره، تحویل سفارش در بازهٔ زمانی.\n\nکِی استفاده نکنیم:\n\n- فقط تاریخ بدون ساعت. (دیت‌پیکر شمسی: /docs/persian/date-picker/)\n- بازهٔ چندروزه مثل رزرو هتل. (بازهٔ تاریخ شمسی: /docs/persian/date-range/)\n\nدرست و نادرست:\n\n- ✓ درست: نوبت‌های پر را با `booked` نشان دهید تا خط‌خورده دیده شوند.\n- ✗ نادرست: نوبت‌های پر را پنهان کنید؛ کاربر فکر می‌کند آن ساعت کاری نیست.\n- ✓ درست: خلاصهٔ پایین («نوبت شما: شنبه ۱۶ آبان، ساعت ۱۰:۰۰») را نگه دارید تا پیش از ثبت دوباره ببیند.\n- ✗ نادرست: ثبت با یک کلیک روی ساعت، بدون دیدن تاریخ و ساعت انتخابی.\n\n## وب کامپوننت\n\nهمین کامپوننت بدون React، برای HTML، وردپرس، Vue، Svelte و Angular: `<kashi-appointment-picker>` در `@kashi-ui/elements`. با `name` دو فیلد `visit_date` و `visit_time` ارسال می‌شوند؛ ساعت‌های دلخواه هر روز با `element.getSlots`.\n\nنصب بدون ابزار ساخت (CDN):\n\n```html\n<link rel=\"stylesheet\" href=\"https://cdn.jsdelivr.net/npm/@kashi-ui/css/dist/kashi.css\">\n<script type=\"module\" src=\"https://cdn.jsdelivr.net/npm/@kashi-ui/elements/dist/cdn/kashi-elements.js\"></script>\n```\n\nیا با npm، فقط همین بخش:\n\n```bash\nnpm i @kashi-ui/elements @kashi-ui/css\n```\n\n```js\nimport \"@kashi-ui/css\";\nimport \"@kashi-ui/elements/appointment.js\";\n```\n\n```html\n<kashi-appointment-picker name=\"visit\" from=\"09:00\" to=\"17:00\" step=\"30\" booked='{\"2026-11-04\": [\"09:30\", \"10:00\"]}'></kashi-appointment-picker>\n```\n\nویژگی‌ها:\n\n| نام | مقدار | پیش‌فرض | توضیح |\n|---|---|---|---|\n| start | YYYY-MM-DD | امروز | اولین روز |\n| days | عدد | 7 | تعداد روزها |\n| from / to / step | HH:MM / دقیقه | 09:00 / 17:00 / 30 | ساعت کاری و طول هر نوبت |\n| booked | JSON | — | نوبت‌های پر: {\"2026-11-04\": [\"09:30\"]} |\n| closed | false | — | همهٔ روزها باز، حتی جمعه و تعطیلات |\n| value | «2026-11-04 10:30» | — | نوبت اولیه |\n| label / name / digits | — | انتخاب نوبت | برچسب، نام فرم و خط ارقام |\n\nرویدادها و JavaScript:\n\n| نام | توضیح |\n|---|---|\n| kashi-change | detail: { date: \"YYYY-MM-DD\", time, value } |\n| element.getSlots | (date) => [\"09:00\", { time, available }] |\n| element.isClosed | (date) => boolean برای روزهای تعطیل دلخواه |\n| element.value | { date, time } |"},{"slug":"persian/bank-card","url":"https://kashi.sepantapouya.com/docs/persian/bank-card/","title":"کارت بانکی","description":"نمایش کارت بانکی با رنگ و لوگوی بانک، شمارهٔ پوشانده و تاریخ انقضای شمسی.","group":"فارسی","components":["BankCard"],"markdown":"```tsx\nimport { BankCard } from \"@kashi-ui/react\";\n```\n\n## نمونه\n\n```tsx\n<BankCard number=\"6104337712345678\" holder=\"سپنتا پویا\"\n expiry={{ year: 1408, month: 5 }} masked />\n```\n\nبانک از شش رقم اول تشخیص داده می‌شود و کارت رنگ و لوگوی همان بانک را می‌گیرد. برای بانک‌های ناشناخته رنگ برند استفاده می‌شود و با `--k-bankcard-color` قابل تغییر است. برای کیف پول، انتخاب کارت ذخیره‌شده و صفحهٔ پرداخت مناسب است. با `masked` فقط شش رقم اول و چهار رقم آخر دیده می‌شود.\n\n| ویژگی | نوع | پیش‌فرض | توضیح |\n|---|---|---|---|\n| number | string | — | با هر خطی |\n| holder | ReactNode | — | نام دارنده |\n| expiry | string \\| { year, month } | — | «۰۸/۰۵» یا سال و ماه شمسی |\n| masked | boolean | false | پنهان کردن ارقام میانی |\n| bank | Bank \\| null | — | بانک دستی |\n| showLogo | boolean | true | — |\n| digits | \"fa\" \\| \"latin\" | \"fa\" | — |\n\n## دسترس‌پذیری\n\nکارت یک `figure` با نام کامل است، مثل «کارت بانک ملت، مختوم به ۵۶۷۸»، تا صفحه‌خوان ۱۶ رقم را جدا جدا نخواند. لوگو تزئینی است. رنگ‌ها تقریبی‌اند و رسمی نیستند.\n\n## راهنمای استفاده\n\nکِی استفاده کنیم:\n\n- کارت‌های ذخیره‌شدهٔ کاربر در کیف پول یا صفحهٔ پرداخت.\n- انتخاب کارت برای برداشت.\n\nکِی استفاده نکنیم:\n\n- گرفتن شمارهٔ کارت. (فیلد فارسی (کارت): /docs/persian/input/)\n\nدرست و نادرست:\n\n- ✓ درست: شماره را با `masked` بپوشانید و فقط چهار رقم آخر را نشان دهید.\n- ✗ نادرست: شانزده رقم کامل روی صفحه، جایی که دیگران هم می‌بینند."},{"slug":"persian/countdown","url":"https://kashi.sepantapouya.com/docs/persian/countdown/","title":"شمارش معکوس","description":"زمان باقی‌مانده با ارقام فارسی، برای ارسال دوبارهٔ کد، پایان تخفیف یا شروع رویداد.","group":"فارسی","components":["Countdown"],"markdown":"```tsx\nimport { Countdown } from \"@kashi-ui/react\";\n```\n\n## نمونه\n\n```tsx\nارسال دوبارهٔ کد تا <Countdown seconds={30} aria-label=\"زمان باقی‌مانده تا ارسال دوباره\" onComplete={() => setDone(true)} />\n\n<Countdown to={launchDate} variant=\"blocks\" aria-label=\"زمان باقی‌مانده تا شروع فروش\" />\n```\n\nبرای شروع دوباره، `key` کامپوننت را عوض کنید. شمارش از ساعت سیستم حساب می‌شود، پس اگر زبانه مدتی پنهان باشد هم درست می‌ماند.\n\n| ویژگی | نوع | پیش‌فرض | توضیح |\n|---|---|---|---|\n| to | Date | — | زمان پایان |\n| seconds | number | — | یا مدت به ثانیه از لحظهٔ نمایش |\n| onComplete | () => void | — | یک بار در پایان |\n| variant | \"clock\" \\| \"blocks\" | \"clock\" | ۰۲:۳۴ یا کادرهای روز و ساعت |\n| digits | \"fa\" \\| \"latin\" | \"fa\" | — |\n| aria-label | string | — | موضوع شمارش |\n| labels | { days?, hours?, minutes?, seconds? } | — | — |\n\n## دسترس‌پذیری\n\n`role=\"timer\"` دارد که صفحه‌خوان آن را هر ثانیه اعلام نمی‌کند. نام دسترس‌پذیر متن کامل است («زمان باقی‌مانده تا ارسال دوباره: ۲ دقیقه و ۳۴ ثانیه»)، پس کاربر هر وقت به آن برسد زمان را می‌شنود.\n\n## راهنمای استفاده\n\nکِی استفاده کنیم:\n\n- زمان تا ارسال دوبارهٔ کد، پایان پیشنهاد ویژه یا شروع رویداد.\n\nکِی استفاده نکنیم:\n\n- فشار ساختگی برای خرید؛ شمارشی که با هر بار باز کردن صفحه از اول شروع می‌شود اعتماد را از بین می‌برد.\n- زمان گذشته («۵ دقیقه پیش»). (زمان نسبی: /docs/persian/utilities/)\n\nدرست و نادرست:\n\n- ✓ درست: بگویید بعد از پایان چه می‌شود: «تا ۰۱:۵۹ دیگر می‌توانید کد را دوباره بخواهید».\n- ✗ نادرست: عدد شمارشگر تنها، بدون اینکه بگوید شمارش برای چیست.\n\n## وب کامپوننت\n\nهمین کامپوننت بدون React، برای HTML، وردپرس، Vue، Svelte و Angular: `<kashi-countdown>` در `@kashi-ui/elements`.\n\nنصب بدون ابزار ساخت (CDN):\n\n```html\n<link rel=\"stylesheet\" href=\"https://cdn.jsdelivr.net/npm/@kashi-ui/css/dist/kashi.css\">\n<script type=\"module\" src=\"https://cdn.jsdelivr.net/npm/@kashi-ui/elements/dist/cdn/kashi-elements.js\"></script>\n```\n\nیا با npm، فقط همین بخش:\n\n```bash\nnpm i @kashi-ui/elements @kashi-ui/css\n```\n\n```js\nimport \"@kashi-ui/css\";\nimport \"@kashi-ui/elements/display.js\";\n```\n\n```html\n<kashi-countdown seconds=\"120\" label=\"ارسال دوباره\"></kashi-countdown>\n<kashi-countdown seconds=\"93784\" variant=\"blocks\"></kashi-countdown>\n```\n\nویژگی‌ها:\n\n| نام | مقدار | پیش‌فرض | توضیح |\n|---|---|---|---|\n| seconds | عدد | — | مدت از لحظهٔ نمایش |\n| to | تاریخ ISO | — | یا زمان پایان |\n| variant | blocks | — | کادرهای روز، ساعت، دقیقه و ثانیه |\n| label | متن | — | نام برای صفحه‌خوان |\n| digits | fa \\| latin | fa | خط ارقام |\n\nرویدادها و JavaScript:\n\n| نام | توضیح |\n|---|---|\n| kashi-complete | در پایان شمارش |\n| element.restart() | شروع دوباره، مثلاً پس از ارسال دوبارهٔ کد |\n| element.remaining | ثانیه‌های باقی‌مانده |"},{"slug":"persian/date-picker","url":"https://kashi.sepantapouya.com/docs/persian/date-picker/","title":"دیت‌پیکر شمسی","description":"فیلد تاریخ با تقویم شمسی؛ تایپ مستقیم یا انتخاب از تقویم، با پیمایش کامل کیبورد و پشتیبانی صفحه‌خوان.","group":"فارسی","components":["DatePicker","Calendar"],"markdown":"```tsx\nimport { DatePicker, Calendar } from \"@kashi-ui/react\";\n```\n\nکاربر می‌تواند تاریخ را مستقیم تایپ کند (با ارقام فارسی یا انگلیسی، مثل `۱۴۰۳/۵/۱۲`) یا آن را از تقویم انتخاب کند. مقدار کامپوننت یک `Date` استاندارد جاوااسکریپت است، پس سرور شما با تاریخ شمسی سروکار ندارد.\n\n## تاریخ تولد\n\nفهرست ماه و سال برای انتخاب سریع تاریخ‌های دور است. با `name`، مقدار میلادی به شکل `YYYY-MM-DD` در فرم ارسال می‌شود.\n\n```tsx\nconst [birth, setBirth] = useState<Date | null>(null);\n\n<DatePicker\n label=\"تاریخ تولد\"\n name=\"birth\"\n max={new Date()}\n yearRange={[1300, 1405]}\n onChange={setBirth}\n/>\n```\n\n## محدود کردن روزها\n\nبا `min`، `max` و `isDateDisabled` روزهای غیرمجاز را مشخص کنید؛ مثلاً برای رزرو نوبت.\n\n```tsx\n<DatePicker\n label=\"تاریخ نوبت\"\n min={new Date()}\n isDateDisabled={(date) => date.getDay() === 5} // جمعه\n/>\n```\n\n## تقویم مستقل\n\n`Calendar` همان تقویم بدون فیلد است؛ برای صفحه‌های رزرو یا داشبورد.\n\n```tsx\nconst [date, setDate] = useState<Date | null>(new Date());\n\n<Calendar value={date} onChange={setDate} />\n```\n\n## کیبورد\n\n| کلید | عمل |\n|---|---|\n| <kbd>←</kbd> / <kbd>→</kbd> | روز بعد / روز قبل (در راست‌به‌چپ) |\n| <kbd>↑</kbd> / <kbd>↓</kbd> | هفتهٔ قبل / هفتهٔ بعد |\n| <kbd>Home</kbd> / <kbd>End</kbd> | شنبه / جمعهٔ همان هفته |\n| <kbd>PageUp</kbd> / <kbd>PageDown</kbd> | ماه قبل / ماه بعد |\n| <kbd>Shift</kbd> + <kbd>PageUp</kbd> / <kbd>PageDown</kbd> | سال قبل / سال بعد |\n| <kbd>Enter</kbd> / <kbd>Space</kbd> | انتخاب روز |\n| <kbd>Esc</kbd> | بستن تقویم و برگشت به دکمه |\n\n## ویژگی‌های DatePicker\n\n| ویژگی | نوع | پیش‌فرض | توضیح |\n|---|---|---|---|\n| value | Date \\| null | — | تاریخ انتخاب‌شده (کنترل‌شده) |\n| defaultValue | Date \\| null | null | مقدار اولیه (کنترل‌نشده) |\n| onChange | (date: Date \\| null) => void | — | با انتخاب، تایپ معتبر یا پاک کردن |\n| min / max | Date | — | محدودهٔ مجاز |\n| isDateDisabled | (date: Date) => boolean | — | غیرفعال کردن روزهای خاص |\n| yearRange | [number, number] | ۱۰۰ سال قبل تا ۲۰ سال بعد | سال‌های فهرست سال (شمسی) |\n| digits | \"fa\" \\| \"latin\" | \"fa\" | خط ارقام |\n| name | string | — | نام فیلد مخفی فرم با مقدار YYYY-MM-DD میلادی |\n| invalidMessage | ReactNode | «تاریخ را به شکل … وارد کنید.» | پیام خطای تایپ نامعتبر |\n| openLabel | string | \"انتخاب تاریخ از تقویم\" | نام دکمهٔ تقویم برای صفحه‌خوان |\n| todayLabel | string | \"امروز\" | متن دکمهٔ امروز |\n\nهمهٔ ویژگی‌های `Input` مثل `label`، `hint` و `error` هم پشتیبانی می‌شوند.\n\n## متغیرهای CSS\n\n| متغیر CSS | پیش‌فرض | توضیح |\n|---|---|---|\n| --k-calendar-day-size | 2.5rem × density | اندازهٔ خانهٔ هر روز |\n| --k-calendar-radius | var(--k-radius-full) | گردی خانهٔ روز |\n| --k-calendar-bg | var(--k-bg-elevated) | پس‌زمینهٔ تقویم شناور |\n| --k-datepicker-width | max-content | عرض تقویم شناور |\n\n## دسترس‌پذیری\n\n- تقویم از الگوی Date Picker در WAI-ARIA پیروی می‌کند: جدول با نقش `grid`، فقط یک روز در ترتیب Tab، و پیمایش با کلیدهای جهت‌دار.\n- هر روز با نام کامل خوانده می‌شود («جمعه ۱۲ مرداد ۱۴۰۳»)، امروز با `aria-current=\"date\"` و روز انتخاب‌شده با `aria-selected` مشخص است.\n- تغییر ماه با یک ناحیهٔ `aria-live` اعلام می‌شود.\n- دکمهٔ تقویم تاریخ فعلی را هم در نام خود دارد، پس کاربر صفحه‌خوان بدون باز کردن تقویم مقدار را می‌شنود.\n- تایپ مستقیم همیشه ممکن است؛ کاربرانی که با تقویم راحت نیستند مجبور به استفاده از آن نیستند.\n\n## تبدیل تاریخ\n\nتبدیل شمسی و میلادی با پکیج `@kashi-ui/persian` انجام می‌شود که برای سال‌های ۱۳۰۰ تا ۱۵۰۰، روزبه‌روز با تقویم رسمی ICU تست شده است. [ابزارهای فارسی](/docs/persian/utilities/) را ببینید.\n\n## تعطیلات\n\nبا `holidays`، جمعه‌ها و تعطیلات رسمی ثابت (نوروز، ۱۲ و ۱۳ فروردین، ۱۴ و ۱۵ خرداد، ۲۲ بهمن و ۲۹ اسفند) قرمز نمایش داده می‌شوند و نامشان برای صفحه‌خوان خوانده می‌شود. تعطیلات قمری هر سال با رؤیت ماه جابه‌جا می‌شوند، پس آن‌ها را از تقویم رسمی همان سال بدهید:\n\n```tsx\nimport { getIranHoliday } from \"@kashi-ui/persian\";\n\n<Calendar holidays /> // فقط جمعه‌ها و تعطیلات ثابت\n\n<Calendar holidays={(d) => getIranHoliday(d, { extra: { \"2026-03-20\": \"عید فطر\" } })} />\n```\n\n## راهنمای استفاده\n\nکِی استفاده کنیم:\n\n- تاریخ شمسی در فرم: تاریخ تولد، تحویل، سررسید.\n- وقتی کاربر هم تایپ می‌کند هم از تقویم انتخاب می‌کند.\n\nکِی استفاده نکنیم:\n\n- بازهٔ دو تاریخ. (بازهٔ تاریخ شمسی: /docs/persian/date-range/)\n- فقط ماه و سال، مثل تاریخ انقضای کارت. (انتخاب ماه: /docs/persian/month-picker/)\n- تاریخ با ساعت نوبت. (انتخاب نوبت: /docs/persian/appointment/)\n\nدرست و نادرست:\n\n- ✓ درست: برای تاریخ تولد `max` امروز بدهید و `yearRange` گسترده تا سال‌های دور با انتخاب سال رسیده شوند.\n- ✗ نادرست: تقویمی که برای سال تولد ۱۳۶۰ باید صدها بار «ماه قبل» زد.\n- ✓ درست: مقدار ارسالی میلادی (`YYYY-MM-DD`) است؛ سرور را با تاریخ شمسی رشته‌ای پر نکنید.\n- ✗ نادرست: ذخیرهٔ «۱۴۰۵/۰۸/۱۶» به‌شکل متن در پایگاه داده.\n\n## وب کامپوننت\n\nهمین کامپوننت بدون React، برای HTML، وردپرس، Vue، Svelte و Angular: `<kashi-date-picker>`، `<kashi-calendar>` در `@kashi-ui/elements`. تاریخ میلادی به شکل `YYYY-MM-DD` در فرم ارسال می‌شود؛ تقویم تنها با `<kashi-calendar>`.\n\nنصب بدون ابزار ساخت (CDN):\n\n```html\n<link rel=\"stylesheet\" href=\"https://cdn.jsdelivr.net/npm/@kashi-ui/css/dist/kashi.css\">\n<script type=\"module\" src=\"https://cdn.jsdelivr.net/npm/@kashi-ui/elements/dist/cdn/kashi-elements.js\"></script>\n```\n\nیا با npm، فقط همین بخش:\n\n```bash\nnpm i @kashi-ui/elements @kashi-ui/css\n```\n\n```js\nimport \"@kashi-ui/css\";\nimport \"@kashi-ui/elements/calendar.js\";\n```\n\n```html\n<kashi-date-picker label=\"تاریخ تحویل\" name=\"delivery\" holidays></kashi-date-picker>\n```\n\nویژگی‌ها:\n\n| نام | مقدار | پیش‌فرض | توضیح |\n|---|---|---|---|\n| label | متن | نام فارسی فیلد | برچسب بالای فیلد؛ بدون آن aria-label بدهید |\n| name | متن | — | نام فیلد در فرم HTML؛ مقدار خام ارسال می‌شود |\n| value | متن | — | مقدار اولیه |\n| hint | متن | — | راهنمای زیر فیلد |\n| error | متن | — | خطای دلخواه، مثلاً از اعتبارسنجی سرور |\n| required / disabled | بولی | — | مثل input معمولی |\n| min / max | 2026-03-21 یا 1405/01/01 | — | کمترین و بیشترین تاریخ |\n| holidays | بولی | — | جمعه‌ها و تعطیلات ثابت رسمی در تقویم |\n| digits | fa \\| latin | fa | خط ارقام |\n| invalid-message | متن | — | پیام تاریخ نامعتبر |\n\nرویدادها و JavaScript:\n\n| نام | توضیح |\n|---|---|\n| kashi-change | detail: { value: \"2026-03-21\", date: Date } |\n| element.date / element.value | تاریخ به‌شکل Date یا YYYY-MM-DD |\n| element.isDateDisabled | تابعی برای غیرفعال کردن روزها، مثل (d) => d.getDay() === 5 |\n| element.open() / close() | باز و بستن تقویم |"},{"slug":"persian/date-range","url":"https://kashi.sepantapouya.com/docs/persian/date-range/","title":"بازهٔ تاریخ شمسی","description":"انتخاب بازهٔ تاریخ با تقویم جلالی و میان‌برهایی مثل «۷ روز اخیر» و «ماه قبل».","group":"فارسی","components":["DateRangePicker"],"markdown":"```tsx\nimport { DateRangePicker } from \"@kashi-ui/react\";\n```\n\n## نمونه\n\nکلیک اول تاریخ شروع و کلیک دوم تاریخ پایان است (ترتیب مهم نیست)؛ پیش از کلیک دوم، بازه با حرکت موس یا فوکوس پیش‌نمایش می‌شود. تغییرها با «تأیید» ثبت می‌شوند.\n\n```tsx\nconst [range, setRange] = useState<DateRange>({ start: null, end: null });\n\n<DateRangePicker label=\"بازهٔ گزارش\" name=\"period\" value={range} onChange={setRange} max={new Date()} />\n```\n\nبا `name`، دو فیلد پنهان `period_start` و `period_end` به شکل میلادی `YYYY-MM-DD` در فرم ارسال می‌شوند.\n\n## میان‌برها\n\nمیان‌برهای پیش‌فرض (`defaultRangePresets`) «۷ روز اخیر»، «۳۰ روز اخیر»، «این ماه» و «ماه قبل» هستند و ماه‌ها شمسی حساب می‌شوند. فهرست خودتان را بدهید یا با `presets={[]}` حذفشان کنید:\n\n```tsx\n<DateRangePicker presets={[\n ...defaultRangePresets,\n { label: \"امسال\", range: (today) => ({ start: toGregorian(toJalali(today).jy, 1, 1), end: today }) },\n]} />\n```\n\n| ویژگی | نوع | پیش‌فرض | توضیح |\n|---|---|---|---|\n| value / defaultValue | { start: Date \\| null; end: Date \\| null } | — | — |\n| onChange | (range) => void | — | پس از «تأیید» |\n| min / max | Date | — | محدودهٔ مجاز |\n| isDateDisabled | (date) => boolean | — | روزهای غیرقابل‌انتخاب |\n| presets | { label, range: (today) => { start, end } }[] | defaultRangePresets | میان‌برها |\n| name | string | — | name_start و name_end |\n| digits | \"fa\" \\| \"latin\" | \"fa\" | — |\n| labels | { placeholder?, separator?, apply?, cancel?, presets?, pickEnd? } | — | متن‌ها |\n\n## دسترس‌پذیری\n\nدکمهٔ باز کردن `aria-haspopup=\"dialog\"` و `aria-expanded` دارد و متن آن بازهٔ انتخاب‌شده است. تقویم همان الگوی grid تقویم تک‌تاریخ را دارد؛ روزهای داخل بازه `aria-selected` می‌گیرند. خلاصهٔ بازه پس از هر انتخاب با `aria-live` اعلام می‌شود و <kbd>Esc</kbd> بدون ثبت تغییر می‌بندد.\n\n## راهنمای استفاده\n\nکِی استفاده کنیم:\n\n- بازهٔ گزارش، فیلتر تاریخ تراکنش‌ها، رزرو چندروزه.\n\nکِی استفاده نکنیم:\n\n- یک تاریخ. (دیت‌پیکر شمسی: /docs/persian/date-picker/)\n- گزارش ماهانه. (انتخاب ماه: /docs/persian/month-picker/)\n\nدرست و نادرست:\n\n- ✓ درست: میان‌برهای پرکار («۷ روز اخیر»، «ماه قبل») را نگه دارید؛ بیشتر کاربران همین‌ها را می‌خواهند.\n- ✗ نادرست: فقط تقویم، تا برای «این ماه» دو کلیک دقیق لازم باشد.\n\n## وب کامپوننت\n\nهمین کامپوننت بدون React، برای HTML، وردپرس، Vue، Svelte و Angular: `<kashi-date-range-picker>` در `@kashi-ui/elements`. با `name` دو فیلد `name_start` و `name_end` با تاریخ میلادی ارسال می‌شوند.\n\nنصب بدون ابزار ساخت (CDN):\n\n```html\n<link rel=\"stylesheet\" href=\"https://cdn.jsdelivr.net/npm/@kashi-ui/css/dist/kashi.css\">\n<script type=\"module\" src=\"https://cdn.jsdelivr.net/npm/@kashi-ui/elements/dist/cdn/kashi-elements.js\"></script>\n```\n\nیا با npm، فقط همین بخش:\n\n```bash\nnpm i @kashi-ui/elements @kashi-ui/css\n```\n\n```js\nimport \"@kashi-ui/css\";\nimport \"@kashi-ui/elements/pickers.js\";\n```\n\n```html\n<kashi-date-range-picker label=\"بازهٔ گزارش\" name=\"report\" holidays></kashi-date-range-picker>\n```\n\nویژگی‌ها:\n\n| نام | مقدار | پیش‌فرض | توضیح |\n|---|---|---|---|\n| label / hint / error | متن | بازهٔ تاریخ | مثل بقیهٔ فیلدها |\n| start / end | 2026-03-21 | — | بازهٔ اولیه |\n| min / max / holidays / digits | — | — | مثل انتخاب تاریخ |\n| presets | false | — | بدون میان‌برهای «۷ روز اخیر» و… |\n\nرویدادها و JavaScript:\n\n| نام | توضیح |\n|---|---|\n| kashi-change | detail: { start, end, value: { start: \"YYYY-MM-DD\", end } } |\n| element.value | { start, end } با Date |"},{"slug":"persian/date-tile","url":"https://kashi.sepantapouya.com/docs/persian/date-tile/","title":"برگ تقویم","description":"کاشی تاریخ شمسی به شکل برگ تقویم، با روز هفته، مناسبت تعطیل و معادل میلادی.","group":"فارسی","components":["DateTile"],"markdown":"```tsx\nimport { DateTile } from \"@kashi-ui/react\";\n```\n\n## نمونه\n\n```tsx\n<DateTile date={new Date()} size=\"lg\" />\n<DateTile date={new Date(2026, 2, 21)} /> {/* نوروز */}\n```\n\nبرای رویدادها، نوبت‌دهی، تاریخ ارسال سفارش و فهرست جلسات. روزهای تعطیل (از جمله جمعه) قرمز می‌شوند و نام مناسبت رسمی نمایش داده می‌شود. تعطیلات قمری را با `holidays={{ extra: { … } }}` اضافه کنید. همان قالب `getIranHoliday` است.\n\n| ویژگی | نوع | پیش‌فرض | توضیح |\n|---|---|---|---|\n| date | Date | — | — |\n| holidays | boolean \\| HolidayOptions | true | مناسبت و رنگ تعطیل |\n| gregorian | boolean | true | معادل میلادی |\n| size | \"sm\" \\| \"md\" \\| \"lg\" | \"md\" | — |\n\n## دسترس‌پذیری\n\nعنصر `<time>` با `datetime` استاندارد است تا موتورهای جستجو و ابزارها تاریخ را بفهمند.\n\n## راهنمای استفاده\n\nکِی استفاده کنیم:\n\n- نمایش برجستهٔ یک تاریخ: زمان رویداد، تاریخ تحویل، تقویم مناسبت‌ها.\n\nکِی استفاده نکنیم:\n\n- تاریخ داخل متن یا جدول؛ همان متن ساده با `formatJalali` بس است. (ابزارهای فارسی: /docs/persian/utilities/)\n\nدرست و نادرست:\n\n- ✓ درست: معادل میلادی را برای مخاطب بین‌المللی روشن بگذارید (`gregorian`).\n- ✗ نادرست: چند برگ تقویم بزرگ برای هر ردیف یک فهرست."},{"slug":"persian/input","url":"https://kashi.sepantapouya.com/docs/persian/input/","title":"فیلد فارسی","description":"یک کامپوننت برای همهٔ داده‌های فارسی؛ کد ملی، شناسهٔ شرکت، موبایل، تلفن، کارت، شبا، مبلغ، کد پستی، ساعت، پلاک و متن با اصلاح زبان صفحه‌کلید.","group":"فارسی","components":["PersianInput"],"markdown":"```tsx\nimport { PersianInput } from \"@kashi-ui/react\";\n```\n\n## نمونه\n\n```tsx\n<PersianInput kind=\"iban\" name=\"iban\" />\n<PersianInput kind=\"price\" min={10000} />\n<PersianInput kind=\"postal\" label=\"کد پستی محل کار\" />\n<PersianInput kind=\"text\" label=\"جستجو\" /> {/* با پیشنهاد «sghl» ← «سلام» */}\n```\n\nبه جای به خاطر سپردن نام یازده کامپوننت، یک فیلد با `kind` بسازید. هر نوع برچسب، قالب، اعتبارسنجی و پیام خطای فارسی خودش را دارد. ویژگی‌های دیگر همان کامپوننت تخصصی است و TypeScript بر اساس `kind` آن‌ها را پیشنهاد می‌دهد. مثلاً `showBank` فقط برای `card` و `iban` مجاز است.\n\n| kind | داده | کامپوننت تخصصی |\n|---|---|---|\n| `text` | متن با پیشنهاد اصلاح زبان صفحه‌کلید | [KeyboardFixInput](#text) |\n| `national-id` | کد ملی | [NationalIdInput](#national-id) |\n| `legal-id` | شناسهٔ ملی شرکت | [LegalIdInput](#legal-id) |\n| `mobile` | موبایل با اپراتور | [MobileInput](#mobile) |\n| `landline` | تلفن ثابت با استان | [LandlineInput](#landline) |\n| `card` | شمارهٔ کارت با بانک | [CardNumberInput](#card) |\n| `iban` | شبا با بانک | [IbanInput](#iban) |\n| `price` | مبلغ با حروف | [PriceInput](#price) |\n| `postal` | کد پستی | [PostalCodeInput](#postal) |\n| `time` | ساعت | [TimeInput](#time) |\n| `plate` | پلاک خودرو | [PlateInput](#plate) |\n\nاگر برچسب ندهید، برچسب پیش‌فرض همان نوع («شمارهٔ شبا»، «کد پستی» و…) گذاشته می‌شود. با `aria-label` برچسب دیده نمی‌شود. فهرست برچسب‌ها در `PERSIAN_INPUT_LABELS` است.\n\n## حجم بارگذاری\n\n`PersianInput` همهٔ یازده فیلد را import می‌کند. اگر در صفحه فقط یکی را لازم دارید، کامپوننت تخصصی را مستقیم import کنید تا حجم کمتری بارگذاری شود.\n\n| ویژگی | نوع | پیش‌فرض | توضیح |\n|---|---|---|---|\n| kind | \"text\" \\| \"national-id\" \\| \"legal-id\" \\| \"mobile\" \\| \"landline\" \\| \"card\" \\| \"iban\" \\| \"price\" \\| \"postal\" \\| \"time\" \\| \"plate\" | — | نوع داده |\n| … | — | — | ویژگی‌های کامپوننت تخصصی همان نوع |\n\n## ویژگی‌های هر نوع\n\nدر بخش‌های زیر، نمونه و ویژگی‌های اختصاصی هر نوع آمده است. همهٔ نوع‌ها ویژگی‌های `Input` مثل `label`، `hint`، `error`، `required` و `disabled` را هم می‌پذیرند.\n\n## کد ملی\n\nفیلد کد ملی با اعتبارسنجی رقم کنترل؛ ارقام فارسی و انگلیسی را می‌پذیرد.\n\n```tsx\n<PersianInput kind=\"national-id\" /> // یا مستقیم: <NationalIdInput />\n```\n\nیک کد ملی آزمایشی معتبر: `0013542419`. یک رقم آخر را عوض کنید تا خطا را ببینید.\n\n```tsx\n<PersianInput kind=\"national-id\"\n label=\"کد ملی\"\n name=\"nationalId\"\n onValueChange={(value, valid) => console.log(value, valid)}\n/>\n```\n\n#### رفتار\n\n- **ارقام:** کاربر می‌تواند با صفحه‌کلید فارسی یا انگلیسی تایپ کند. نمایش با ارقام فارسی است (`digits=\"latin\"` برای انگلیسی)، ولی مقدار همیشه با ارقام انگلیسی به شما داده می‌شود.\n- **محدودیت:** هر نویسهٔ غیر رقمی حذف می‌شود و بیش از ۱۰ رقم پذیرفته نمی‌شود. در گوشی، صفحه‌کلید عددی باز می‌شود.\n- **اعتبارسنجی:** رقم دهم با الگوریتم رسمی رقم کنترل بررسی می‌شود. پیام خطا فقط پس از خروج از فیلد یا کامل شدن ۱۰ رقم نمایش داده می‌شود، نه هنگام تایپ.\n- **فرم:** با `name`، مقدار با ارقام انگلیسی در یک فیلد مخفی ارسال می‌شود.\n\n> اعتبارسنجی رقم کنترل فقط درستی ساختار کد را نشان می‌دهد، نه وجود آن را. برای احراز هویت، کد را با سرویس رسمی استعلام کنید.\n\n#### ویژگی‌ها\n\n| ویژگی | نوع | پیش‌فرض | توضیح |\n|---|---|---|---|\n| value | string | — | مقدار کنترل‌شده با ارقام انگلیسی |\n| defaultValue | string | \"\" | مقدار اولیه |\n| onValueChange | (value: string, valid: boolean) => void | — | با هر تغییر |\n| digits | \"fa\" \\| \"latin\" | \"fa\" | خط ارقام نمایشی |\n| invalidMessage | ReactNode | \"کد ملی معتبر نیست.\" | پیام خطا |\n| name | string | — | نام فیلد مخفی فرم |\n\nهمهٔ ویژگی‌های `Input` مثل `label`، `hint`، `error` و `required` هم پشتیبانی می‌شوند.\n\n#### بدون React\n\n```ts\nisValidNationalId(\"۰۰۱۳۵۴۲۴۱۹\"); // true\nisValidNationalId(\"1111111111\"); // false\n```\n\n#### دسترس‌پذیری\n\n- خطا با `aria-invalid` و `aria-describedby` به فیلد متصل است.\n- فیلد با `dir=\"ltr\"` است تا ترتیب ارقام هنگام تایپ جابه‌جا نشود.\n\n## شناسهٔ ملی شرکت\n\nشناسهٔ ملی ۱۱ رقمی اشخاص حقوقی با بررسی رقم کنترل.\n\n```tsx\n<PersianInput kind=\"legal-id\" /> // یا مستقیم: <LegalIdInput />\n```\n\n```tsx\n<PersianInput kind=\"legal-id\" label=\"شناسهٔ ملی شرکت\" name=\"legalId\"\n onValueChange={(value, valid) => …} />\n```\n\nشناسهٔ ملی شرکت‌ها، مؤسسات و سازمان‌ها ۱۱ رقم دارد و رقم آخر آن رقم کنترل است. برای فرم‌های B2B، فاکتور رسمی و ثبت فروشنده در بازارگاه‌ها لازم است. ارقام فارسی و انگلیسی پذیرفته می‌شوند. بدون React هم با `isValidLegalId` از `@kashi-ui/persian` در دسترس است:\n\n```ts\nisValidLegalId(\"10380284790\"); // true\n```\n\n| ویژگی | نوع | پیش‌فرض | توضیح |\n|---|---|---|---|\n| value / defaultValue | string | — | ارقام لاتین |\n| onValueChange | (value: string, valid: boolean) => void | — | — |\n| digits | \"fa\" \\| \"latin\" | \"fa\" | — |\n| invalidMessage | ReactNode | «شناسهٔ ملی معتبر نیست.» | — |\n| name | string | — | فیلد پنهان با ۱۱ رقم لاتین |\n\n#### دسترس‌پذیری\n\nخطا پس از خروج از فیلد یا کامل شدن ۱۱ رقم نمایش داده می‌شود و با `aria-describedby` به فیلد وصل است.\n\n## شماره موبایل\n\nفیلد شماره موبایل ایران با تبدیل خودکار +98، گروه‌بندی و تشخیص اپراتور.\n\n```tsx\n<PersianInput kind=\"mobile\" /> // یا مستقیم: <MobileInput />\n```\n\nشماره را به هر شکلی بنویسید یا بچسبانید: `09123456789`، `+98 912 345 6789` یا `00989123456789`.\n\n```tsx\n<PersianInput kind=\"mobile\"\n label=\"شماره موبایل\"\n name=\"mobile\"\n onValueChange={(mobile, valid, operator) => console.log(mobile, valid, operator)}\n/>\n```\n\n#### رفتار\n\n- **یکدست‌سازی:** `+98`، `0098`، `98` و شمارهٔ بدون صفر اول (`912…`) به شکل استاندارد `09xxxxxxxxx` درمی‌آیند.\n- **اپراتور:** همراه اول، ایرانسل و رایتل از روی پیش‌شماره تشخیص داده می‌شوند. به دلیل امکان ترابرد شماره، اپراتور فقط برای راهنمایی است.\n- **صفحه‌کلید:** در گوشی صفحه‌کلید تلفن باز می‌شود و مرورگر می‌تواند شمارهٔ ذخیره‌شده را پیشنهاد دهد.\n\n#### ویژگی‌ها\n\n| ویژگی | نوع | پیش‌فرض | توضیح |\n|---|---|---|---|\n| value | string | — | مقدار کنترل‌شده، به هر شکل |\n| defaultValue | string | — | مقدار اولیه |\n| onValueChange | (mobile: string, valid: boolean, operator: string \\| null) => void | — | مقدار به شکل ۰۹xxxxxxxxx |\n| digits | \"fa\" \\| \"latin\" | \"fa\" | خط ارقام نمایشی |\n| showOperator | boolean | true | نمایش نام اپراتور |\n| invalidMessage | ReactNode | \"شماره موبایل معتبر نیست.\" | پیام خطا |\n| name | string | — | نام فیلد مخفی فرم |\n\n#### بدون React\n\n```ts\nnormalizeMobile(\"+98 912 345 6789\"); // \"09123456789\"\ngetMobileOperator(\"0935\")?.name; // \"ایرانسل\"\nformatMobile(\"09123456789\"); // \"0912 345 6789\"\n```\n\n## تلفن ثابت\n\nشمارهٔ تلفن ثابت با پیش‌شماره، گروه‌بندی و تشخیص استان.\n\n```tsx\n<PersianInput kind=\"landline\" /> // یا مستقیم: <LandlineInput />\n```\n\n```tsx\n<PersianInput kind=\"landline\" label=\"تلفن ثابت\" name=\"tel\"\n onValueChange={(value, valid, province) => …} />\n```\n\n+98 و 0098 به ۰ تبدیل می‌شوند و نام استان از روی پیش‌شمارهٔ سه‌رقمی (مثل ۰۳۱ برای اصفهان) کنار فیلد نمایش داده می‌شود. پیش‌شمارهٔ هر ۳۱ استان در `getLandlineProvince` است.\n\n| ویژگی | نوع | پیش‌فرض | توضیح |\n|---|---|---|---|\n| value / defaultValue | string | — | مثل 02188776655 |\n| onValueChange | (value, valid, province) => void | — | — |\n| showProvince | boolean | true | نام استان کنار فیلد |\n| digits | \"fa\" \\| \"latin\" | \"fa\" | — |\n| invalidMessage | ReactNode | «شمارهٔ تلفن ثابت معتبر نیست.» | — |\n| name | string | — | فیلد پنهان با ۱۱ رقم لاتین |\n\n#### دسترس‌پذیری\n\nنام استان با `aria-describedby` به فیلد وصل است. `autocomplete=\"tel-national\"` و صفحه‌کلید عددی گوشی فعال است.\n\n## شماره کارت\n\nفیلد شماره کارت بانکی با گروه‌بندی خودکار، تشخیص بانک و اعتبارسنجی Luhn.\n\n```tsx\n<PersianInput kind=\"card\" /> // یا مستقیم: <CardNumberInput />\n```\n\nشش رقم اول را تایپ کنید، مثلاً `603799` یا `610433`، تا لوگو و نام بانک نمایش داده شوند.\n\n```tsx\n<PersianInput kind=\"card\"\n label=\"شماره کارت\"\n name=\"card\"\n onValueChange={(value, valid, bank) => console.log(value, valid, bank)}\n/>\n```\n\n#### رفتار\n\n- **گروه‌بندی:** ارقام به‌صورت خودکار در گروه‌های چهارتایی نمایش داده می‌شوند. هنگام ویرایش وسط شماره، مکان‌نما سر جای درستش می‌ماند.\n- **چسباندن:** شماره‌ای که با فاصله، خط تیره یا ارقام فارسی کپی شده هم درست خوانده می‌شود.\n- **تشخیص بانک:** لوگو و نام بانک از شش رقم اول (BIN) تشخیص داده می‌شوند. لوگوها فقط وقتی بانک شناخته شد بارگذاری می‌شوند و حجم اولیهٔ برنامه را زیاد نمی‌کنند ([لوگوی بانک‌ها](/docs/persian/bank-logos/)). فهرست بانک‌ها ممکن است کامل نباشد؛ از آن فقط برای راهنمایی کاربر استفاده کنید.\n- **اعتبارسنجی:** شمارهٔ ۱۶ رقمی با الگوریتم Luhn بررسی می‌شود.\n- **تکمیل خودکار:** با `autocomplete=\"cc-number\"`، مرورگر می‌تواند کارت ذخیره‌شده را پیشنهاد دهد.\n\n> این فیلد شماره را فقط در مرورگر قالب‌بندی می‌کند. شمارهٔ کارت داده‌ای حساس است؛ آن را فقط از طریق HTTPS ارسال کنید و بدون نیاز ذخیره نکنید.\n\n#### ویژگی‌ها\n\n| ویژگی | نوع | پیش‌فرض | توضیح |\n|---|---|---|---|\n| value | string | — | مقدار کنترل‌شده: ۱۶ رقم انگلیسی بدون فاصله |\n| defaultValue | string | \"\" | مقدار اولیه |\n| onValueChange | (value: string, valid: boolean, bank: string \\| null) => void | — | با هر تغییر |\n| digits | \"fa\" \\| \"latin\" | \"fa\" | خط ارقام نمایشی |\n| showBank | boolean | true | نمایش نام بانک |\n| showBankLogo | boolean | true | نمایش لوگوی بانک (بارگذاری تنبل) |\n| invalidMessage | ReactNode | \"شماره کارت معتبر نیست.\" | پیام خطا |\n| name | string | — | نام فیلد مخفی فرم با ارقام بدون فاصله |\n\n#### بدون React\n\n```ts\nformatCardNumber(\"6037991234567890\"); // \"6037 9912 3456 7890\"\ngetBankByCard(\"603799\")?.name; // \"بانک ملی\"\nisValidCardNumber(\"4111 1111 1111 1111\"); // true\n```\n\n#### دسترس‌پذیری\n\n- نام بانک با `aria-describedby` به فیلد متصل است و صفحه‌خوان آن را همراه فیلد می‌خواند.\n- خطا پس از خروج از فیلد یا کامل شدن ۱۶ رقم نمایش داده می‌شود، نه هنگام تایپ.\n\n## شماره شبا\n\nفیلد شبا (IBAN ایران) با IR خودکار، گروه‌بندی، لوگو و نام بانک و بررسی رقم‌های کنترل.\n\n```tsx\n<PersianInput kind=\"iban\" /> // یا مستقیم: <IbanInput />\n```\n\nفقط ارقام را تایپ کنید؛ «IR» خودکار اضافه می‌شود. با تایپ ارقام ۳ تا ۵ (کد بانک)، لوگو و نام بانک نمایش داده می‌شوند.\n\n```tsx\n<PersianInput kind=\"iban\"\n label=\"شماره شبا\"\n name=\"iban\"\n onValueChange={(iban, valid, bank) => console.log(iban, valid, bank)}\n/>\n```\n\n#### رفتار\n\n- **ورودی آزاد:** شبا با یا بدون «IR»، با فاصله یا بدون آن، و با ارقام فارسی یا انگلیسی پذیرفته می‌شود.\n- **اعتبارسنجی:** رقم‌های کنترل با استاندارد ISO 13616 (باقیماندهٔ ۹۷) بررسی می‌شوند؛ هر رقم اشتباه یا جابه‌جایی ارقام مجاور تشخیص داده می‌شود.\n- **مقدار:** همیشه به شکل `IR` + ۲۴ رقم بدون فاصله به شما داده می‌شود.\n- **ارقام:** شبا معمولاً با ارقام انگلیسی نوشته می‌شود؛ برای ارقام فارسی `digits=\"fa\"` بدهید.\n\n#### ویژگی‌ها\n\n| ویژگی | نوع | پیش‌فرض | توضیح |\n|---|---|---|---|\n| value | string | — | شبای کنترل‌شده، با یا بدون IR |\n| defaultValue | string | — | مقدار اولیه |\n| onValueChange | (iban: string, valid: boolean, bank: string \\| null) => void | — | با هر تغییر |\n| digits | \"fa\" \\| \"latin\" | \"latin\" | خط ارقام نمایشی |\n| showBank | boolean | true | نمایش نام بانک |\n| showBankLogo | boolean | true | نمایش لوگوی بانک (بارگذاری تنبل) |\n| invalidMessage | ReactNode | \"شماره شبا معتبر نیست.\" | پیام خطا |\n| name | string | — | نام فیلد مخفی فرم با مقدار IR… |\n\n#### بدون React\n\n```ts\nisValidIban(\"IR06 2960 0000 0010 0324 2000 01\");\ngetBankByIban(\"IR..0170...\")?.name; // \"بانک ملی\"\nformatIban(\"062960000000100324200001\"); // \"IR06 2960 0000 0010 0324 2000 01\"\n```\n\n#### دسترس‌پذیری\n\n- نام بانک با `aria-describedby` به فیلد متصل است؛ لوگو تزئینی است (`alt=\"\"`) تا صفحه‌خوان نام بانک را دو بار نخواند.\n\n## مبلغ\n\nفیلد مبلغ با جداکنندهٔ هزارگان فارسی، واحد پول و نمایش مبلغ به حروف.\n\n```tsx\n<PersianInput kind=\"price\" /> // یا مستقیم: <PriceInput />\n```\n\nمبلغ به حروف زیر فیلد نمایش داده می‌شود تا کاربر مطمئن شود صفرها را درست وارد کرده است؛ اشتباهی رایج و پرهزینه در پرداخت‌ها.\n\n```tsx\n<PersianInput kind=\"price\"\n label=\"مبلغ پرداخت\"\n name=\"amount\"\n max={500_000_000}\n onValueChange={(amount) => console.log(amount)}\n/>\n```\n\n#### رفتار\n\n- **قالب‌بندی:** جداکنندهٔ هزارگان فارسی (`٬`) هنگام تایپ اضافه می‌شود و مکان‌نما سر جایش می‌ماند.\n- **به حروف:** «یک میلیون و دویست و پنجاه هزار تومان». با `showWords={false}` خاموش می‌شود.\n- **واحد:** پیش‌فرض «تومان»؛ با `unit=\"ریال\"` یا هر متن دیگری عوض می‌شود.\n- **مقدار:** عدد (`number`) یا `null` برای فیلد خالی. صفرهای ابتدایی حذف می‌شوند. تا ۱۵ رقم.\n- **محدوده:** با `min` و `max`، پس از خروج از فیلد پیام خطا نمایش داده می‌شود.\n\n#### ویژگی‌ها\n\n| ویژگی | نوع | پیش‌فرض | توضیح |\n|---|---|---|---|\n| value | number \\| null | — | مقدار کنترل‌شده |\n| defaultValue | number \\| null | — | مقدار اولیه |\n| onValueChange | (value: number \\| null) => void | — | با هر تغییر |\n| unit | ReactNode | \"تومان\" | واحد پول کنار فیلد |\n| showWords | boolean | true | نمایش مبلغ به حروف در متن راهنما |\n| min / max | number | — | محدودهٔ مجاز |\n| rangeMessage | (min?, max?) => ReactNode | خودکار | پیام خطای محدوده |\n| digits | \"fa\" \\| \"latin\" | \"fa\" | خط ارقام نمایشی |\n| name | string | — | نام فیلد مخفی فرم با مقدار عددی خام |\n\n#### بدون React\n\n```ts\nnumberToWords(1250000); // \"یک میلیون و دویست و پنجاه هزار\"\nformatNumber(1250000); // \"۱٬۲۵۰٬۰۰۰\"\n```\n\n#### دسترس‌پذیری\n\n- مبلغ به حروف در متن راهنما است و با `aria-describedby` به فیلد متصل است؛ صفحه‌خوان پس از نام فیلد آن را می‌خواند.\n- واحد پول کنار فیلد نمایش داده می‌شود؛ آن را در برچسب هم تکرار کنید اگر برای فهم فیلد ضروری است.\n\n## کد پستی\n\nکد پستی ۱۰ رقمی ایران با قالب ۱۲۳۴۵-۶۷۸۹۰ و بررسی قواعد آن.\n\n```tsx\n<PersianInput kind=\"postal\" /> // یا مستقیم: <PostalCodeInput />\n```\n\n```tsx\n<PersianInput kind=\"postal\" label=\"کد پستی\" name=\"postal\"\n onValueChange={(value, valid) => …} />\n```\n\nارقام فارسی و انگلیسی پذیرفته می‌شوند و خط تیره خودکار اضافه می‌شود. کد پستی ایران در پنج رقم اول ۰ و ۲ ندارد و چهار رقم یکسان پشت سر هم ندارد؛ `isValidPostalCode` همین قواعد را بررسی می‌کند. این بررسی وجود واقعی کد را تضمین نمی‌کند.\n\n| ویژگی | نوع | پیش‌فرض | توضیح |\n|---|---|---|---|\n| value / defaultValue | string | — | ارقام لاتین |\n| onValueChange | (value: string, valid: boolean) => void | — | — |\n| digits | \"fa\" \\| \"latin\" | \"fa\" | — |\n| invalidMessage | ReactNode | «کد پستی معتبر نیست.» | — |\n| name | string | — | فیلد پنهان با ۱۰ رقم لاتین |\n\n#### دسترس‌پذیری\n\n`autocomplete=\"postal-code\"` دارد تا مرورگر بتواند آن را پر کند. خطا پس از خروج از فیلد یا کامل شدن ۱۰ رقم نمایش داده می‌شود، نه هنگام تایپ.\n\n## ساعت\n\nورود ساعت ۲۴ساعته با ارقام فارسی، تایپ سریع و گام‌های ↑↓.\n\n```tsx\n<PersianInput kind=\"time\" /> // یا مستقیم: <TimeInput />\n```\n\nکاربر می‌تواند «۹۳۰»، «۹:۳۰» یا «9.30» بنویسد؛ پس از خروج از فیلد به «۰۹:۳۰» تبدیل می‌شود. <kbd>↑</kbd>/<kbd>↓</kbd> به گام بعدی یا قبلی می‌پرد.\n\n```tsx\n<PersianInput kind=\"time\" label=\"ساعت شروع جلسه\" min=\"08:00\" max=\"20:00\" value={t} onChange={setT} name=\"start\" />\n```\n\n| ویژگی | نوع | پیش‌فرض | توضیح |\n|---|---|---|---|\n| value / defaultValue | \"HH:MM\" \\| null | null | با ارقام لاتین |\n| onChange | (value: string \\| null) => void | — | — |\n| step | number | 15 | گام ↑↓ به دقیقه |\n| min / max | \"HH:MM\" | — | محدودهٔ مجاز |\n| digits | \"fa\" \\| \"latin\" | \"fa\" | — |\n| invalidMessage | ReactNode | — | پیام ساعت نامعتبر |\n| rangeMessage | (min, max) => ReactNode | «ساعت باید بین … و … باشد.» | پیام ساعت خارج از محدوده |\n\nابزارهای `parseTime` و `formatTime` در `@kashi-ui/persian` هم در دسترس‌اند.\n\n#### دسترس‌پذیری\n\nیک فیلد متنی ساده با برچسب و راهنما؛ برخلاف `<input type=\"time\">`، در همهٔ مرورگرها یکسان و با ارقام فارسی کار می‌کند. خطا پس از خروج از فیلد نمایش داده و به آن وصل می‌شود.\n\n## پلاک خودرو\n\nورود پلاک خودروی ایران با ظاهر واقعی پلاک: دو رقم، حرف، سه رقم و کد استان.\n\n```tsx\n<PersianInput kind=\"plate\" /> // یا مستقیم: <PlateInput />\n```\n\n```tsx\n<PersianInput kind=\"plate\" label=\"پلاک خودرو\" name=\"car\"\n onChange={(plate, valid) => …} />\n// plate = { first: \"12\", letter: \"ب\", middle: \"345\", region: \"67\" }\n```\n\nپلاک نسبت واقعی ۵۲۰ در ۱۱۰ میلی‌متر را دارد و رنگش مثل پلاک واقعی از روی حرف تعیین می‌شود (`plateType`):\n\n| حرف | نوع | رنگ |\n|---|---|---|\n| ب، ج، د، س… | شخصی | سفید |\n| ت / ع / ک | تاکسی / عمومی / کشاورزی | زرد |\n| الف | دولتی | قرمز |\n| پ / ث | پلیس / سپاه | سبز |\n| ش | ارتش | قهوه‌ای روشن |\n| ز / ف | وزارت دفاع / ستاد کل | آبی روشن |\n| ژ | معلولین | سفید |\n\nارتفاع با `--k-plate-height` (پیش‌فرض 3.5rem) عوض می‌شود و عرض به همان نسبت تغییر می‌کند. با پر شدن هر بخش، فوکوس به بخش بعد می‌رود و <kbd>Backspace</kbd> در بخش خالی به بخش قبل برمی‌گردد. `formatPlate` متن خوانا می‌سازد: «۱۲ ب ۳۴۵ ایران ۶۷».\n\n| ویژگی | نوع | پیش‌فرض | توضیح |\n|---|---|---|---|\n| value / defaultValue | { first, letter, middle, region } | — | ارقام لاتین |\n| onChange | (plate, valid: boolean) => void | — | — |\n| name | string | — | چهار فیلد پنهان: name_first، name_letter، name_middle، name_region |\n| labels | { first?, letter?, middle?, region? } | — | نام هر بخش برای صفحه‌خوان |\n| invalidMessage | ReactNode | «پلاک خودرو معتبر نیست.» | — |\n\n#### دسترس‌پذیری\n\nیک `role=\"group\"` با نام «پلاک خودرو» و چهار کنترل که هرکدام نام خودش را دارد («دو رقم اول پلاک»، «حرف پلاک»…). حرف با `<select>` بومی انتخاب می‌شود. وقتی پلاک کامل و معتبر شد، متن کامل آن با `aria-live` خوانده می‌شود.\n\n## اصلاح زبان صفحه‌کلید\n\nاگر کاربر فارسی را با صفحه‌کلید انگلیسی تایپ کند، شکل درست را پیشنهاد می‌دهد: sghl ← سلام.\n\n```tsx\n<PersianInput kind=\"text\" /> // یا مستقیم: <KeyboardFixInput />\n```\n\n```tsx\n<PersianInput kind=\"text\" label=\"جستجو\" value={query} onValueChange={setQuery} />\n```\n\nکاربر فارسی‌زبان زیاد پیش می‌آید که فراموش کند زبان صفحه‌کلید را عوض کند و «سلام» را «sghl» تایپ کند. این فیلد شکل فارسی را زیر فیلد پیشنهاد می‌دهد و با یک کلیک جایگزین می‌کند. برای جستجو بیشترین کاربرد را دارد.\n\nتشخیص به‌صورت پیش‌فرض حدسی است (`suggest=\"auto\"`): کلمه‌های لاتینِ بدون حرف صدادار یا با `;`، `'` و `[` در وسط کلمه مشکوک‌اند. برای پیشنهاد روی هر متن لاتین از `suggest=\"always\"` استفاده کنید. تبدیل بر اساس چیدمان استاندارد صفحه‌کلید فارسی است.\n\n```ts\nfixKeyboardLayout(\"sghl o,fd?\"); // \"سلام خوبی؟\"\nfixKeyboardLayout(\"لخخلمث\", \"en\"); // \"google\"\nlooksLikeWrongLayout(\"sghl\"); // true\n```\n\n| ویژگی | نوع | پیش‌فرض | توضیح |\n|---|---|---|---|\n| value / defaultValue | string | — | — |\n| onValueChange | (value: string) => void | — | — |\n| suggest | \"auto\" \\| \"always\" | \"auto\" | — |\n| renderSuggestion | (fixed, apply) => ReactNode | — | متن دلخواه پیشنهاد |\n\n#### دسترس‌پذیری\n\nپیشنهاد در ناحیهٔ `role=\"status\"` است تا صفحه‌خوان آن را اعلام کند و دکمهٔ آن با کیبورد در دسترس است. پس از جایگزینی، فوکوس به فیلد برمی‌گردد.\n\n## راهنمای استفاده\n\nکِی استفاده کنیم:\n\n- کد ملی، شناسهٔ شرکت، موبایل، تلفن ثابت، کارت، شبا، مبلغ، کد پستی، ساعت و پلاک.\n- هر جا داده ساختار ایرانی دارد و باید ارقام فارسی و لاتین هر دو پذیرفته شوند.\n\nکِی استفاده نکنیم:\n\n- متن آزاد. (ورودی: /docs/components/input/)\n- عدد کوچک با دکمهٔ افزایش. (ورودی عدد: /docs/components/number-input/)\n\nدرست و نادرست:\n\n- ✓ درست: مبلغ با هزارگان و به حروف زیرش؛ کاربر اشتباه یک صفر را همان لحظه می‌بیند.\n- ✗ نادرست: فیلد متنی معمولی که «1250000» می‌گیرد و هیچ بازخوردی نمی‌دهد.\n- ✓ درست: خطا را بعد از کامل شدن رقم‌ها یا خروج از فیلد نشان دهید (رفتار پیش‌فرض کاشی).\n- ✗ نادرست: با اولین رقم «کد ملی معتبر نیست» قرمز شود.\n\nمتن:\n\n- برچسب نام داده است: «کد ملی»، «شمارهٔ شبا»؛ نوع فیلد را در برچسب نیاورید («فیلد کد ملی»).\n\n## وب کامپوننت\n\nهمین کامپوننت بدون React، برای HTML، وردپرس، Vue، Svelte و Angular: `<kashi-national-id>`، `<kashi-mobile>`، `<kashi-card-number>`، `<kashi-iban>`، `<kashi-price>`، `<kashi-postal-code>`، `<kashi-landline>`، `<kashi-legal-id>`، `<kashi-time-input>`، `<kashi-plate-input>` در `@kashi-ui/elements`. هر فیلد با `name` مقدار خام را با ارقام انگلیسی ارسال می‌کند و مقدار نامعتبر جلوی ارسال فرم را می‌گیرد.\n\nنصب بدون ابزار ساخت (CDN):\n\n```html\n<link rel=\"stylesheet\" href=\"https://cdn.jsdelivr.net/npm/@kashi-ui/css/dist/kashi.css\">\n<script type=\"module\" src=\"https://cdn.jsdelivr.net/npm/@kashi-ui/elements/dist/cdn/kashi-elements.js\"></script>\n```\n\nیا با npm، فقط همین بخش:\n\n```bash\nnpm i @kashi-ui/elements @kashi-ui/css\n```\n\n```js\nimport \"@kashi-ui/css\";\nimport \"@kashi-ui/elements/inputs.js\";\n```\n\n```html\n<kashi-national-id name=\"national_id\" required></kashi-national-id>\n<kashi-mobile name=\"mobile\"></kashi-mobile>\n<kashi-card-number name=\"card\"></kashi-card-number>\n<kashi-price name=\"amount\" max=\"50000000\"></kashi-price>\n```\n\n| عنصر | کاربرد |\n|---|---|\n| <kashi-national-id> | کد ملی، ۱۰ رقم با بررسی رقم کنترل |\n| <kashi-legal-id> | شناسهٔ ملی شرکت، ۱۱ رقم |\n| <kashi-mobile> | موبایل؛ +98 را به ۰۹ تبدیل می‌کند و اپراتور را نشان می‌دهد |\n| <kashi-landline> | تلفن ثابت با نام استان از روی پیش‌شماره |\n| <kashi-card-number> | شمارهٔ کارت با نام و لوگوی بانک و بررسی Luhn |\n| <kashi-iban> | شبا با IR خودکار و نام بانک؛ ارقام لاتین |\n| <kashi-postal-code> | کد پستی ۱۰ رقمی با قالب ۱۲۳۴۵-۶۷۸۹۰ |\n| <kashi-price> | مبلغ با جداکنندهٔ هزارگان و مبلغ به حروف زیر فیلد |\n| <kashi-time-input> | ساعت ۲۴ساعته؛ ↑↓ با گام step دقیقه، min و max؛ بخش npm: forms.js |\n| <kashi-plate-input> | پلاک خودرو در چهار بخش؛ با name فیلدهای name_first، name_letter، name_middle و name_region؛ بخش npm: forms.js |\n\nویژگی‌ها:\n\n| نام | مقدار | پیش‌فرض | توضیح |\n|---|---|---|---|\n| label | متن | نام فارسی فیلد | برچسب بالای فیلد؛ بدون آن aria-label بدهید |\n| name | متن | — | نام فیلد در فرم HTML؛ مقدار خام ارسال می‌شود |\n| value | متن | — | مقدار اولیه |\n| hint | متن | — | راهنمای زیر فیلد |\n| error | متن | — | خطای دلخواه، مثلاً از اعتبارسنجی سرور |\n| required / disabled | بولی | — | مثل input معمولی |\n| digits | fa \\| latin | fa (شبا: latin) | خط ارقام نمایشی |\n| invalid-message | متن | — | پیام خطای دلخواه برای مقدار نامعتبر |\n| show-meta | false | — | پنهان کردن نام بانک، اپراتور یا استان |\n| bank-logo | false | — | کارت و شبا: بدون لوگوی بانک |\n| unit / min / max / show-words | — | تومان | فقط مبلغ: واحد، محدوده و نمایش به حروف |\n\nرویدادها و JavaScript:\n\n| نام | توضیح |\n|---|---|\n| kashi-change | با هر تغییر؛ detail: { value, valid } و برای موبایل operator، کارت و شبا bank، تلفن ثابت province |\n| element.value / element.valid | مقدار خام و معتبر بودن؛ مبلغ element.valueAsNumber هم دارد |"},{"slug":"persian/iran-map","url":"https://kashi.sepantapouya.com/docs/persian/iran-map/","title":"نقشهٔ ایران","description":"نقشهٔ ۳۱ استان ایران برای نمایش داده به تفکیک استان یا انتخاب یک یا چند استان؛ با صفحه‌کلید و جدول هم‌ارز برای صفحه‌خوان.","group":"فارسی","components":["IranMap"],"markdown":"```tsx\nimport { IranMap } from \"@kashi-ui/react\";\n```\n\n## نقشهٔ رنگی\n\nبه هر استان یک عدد بدهید؛ استان‌ها بر اساس مقدار در طیف رنگ برند رنگ می‌گیرند و استان‌های بدون داده خاکستری می‌مانند. با رفتن روی هر استان نام و مقدارش نمایش داده می‌شود. داده‌های این نمونه ساختگی‌اند.\n\n```tsx\n<IranMap\n data={{ \"تهران\": 18420, \"خراسان رضوی\": 7310, \"اصفهان\": 6980, /* … */ }}\n unit=\"سفارش\"\n label=\"سفارش‌های ماه گذشته به تفکیک استان\"\n/>\n```\n\nکلیدها همان نام‌های `PROVINCES` در `@kashi-ui/persian` هستند، مثل `\"تهران\"` یا `\"سیستان و بلوچستان\"`. تعداد پله‌های رنگ با `steps` (پیش‌فرض ۵) و قالب عدد با `format` عوض می‌شود. چون معمولاً یک استان (مثل تهران) خیلی بزرگ‌تر از بقیه است، پله‌ها به‌طور پیش‌فرض بر اساس رتبه پخش می‌شوند (`scale=\"quantile\"`) تا تفاوت بقیهٔ استان‌ها هم دیده شود؛ برای بازه‌های مساوی `scale=\"linear\"` بدهید.\n\n## انتخاب استان\n\nبا دادن `value` یا `onChange` نقشه قابل انتخاب می‌شود. مقدار همان نام استان است، پس با [انتخاب استان](/docs/persian/province/) هماهنگ کار می‌کند:\n\n```tsx\n<ProvinceSelect value={province} onChange={setProvince} />\n<IranMap value={province} onChange={setProvince} label=\"استان محل سکونت\" />\n```\n\n## انتخاب چند استان\n\nبا `multiple` مقدار یک آرایه است؛ مثلاً برای مناطق تحت پوشش ارسال یا فیلتر گزارش:\n\n```tsx\n<IranMap multiple value={regions} onChange={setRegions} label=\"استان‌های تحت پوشش ارسال\" />\n```\n\nداده و انتخاب را می‌شود با هم داشت: استان‌ها رنگ داده را نشان می‌دهند و استان انتخاب‌شده با رنگ برند و حاشیه برجسته می‌شود.\n\n| ویژگی | نوع | پیش‌فرض | توضیح |\n|---|---|---|---|\n| data | Record<string, number> | — | مقدار هر استان برای نقشهٔ رنگی |\n| value / defaultValue | string \\| string[] \\| null | — | استان یا استان‌های انتخاب‌شده |\n| onChange | (value: string \\| string[] \\| null) => void | — | با هر انتخاب؛ نقشه را قابل انتخاب می‌کند |\n| multiple | boolean | false | انتخاب چند استان |\n| label | string | «نقشهٔ استان‌های ایران» | نام دسترس‌پذیر |\n| unit | string | — | واحد در راهنما و جدول، مثل «سفارش» |\n| format | (n: number) => string | ارقام فارسی | قالب عدد |\n| steps | number | 5 | تعداد پله‌های رنگ |\n| scale | \"quantile\" \\| \"linear\" | \"quantile\" | quantile: هر پله تعداد تقریباً برابری استان؛ linear: بازهٔ مساوی |\n| legend | boolean | true با data | راهنمای رنگ زیر نقشه |\n| tooltip | (province, value) => string | — | خط اضافه در راهنمای هر استان |\n\nمرز و نقطهٔ وسط هر استان با `IRAN_PROVINCES` و `IRAN_VIEWBOX` هم در دسترس است، برای وقتی که نقشهٔ خودتان را می‌سازید.\n\n## متغیرهای CSS\n\n| متغیر CSS | پیش‌فرض | توضیح |\n|---|---|---|\n| --k-iran-map-empty | var(--k-bg-tertiary) | رنگ استان بدون داده |\n| --k-iran-map-selected | var(--k-bg-brand) | رنگ استان انتخاب‌شده |\n| --k-iran-map-border | var(--k-bg-primary) | رنگ مرز بین استان‌ها |\n\n## دسترس‌پذیری\n\n- نقشهٔ بدون انتخاب یک تصویر با نام است و همهٔ مقدارها در یک جدول پنهان، از بیشترین به کمترین، برای صفحه‌خوان و چاپ آمده‌اند.\n- در حالت انتخاب، استان‌ها `radio` (یا `checkbox` با `multiple`) هستند و فقط یکی در ترتیب Tab است؛ جهت‌نماها بین استان‌ها جابه‌جا می‌شوند، Home و End اول و آخر، و Enter یا فاصله انتخاب می‌کند. نام هر استان همراه مقدارش خوانده می‌شود.\n- رنگ تنها نشانهٔ انتخاب نیست؛ استان انتخاب‌شده حاشیهٔ پررنگ هم دارد و در حالت forced-colors با رنگ سیستم برجسته می‌شود.\n\nمرزها از [Natural Earth](https://www.naturalearthdata.com) (مالکیت عمومی) گرفته و ساده شده‌اند؛ برای کارهای نقشه‌برداری دقیق یا حقوقی مناسب نیستند.\n\n## راهنمای استفاده\n\nکِی استفاده کنیم:\n\n- داده به تفکیک استان: فروش، کاربران، شعبه‌ها.\n- انتخاب یک یا چند استان، کنار فهرست.\n\nکِی استفاده نکنیم:\n\n- مقایسهٔ دقیق عددها؛ کنار نقشه جدول یا میله‌ای افقی بگذارید. (نمودار: /docs/components/charts/)\n- داده‌ای که فقط برای دو سه استان هست؛ نقشهٔ تقریباً خاکستری چیزی نمی‌گوید.\n\nدرست و نادرست:\n\n- ✓ درست: `unit` و `label` بدهید تا راهنما و جدول صفحه‌خوان «۱۸٬۴۲۰ سفارش» بخوانند.\n- ✗ نادرست: نقشه بدون راهنمای رنگ که معلوم نیست پررنگ یعنی بیشتر یا کمتر.\n\n## وب کامپوننت\n\nهمین کامپوننت بدون React، برای HTML، وردپرس، Vue، Svelte و Angular: `<kashi-iran-map>` در `@kashi-ui/elements`.\n\nنصب بدون ابزار ساخت (CDN):\n\n```html\n<link rel=\"stylesheet\" href=\"https://cdn.jsdelivr.net/npm/@kashi-ui/css/dist/kashi.css\">\n<script type=\"module\" src=\"https://cdn.jsdelivr.net/npm/@kashi-ui/elements/dist/cdn/kashi-elements.js\"></script>\n```\n\nیا با npm، فقط همین بخش:\n\n```bash\nnpm i @kashi-ui/elements @kashi-ui/css\n```\n\n```js\nimport \"@kashi-ui/css\";\nimport \"@kashi-ui/elements/map.js\";\n```\n\n```html\n<kashi-iran-map selectable name=\"province\" unit=\"سفارش\" data='{\"تهران\": 1200, \"اصفهان\": 420, \"خراسان رضوی\": 380, \"فارس\": 260, \"آذربایجان شرقی\": 190}'></kashi-iran-map>\n```\n\nویژگی‌ها:\n\n| نام | مقدار | پیش‌فرض | توضیح |\n|---|---|---|---|\n| data | JSON | — | نام استان ← عدد، برای نقشهٔ رنگی |\n| selectable | بولی | — | انتخاب یک استان |\n| multiple | بولی | — | انتخاب چند استان |\n| value / name | متن | — | استان انتخاب‌شده و نام فیلد فرم |\n| unit | متن | — | واحد در راهنما و جدول، مثل «سفارش» |\n| steps / scale | عدد / linear | 5 / quantile | پله‌های رنگ و نحوهٔ تقسیم داده |\n| legend | false | — | بدون راهنمای رنگ |\n| label | متن | نقشهٔ استان‌های ایران | نام برای صفحه‌خوان |\n\nرویدادها و JavaScript:\n\n| نام | توضیح |\n|---|---|\n| kashi-change | detail: { value }؛ در multiple آرایه |\n| element.data | داده با JavaScript |"},{"slug":"persian/money","url":"https://kashi.sepantapouya.com/docs/persian/money/","title":"نمایش مبلغ","description":"مبلغ با تومان یا ریال، حالت خلاصه مثل «۱٫۲ میلیون» و مبلغ به حروف برای صفحه‌خوان.","group":"فارسی","components":["Money"],"markdown":"```tsx\nimport { Money } from \"@kashi-ui/react\";\n```\n\n## نمونه\n\n```tsx\n<Money value={1250000} />\n<Money value={1250000} compact />\n<Money value={48500000} from=\"rial\" compact /> {/* ریال به تومان */}\n<Money value={-85000} signed />\n```\n\nدر محصولات ایرانی مبلغ معمولاً در پایگاه داده به ریال است و به کاربر به تومان نشان داده می‌شود. با `from=\"rial\"` تبدیل خودکار انجام می‌شود. حالت `compact` برای کارت‌ها و جدول‌ها مناسب است و برای اعداد بسیار بزرگ «همت» (هزار میلیارد تومان) را به کار می‌برد.\n\n```ts\nimport { formatMoney } from \"@kashi-ui/persian\";\nformatMoney(1250000); // \"۱٬۲۵۰٬۰۰۰ تومان\"\nformatMoney(1250000, { compact: true }); // \"۱٫۲۵ میلیون تومان\"\nformatMoney(5000, { unit: \"rial\" }); // \"۵۰٬۰۰۰ ریال\"\n```\n\n| ویژگی | نوع | پیش‌فرض | توضیح |\n|---|---|---|---|\n| value | number | — | — |\n| unit | \"toman\" \\| \"rial\" | \"toman\" | واحد نمایش |\n| from | \"toman\" \\| \"rial\" | \"toman\" | واحد مقدار ورودی |\n| compact | boolean | false | هزار، میلیون، میلیارد، همت |\n| signed | boolean | false | + و رنگ سبز یا قرمز |\n| words | boolean | true | مبلغ به حروف در title و برای صفحه‌خوان |\n| size | \"sm\" \\| \"md\" \\| \"lg\" \\| \"xl\" | \"md\" | — |\n\n## دسترس‌پذیری\n\nعنصر `<data>` مقدار عددی خام را در `value` نگه می‌دارد. صفحه‌خوان به جای ارقام جداشده، مبلغ را به حروف می‌خواند («یک میلیون و دویست و پنجاه هزار تومان»). رنگ مثبت و منفی همیشه همراه علامت + و − است.\n\n## راهنمای استفاده\n\nکِی استفاده کنیم:\n\n- هر مبلغی که نمایش داده می‌شود: قیمت، موجودی، جمع سفارش، تراکنش.\n- مثبت و منفی با `signed` در گردش حساب.\n\nکِی استفاده نکنیم:\n\n- گرفتن مبلغ از کاربر. (فیلد فارسی (مبلغ): /docs/persian/input/)\n\nدرست و نادرست:\n\n- ✓ درست: یک واحد در کل محصول (تومان یا ریال)، و `compact` برای عددهای بزرگ در کارت آمار: «۱٫۲ میلیارد تومان».\n- ✗ نادرست: تومان در یک صفحه و ریال در صفحهٔ دیگر.\n\n## وب کامپوننت\n\nهمین کامپوننت بدون React، برای HTML، وردپرس، Vue، Svelte و Angular: `<kashi-money>` در `@kashi-ui/elements`.\n\nنصب بدون ابزار ساخت (CDN):\n\n```html\n<link rel=\"stylesheet\" href=\"https://cdn.jsdelivr.net/npm/@kashi-ui/css/dist/kashi.css\">\n<script type=\"module\" src=\"https://cdn.jsdelivr.net/npm/@kashi-ui/elements/dist/cdn/kashi-elements.js\"></script>\n```\n\nیا با npm، فقط همین بخش:\n\n```bash\nnpm i @kashi-ui/elements @kashi-ui/css\n```\n\n```js\nimport \"@kashi-ui/css\";\nimport \"@kashi-ui/elements/display.js\";\n```\n\n```html\n<kashi-money value=\"1250000\" size=\"lg\"></kashi-money>\n<kashi-money value=\"3800000000\" compact></kashi-money>\n<kashi-money value=\"-45000\" signed></kashi-money>\n```\n\nویژگی‌ها:\n\n| نام | مقدار | پیش‌فرض | توضیح |\n|---|---|---|---|\n| value | عدد | — | مبلغ |\n| unit / from | toman \\| rial | toman | واحد نمایش و واحد مقدار ورودی |\n| compact | بولی | — | ۱٫۲ میلیون تومان |\n| signed | بولی | — | رنگ سبز و قرمز با علامت + |\n| size | sm \\| md \\| lg \\| xl | md | اندازه |\n| words | false | — | بدون مبلغ به حروف برای صفحه‌خوان |"},{"slug":"persian/month-picker","url":"https://kashi.sepantapouya.com/docs/persian/month-picker/","title":"انتخاب ماه","description":"انتخاب ماه و سال شمسی برای گزارش ماهانه، صورت‌حساب یا تاریخ انقضا.","group":"فارسی","components":["MonthPicker"],"markdown":"```tsx\nimport { MonthPicker, type JalaliMonth } from \"@kashi-ui/react\";\n```\n\n## نمونه\n\n```tsx\nconst [m, setM] = useState<JalaliMonth | null>(null);\n\n<MonthPicker aria-label=\"ماه گزارش\" value={m} onChange={setM} max={{ year: 1405, month: 12 }} />\n```\n\nبرای تبدیل به تاریخ میلادی: `toGregorian(m.year, m.month, 1)` از `@kashi-ui/persian`.\n\n| ویژگی | نوع | پیش‌فرض | توضیح |\n|---|---|---|---|\n| value / defaultValue | { year, month } \\| null | null | ماه شمسی؛ month از ۱ تا ۱۲ |\n| onChange | (value) => void | — | — |\n| min / max | { year, month } | — | محدودهٔ مجاز |\n| digits | \"fa\" \\| \"latin\" | \"fa\" | — |\n| aria-label | string | «انتخاب ماه» | — |\n| labels | { prevYear?, nextYear? } | «سال قبل»، «سال بعد» | — |\n\n## کیبورد\n\n| کلید | عمل |\n|---|---|\n| <kbd>←</kbd> / <kbd>→</kbd> | ماه بعد / قبل (در RTL) |\n| <kbd>↑</kbd> / <kbd>↓</kbd> | سه ماه قبل / بعد |\n| <kbd>PageUp</kbd> / <kbd>PageDown</kbd> | سال قبل / بعد |\n| <kbd>Home</kbd> / <kbd>End</kbd> | فروردین / اسفند |\n\n## دسترس‌پذیری\n\nهر ماه دکمه‌ای با نام کامل («آبان ۱۴۰۵») و `aria-pressed` است؛ ماه جاری `aria-current=\"date\"` دارد. فقط یک ماه در ترتیب Tab است و سال با `aria-live` اعلام می‌شود.\n\n## راهنمای استفاده\n\nکِی استفاده کنیم:\n\n- گزارش ماهانه، صورت‌حساب، تاریخ انقضای کارت.\n\nکِی استفاده نکنیم:\n\n- روز مشخص. (دیت‌پیکر شمسی: /docs/persian/date-picker/)\n\nدرست و نادرست:\n\n- ✓ درست: ماه‌های آینده را برای گزارش با `max` غیرفعال کنید.\n- ✗ نادرست: اجازهٔ انتخاب ماهی که هنوز داده ندارد و گزارش خالی می‌دهد.\n\n## وب کامپوننت\n\nهمین کامپوننت بدون React، برای HTML، وردپرس، Vue، Svelte و Angular: `<kashi-month-picker>` در `@kashi-ui/elements`.\n\nنصب بدون ابزار ساخت (CDN):\n\n```html\n<link rel=\"stylesheet\" href=\"https://cdn.jsdelivr.net/npm/@kashi-ui/css/dist/kashi.css\">\n<script type=\"module\" src=\"https://cdn.jsdelivr.net/npm/@kashi-ui/elements/dist/cdn/kashi-elements.js\"></script>\n```\n\nیا با npm، فقط همین بخش:\n\n```bash\nnpm i @kashi-ui/elements @kashi-ui/css\n```\n\n```js\nimport \"@kashi-ui/css\";\nimport \"@kashi-ui/elements/pickers.js\";\n```\n\n```html\n<kashi-month-picker value=\"1405-01\" name=\"month\"></kashi-month-picker>\n```\n\nویژگی‌ها:\n\n| نام | مقدار | پیش‌فرض | توضیح |\n|---|---|---|---|\n| value | 1405-01 | — | سال و ماه شمسی |\n| min / max | 1404-01 | — | محدودهٔ ماه‌های قابل انتخاب |\n| name / label / digits | — | — | نام فرم، نام صفحه‌خوان و خط ارقام |\n\nرویدادها و JavaScript:\n\n| نام | توضیح |\n|---|---|\n| kashi-change | detail: { value: \"1405-01\", year, month } |"},{"slug":"persian/otp","url":"https://kashi.sepantapouya.com/docs/persian/otp/","title":"کد تأیید (OTP)","description":"ورود کد پیامکی یک‌بارمصرف؛ با پر شدن خودکار از پیامک در گوشی و ارقام فارسی.","group":"فارسی","components":["OtpInput"],"markdown":"```tsx\nimport { OtpInput } from \"@kashi-ui/react\";\n```\n\n## نمونه\n\nکد آزمایشی `123456` را تایپ کنید، یا کد را کپی کنید و بچسبانید.\n\n```tsx\n<OtpInput\n label=\"کد ارسال‌شده به ۰۹۱۲ ••• ۶۷۸۹\"\n length={6}\n groupAfter={3}\n onComplete={(code) => verify(code)}\n/>\n```\n\n## چرا یک input؟\n\nبیشتر پیاده‌سازی‌های OTP شش فیلد جدا دارند که چسباندن، پاک کردن و صفحه‌خوان را خراب می‌کند. کاشی **یک input واقعی** زیر خانه‌های نمایشی دارد، پس:\n\n- **پر شدن خودکار:** با `autocomplete=\"one-time-code\"`، iOS و Android کد پیامک دریافتی را بالای صفحه‌کلید پیشنهاد می‌دهند.\n- **چسباندن:** «۱۲۳ ۴۵۶» یا «123-456» هم درست خوانده می‌شود؛ ارقام فارسی و فاصله‌ها پذیرفته می‌شوند.\n- **صفحه‌خوان:** یک فیلد با یک برچسب می‌خواند، نه شش فیلد بی‌نام.\n- **پاک کردن و جابه‌جایی:** <kbd>Backspace</kbd>، کلیدهای جهت‌دار و انتخاب متن همان رفتار عادی را دارند.\n\n## ویژگی‌ها\n\n| ویژگی | نوع | پیش‌فرض | توضیح |\n|---|---|---|---|\n| length | number | 6 | تعداد رقم |\n| value / defaultValue | string | — | کد با ارقام انگلیسی |\n| onChange | (code) => void | — | با هر تغییر |\n| onComplete | (code) => void | — | وقتی همهٔ رقم‌ها وارد شد |\n| groupAfter | number | — | خط فاصله بعد از این تعداد رقم |\n| label / hint / error | ReactNode | — | مثل Input |\n| digits | \"fa\" \\| \"latin\" | \"fa\" | خط ارقام نمایشی |\n| size | \"sm\" \\| \"md\" | \"md\" | اندازهٔ خانه‌ها |\n| name | string | — | نام فیلد مخفی فرم |\n\n## راهنمای استفاده\n\nکِی استفاده کنیم:\n\n- کد پیامکی یک‌بارمصرف برای ورود، پرداخت یا تأیید شماره.\n\nکِی استفاده نکنیم:\n\n- رمز عبور ثابت. (ورودی: /docs/components/input/)\n\nدرست و نادرست:\n\n- ✓ درست: کنار فیلد بگویید کد به کدام شماره رفت و دکمهٔ ارسال دوباره با شمارش معکوس بگذارید.\n- ✗ نادرست: فیلد کد تنها، بدون راه برای ارسال دوباره یا عوض کردن شماره.\n- ✓ درست: با `onComplete` کد را خودکار بفرستید؛ دکمهٔ «تأیید» اضافه لازم نیست.\n- ✗ نادرست: پاک کردن کد پس از خطا بدون اینکه بگویید اشتباه بود.\n\n## وب کامپوننت\n\nهمین کامپوننت بدون React، برای HTML، وردپرس، Vue، Svelte و Angular: `<kashi-otp>` در `@kashi-ui/elements`.\n\nنصب بدون ابزار ساخت (CDN):\n\n```html\n<link rel=\"stylesheet\" href=\"https://cdn.jsdelivr.net/npm/@kashi-ui/css/dist/kashi.css\">\n<script type=\"module\" src=\"https://cdn.jsdelivr.net/npm/@kashi-ui/elements/dist/cdn/kashi-elements.js\"></script>\n```\n\nیا با npm، فقط همین بخش:\n\n```bash\nnpm i @kashi-ui/elements @kashi-ui/css\n```\n\n```js\nimport \"@kashi-ui/css\";\nimport \"@kashi-ui/elements/fields.js\";\n```\n\n```html\n<kashi-otp label=\"کد ارسال‌شده\" length=\"6\" group-after=\"3\" name=\"code\"></kashi-otp>\n```\n\nویژگی‌ها:\n\n| نام | مقدار | پیش‌فرض | توضیح |\n|---|---|---|---|\n| length | عدد | 6 | تعداد رقم |\n| group-after | عدد | — | خط فاصله پس از این تعداد رقم |\n| label / name / value / hint / error | متن | — | مثل بقیهٔ فیلدها |\n| size | sm | — | کوچک‌تر |\n| digits | fa \\| latin | fa | خط ارقام |\n\nرویدادها و JavaScript:\n\n| نام | توضیح |\n|---|---|\n| kashi-change | detail: { value } |\n| kashi-complete | وقتی همهٔ رقم‌ها وارد شد؛ detail: { value } |"},{"slug":"persian/province","url":"https://kashi.sepantapouya.com/docs/persian/province/","title":"انتخاب استان","description":"انتخاب یکی از ۳۱ استان با جستجو؛ با نام مرکز استان هم پیدا می‌شود.","group":"فارسی","components":["ProvinceSelect"],"markdown":"```tsx\nimport { ProvinceSelect } from \"@kashi-ui/react\";\n```\n\n## نمونه\n\n```tsx\n<ProvinceSelect value={province} onChange={setProvince} name=\"province\" />\n```\n\nروی `Combobox` ساخته شده، پس همهٔ ویژگی‌های آن را دارد. فهرست استان‌ها با مرکز و پیش‌شمارهٔ تلفن ثابت در `PROVINCES` از `@kashi-ui/persian` هم در دسترس است:\n\n```ts\nimport { PROVINCES } from \"@kashi-ui/persian\";\n// [{ name: \"اصفهان\", capital: \"اصفهان\", areaCode: \"31\" }, …]\n```\n\n| ویژگی | نوع | پیش‌فرض | توضیح |\n|---|---|---|---|\n| value / defaultValue | string \\| null | — | نام استان |\n| onChange | (value: string \\| null) => void | — | — |\n| showCapital | boolean | true | مرکز استان زیر نام |\n| … | ComboboxProps | — | همهٔ ویژگی‌های Combobox |\n\n## دسترس‌پذیری\n\nهمان الگوی Combobox در WAI-ARIA؛ تعداد نتیجه‌ها پس از هر تایپ اعلام می‌شود.\n\n## راهنمای استفاده\n\nکِی استفاده کنیم:\n\n- انتخاب استان در فرم یا فیلتر؛ با نام مرکز استان هم پیدا می‌شود.\n\nکِی استفاده نکنیم:\n\n- نشانی کامل. (فرم نشانی: /docs/persian/address/)\n- انتخاب روی نقشه یا چند استان. (نقشهٔ ایران: /docs/persian/iran-map/)\n\nدرست و نادرست:\n\n- ✓ درست: بگذارید کاربر بنویسد «مشهد» و خراسان رضوی را بگیرد.\n- ✗ نادرست: سلکت بومی با ۳۱ گزینهٔ بی‌جستجو.\n\n## وب کامپوننت\n\nهمین کامپوننت بدون React، برای HTML، وردپرس، Vue، Svelte و Angular: `<kashi-province-select>`، `<kashi-city-select>` در `@kashi-ui/elements`. استان با نام مرکزش هم پیدا می‌شود؛ شهر بدون `province` همهٔ شهرهای ایران را با نام استان نشان می‌دهد.\n\nنصب بدون ابزار ساخت (CDN):\n\n```html\n<link rel=\"stylesheet\" href=\"https://cdn.jsdelivr.net/npm/@kashi-ui/css/dist/kashi.css\">\n<script type=\"module\" src=\"https://cdn.jsdelivr.net/npm/@kashi-ui/elements/dist/cdn/kashi-elements.js\"></script>\n```\n\nیا با npm، فقط همین بخش:\n\n```bash\nnpm i @kashi-ui/elements @kashi-ui/css\n```\n\n```js\nimport \"@kashi-ui/css\";\nimport \"@kashi-ui/elements/combobox.js\";\n```\n\n```html\n<kashi-province-select name=\"province\"></kashi-province-select>\n<kashi-city-select name=\"city\"></kashi-city-select>\n```\n\n| عنصر | کاربرد |\n|---|---|\n| <kashi-province-select> | ۳۱ استان با جستجو |\n| <kashi-city-select> | ۱۲۷۸ شهر؛ با province فقط شهرهای همان استان |\n\nویژگی‌ها:\n\n| نام | مقدار | پیش‌فرض | توضیح |\n|---|---|---|---|\n| label | متن | نام فارسی فیلد | برچسب بالای فیلد؛ بدون آن aria-label بدهید |\n| name | متن | — | نام فیلد در فرم HTML؛ مقدار خام ارسال می‌شود |\n| value | متن | — | مقدار اولیه |\n| hint | متن | — | راهنمای زیر فیلد |\n| error | متن | — | خطای دلخواه، مثلاً از اعتبارسنجی سرور |\n| required / disabled | بولی | — | مثل input معمولی |\n| placeholder | متن | — | متن خالی فیلد |\n| show-capital | false | — | استان: بدون نام مرکز استان زیر گزینه‌ها |\n| province | نام استان | — | شهر: فقط شهرهای این استان |\n| allow-custom | false | — | شهر: نپذیرفتن شهر خارج از فهرست |\n\nرویدادها و JavaScript:\n\n| نام | توضیح |\n|---|---|\n| kashi-change | استان: detail: { value, province }؛ شهر: detail: { value, province } |"},{"slug":"persian/receipt","url":"https://kashi.sepantapouya.com/docs/persian/receipt/","title":"رسید پرداخت","description":"صفحهٔ نتیجهٔ پرداخت با وضعیت، مبلغ به حروف، کد پیگیری قابل کپی و کارت پوشانده.","group":"فارسی","components":["PaymentReceipt"],"markdown":"```tsx\nimport { PaymentReceipt } from \"@kashi-ui/react\";\n```\n\n## نمونه\n\n```tsx\n<PaymentReceipt status=\"success\" amount={1250000} merchant=\"فروشگاه کاشی\"\n trackingCode=\"482913\" referenceId=\"140508270123\"\n cardNumber=\"6104337712345678\" date={paidAt}>\n <Button variant=\"primary\">بازگشت به فروشگاه</Button>\n</PaymentReceipt>\n```\n\nصفحه‌ای که کاربر پس از بازگشت از درگاه بانکی می‌بیند. سه وضعیت دارد: `success`، `pending` (بانک هنوز تأیید نکرده) و `failed`. برای پرداخت ناموفق، در `description` توضیح دهید که مبلغ کسرشده کی برمی‌گردد. کارت خودکار پوشانده می‌شود و تاریخ شمسی است. ردیف‌های دیگر را با `rows` اضافه کنید.\n\n| ویژگی | نوع | پیش‌فرض | توضیح |\n|---|---|---|---|\n| status | \"success\" \\| \"pending\" \\| \"failed\" | — | — |\n| amount | number | — | — |\n| unit | \"toman\" \\| \"rial\" | \"toman\" | — |\n| title / description | ReactNode | — | پیش‌فرض بر اساس وضعیت |\n| merchant | ReactNode | — | پذیرنده |\n| trackingCode | string | — | با دکمهٔ کپی |\n| referenceId | string | — | — |\n| cardNumber | string | — | پوشانده با لوگوی بانک |\n| date | Date | — | — |\n| rows | [ReactNode, ReactNode][] | — | ردیف‌های دیگر |\n| children | ReactNode | — | دکمه‌ها |\n\n## دسترس‌پذیری\n\nرسید یک `section` است که عنوانش نام آن است. وضعیت با آیکون، رنگ و متن عنوان بیان می‌شود، نه فقط با رنگ. دکمهٔ کپی کد پیگیری پس از کپی «کپی شد» را اعلام می‌کند.\n\n## راهنمای استفاده\n\nکِی استفاده کنیم:\n\n- صفحهٔ نتیجهٔ پرداخت: موفق، ناموفق، در انتظار.\n\nکِی استفاده نکنیم:\n\n- تأیید کوتاه داخل صفحه؛ رسید جای خودش را می‌خواهد. (توست: /docs/components/toast/)\n\nدرست و نادرست:\n\n- ✓ درست: در پرداخت ناموفق بگویید پول چه شد: «مبلغ تا ۷۲ ساعت به حساب شما برمی‌گردد».\n- ✗ نادرست: فقط «پرداخت ناموفق» که کاربر را نگران پولش می‌گذارد.\n- ✓ درست: کد پیگیری قابل کپی و درشت؛ همان چیزی است که کاربر به پشتیبانی می‌گوید.\n- ✗ نادرست: کد پیگیری کوچک در پایین صفحه که فقط با اسکرین‌شات حفظ می‌شود."},{"slug":"persian/text-fix","url":"https://kashi.sepantapouya.com/docs/persian/text-fix/","title":"ویرایش نگارشی","description":"ناحیهٔ متن که «ي» و «ك» عربی، نیم‌فاصله، علامت‌های فارسی و ارقام را پیدا و با یک کلیک اصلاح می‌کند.","group":"فارسی","components":["PersianTextarea"],"markdown":"```tsx\nimport { PersianTextarea } from \"@kashi-ui/react\";\n```\n\n## نمونه\n\n```tsx\n<PersianTextarea label=\"توضیحات محصول\" value={text} onValueChange={setText} />\n```\n\nمتنی که از ورد، سایت‌های عربی یا صفحه‌کلیدهای مختلف کپی می‌شود معمولاً «ي» و «ك» عربی دارد که جستجو را خراب می‌کند. این فیلد تعداد مشکل‌ها را زیر متن نشان می‌دهد و با «اصلاح نگارش» همه را درست می‌کند:\n\n| اصلاح | قبل | بعد |\n|---|---|---|\n| حروف عربی | كتاب، ايران | کتاب، ایران |\n| نیم‌فاصله | مي خوانم، كتاب ها، بزرگ تر | می‌خوانم، کتاب‌ها، بزرگ‌تر |\n| علامت‌ها | چرا ? خوب , بد | چرا؟ خوب، بد |\n| ارقام | ۲۰ را 20 نوشتن | ۲۰ |\n| فاصله | فاصلهٔ اضافه و فاصله پیش از نقطه | یک فاصله |\n\nبا `fixOnBlur` اصلاح هنگام خروج از فیلد خودکار انجام می‌شود. همین منطق در `fixPersianText` بدون React در دسترس است و تعداد هر نوع اصلاح را هم برمی‌گرداند.\n\n| ویژگی | نوع | پیش‌فرض | توضیح |\n|---|---|---|---|\n| value / defaultValue | string | — | — |\n| onValueChange | (value: string) => void | — | — |\n| fixDigits | boolean | true | ارقام لاتین کنار متن فارسی |\n| fixOnBlur | boolean | false | اصلاح خودکار هنگام خروج |\n| labels | { fix?, clean? } | — | — |\n\nهمهٔ ویژگی‌های `Textarea` مثل `autoResize` و `showCount` را هم می‌پذیرد.\n\n## راهنمای استفاده\n\nکِی استفاده کنیم:\n\n- متن‌هایی که کاربر از جای دیگر کپی می‌کند: توضیح محصول، آگهی، مقاله.\n- پنل مدیریت محتوا که نگارش یکدست مهم است.\n\nکِی استفاده نکنیم:\n\n- فیلد کوتاه مثل نام؛ `normalizePersian` را بی‌صدا هنگام ذخیره صدا بزنید. (ابزارهای فارسی: /docs/persian/utilities/)\n\nدرست و نادرست:\n\n- ✓ درست: اصلاح با دکمه، تا کاربر ببیند چه عوض می‌شود.\n- ✗ نادرست: تغییر بی‌خبر متن کاربر هنگام تایپ.\n\n## وب کامپوننت\n\nهمین کامپوننت بدون React، برای HTML، وردپرس، Vue، Svelte و Angular: `<kashi-persian-textarea>`، `<kashi-keyboard-fix>` در `@kashi-ui/elements`.\n\nنصب بدون ابزار ساخت (CDN):\n\n```html\n<link rel=\"stylesheet\" href=\"https://cdn.jsdelivr.net/npm/@kashi-ui/css/dist/kashi.css\">\n<script type=\"module\" src=\"https://cdn.jsdelivr.net/npm/@kashi-ui/elements/dist/cdn/kashi-elements.js\"></script>\n```\n\nیا با npm، فقط همین بخش:\n\n```bash\nnpm i @kashi-ui/elements @kashi-ui/css\n```\n\n```js\nimport \"@kashi-ui/css\";\nimport \"@kashi-ui/elements/forms.js\";\n```\n\n```html\n<kashi-persian-textarea label=\"توضیحات\" name=\"body\" value=\"اين متن ، ي و ك عربي دارد\"></kashi-persian-textarea>\n<kashi-keyboard-fix label=\"جستجو\" value=\"sghl\"></kashi-keyboard-fix>\n```\n\n| عنصر | کاربرد |\n|---|---|\n| <kashi-persian-textarea> | متن چندخطی با شمارش و اصلاح ی و ک عربی، نیم‌فاصله، علامت‌ها و ارقام |\n| <kashi-keyboard-fix> | فیلد متنی که متن تایپ‌شده با صفحه‌کلید انگلیسی را تشخیص می‌دهد («sghl» ← «سلام») |\n\nویژگی‌ها:\n\n| نام | مقدار | پیش‌فرض | توضیح |\n|---|---|---|---|\n| label / name / value / placeholder / required | متن | — | مثل بقیهٔ فیلدها |\n| fix-on-blur | بولی | — | متن: اصلاح خودکار هنگام خروج از فیلد |\n| fix-digits | false | — | متن: ارقام انگلیسی اصلاح نشوند |\n| rows | عدد | 4 | متن: تعداد خط |\n| suggest | always | auto | صفحه‌کلید: پیشنهاد برای هر متن لاتین |\n\nرویدادها و JavaScript:\n\n| نام | توضیح |\n|---|---|\n| kashi-change | detail: { value } |"},{"slug":"persian/utilities","url":"https://kashi.sepantapouya.com/docs/persian/utilities/","title":"ابزارهای فارسی","description":"تاریخ شمسی، ارقام فارسی، قالب‌بندی اعداد و اعتبارسنجی؛ بدون وابستگی و برای هر فریم‌ورکی.","group":"فارسی","components":[],"markdown":"پکیج `@kashi-ui/persian` هیچ وابستگی‌ای ندارد و در مرورگر، Node.js و هر فریم‌ورکی (React، Vue، Svelte، Angular) کار می‌کند. کامپوننت‌های فارسی کاشی هم روی همین پکیج ساخته شده‌اند.\n\n```bash\nnpm i @kashi-ui/persian\n```\n\n## تاریخ شمسی\n\n```ts\nimport { toJalali, toGregorian, formatJalali, parseJalali } from \"@kashi-ui/persian\";\n\ntoJalali(new Date(2024, 7, 2)); // { jy: 1403, jm: 5, jd: 12 }\ntoGregorian(1403, 5, 12); // Date: 2024-08-02\n\nformatJalali(date); // \"۱۴۰۳/۰۵/۱۲\"\nformatJalali(date, { style: \"long\" }); // \"۱۲ مرداد ۱۴۰۳\"\nformatJalali(date, { style: \"full\" }); // \"جمعه ۱۲ مرداد ۱۴۰۳\"\n\nparseJalali(\"۱۴۰۳/۵/۱۲\"); // Date\nparseJalali(\"1404/12/30\"); // null (۱۴۰۴ کبیسه نیست)\n```\n\n| تابع | کاربرد |\n|---|---|\n| `toJalali(date)` | میلادی به شمسی |\n| `toGregorian(jy, jm, jd)` | شمسی به `Date` |\n| `formatJalali(date, options)` | نمایش؛ سبک `numeric`، `long` یا `full` |\n| `parseJalali(text)` | خواندن متن کاربر؛ ارقام فارسی و جداکننده‌های `/`، `-` و `.` |\n| `isLeapJalaliYear(jy)` | سال کبیسه |\n| `jalaliMonthLength(jy, jm)` | تعداد روزهای ماه |\n| `isValidJalaliDate(jy, jm, jd)` | وجود تاریخ |\n| `jalaliWeekday(date)` | روز هفته؛ ۰ = شنبه |\n| `toISODate(date)` | `YYYY-MM-DD` بر اساس وقت محلی، برای ارسال به سرور |\n| `fromISODate(text)` | `YYYY-MM-DD` به `Date` محلی |\n| `JALALI_MONTHS`، `WEEKDAYS` | نام ماه‌ها و روزها |\n\n> **هشدار منطقهٔ زمانی:** برای ارسال تاریخ به سرور از `date.toISOString()` استفاده نکنید. این تابع زمان را به UTC تبدیل می‌کند و چون ایران ۳٫۵ ساعت جلوتر است، نیمه‌شب را روز قبل می‌کند. به‌جای آن از `toISODate(date)` استفاده کنید.\n\nدقت: تبدیل برای همهٔ ۷۲٬۹۷۰ روز از ۱۳۰۰ تا ۱۵۰۰ با تقویم persian کتابخانهٔ ICU (همان تقویم مرورگرها) مقایسه و تست شده است.\n\n## زمان نسبی\n\n```ts\nimport { formatRelative } from \"@kashi-ui/persian\";\n\nformatRelative(date); // «همین الان»، «۵ دقیقه پیش»، «دیروز»، «۳ روز دیگر»، «۲ سال پیش»\n```\n\n«دیروز» و «فردا» بر اساس روز تقویمی محاسبه می‌شوند، نه ۲۴ ساعت. در React از `<RelativeTime date={date} />` استفاده کنید که هر دقیقه به‌روز می‌شود و تاریخ کامل شمسی را در `title` دارد.\n\n## ارقام و اعداد\n\n```ts\nimport { toFaDigits, toEnDigits, onlyDigits, formatNumber } from \"@kashi-ui/persian\";\n\ntoFaDigits(1403); // \"۱۴۰۳\"\ntoEnDigits(\"۰۹۱۲ ٣٤٥\"); // \"0912 345\" (ارقام عربی هم)\nonlyDigits(\"۰۹۱۲-۳۴۵ 67\"); // \"091234567\"\nformatNumber(1250000); // \"۱٬۲۵۰٬۰۰۰\"\n```\n\n`toEnDigits` را همیشه پیش از ذخیرهٔ ورودی کاربر استفاده کنید؛ کاربر ایرانی ممکن است با هر دو صفحه‌کلید تایپ کند.\n\n## عدد به حروف\n\n```ts\nimport { numberToWords } from \"@kashi-ui/persian\";\n\nnumberToWords(1250000); // \"یک میلیون و دویست و پنجاه هزار\"\nnumberToWords(1001); // \"هزار و یک\"\nnumberToWords(-45); // \"منفی چهل و پنج\"\n```\n\n## یکدست‌سازی متن\n\nکاربران با صفحه‌کلید عربی «ي» و «ك» تایپ می‌کنند و جستجوی «علی» دیگر «علي» را پیدا نمی‌کند. پیش از ذخیره و جستجو، متن را یکدست کنید:\n\n```ts\nimport { normalizePersian } from \"@kashi-ui/persian\";\n\nnormalizePersian(\"علي كريمي\"); // \"علی کریمی\"\nnormalizePersian(\"مـــدرسه\"); // \"مدرسه\" (حذف کشیده)\nnormalizePersian(\"كتاب ١٢\", { digits: \"fa\" }); // \"کتاب ۱۲\"\n```\n\nبرای متنی که کاربر می‌نویسد (توضیحات، نظرها، مقاله‌ها)، `fixPersianText` ویرایش نگارشی هم انجام می‌دهد: نیم‌فاصله، ویرگول و علامت سؤال فارسی و ارقام. تعداد هر نوع اصلاح را هم برمی‌گرداند. کامپوننت آن [ویرایش نگارشی](/docs/persian/text-fix/) است.\n\n```ts\nimport { fixPersianText, fixKeyboardLayout } from \"@kashi-ui/persian\";\n\nfixPersianText(\"كتاب ها را مي خوانم ?\").text; // \"کتاب‌ها را می‌خوانم؟\"\nfixKeyboardLayout(\"sghl\"); // \"سلام\" (صفحه‌کلید انگلیسی به فارسی)\n```\n\n## مبلغ\n\n```ts\nimport { formatMoney, rialToToman } from \"@kashi-ui/persian\";\n\nformatMoney(1250000); // \"۱٬۲۵۰٬۰۰۰ تومان\"\nformatMoney(1250000, { compact: true }); // \"۱٫۲۵ میلیون تومان\"\nformatMoney(125000000, { from: \"rial\", compact: true }); // \"۱۲٫۵ میلیون تومان\"\nrialToToman(50000); // 5000\n```\n\n## اعتبارسنجی\n\n```ts\nimport { isValidNationalId, isValidCardNumber, getBankByCard, formatCardNumber } from \"@kashi-ui/persian\";\n\nisValidNationalId(\"0013542419\"); // true\nisValidLegalId(\"10380284790\"); // true (شناسهٔ ملی شرکت)\nisValidIban(\"IR062960000000100324200001\");\nisValidMobile(\"+98 912 345 6789\"); // true\ngetMobileOperator(\"0935\")?.name; // \"ایرانسل\"\nisValidCardNumber(\"4111111111111111\"); // true\ngetBankByCard(\"610433\"); // { name: \"بانک ملت\", slug: \"mellat\" }\nformatCardNumber(\"6104331234\"); // \"6104 3312 34\"\nisValidPostalCode(\"1193653471\"); // true\nformatPostalCode(\"1193653471\"); // \"11936-53471\"\nisValidPlate({ first: \"12\", letter: \"ب\", middle: \"345\", region: \"67\" }); // true\nformatPlate({ first: \"12\", letter: \"ب\", middle: \"345\", region: \"67\" }); // \"۱۲ ب ۳۴۵ ایران ۶۷\"\nplateType(\"ت\"); // \"taxi\"\nisValidLandline(\"+98 21 8877 6655\"); // true\ngetLandlineProvince(\"031\"); // \"اصفهان\"\nformatLandline(\"02188776655\"); // \"021 8877 6655\"\n```\n\n## ساعت\n\n```ts\nimport { parseTime, formatTime } from \"@kashi-ui/persian\";\n\nparseTime(\"۹:۳۰\"); // { hours: 9, minutes: 30 }\nparseTime(\"2145\"); // { hours: 21, minutes: 45 }\nparseTime(\"25:00\"); // null\nformatTime({ hours: 9, minutes: 5 }); // \"09:05\"\n```\n\n## راهنمای استفاده\n\nکِی استفاده کنیم:\n\n- قالب‌بندی تاریخ، عدد و مبلغ و اعتبارسنجی کد ملی، کارت و شبا در هر جای برنامه، حتی سمت سرور.\n- اعتبارسنجی دوباره در سرور با همان قاعده‌های فرم.\n\nکِی استفاده نکنیم:\n\n- ساختن فیلد ورودی با این تابع‌ها از صفر؛ فیلدهای آماده رفتار کیبورد و صفحه‌خوان را هم دارند. (فیلد فارسی: /docs/persian/input/)\n\nدرست و نادرست:\n\n- ✓ درست: در سرور هم `isValidNationalId` و `isValidIban` را صدا بزنید؛ اعتبارسنجی مرورگر دورزدنی است.\n- ✗ نادرست: فقط به بررسی سمت کاربر اعتماد کنید.\n\n## وب کامپوننت\n\nهمین کامپوننت بدون React، برای HTML، وردپرس، Vue، Svelte و Angular: `<kashi-date>`، `<kashi-relative-time>` در `@kashi-ui/elements`.\n\nنصب بدون ابزار ساخت (CDN):\n\n```html\n<link rel=\"stylesheet\" href=\"https://cdn.jsdelivr.net/npm/@kashi-ui/css/dist/kashi.css\">\n<script type=\"module\" src=\"https://cdn.jsdelivr.net/npm/@kashi-ui/elements/dist/cdn/kashi-elements.js\"></script>\n```\n\nیا با npm، فقط همین بخش:\n\n```bash\nnpm i @kashi-ui/elements @kashi-ui/css\n```\n\n```js\nimport \"@kashi-ui/css\";\nimport \"@kashi-ui/elements/display.js\";\n```\n\n```html\n<kashi-date datetime=\"2026-03-21\" format=\"full\"></kashi-date>\n<kashi-relative-time datetime=\"2026-01-01T09:30:00Z\"></kashi-relative-time>\n```\n\n| عنصر | کاربرد |\n|---|---|\n| <kashi-date> | تاریخ شمسی در یک <time> |\n| <kashi-relative-time> | «۵ دقیقه پیش» که خودش به‌روز می‌شود |\n\nویژگی‌ها:\n\n| نام | مقدار | پیش‌فرض | توضیح |\n|---|---|---|---|\n| datetime | تاریخ ISO | — | تاریخ یا زمان |\n| format | numeric \\| long \\| full | long | kashi-date: قالب تاریخ |\n| refresh | میلی‌ثانیه | 60000 | kashi-relative-time: فاصلهٔ به‌روزرسانی |\n| digits | fa \\| latin | fa | خط ارقام |"},{"slug":"security","url":"https://kashi.sepantapouya.com/docs/security/","title":"امنیت","description":"چطور آسیب‌پذیری را گزارش کنید، در چه مدت جواب می‌گیرید و کاشی برای امنیت زنجیرهٔ تأمین چه می‌کند.","group":"پایداری و امنیت","components":[],"markdown":"## گزارش آسیب‌پذیری\n\nاگر در کاشی مشکل امنیتی پیدا کرده‌اید، **آن را در Issue عمومی ننویسید.** از گزارش خصوصی GitHub استفاده کنید تا تا زمان رفع، فقط نگهدارندهٔ کاشی آن را ببیند:\n\n**[گزارش خصوصی آسیب‌پذیری](https://github.com/Sepantapouya/Kashi/security/advisories/new)**\n\nلطفاً بنویسید کدام پکیج و نسخه، مشکل چیست، چطور می‌شود آن را تکرار کرد و چه اثری دارد. اگر راه رفعی به ذهنتان رسیده، آن را هم بنویسید.\n\n## زمان پاسخ\n\n| مرحله | زمان |\n|---|---|\n| تأیید دریافت گزارش | تا ۳ روز کاری |\n| بررسی و تعیین شدت | تا ۷ روز کاری |\n| رفع آسیب‌پذیری بحرانی و شدید | تا ۱۴ روز |\n| رفع آسیب‌پذیری متوسط و کم | در نسخهٔ بعدی |\n\nپس از انتشار نسخهٔ رفع‌شده، یک GitHub Security Advisory با شمارهٔ CVE (در صورت نیاز) منتشر و از گزارش‌دهنده، اگر بخواهد، نام برده می‌شود. لطفاً پیش از انتشار رفع، جزئیات را جایی منتشر نکنید.\n\n## نسخه‌های پشتیبانی‌شده\n\n| نسخه | رفع امنیتی |\n|---|---|\n| ۱.x | ✓ |\n| ۰.x | ✗ (به ۱.۰ مهاجرت کنید؛ تغییر کدی لازم ندارد) |\n\nپس از انتشار نسخهٔ ۲.۰، رفع‌های امنیتی ۱.x تا ۱۲ ماه ادامه دارد. [سیاست تغییرات](/docs/versioning/)\n\n## کاشی برای امنیت چه می‌کند\n\n- **وابستگی کم:** `@kashi-ui/persian`، `@kashi-ui/tokens`، `@kashi-ui/icons` و `@kashi-ui/tailwind` هیچ وابستگی‌ای ندارند. `@kashi-ui/react` و `@kashi-ui/elements` فقط به پکیج‌های خود کاشی وابسته‌اند (و React). تنها استثنا سرور MCP است که از SDK رسمی MCP استفاده می‌کند.\n- **بدون اجرای کد هنگام نصب:** هیچ پکیج کاشی اسکریپت `install` یا `postinstall` ندارد.\n- **بدون ارسال داده:** کامپوننت‌ها هیچ درخواستی به اینترنت نمی‌فرستند. لوگوی بانک‌ها و فونت داخل پکیج‌اند.\n- **متن کاربر به‌شکل متن:** کامپوننت‌های React متن را هرگز به‌شکل HTML خام نمی‌نویسند و وب کامپوننت‌ها مقدارها و ویژگی‌ها را پیش از ساختن HTML امن (escape) می‌کنند.\n- **بررسی خودکار وابستگی‌ها:** در هر تغییر، `npm audit` اجرا می‌شود و آسیب‌پذیری شدید جلوی ادغام را می‌گیرد.\n- **حساب انتشار** با ورود دومرحله‌ای محافظت می‌شود.\n\n## اگر از کاشی در محصولتان استفاده می‌کنید\n\n- پکیج‌ها را با `^1` نصب کنید تا رفع‌های امنیتی با `npm update` برسد.\n- برای خبر شدن از نسخه‌های امنیتی، در GitHub روی **Watch ← Custom ← Security alerts** بزنید."},{"slug":"tailwind","url":"https://kashi.sepantapouya.com/docs/tailwind/","title":"Tailwind","description":"رنگ‌ها، فونت، گردی گوشه و سایه‌های کاشی به‌شکل کلاس‌های Tailwind؛ همگام با تم تیره و روشن و رنگ برند شما.","group":"شروع","components":[],"markdown":"با پریست `@kashi-ui/tailwind`، توکن‌های کاشی در پروژهٔ Tailwind شما به‌شکل کلاس‌های آشنا در دسترس‌اند. کامپوننت‌های کاشی همان‌طور که هستند کار می‌کنند و برای بخش‌های دلخواه صفحه، به‌جای `bg-[var(--k-bg-secondary)]` می‌نویسید `bg-secondary`:\n\n```tsx\n<div className=\"bg-secondary border border-brand rounded-lg shadow-md p-4\">\n <h3 className=\"text-primary text-lg font-semibold\">سفارش‌های امروز</h3>\n <p className=\"text-tertiary text-sm\">۱۲ سفارش تازه</p>\n <Button variant=\"primary\">مشاهده</Button>\n</div>\n```\n\nکلاس‌ها کپی رنگ‌ها نیستند؛ همه به همان متغیرهای کاشی (`var(--k-…)`) اشاره می‌کنند. پس تم تیره و روشن، رنگ برندی که با `createTheme` ساخته‌اید و هر نسخهٔ جدید کاشی، بدون هیچ کاری روی کلاس‌های Tailwind هم اعمال می‌شود. ساختار کاشی هم تغییری نمی‌کند؛ این پکیج فقط یک پل است.\n\n## نصب\n\n```bash\nnpm i @kashi-ui/react @kashi-ui/css @kashi-ui/tailwind\n```\n\n### Tailwind 4\n\nدر فایل CSS اصلی پروژه (مثلاً `app/globals.css`) این سه خط را **به همین ترتیب** بگذارید:\n\n```css\n@import \"@kashi-ui/tailwind\";\n@import \"tailwindcss\";\n@import \"@kashi-ui/css\";\n```\n\n> **ترتیب مهم است:** `@kashi-ui/tailwind` باید اول بیاید تا ترتیب لایه‌های CSS را تعیین کند و کلاس‌های Tailwind همیشه بر استایل کامپوننت‌های کاشی برنده باشند. با ترتیب دیگر، استایل کاشی برنده می‌شود و مثلاً `rounded-none` روی دکمه اثر نمی‌کند.\n\n### Tailwind 3\n\nدر `tailwind.config.js` پریست را اضافه کنید و CSS کاشی را مثل همیشه وارد کنید. در Tailwind 3 کلاس‌ها بیرون از لایه‌ها ساخته می‌شوند و خودبه‌خود بر کاشی برنده‌اند:\n\n```js\nmodule.exports = {\n presets: [require(\"@kashi-ui/tailwind/preset\")],\n content: [\"./src/**/*.{js,ts,jsx,tsx}\"],\n};\n```\n\n```tsx\nimport \"@kashi-ui/css\";\n```\n\n## کلاس‌ها\n\n### رنگ‌های معنایی\n\nهمان نام‌های توکن‌های کاشی، بدون پیشوند. این‌ها با تم تیره و روشن عوض می‌شوند و برای بیشتر رابط کاربری کافی‌اند.\n\n<details class=\"tw-more\">\n<summary>فهرست متنی همهٔ رنگ‌های معنایی</summary>\n\nپس‌زمینه `bg-primary`، `bg-secondary`، `bg-tertiary`، `bg-elevated`، `bg-hover`، `bg-active`، `bg-brand`، `bg-brand-hover`، `bg-brand-subtle`، `bg-error`، `bg-error-hover`، `bg-error-subtle`، `bg-warning-subtle`، `bg-success-subtle` و `bg-info-subtle`؛ متن `text-primary`، `text-secondary`، `text-tertiary`، `text-quaternary`، `text-on-brand`، `text-on-error`، `text-brand`، `text-error`، `text-warning`، `text-success` و `text-info`؛ حاشیه `border-primary`، `border-secondary`، `border-brand`، `border-input` و `border-error`. همهٔ حالت‌های Tailwind هم کار می‌کنند: `hover:bg-hover`، `focus:border-brand`، `md:bg-secondary`.\n\n</details>\n\n### طیف رنگ‌ها\n\nطیف برند و رنگ‌های ایرانی کاشی، از ۵۰ تا ۹۵۰، با پشتیبانی از شفافیت مثل `bg-brand-500/50`. روی هر خانه بزنید تا کلاسش کپی شود:\n\nنام طیف‌ها: `brand` (همان رنگی که `createTheme` می‌سازد)، `firoozeh` (فیروزه)، `lajvard` (لاجورد)، `zafaran` (زعفران)، `anar` (انار)، `pesteh` (پسته) و `shab` (شب)؛ مثل `bg-firoozeh-100`، `text-anar-600` یا `border-lajvard-300`.\n\n### گردی و سایه\n\nضریب گردی کاشی (`--k-radius-factor`) روی همهٔ `rounded-*` ها اعمال می‌شود.\n\n### متن، لایه و حرکت\n\n| گروه | کلاس‌ها |\n|---|---|\n| فونت | `font-sans` (وزیرمتن یا فونت شما) · `font-display` · `font-mono` |\n| اندازهٔ متن | `text-xs` تا `text-6xl`، با ضریب `--k-text-scale` |\n| وزن | `font-light` · `font-regular` · `font-medium` · `font-semibold` · `font-bold` · `font-black` |\n| ارتفاع خط | `leading-tight` · `leading-normal` · `leading-relaxed` |\n| لایه | `z-dropdown` · `z-sticky` · `z-overlay` · `z-modal` · `z-toast` |\n| حرکت | `ease-standard` · `ease-emphasized` |\n\nفاصله‌ها (`p-4`، `gap-6`، `mt-8`) همان مقیاس پیش‌فرض Tailwind‌اند که با مقیاس کاشی (پله‌های ۴ پیکسلی) یکی است؛ `p-4` در هر دو ۱۶ پیکسل است.\n\n## تم تیره و روشن\n\nکاشی به‌طور پیش‌فرض تیره است و با `data-theme=\"light\"` روی `<html>` روشن می‌شود. حالت‌های `dark:` و `light:` هم از همین پیروی می‌کنند:\n\n```tsx\n<div className=\"shadow-none light:shadow-sm\">…</div>\n```\n\nبرای بیشتر کارها به این حالت‌ها نیازی ندارید؛ رنگ‌های معنایی مثل `bg-secondary` خودشان با تم عوض می‌شوند.\n\n## رنگ برند\n\nتمی که با `createTheme` یا [تم‌ساز](/docs/theming/) می‌سازید، روی کلاس‌های Tailwind هم اعمال می‌شود:\n\n```ts\nimport { createTheme } from \"@kashi-ui/tokens\";\n\nconst css = createTheme({ brand: \"#3d5ef8\", radius: 1.5 });\n// حالا bg-brand، text-brand و rounded-lg هم لاجوردی و گردتر شده‌اند\n```\n\n## راست‌به‌چپ\n\nTailwind کلاس‌های منطقی دارد که در RTL و LTR درست کار می‌کنند؛ مثل کامپوننت‌های کاشی، به‌جای `ml-*` و `pr-*` از این‌ها استفاده کنید:\n\n| به‌جای | بنویسید |\n|---|---|\n| `ml-4` / `mr-4` | `ms-4` / `me-4` |\n| `pl-4` / `pr-4` | `ps-4` / `pe-4` |\n| `left-0` / `right-0` | `start-0` / `end-0` |\n| `text-left` | `text-start` |\n\nبرای جاهایی که واقعاً به جهت وابسته‌اند، حالت‌های `rtl:` و `ltr:` هم هست، مثل `rtl:rotate-180` برای فلش."},{"slug":"theming","url":"https://kashi.sepantapouya.com/docs/theming/","title":"سفارشی‌سازی و تم","description":"کاشی را با برند خود هماهنگ کنید؛ از یک رنگ تا جزئی‌ترین متغیر هر کامپوننت.","group":"شروع","components":[],"markdown":"## تم‌ساز\n\nتغییرات روی کل همین صفحه اعمال می‌شوند. کد تولیدشده را کپی کنید و در CSS پروژه بگذارید.\n\n## چهار لایهٔ سفارشی‌سازی\n\n### ۱. رنگ برند\n\nهمهٔ کامپوننت‌ها رنگ برند را از طیف `--k-brand-50` تا `--k-brand-950` می‌خوانند. تابع `createTheme()` این طیف را از یک رنگ می‌سازد:\n\n```ts\nimport { createTheme } from \"@kashi-ui/tokens\";\n\nconst css = createTheme({ brand: \"#7c3aed\" });\n// css را در یک تگ <style> یا فایل CSS قرار دهید\n```\n\nاگر طیف دقیق برندتان را دارید، آن را مستقیم بدهید:\n\n```ts\ncreateTheme({ brand: { 50: \"#f5f3ff\", 100: \"#ede9fe\", /* … */ 950: \"#2e1065\" } });\n```\n\n### ۲. پیچ‌های سراسری\n\n| متغیر | پیش‌فرض | اثر |\n|---|---|---|\n| `--k-radius-factor` | `1` | ضریب همهٔ گوشه‌ها؛ `0` تیز، `2` بسیار گرد |\n| `--k-density` | `1` | ضریب ارتفاع و فاصلهٔ داخلی کنترل‌ها؛ `0.875` فشرده |\n| `--k-font-sans` | وزیرمتن | فونت اصلی؛ راهنمای کامل در [فونت و تایپوگرافی](/docs/typography/) |\n| `--k-text-scale` | `1` | ضریب اندازهٔ همهٔ متن‌ها |\n\n### ۳. متغیرهای هر کامپوننت\n\nهر کامپوننت متغیرهای عمومی با الگوی `--k-{کامپوننت}-{ویژگی}` دارد. فهرست کامل در صفحهٔ هر کامپوننت آمده است.\n\n```css\n:root {\n --k-btn-radius: 999px; /* دکمه‌های کاملاً گرد */\n --k-input-height-md: 3rem; /* فیلدهای بلندتر */\n}\n\n/* فقط در یک بخش خاص */\n.checkout {\n --k-card-padding: 2rem;\n}\n```\n\n### ۴. کلاس برای هر بخش داخلی\n\nکامپوننت‌های چندبخشی `classNames` می‌پذیرند تا بدون دستکاری CSS کاشی، هر بخش را جدا استایل دهید (مثلاً با Tailwind):\n\n```tsx\n<Input\n label=\"ایمیل\"\n classNames={{ label: \"text-lg\", box: \"rounded-full\", control: \"font-mono\" }}\n/>\n<DataTable classNames={{ row: \"h-14\", header: \"uppercase\" }} … />\n```\n\nهمچنین ریشهٔ کامپوننت‌ها ویژگی‌های `data-variant`، `data-size`، `data-tone`، `data-state` و `data-placement` دارند:\n\n```css\n.k-btn[data-variant=\"primary\"][data-size=\"lg\"] { letter-spacing: 0; }\n.k-input[data-state=\"invalid\"] { background: var(--k-bg-error-subtle); }\n```\n\n### ۵. بازنویسی مستقیم\n\nهمهٔ استایل‌های کاشی در Cascade Layer با نام `kashi` قرار دارند. هر CSS معمولی شما، با هر میزان اختصاصی بودن، روی آن‌ها اولویت دارد:\n\n```css\n/* بدون !important کار می‌کند */\n.k-btn { letter-spacing: 0; }\n```\n\n> **دام رایج: استایل‌های سراسری عناصر.** چون CSS شما همیشه بر کاشی اولویت دارد، قاعده‌ای مثل `a { color: blue }` رنگ دکمه‌ای را هم که با `<a class=\"k-btn\">` ساخته شده عوض می‌کند. دو راه حل:\n>\n> ```css\n> /* ۱. دکمه‌ها را مستثنا کنید و اختصاصی بودن را صفر نگه دارید */\n> :where(a:not(.k-btn)) { color: blue; }\n>\n> /* ۲. یا ریست‌ها و استایل‌های پایهٔ خودتان را در لایه‌ای پیش از کاشی بگذارید */\n> @layer my-reset, kashi;\n> @layer my-reset { a { color: blue; } }\n> ```\n\n## تم روشن و تیره\n\nتم پیش‌فرض تیره است. ویژگی `data-theme` را روی `<html>` یا هر عنصری بگذارید:\n\n```html\n<html data-theme=\"light\">\n<section data-theme=\"dark\">…</section>\n```\n\nبرای پیروی از تنظیم سیستم کاربر:\n\n```js\nconst dark = matchMedia(\"(prefers-color-scheme: dark)\").matches;\ndocument.documentElement.dataset.theme = dark ? \"dark\" : \"light\";\n```\n\n> طیفی که `createTheme()` می‌سازد خودکار از نظر کنتراست بررسی نمی‌شود. پس از انتخاب رنگ برند، کنتراست دکمهٔ اصلی را بررسی کنید."},{"slug":"typography","url":"https://kashi.sepantapouya.com/docs/typography/","title":"فونت و تایپوگرافی","description":"وزیرمتن پیش‌فرض است، ولی هر فونتی، رایگان یا خریداری‌شده، با یک خط قابل‌جایگزینی است.","group":"شروع","components":[],"markdown":"## پیش‌نمایش زنده\n\nفونت‌ها فقط هنگام انتخاب بارگذاری می‌شوند. کد تولیدشده را کپی کنید.\n\n## سه متغیر برای فونت\n\n| متغیر | پیش‌فرض | کاربرد |\n|---|---|---|\n| `--k-font-sans` | وزیرمتن | متن و همهٔ کامپوننت‌ها |\n| `--k-font-display` | همان `--k-font-sans` | عنوان‌ها (h1 تا h6) |\n| `--k-font-mono` | فونت سیستم + وزیرمتن | کد |\n| `--k-text-scale` | `1` | ضریب اندازهٔ همهٔ متن‌ها |\n\n## فونت رایگان\n\nفونت را با npm نصب و CSS آن را وارد کنید، سپس نامش را به کاشی بدهید:\n\n```ts\nimport \"@fontsource-variable/estedad\"; // npm i @fontsource-variable/estedad\nimport { createTheme } from \"@kashi-ui/tokens\";\n\nconst css = createTheme({ fontFamily: \"Estedad Variable\" });\n```\n\nیا فقط با CSS:\n\n```css\n@import \"@fontsource-variable/estedad\";\n:root { --k-font-sans: \"Estedad Variable\", \"Vazirmatn\", Tahoma, sans-serif; }\n```\n\n<div class=\"table-wrap\" role=\"region\" aria-label=\"فونت‌های رایگان پیشنهادی\" tabindex=\"0\">\n <table class=\"ref-table\">\n <thead><tr><th scope=\"col\">فونت</th><th scope=\"col\">پکیج npm</th><th scope=\"col\">کاربرد</th><th scope=\"col\">مجوز</th></tr></thead>\n <tbody>\n {fonts.map((f) => (\n <tr>\n <th scope=\"row\">{f.name}</th>\n <td><code class=\"type\">{f.pkg}</code></td>\n <td>{f.kind}؛ {f.note}</td>\n <td>{f.license}</td>\n </tr>\n ))}\n </tbody>\n </table>\n</div>\n\n## فونت خریداری‌شده\n\nفونت‌های تجاری مثل ایران‌سنس، یکان بخ، دانا یا پیدا مجوز جداگانه برای هر وب‌سایت دارند و نمی‌توان آن‌ها را در پکیج‌های عمومی منتشر کرد؛ فایل‌هایی را که خریده‌اید روی سرور خودتان بگذارید و به کاشی معرفی کنید:\n\n```ts\nimport { createTheme } from \"@kashi-ui/tokens\";\n\nconst css = createTheme({\n fonts: [\n // فونت متغیر: یک فایل، همهٔ وزن‌ها\n { family: \"IRANSansX\", src: \"/fonts/IRANSansXV.woff2\", weight: \"100 900\" },\n // یا فونت ثابت: هر وزن یک فایل\n { family: \"Yekan Bakh\", src: \"/fonts/YekanBakh-Regular.woff2\", weight: 400 },\n { family: \"Yekan Bakh\", src: \"/fonts/YekanBakh-Bold.woff2\", weight: 700 },\n ],\n fontFamily: \"IRANSansX\",\n displayFontFamily: \"Yekan Bakh\",\n});\n// css شامل قواعد @font-face و متغیرهاست؛ آن را در یک <style> یا فایل CSS بگذارید\n```\n\nنام تنها (مثل `\"IRANSansX\"`) خودکار با وزیرمتن و فونت‌های سیستم کامل می‌شود تا تا زمان بارگذاری فونت اصلی، متن نامرئی نباشد.\n\n> **حجم:** اگر از وزیرمتن استفاده نمی‌کنید، به‌جای `@kashi-ui/css` فایل `@kashi-ui/css/kashi.nofont.css` را وارد کنید تا فایل فونت (حدود ۱۱۰ کیلوبایت) در خروجی شما نیاید. مرورگر به‌هرحال فقط فونتی را دانلود می‌کند که واقعاً استفاده شود.\n\n## حروف لاتین با فونت جدا\n\nبیشتر فونت‌های فارسی حروف لاتین هم دارند. اگر برای متن انگلیسی فونت دیگری می‌خواهید، آن را **اول** بیاورید؛ مرورگر هر حرف را از اولین فونتی که آن را دارد می‌گیرد و چون Inter حروف فارسی ندارد، فارسی از فونت دوم خوانده می‌شود:\n\n```css\n:root { --k-font-sans: \"Inter\", \"Vazirmatn\", Tahoma, sans-serif; }\n```\n\n## اندازه و ارتفاع خط\n\nفونت‌ها در یک اندازهٔ یکسان، درشتی متفاوتی دارند. با `textScale` (یا `--k-text-scale`) همهٔ اندازه‌ها را با هم کوچک یا بزرگ کنید، مثلاً `0.95` برای فونتی که درشت‌تر دیده می‌شود. ارتفاع خط با `--k-leading-normal` (پیش‌فرض ۱٫۷۵) قابل‌تغییر است؛ حروف فارسی به‌خاطر نقطه‌ها و دندانه‌ها فضای عمودی بیشتری از لاتین لازم دارند.\n\n## ویژگی‌های فونت در createTheme\n\n| ویژگی | نوع | پیش‌فرض | توضیح |\n|---|---|---|---|\n| fontFamily | string | — | فونت متن؛ نام تنها یا فهرست کامل |\n| displayFontFamily | string | — | فونت عنوان‌ها |\n| monoFontFamily | string | — | فونت کد |\n| textScale | number | 1 | ضریب اندازهٔ متن‌ها |\n| fonts | { family, src, weight?, style?, display?, unicodeRange? }[] | — | فایل‌های فونت برای ساخت @font-face |\n\n`fontFace()` و `fontStack()` هم جداگانه از `@kashi-ui/tokens` صادر می‌شوند."},{"slug":"versioning","url":"https://kashi.sepantapouya.com/docs/versioning/","title":"نسخه‌ها و سیاست تغییرات","description":"کاشی از نسخهٔ ۱.۰ قول می‌دهد که API کامپوننت‌ها بی‌خبر نشکند؛ هر حذفی دست‌کم یک نسخه پیش‌تر «منسوخ» اعلام می‌شود و هر نسخهٔ اصلی راهنمای مهاجرت دارد.","group":"پایداری و امنیت","components":[],"markdown":"از نسخهٔ ۱.۰، کاشی برای استفاده در محصولات سازمانی پایدار است. این صفحه می‌گوید چه چیزی ثابت می‌ماند، چه چیزی چطور عوض می‌شود و هر نسخه تا کی پشتیبانی می‌شود.\n\n## شمارهٔ نسخه\n\nکاشی از [Semantic Versioning](https://semver.org/lang/fa/) پیروی می‌کند. همهٔ پکیج‌ها (`@kashi-ui/react`، `css`، `elements`، `persian`، `tokens`، `icons`، `bank-logos`، `tailwind` و `mcp`) با **یک شمارهٔ نسخه** منتشر می‌شوند تا هیچ‌وقت لازم نباشد بپرسید کدام با کدام سازگار است.\n\n| تغییر | مثال | نسخه |\n|---|---|---|\n| رفع باگ، بهتر شدن ظاهر بدون تغییر API | فاصلهٔ درست بین برچسب و فیلد | `1.0.0` ← `1.0.1` |\n| قابلیت یا کامپوننت تازه، prop اختیاری تازه | کامپوننت انتخاب نوبت | `1.0.1` ← `1.1.0` |\n| تغییر ناسازگار | حذف یا تغییر نام prop، تغییر مقدار پیش‌فرض، حذف کلاس CSS | `1.1.0` ← `2.0.0` |\n\n### چه چیزهایی API عمومی است\n\n- همهٔ کامپوننت‌ها، propها و نوع‌های TypeScript که از `@kashi-ui/react` و `@kashi-ui/elements` export شده‌اند.\n- نام تگ‌های وب کامپوننت (`kashi-*`)، ویژگی‌ها (attributes) و رویدادهای `kashi-change`.\n- کلاس‌های CSS با پیشوند `k-` و متغیرهای CSS با پیشوند `--k-`.\n- تابع‌های `@kashi-ui/persian` و توکن‌های `@kashi-ui/tokens`.\n- کلیدهای فرهنگ متن‌ها (`faMessages`، `enMessages`).\n\nساختار داخلی HTML، کلاس‌ها و متغیرهایی که با `_` شروع می‌شوند (مثل `--_p`) و متن پیش‌فرض برچسب‌ها جزو API نیستند. متن‌ها ممکن است در نسخهٔ کوچک بهتر شوند؛ اگر به متن دقیقی نیاز دارید، آن را با prop یا `messages` خودتان بدهید.\n\n## منسوخ کردن، پیش از حذف\n\nهیچ قابلیتی بی‌خبر حذف نمی‌شود:\n\n۱. **اعلام منسوخ شدن** در یک نسخهٔ کوچک (مثلاً `1.4.0`): قابلیت هنوز کار می‌کند، در حالت توسعه یک هشدار `[kashi] منسوخ: …` در کنسول می‌آید که جایگزین را می‌گوید، در صفحهٔ همان کامپوننت برچسب «منسوخ» می‌خورد و در [یادداشت نسخه‌ها](/docs/changelog/) نوشته می‌شود.\n۲. **دست‌کم یک نسخهٔ کوچک** با هر دو راه، قدیمی و جدید، منتشر می‌شود تا فرصت مهاجرت داشته باشید.\n۳. **حذف** فقط در نسخهٔ اصلی بعدی (مثلاً `2.0.0`)، همراه با راهنمای مهاجرت.\n\nهشدارها فقط در حالت توسعه نشان داده می‌شوند و در بیلد production هیچ هزینه‌ای ندارند.\n\n## راهنمای مهاجرت\n\nهر نسخهٔ اصلی صفحهٔ مهاجرت خودش را دارد: چه چیزی عوض شده، چرا، و کد قبل و بعد برای هر مورد. [مهاجرت به ۱.۰](/docs/migration/)\n\n## مدت پشتیبانی\n\n| نسخه | وضعیت | پشتیبانی |\n|---|---|---|\n| ۱.x | فعلی | قابلیت تازه، رفع باگ و رفع امنیتی |\n| ۰.x | قدیمی | به ۱.۰ مهاجرت کنید؛ مهاجرت تغییر کدی لازم ندارد |\n\nوقتی نسخهٔ ۲.۰ منتشر شود، ۱.x تا **۱۲ ماه** رفع‌های امنیتی و باگ‌های جدی را می‌گیرد. جزئیات گزارش و رفع آسیب‌پذیری‌ها در [امنیت](/docs/security/) آمده است.\n\n## سازگاری\n\n| | پشتیبانی‌شده |\n|---|---|\n| React | ۱۸ و ۱۹؛ در App Router نکست کامپوننت‌ها در صفحه‌های سرور هم کار می‌کنند |\n| فریم‌ورک‌ها | Next.js (در هر تغییر خودکار نصب و بیلد می‌شود)، Vite، Remix؛ وب کامپوننت‌ها برای Vue، Svelte، Angular و HTML |\n| مرورگرها | Chrome و Edge ۱۲۰+، Firefox ۱۲۱+، Safari ۱۶٫۴+ (iOS و macOS) |\n| Tailwind | ۳ و ۴ |\n| Node.js (برای ابزارها) | ۲۲ به بالا |\n\n## چطور کیفیت حفظ می‌شود\n\nدر هر تغییر، پیش از انتشار:\n\n- **تست رفتار** برای همهٔ کامپوننت‌ها، ابزارهای فارسی و وب کامپوننت‌ها.\n- **تست دوزبانه:** همهٔ کامپوننت‌ها به انگلیسی رندر می‌شوند و نباید حتی یک حرف فارسی در متن‌هایشان بماند.\n- **تست تصویری:** از همهٔ نمونه‌های این سایت و حالت‌های حساس (خطا کنار فیلد بی‌خطا، متن بلند، غیرفعال، انگلیسی) در تم روشن و تیره عکس گرفته و با عکس مرجع مقایسه می‌شود.\n- **سقف حجم:** هر کامپوننت سقف حجم دارد و بزرگ‌تر شدنش بی‌اجازه ممکن نیست. [حجم کامپوننت‌ها](/docs/sizes/)\n- **کنتراست رنگ** همهٔ جفت‌رنگ‌های تم با WCAG 2.2 AA.\n- **نصب واقعی** در یک پروژهٔ Next.js، با npm و با shadcn.\n- **کیت فیگما** در شبیه‌ساز اجرا و پیکسل‌به‌پیکسل با CSS مقایسه می‌شود."},{"slug":"web-components","url":"https://kashi.sepantapouya.com/docs/web-components/","title":"وب کامپوننت‌ها","description":"کامپوننت‌های کاشی بدون React؛ با یک خط script در HTML، وردپرس، Vue، Svelte و Angular. فیلدهای فارسی، تقویم شمسی، فرم نشانی، نقشهٔ ایران، جدول داده، منو، پنجره و اعلان.","group":"شروع","components":[],"markdown":"پکیج `@kashi-ui/elements` کامپوننت‌های تعاملی کاشی را به‌شکل **وب کامپوننت** (Custom Elements استاندارد مرورگر) دارد. با هر فریم‌ورکی کار می‌کنند، یا بدون فریم‌ورک:\n\n- **HTML ساده و وردپرس:** یک خط CSS و یک خط script، بدون ابزار ساخت.\n- **Vue، Svelte و Angular:** مثل هر تگ HTML دیگری.\n- **فرم معمولی HTML:** مقدار هر فیلد با `name` ارسال می‌شود و فرم با مقدار نامعتبر ارسال نمی‌شود.\n\nظاهر از همان `@kashi-ui/css` می‌آید و عنصرها Shadow DOM ندارند؛ پس تم، رنگ برند، حالت روشن و تیره و CSS خودتان بی‌تغییر روی آن‌ها اعمال می‌شود. در پروژهٔ React همان [`@kashi-ui/react`](/docs/installation/) را به کار ببرید.\n\nاین صفحه راه‌اندازی کلی است. نصب، نمونهٔ زنده، ویژگی‌ها و رویدادهای هر کامپوننت در بخش **«وب کامپوننت»** صفحهٔ همان کامپوننت آمده است ([فهرست](#کامپوننتها)).\n\n## نصب\n\nبدون نصب، از CDN، در `<head>` صفحه:\n\n```html\n<link rel=\"stylesheet\" href=\"https://cdn.jsdelivr.net/npm/@kashi-ui/css/dist/kashi.css\">\n<script type=\"module\" src=\"https://cdn.jsdelivr.net/npm/@kashi-ui/elements/dist/cdn/kashi-elements.js\"></script>\n```\n\nیا با npm، در پروژه‌ای که ابزار ساخت دارد (Vite، Nuxt، SvelteKit، Angular):\n\n```bash\nnpm i @kashi-ui/elements @kashi-ui/css\n```\n\n```js\nimport \"@kashi-ui/css\";\nimport \"@kashi-ui/elements\"; // همه؛ یا فقط یک بخش، مثل \"@kashi-ui/elements/inputs.js\"\n```\n\nصفحه را راست‌به‌چپ کنید: `<html lang=\"fa\" dir=\"rtl\">`.\n\n## نمونه\n\nیک فرم معمولی HTML با چند کامپوننت کاشی:\n\n```html\n<form action=\"/checkout\" method=\"post\">\n <kashi-national-id name=\"national_id\" required></kashi-national-id>\n <kashi-mobile name=\"mobile\"></kashi-mobile>\n <kashi-date-picker label=\"تاریخ تحویل\" name=\"delivery\" holidays></kashi-date-picker>\n <button class=\"k-btn k-btn--primary\">ثبت سفارش</button>\n</form>\n```\n\n## کامپوننت‌ها\n\n۶۳ عنصر؛ روی نام هر کامپوننت بزنید تا نصب، نمونه، ویژگی‌ها و رویدادهایش را ببینید.\n\n| کامپوننت | عنصرها | بخش npm |\n|---|---|---|\n| [فیلدهای فارسی](/docs/persian/input/#وب-کامپوننت) | `<kashi-national-id>`، `<kashi-legal-id>`، `<kashi-mobile>`، `<kashi-landline>`، `<kashi-card-number>`، `<kashi-iban>`، `<kashi-postal-code>`، `<kashi-price>` | `inputs.js` |\n| [ساعت و پلاک خودرو](/docs/persian/input/#وب-کامپوننت) | `<kashi-time-input>`، `<kashi-plate-input>` | `forms.js` |\n| [ویرایش نگارشی](/docs/persian/text-fix/#وب-کامپوننت) | `<kashi-persian-textarea>`، `<kashi-keyboard-fix>` | `forms.js` |\n| [بازهٔ تاریخ شمسی](/docs/persian/date-range/#وب-کامپوننت) | `<kashi-date-range-picker>` | `pickers.js` |\n| [انتخاب ماه](/docs/persian/month-picker/#وب-کامپوننت) | `<kashi-month-picker>` | `pickers.js` |\n| [انتخاب نوبت](/docs/persian/appointment/#وب-کامپوننت) | `<kashi-appointment-picker>` | `appointment.js` |\n| [هیت‌مپ فعالیت](/docs/persian/activity-heatmap/#وب-کامپوننت) | `<kashi-activity-heatmap>` | `charts.js` |\n| [دیت‌پیکر شمسی](/docs/persian/date-picker/#وب-کامپوننت) | `<kashi-date-picker>`، `<kashi-calendar>` | `calendar.js` |\n| [کد تأیید](/docs/persian/otp/#وب-کامپوننت) | `<kashi-otp>` | `fields.js` |\n| [انتخاب استان و شهر](/docs/persian/province/#وب-کامپوننت) | `<kashi-province-select>`، `<kashi-city-select>` | `combobox.js` |\n| [نقشهٔ ایران](/docs/persian/iran-map/#وب-کامپوننت) | `<kashi-iran-map>` | `map.js` |\n| [فرم نشانی](/docs/persian/address/#وب-کامپوننت) | `<kashi-address-form>` | `map.js` |\n| [نمایش مبلغ](/docs/persian/money/#وب-کامپوننت) | `<kashi-money>` | `display.js` |\n| [شمارش معکوس](/docs/persian/countdown/#وب-کامپوننت) | `<kashi-countdown>` | `display.js` |\n| [تاریخ و زمان نسبی](/docs/persian/utilities/#وب-کامپوننت) | `<kashi-date>`، `<kashi-relative-time>` | `display.js` |\n| [فیلد متنی](/docs/components/input/#وب-کامپوننت) | `<kashi-input>`، `<kashi-textarea>` | `basic.js` |\n| [بنر](/docs/components/banner/#وب-کامپوننت) | `<kashi-banner>` | `basic.js` |\n| [Combobox](/docs/components/combobox/#وب-کامپوننت) | `<kashi-combobox>` | `combobox.js` |\n| [انتخاب چندتایی](/docs/components/multi-select/#وب-کامپوننت) | `<kashi-multi-select>` | `forms.js` |\n| [اسلایدر](/docs/components/slider/#وب-کامپوننت) | `<kashi-slider>` | `forms.js` |\n| [چیپس](/docs/components/chips/#وب-کامپوننت) | `<kashi-chip-group>` | `forms.js` |\n| [قدرت رمز](/docs/components/password-strength/#وب-کامپوننت) | `<kashi-password-strength>` | `forms.js` |\n| [ویرایش درجا](/docs/components/inline-edit/#وب-کامپوننت) | `<kashi-inline-edit>` | `forms.js` |\n| [بارگذاری فایل](/docs/components/file-upload/#وب-کامپوننت) | `<kashi-file-upload>` | `pickers.js` |\n| [انتخاب رنگ](/docs/components/color-picker/#وب-کامپوننت) | `<kashi-color-picker>` | `pickers.js` |\n| [نمودار](/docs/components/charts/#وب-کامپوننت) | `<kashi-bar-chart>`، `<kashi-line-chart>`، `<kashi-pie-chart>`، `<kashi-bar-list>`، `<kashi-proportion-bar>`، `<kashi-sparkline>` | `charts.js` |\n| [جدول داده](/docs/components/table/#وب-کامپوننت) | `<kashi-data-table>` | `table.js` |\n| [صفحه‌بندی](/docs/components/pagination/#وب-کامپوننت) | `<kashi-pagination>` | `controls.js` |\n| [فیلد برچسب](/docs/components/tag-input/#وب-کامپوننت) | `<kashi-tag-input>` | `controls.js` |\n| [فیلد عدد](/docs/components/number-input/#وب-کامپوننت) | `<kashi-number-input>` | `controls.js` |\n| [امتیاز](/docs/components/rating/#وب-کامپوننت) | `<kashi-rating>` | `controls.js` |\n| [پنجرهٔ گفتگو](/docs/components/dialog/#وب-کامپوننت) | `<kashi-dialog>` | `overlay.js` |\n| [زبانه‌ها](/docs/components/tabs/#وب-کامپوننت) | `<kashi-tabs>` | `overlay.js` |\n| [راهنما](/docs/components/tooltip/#وب-کامپوننت) | `<kashi-tooltip>` | `overlay.js` |\n| [دکمهٔ کپی](/docs/components/copy-button/#وب-کامپوننت) | `<kashi-copy-button>` | `overlay.js` |\n| [منوی کشویی](/docs/components/dropdown-menu/#وب-کامپوننت) | `<kashi-dropdown-menu>` | `menu.js` |\n| [پاپ‌اور](/docs/components/popover/#وب-کامپوننت) | `<kashi-popover>` | `menu.js` |\n| [اعلان](/docs/components/toast/#وب-کامپوننت) | `<kashi-toaster>` و تابع `toast()` | `toast.js` |\n| [پالت فرمان](/docs/components/command-palette/#وب-کامپوننت) | `<kashi-command-palette>` | `nav.js` |\n| [منوی راست‌کلیک](/docs/components/context-menu/#وب-کامپوننت) | `<kashi-context-menu>` | `nav.js` |\n| [کاروسل](/docs/components/carousel/#وب-کامپوننت) | `<kashi-carousel>` | `nav.js` |\n| [نمای درختی](/docs/components/tree-view/#وب-کامپوننت) | `<kashi-tree-view>` | `nav.js` |\n| [تور معرفی](/docs/components/tour/#وب-کامپوننت) | `<kashi-tour>` | `nav.js` |\n| [اسپلیتر](/docs/components/splitter/#وب-کامپوننت) | `<kashi-splitter>` | `nav.js` |\n| [منوی کناری](/docs/components/side-nav/#وب-کامپوننت) | `<kashi-side-nav>` | `nav.js` |\n\nفیلدها ویژگی‌های مشترک دارند: `label`، `name`، `value`، `hint`، `error` (خطای دلخواه، مثلاً از سرور)، `required`، `disabled` و `aria-label` (به‌جای برچسب دیداری).\n\n## ارسال فرم و اعتبارسنجی\n\nهر فیلد با `name` مقدار **خام** خود را با ارقام انگلیسی ارسال می‌کند: کد ملی `0013542419`، موبایل `09123456789`، شبا `IR…`، مبلغ `2500000` و تاریخ `2026-03-21`. قالب نمایشی (فاصله‌ها و ارقام فارسی) به سرور نمی‌رسد.\n\nمقدار نامعتبر جلوی ارسال فرم را می‌گیرد و مرورگر پیام خطای فارسی همان فیلد را نشان می‌دهد؛ `required` هم مثل input معمولی کار می‌کند.\n\n```html\n<form action=\"/checkout\" method=\"post\">\n <kashi-national-id name=\"national_id\" required></kashi-national-id>\n <kashi-postal-code name=\"postal_code\" required></kashi-postal-code>\n <button class=\"k-btn k-btn--primary\">ثبت سفارش</button>\n</form>\n```\n\n## رویدادها و JavaScript\n\nهر فیلد با هر تغییر رویداد `kashi-change` می‌فرستد. در `detail` مقدار، معتبر بودن و اطلاعات اضافه می‌آید، مثل نام بانک یا اپراتور. ویژگی‌های `value` و `valid` را هم مستقیم می‌شود خواند:\n\n```js\nconst card = document.querySelector(\"kashi-card-number\");\ncard.addEventListener(\"kashi-change\", (e) => {\n console.log(e.detail); // { value: \"6037…\", valid: true, bank: \"بانک ملی\" }\n});\n\ncard.value = \"6037991234567893\"; // مقدار از بیرون\ncard.valid; // true\n```\n\nرویدادها و ویژگی‌های JavaScript هر عنصر در بخش «وب کامپوننت» صفحهٔ همان کامپوننت آمده است.\n\n## زبان انگلیسی\n\nهمهٔ متن‌های داخلی عنصرها (دکمه‌ها، متن صفحه‌خوان، پیام‌های خطا، ارقام و نام ماه‌ها) با یک خط انگلیسی می‌شوند؛ پیش از ساخته شدن عنصرها صدا بزنید:\n\n```ts\nimport { setLocale } from \"@kashi-ui/elements\";\nsetLocale(\"en\");\n```\n\nجزئیات و عوض کردن متن‌های دلخواه: [دوزبانه، فارسی و انگلیسی](/docs/i18n/)\n\n## Vue، Svelte و Angular\n\n**Vue:** به Vue بگویید تگ‌های `kashi-` کامپوننت Vue نیستند:\n\n```js\n// vite.config.js\nimport vue from \"@vitejs/plugin-vue\";\n\nexport default {\n plugins: [vue({ template: { compilerOptions: { isCustomElement: (tag) => tag.startsWith(\"kashi-\") } } })],\n};\n```\n\n```html\n<kashi-mobile :value=\"mobile\" @kashi-change=\"mobile = $event.detail.value\"></kashi-mobile>\n```\n\n**Svelte:** تنظیمی لازم ندارد:\n\n```html\n<kashi-mobile value={mobile} on:kashi-change={(e) => (mobile = e.detail.value)}></kashi-mobile>\n```\n\n**Angular:** `CUSTOM_ELEMENTS_SCHEMA` را به کامپوننت اضافه کنید:\n\n```ts\n@Component({\n schemas: [CUSTOM_ELEMENTS_SCHEMA],\n template: `<kashi-mobile [value]=\"mobile\" (kashi-change)=\"mobile = $event.detail.value\"></kashi-mobile>`,\n})\n```\n\nدر TypeScript نوع همهٔ عنصرها ثبت شده است؛ `document.querySelector(\"kashi-iban\")` نوع `KashiIban` را برمی‌گرداند.\n\n## کامپوننت‌های دیگر\n\nبیشتر کامپوننت‌های دیگر کاشی (دکمه، کارت، نشان، هشدار، آکاردئون، مراحل، پیشرفت، فیلد متنی و…) به JavaScript نیاز ندارند و با کلاس‌های `@kashi-ui/css` ساخته می‌شوند؛ کد HTML هر کدام در تب «HTML» صفحهٔ همان کامپوننت آمده است.\n\nهمهٔ کامپوننت‌های تعاملی کاشی نسخهٔ وب کامپوننت دارند.\n\n## حجم\n\nفایل CDN حدود ۳ کیلوبایت (gzip) است و هر بخش را فقط وقتی یکی از تگ‌هایش در صفحه پیدا شود دانلود می‌کند؛ صفحه‌ای که فقط فیلد کد ملی دارد، نمودار و نقشه را دانلود نمی‌کند. لوگوی بانک‌ها هم فقط هنگام وارد کردن شمارهٔ کارت یا شبا می‌آید. اگر عنصرها را با JavaScript می‌سازید و می‌خواهید از همان اول آماده باشند، `loadAll()` را صدا بزنید:\n\n```js\nimport { loadAll } from \"https://cdn.jsdelivr.net/npm/@kashi-ui/elements/dist/cdn/kashi-elements.js\";\nawait loadAll();\n```\n\nبا npm فقط بخشی را که لازم دارید وارد کنید (ستون «بخش npm» جدول بالا)؛ مثلاً فقط فیلدهای فارسی:\n\n```js\nimport \"@kashi-ui/elements/inputs.js\";\n```"},{"slug":"writing","url":"https://kashi.sepantapouya.com/docs/writing/","title":"نوشتن متن رابط","description":"راهنمای جامع فارسی‌نویسی در محصول؛ از لحن و برچسب دکمه و پیام خطا تا نیم‌فاصله، نشانه‌ها، عدد، واژه‌گزینی، دسترس‌پذیری و تایپوگرافی متن فارسی.","group":"شروع","components":[],"markdown":"متن رابط بخشی از طراحی است. دکمه‌ای که «تأیید» نوشته و معلوم نیست چه می‌کند، یا خطایی که فقط می‌گوید «خطا رخ داد»، به اندازهٔ یک دکمهٔ کج به محصول آسیب می‌زند؛ و «میخواهید» بی‌نیم‌فاصله یا «ي» عربی، حس بی‌دقتی می‌دهد، حتا اگر خواننده نداند چرا. این راهنما در دو بخش است: **چه بنویسیم** (لحن، ساختار و هر جزء رابط) و **چطور بنویسیم** (دستور خط، نشانه‌ها، عدد و واژه‌گزینی). متن‌های داخلی خود کاشی با همین قاعده‌ها نوشته شده‌اند.\n\n> **مبنا:** برای درست‌نویسی، کاشی از [دستور خط فارسی فرهنگستان زبان و ادب فارسی](https://virastaran.net/khat/) پیروی می‌کند. جاهایی که شیوه‌نامه‌های معتبر با هم فرق دارند (مثلاً «حتی» یا «حتا»، «خانهٔ» یا «خانه‌ی»)، هر دو را آورده‌ایم؛ یکی را برای محصولتان انتخاب کنید و **همه‌جا یکدست** بنویسید. یکدستی از هر انتخابی مهم‌تر است.\n\n## بخش اول: چه بنویسیم\n\n## اصول\n\n۱. **روشن:** کاربر بی‌فکر بفهمد. واژهٔ آشنا، جملهٔ کوتاه، فعل معلوم.\n۲. **کوتاه:** هر واژه‌ای که بی‌آن معنا عوض نمی‌شود، حذف شود. کاربر رابط را می‌خواند تا کارش را انجام دهد، نه برای لذت خواندن.\n۳. **مفید:** هر متن به یک پرسش کاربر جواب می‌دهد: این چیست؟ چه کنم؟ چه شد؟ حالا چه؟\n۴. **انسانی:** مثل آدمِ مؤدب حرف بزنید، نه مثل نامهٔ اداری و نه مثل ماشین.\n۵. **یکدست:** برای یک مفهوم در کل محصول یک واژه. اگر جایی «سبد خرید» نوشتید، جای دیگر «کارت» یا «سبد» ننویسید.\n\n## صدا و لحن\n\n**صدا** شخصیت ثابت محصول است؛ **لحن** با موقعیت عوض می‌شود. یک آدم با همان شخصیت، در جشن تولد و در بیمارستان یک‌جور حرف نمی‌زند.\n\n| موقعیت | لحن | نمونه |\n|---|---|---|\n| خوشامد و معرفی | گرم و تشویق‌کننده | به کاشی خوش آمدید. اولین پروژه‌تان را بسازید. |\n| کار عادی | خنثی و کوتاه | نشانی ذخیره شد. |\n| خطا | آرام و راهنما، بدون شوخی | پرداخت انجام نشد. مبلغی از حساب شما کم نشده است. |\n| پول، حریم خصوصی، امنیت | جدی و دقیق | برای برداشت، رمز دوم کارت لازم است. |\n| حذف و کار برگشت‌ناپذیر | صریح | این سفارش برای همیشه حذف می‌شود. |\n| موفقیت بزرگ | شاد، ولی کوتاه | تبریک! اولین فروشتان ثبت شد. |\n\n### رسمیت\n\n- در رابط، **فارسی معیارِ ساده** بنویسید: نه اداری («مبذول فرمایید»، «حسب‌الدستور») و نه شکسته («میشه»، «رو»).\n- **شکسته‌نویسی** را، اگر برندتان می‌خواهد، فقط در بازاریابی، شبکه‌های اجتماعی یا گفتگوی پشتیبانی به کار ببرید؛ هرگز در پیام خطا، پرداخت، قرارداد و متن‌های امنیتی. دستور خط شکسته هنوز مرجع واحد ندارد و خواندنش برای همه یکسان نیست.\n- کاربر را **«شما»** خطاب کنید و فعل را جمع بیاورید («وارد کنید»). نه «تو»، و نه سوم‌شخص اداری («کاربر گرامی، … نمایید»).\n- **«لطفاً»** را فقط وقتی بنویسید که کار اضافه‌ای می‌خواهید («لطفاً چند لحظه صبر کنید»)، نه برای هر دستوری.\n- **فرض جنسیتی نکنید:** «آقا/خانم» را فقط وقتی بنویسید که کاربر خودش آن را انتخاب کرده است. فارسی خوشبختانه ضمیر جنسیتی ندارد؛ با «کاربر گرامی» و «دوست عزیز» هم آن را خراب نکنید.\n\n| درست | نادرست |\n|---|---|\n| رمز خود را وارد کنید. | رمزت را وارد کن. / کاربر گرامی، رمز را وارد نمایید. |\n| پرداخت انجام شد. | پرداخت با موفقیت صورت پذیرفت. |\n| نشانی را ذخیره کردیم. | نشانی ذخیره گردید. |\n| کد پستی ده رقم است. | لطفاً توجه داشته باشید که کد پستی باید ده رقم باشد. |\n| پرداخت انجام نشد. | اوه! انگار پولت گم شد 😅 |\n\n## ساختار متن\n\nکاربر متن رابط را **نمی‌خواند، می‌پیماید**: چشمش اول عنوان و چند واژهٔ اول هر خط را می‌گیرد.\n\n- **مهم‌ترین چیز اول:** «کد پستی ده رقم است»، نه «با توجه به استاندارد پست، تعداد ارقام کد پستی ده است».\n- **واژهٔ کلیدی در ابتدای جمله و فهرست:** «سفارش‌ها»، «پرداخت‌ها»، «تنظیمات»؛ نه «مشاهدهٔ سفارش‌ها»، «مشاهدهٔ پرداخت‌ها».\n- **یک ایده در هر جمله** و حداکثر دو سه جمله در هر بلوک. متن بلندتر را فهرست کنید یا به صفحهٔ راهنما ببرید.\n- **فعل امر برای کار، اسم برای جا:** دکمه «ذخیرهٔ تغییرات»، منو «تنظیمات».\n- **«شما» و «ما» را درست جدا کنید:** «سفارش‌های من» یا «سفارش‌های شما»؛ یکی را برگزینید و در کل محصول همان را بنویسید.\n\n## جزء به جزء رابط\n\nپیش از نوشتن، جای پیام را درست انتخاب کنید؛ متن خوب در جای نادرست هم دیده نمی‌شود.\n\n### عنوان صفحه و بخش\n\n- اسم آنچه در صفحه هست: «سفارش‌ها»، «نشانی تحویل». بدون نقطه در پایان.\n- کوتاه، و همان واژه‌ای که در منو و پیوند رسیدن به این صفحه آمده است.\n\n### برچسب دکمه\n\nدکمه کاری انجام می‌دهد، پس برچسبش **فعل** است و می‌گوید بعد از کلیک چه می‌شود.\n\n| درست | نادرست | چرا |\n|---|---|---|\n| ذخیرهٔ تغییرات | تأیید | «تأیید» نمی‌گوید چه اتفاقی می‌افتد |\n| حذف سفارش | بله | دکمه بدون خواندن پرسش هم باید معنا داشته باشد |\n| پرداخت ۲۵۰٬۰۰۰ تومان | ادامه | مبلغ همان لحظهٔ تصمیم دیده می‌شود |\n| ارسال دوبارهٔ کد | کلیک کنید | کار را نمی‌گوید؛ روی گوشی هم کلیکی نیست |\n| افزودن به سبد | اقدام به خرید | «اقدام به … کردن» درازنویسی است |\n\n- دو تا چهار واژه. «دکمهٔ»، «اینجا» و «لطفاً» ننویسید.\n- در گفتگوی تأیید، دکمهٔ اصلی همان فعلِ عنوان است: عنوان «حذف این سفارش؟»، دکمه‌ها «انصراف» و «حذف سفارش».\n- دکمهٔ خطرناک را با `variant=\"danger\"` بسازید، نه فقط با متن قرمز.\n- دکمهٔ فقط‌آیکون `aria-label` می‌خواهد: «حذف سفارش ۴۸۱۲۷»، نه «آیکون سطل».\n\n### پیوند\n\n- بگوید به کجا می‌رود: «راهنمای بازگشت کالا»، نه «اینجا»، «بیشتر» یا «کلیک کنید». صفحه‌خوان‌ها فهرست پیوندهای صفحه را جدا از متن می‌خوانند.\n\n### برچسب فیلد، placeholder و راهنما\n\n- **برچسب** اسم داده است و همیشه دیده می‌شود: «ایمیل»، نه «ایمیل خود را وارد کنید».\n- **placeholder** فقط نمونهٔ قالب است («۰۹۱۲ ۳۴۵ ۶۷۸۹») و جای برچسب نیست؛ با شروع تایپ ناپدید می‌شود.\n- **راهنما** زیر فیلد، آنچه پیش از نوشتن باید بداند: «ده رقم، بدون خط تیره». با نقطه، چون جملهٔ کامل است.\n- فیلدهای اختیاری را با «اختیاری» نشان دهید، نه همهٔ اجباری‌ها را با ستاره (اگر بیشترشان اجباری‌اند).\n\n### پیام خطا\n\n**هر پیام خطا دو چیز می‌گوید: چه شد، و کاربر حالا چه کند.**\n\n| نوع | درست | نادرست |\n|---|---|---|\n| قالب فیلد | کد ملی باید ده رقم باشد. | ورودی نامعتبر است. |\n| داده‌ای که قبلاً هست | این ایمیل قبلاً ثبت شده است. وارد شوید یا رمز را بازیابی کنید. | خطای ۴۰۹ |\n| شبکه | اتصال اینترنت قطع است. دوباره امتحان کنید. | Network Error |\n| محدودیت | مبلغ باید حداقل ۱۰٬۰۰۰ تومان باشد. | مبلغ اشتباه است! |\n| دسترسی | برای دیدن این صفحه باید مدیر پروژه باشید. از مدیر بخواهید شما را اضافه کند. | دسترسی غیرمجاز |\n| خطای سرور | مشکلی از سمت ما پیش آمد و پرداخت انجام نشد. چند دقیقهٔ دیگر امتحان کنید. کد پیگیری: ۴۸۱۲ | Internal Server Error |\n\n- **کاربر را مقصر نکنید:** «شمارهٔ کارت کامل نیست»، نه «شمارهٔ کارت را اشتباه وارد کردید».\n- **مسئولیت اشکال خودتان را بپذیرید:** «مشکلی از سمت ما پیش آمد».\n- **کد فنی** را فقط اگر برای پشتیبانی لازم است و در خط دوم بنویسید.\n- **علامت تعجب نگذارید.** خطا داد زدن نمی‌خواهد.\n- **کنار همان فیلد** نشانش دهید. پیام کلی بالای فرم فقط وقتی لازم است که چند فیلد مشکل دارند، و آن هم بگوید کدام‌ها.\n- **پیش از پایان تایپ خطا نشان ندهید.** فیلدهای فارسی کاشی خطا را بعد از خروج از فیلد یا کامل شدن رقم‌ها نشان می‌دهند.\n- **متن کاربر را پاک نکنید.** پس از خطای ارسال، هر چه نوشته بود باید سر جایش بماند.\n\n### حالت خالی، بارگذاری و موفقیت\n\n| حالت | درست | نادرست |\n|---|---|---|\n| خالی برای اولین بار | هنوز سفارشی ثبت نکرده‌اید. اولین محصول را از فروشگاه انتخاب کنید. | داده‌ای وجود ندارد. |\n| خالی پس از جستجو | نتیجه‌ای برای «کتاب قدیمی» پیدا نشد. املای واژه را بررسی کنید. | ۰ نتیجه |\n| خالی پس از فیلتر | با این فیلترها سفارشی نیست. فیلترها را کم کنید. | نتیجه‌ای یافت نشد |\n| بارگذاری | در حال دریافت سفارش‌ها… | لطفاً صبر کنید |\n| موفقیت | سفارش ثبت شد. کد پیگیری: ۴۸۱۲۷ | عملیات با موفقیت انجام شد. |\n\n- «با موفقیت» را ننویسید؛ «ذخیره شد» خودش یعنی موفق.\n- پیام موفقیت اگر اطلاعات مهمی دارد (کد پیگیری، زمان تحویل) همان را می‌گوید و جای ماندگار می‌خواهد، نه توستی که ناپدید شود.\n\n### گفتگوی تأیید\n\n- **عنوان پرسش است** و کار را نام می‌برد: «حذف این سفارش؟».\n- **متن می‌گوید دقیقاً چه از بین می‌رود** و آیا برگشت دارد: «سفارش ۴۸۱۲۷ و رسیدهایش برای همیشه حذف می‌شوند.»\n- **دکمه‌ها فعل‌اند:** «انصراف» و «حذف سفارش»، نه «خیر» و «بله».\n- برای کارهای برگشت‌پذیر، به‌جای گفتگو کار را انجام دهید و «واگرد» بدهید.\n\n### اعلان، ایمیل و پیامک\n\n- **پیامک:** نام برند در ابتدا، کار اصلی بعد و کوتاه. پیامک فارسی در هر بخش حدود ۷۰ نویسه جا دارد و بیشتر از آن چند پیامک حساب می‌شود.\n- **کد یک‌بارمصرف:** «کد ورود شما به کاشی: ۴۸۱۲۷. این کد را به کسی ندهید.» کد را اول بیاورید تا در پیش‌نمایش اعلان دیده شود.\n- **عنوان ایمیل** موضوع را بگوید («سفارش ۴۸۱۲۷ ارسال شد»)، نه «اطلاعیه» یا «خبر مهم».\n- **اعلان** یک خبر؛ اگر کاری لازم است، همان را بگوید: «سفارش شما رسید. امتیاز بدهید.»\n\n### معرفی و آموزش\n\n- هر قدم یک جمله دربارهٔ اینکه این بخش **برای کاربر** چه می‌کند، نه اینکه نام دکمه چیست.\n- بیش از سه تا پنج قدم نشود؛ آموزش کامل را در راهنمای جدا بنویسید.\n\n### متن حقوقی و رضایت\n\n- چک‌باکس رضایت مثبت و روشن: «شرایط استفاده را خوانده‌ام و می‌پذیرم»، با پیوند به همان شرایط.\n- رضایت‌های جدا (خبرنامه، پیامک تبلیغاتی) چک‌باکس جدا و **بدون تیک پیش‌فرض** می‌خواهند.\n- خلاصهٔ ساده بالای متن حقوقی بلند، زحمت کاربر را کم و اعتمادش را بیشتر می‌کند.\n\n## بخش دوم: چطور بنویسیم\n\n## نیم‌فاصله و فاصله\n\n**نیم‌فاصله** (فاصلهٔ مجازی، ZWNJ) جزءهای یک واژه را جدا نشان می‌دهد بی‌آنکه واژه بشکند. بی‌آن یا «میخواهم» می‌شود و خواندنش کند، یا با فاصلهٔ کامل «می خواهم» می‌شود و در شکستن خط، «می» تنها سر یک خط می‌ماند.\n\n**قاعدهٔ کلی:** بین دو واژه یک فاصلهٔ کامل؛ داخل یک واژه (پیشوند، پسوند، ترکیب) نیم‌فاصله؛ هیچ‌جا دو فاصله.\n\n| جزء | درست | نادرست |\n|---|---|---|\n| «می» و «نمی» | می‌خواهم، نمی‌شود | میخواهم، می خواهم |\n| «ها»ی جمع | کتاب‌ها، سفارش‌ها | کتابها، کتاب ها |\n| «تر» و «ترین» | سریع‌تر، بزرگ‌ترین | سریعتر، بزرگ ترین |\n| «بی»، «نا»، «هم» | بی‌نام، ناامن، هم‌کار | بینام، نا امن، همکار* |\n| ضمیر و فعل ربطی پس از «ه» و «ی» | خانه‌ام، زندگی‌اش، آماده‌اید | خانه ام، زندگیش |\n| واژهٔ مرکب | کسب‌وکار، جست‌وجو، گفت‌وگو، پیش‌نمایش، به‌روزرسانی | کسب و کار، جستجو، پیشنمایش |\n| مصدر مرکب | ذخیره‌کردن، به‌دست‌آوردن | (در فعل صرف‌شده جدا: ذخیره می‌کنیم) |\n| «به» قیدساز | به‌تدریج، به‌سرعت، به‌طور کلی | بتدریج |\n\n\\* برخی ترکیب‌های جاافتاده سرهم هم درست‌اند (همکار، بیمار)؛ در شک، [فرهنگ املایی](https://virastaran.net/khat/) را ببینید.\n\n**جدا، همیشه:**\n\n- «به» حرف اضافه: «به تهران»، «به شما»؛ نه «بشما» و نه «بتهران».\n- «که»، «را»، «این»، «آن»، «هر»، «هیچ»، «عدم»، «بدون»: «آنچه را»، «هر روز».\n- حرف اضافهٔ پسین: «نسبت به»، «با توجه به»، «مربوط به».\n\n**اختلاف شیوه‌نامه‌ها؛ یکی را انتخاب کنید:**\n\n| مورد | فرهنگستان (شیوهٔ کاشی) | شیوهٔ رایج دیگر |\n|---|---|---|\n| «بیشتر»، «کمتر»، «بهتر» | سرهم: بیشتر، کمتر | جدا: بیش‌تر، کم‌تر |\n| اضافه پس از «ه» | خانهٔ ما، صفحهٔ اصلی | خانه‌ی ما، صفحه‌ی اصلی |\n| «آنچه»، «اینکه» | سرهم: آنچه، اینکه | جدا: آن‌چه، این‌که |\n\n«ـهٔ» در رابط کوچک‌تر و تمیزتر است و کاشی همین را به کار می‌برد؛ «ـه‌ی» برای خوانندهٔ کم‌تجربه آشکارتر است. هر کدام را برگزیدید، در کل محصول همان باشد.\n\n## حروف و املا\n\n| | درست | نادرست | توضیح |\n|---|---|---|---|\n| ی و ک | ی، ک | ي، ك | ي و ك عربی جستجو را خراب می‌کنند: «كتاب» با «کتاب» پیدا نمی‌شود |\n| «ة» و «ۀ» | ت، ـهٔ | ة، ۀ (نویسهٔ جدا) | ت گرد در فارسی نیست؛ همزهٔ «ـهٔ» را با نشانهٔ همزه روی «ه» بنویسید |\n| همزهٔ پایانی | املا، امضا، انشا | املاء، امضاء | به گفتهٔ فرهنگستان در واژه‌های رایج همزهٔ پایانی نمی‌آید |\n| همزهٔ میانی | تأیید، مسئول، هیئت، رئیس | تائید، مسوول (در شیوهٔ فرهنگستان) | شیوهٔ دیگری همزه را برمی‌دارد (مسوول، هییت)؛ یکی را برگزینید |\n| تنوین | کاملاً، دقیقاً | کاملا، دقیقا | بهتر: معادل فارسی (به‌کلی، به‌دقت)؛ شیوهٔ دیگری «کاملن» می‌نویسد |\n| ت و ط | اتاق، تهران، بلیت، باتری، اتو | اطاق، طهران، بلیط، باطری، اطو | املای فارسیِ واژه‌های غیرعربی |\n| «ناهار» | ناهار | نهار | |\n| «گزاردن» و «گذاشتن» | خدمت‌گزار، نمازگزار، سپاس‌گزار | خدمت‌گذار، سپاسگذار | «گزاردن» یعنی به‌جا آوردن |\n| «حتی» | حتی (فرهنگستان) | | شیوهٔ دیگر: «حتا»، همان‌طور که خوانده می‌شود |\n\n[`fixPersianText`](/docs/persian/utilities/) و [ویرایش نگارشی](/docs/persian/text-fix/) ی و ک عربی، نیم‌فاصلهٔ «می» و «ها»، نشانه‌ها و ارقام را خودکار درست می‌کنند؛ متن‌هایی که کاربر از جای دیگر می‌چسباند هم.\n\n## نشانه‌ها\n\n**قاعدهٔ فاصله:** نشانه‌های درنگ (. ، ؛ : ؟ ! …) **بی‌فاصله** به واژهٔ پیش و **با یک فاصله** از واژهٔ بعد. نشانه‌های دربرگیر (« » ( ) [ ]) **بی‌فاصله** با درون و **با فاصله** با بیرون.\n\n| | درست | نادرست |\n|---|---|---|\n| ویرگول | سلام، خوبید؟ | سلام , خوبید ? |\n| نقطه‌ویرگول | ارسال رایگان است؛ ولی… | ارسال رایگان است ; ولی… |\n| علامت پرسش | حذف این سفارش؟ | حذف این سفارش ? |\n| گیومه | «کاشی» | \"کاشی\" ، \" کاشی \" |\n| پرانتز | (اختیاری) | ( اختیاری ) |\n| سه‌نقطه | در حال بارگذاری… | در حال بارگذاری... |\n\n- **نقطه** پایان جملهٔ کامل (راهنما، پیام خطا، متن گفتگو) می‌آید؛ **پس از عنوان، برچسب، دکمه و آیتم کوتاه فهرست نه.**\n- **گیومه** برای نقل‌قول و نام چیزی که کاربر باید آن را تشخیص دهد («دکمهٔ «ذخیره» را بزنید»). برای تأکید از گیومه استفاده نکنید؛ پررنگ کنید.\n- **علامت تعجب** در رابط تقریباً هرگز؛ فقط برای تبریک واقعی، و تنها یکی.\n- **خط تیره** برای جملهٔ معترضه در رابط کم‌کاربرد و در RTL دردسرساز است؛ ویرگول یا جملهٔ جدا بهتر است.\n- **نقطه در پایان نقل‌قولِ کامل** داخل گیومه می‌آید: «همه‌چیز آماده است.»\n- **نشانه‌های ریاضی** (+ − = × /) در متن فارسی با یک فاصله از دو طرف: ۲ + ۲ = ۴.\n\n## عدد، تاریخ، واحد و پول\n\n| | درست | نادرست |\n|---|---|---|\n| ارقام در متن فارسی | ۱۴۰۵ | 1405 |\n| جداکنندهٔ هزارگان | ۱٬۲۵۰٬۰۰۰ | ۱,۲۵۰,۰۰۰ یا 1,250,000 |\n| ممیز | ۲٫۵ | ۲.۵ یا 2.5 |\n| درصد | ۲۵٪ | ۲۵ درصد٪ یا %25 |\n| بازه | ۱۰ تا ۲۰ نفر | ۱۰-۲۰ نفر |\n| عدد و معدود | ۲۵ تومان، ۳ کالا | ۲۵تومان |\n| ساعت | ساعت ۱۴:۳۰ | 14:30 در متن فارسی |\n| تاریخ در متن | ۱۶ آبان ۱۴۰۵ | ۱۴۰۵/۸/۱۶ در جمله |\n| تاریخ در جدول و فیلد | ۱۴۰۵/۰۸/۱۶ | ۱۶ آبان ۱۴۰۵ در ستون باریک |\n\n- **ارقام فارسی** در همهٔ متن‌های فارسی؛ کامپوننت‌های کاشی خودشان این کار را می‌کنند. ارقام لاتین فقط کنار واژهٔ لاتین («iPhone 16») و در کد.\n- **شماره‌ها** (موبایل، کارت، شبا، کد پستی) چپ‌به‌راست نوشته می‌شوند تا ترتیب رقم‌ها به‌هم نریزد، ولی ارقامشان در رابط فارسی همچنان فارسی است.\n- **عدد کوچک** در متن روان را می‌شود با حروف نوشت («دو روز دیگر»)؛ در رابط، رقم سریع‌تر خوانده می‌شود («۲ روز»). در یک محصول یکی را انتخاب کنید.\n- **واحد پول** را همیشه بنویسید و یکی را برگزینید: تومان یا ریال، نه هر دو. مبلغ بزرگ را کوتاه کنید: «۱٫۲ میلیون تومان» ([نمایش مبلغ](/docs/persian/money/)).\n- **زمان نسبی** («۵ دقیقه پیش») برای رویداد تازه، و تاریخ کامل در `title` برای کسی که دقیقش را می‌خواهد.\n- **تقویم:** شمسی پیش‌فرض است. اگر میلادی هم لازم است، کنارش و کم‌رنگ‌تر: «۱۶ آبان ۱۴۰۵ (۷ نوامبر ۲۰۲۶)».\n\n## واژه‌گزینی\n\n### ساختارهای اداری و ترجمه‌ای\n\nاین‌ها رایج‌ترین ساختارهایی‌اند که متن را اداری، دراز یا ترجمه‌زده می‌کنند. بسیاری از آن‌ها را [«آروانی نوشتن»](https://www.arvancloud.ir/fa/culture/orthography) هم با توضیح ریشه‌شان گرد آورده است.\n\n| به‌جای | بنویسید | چرا |\n|---|---|---|\n| می‌باشد، نمی‌باشد | است، نیست | |\n| می‌گردد، گردید | می‌شود، شد | «گردیدن» یعنی چرخیدن |\n| … نمایید، … نمود | … کنید، … کرد | «نمودن» یعنی نشان دادن |\n| صورت گرفت، صورت پذیرفت | انجام شد | |\n| اقدام به پرداخت نمایید | پرداخت کنید | درازنویسی |\n| سفارش توسط شما ثبت شد | سفارش را ثبت کردید / سفارش ثبت شد | مجهولِ همراه فاعل ترجمه‌زده است |\n| به‌عنوان مدیر انتخاب شد | مدیر شد | «به‌عنوان» (as) اغلب زائد است |\n| در رابطه با، در ارتباط با | دربارهٔ، برای | ترجمهٔ لفظ‌به‌لفظ |\n| در مورد | دربارهٔ | |\n| عدم پرداخت | پرداخت نکردن، نپرداختن | |\n| جهت ثبت | برای ثبت | «جهت» یعنی سو |\n| لذا | پس، برای همین | |\n| آنچه که | آنچه | «آنچه» خودش «آن چیزی که» است |\n| بر روی | روی | حشو |\n| کلیهٔ کاربران | همهٔ کاربران | |\n| قابل قبول، غیرقابل استفاده | پذیرفتنی، بی‌استفاده | ترجمهٔ able |\n| لازم به ذکر است که | (حذف) | جمله بی‌آن کامل است |\n| به اندازهٔ کافی | (بسته به جمله) بس، کافی | ترجمهٔ enough |\n| با موفقیت انجام شد | انجام شد | |\n| مبادرت ورزید | (فعل اصلی) | |\n\n### واژه‌های رابط\n\nبرای مفهوم‌های تکراری رابط، این واژه‌ها را پیشنهاد می‌کنیم. اگر محصولتان واژهٔ دیگری را جا انداخته (و کاربرانتان آن را بهتر می‌فهمند)، همان را نگه دارید؛ ولی همه‌جا همان.\n\n| مفهوم | پیشنهاد | نه |\n|---|---|---|\n| Save | ذخیره | سیو |\n| Login، Sign up | ورود، ثبت‌نام | لاگین، ساین‌آپ |\n| Account | حساب کاربری | اکانت |\n| Username، Password | نام کاربری، رمز عبور | یوزرنیم، پسورد |\n| Settings | تنظیمات | ستینگ |\n| Search | جستجو (یا جست‌وجو؛ یکدست) | سرچ |\n| Upload، Download | بارگذاری، دانلود | آپلود (برای مخاطب عمومی) |\n| Link | پیوند | لینک (مگر مخاطب فنی است) |\n| Dashboard | پیشخوان یا داشبورد | (هر دو رایج؛ یکی را برگزینید) |\n| Delete، Remove | حذف | پاک‌سازی (برای یک مورد) |\n| Cancel | انصراف | کنسل |\n| Back | بازگشت | برگشت به عقب |\n| Email | ایمیل | (رایانامه درست است ولی کم‌آشنا) |\n\n- **نام‌های خاص** (Google، iPhone، API) را به خط اصلی بنویسید؛ «اپل» و «گوگل» را هم اگر برای مخاطب عمومی است فارسی.\n- **کوته‌نوشت انگلیسی** (API، PDF، SMS) را با حروف بزرگ و بدون نقطه بنویسید و در اولین بار، اگر مخاطب عمومی است، توضیحش را بدهید.\n- **جمع واژهٔ لاتین** با «ها»: «PDFها»؛ کاشی آن را در `<bdi>` نگه می‌دارد تا جای «ها» عوض نشود.\n\n## راست‌به‌چپ و متن دوزبانه\n\n- **واژهٔ لاتین در جملهٔ فارسی** ترتیب نمایش را به‌هم می‌ریزد، به‌ویژه کنار عدد و نشانه. نام محصول و کد را در `<bdi>` بگذارید (کامپوننت‌های کاشی این کار را خودشان می‌کنند).\n- **دو واژهٔ لاتین با ویرگول فارسی** بینشان، جایشان عوض می‌شود («Claude، Cursor»). جمله را طوری بنویسید که پشت‌سرهم نیایند: «Claude و همچنین Cursor».\n- **جملهٔ فارسی را با واژهٔ لاتین شروع نکنید**؛ تراز خط و جای نشانهٔ پایان به‌هم می‌ریزد. «برنامهٔ Figma» بهتر از «Figma برنامه‌ای…» است.\n- **فلش و آیکون جهت‌دار** در RTL آینه می‌شوند؛ «بعدی» به چپ اشاره می‌کند. در متن «دکمهٔ سمت چپ» ننویسید؛ نام دکمه را بنویسید.\n- **نسخهٔ انگلیسی** را جدا و درست بنویسید، نه ترجمهٔ واژه‌به‌واژه: Sentence case در دکمه‌ها («Delete order»، نه «Delete Order»). [دوزبانه](/docs/i18n/)\n\n## تایپوگرافی متن فارسی\n\n- **فاصلهٔ حروف (letter-spacing) را روی متن فارسی نگذارید.** خط فارسی پیوسته است؛ فاصله‌گذاری اتصال حروف را می‌شکند. کاشی فقط روی عددها و متن لاتین فاصله می‌گذارد.\n- **ایتالیک ندارد.** کج کردن ساختگی خط فارسی زشت و ناخواناست؛ برای تأکید پررنگ یا رنگ.\n- **تراز دوطرفه (justify) در وب** فاصلهٔ ناهموار بین واژه‌ها می‌سازد و فارسی واژه‌شکن (hyphenation) ندارد؛ متن رابط را راست‌چین بگذارید. [W3C: Arabic & Persian Layout Requirements](https://www.w3.org/TR/alreq/)\n- **کشیده (ـ)** را برای پر کردن خط یا تزئین به کار نبرید؛ جستجو و صفحه‌خوان را خراب می‌کند.\n- **طول خط** متن خواندنی ۶۰ تا ۸۰ نویسه؛ برای متن بلندتر ستون را باریک کنید.\n- **حروف کوچک ریز** را با وزن کم ننویسید؛ دندانه‌ها و نقطه‌های فارسی در وزن نازک و اندازهٔ کوچک گم می‌شوند. [فونت و تایپوگرافی](/docs/typography/)\n\n## متن برای صفحه‌خوان\n\n- **دکمهٔ فقط‌آیکون** `aria-label` می‌خواهد که کار را بگوید.\n- **تصویر تزئینی** `alt=\"\"` می‌گیرد؛ تصویری که اطلاعات دارد همان را در `alt` بگوید، نه «تصویر».\n- **ایموجی** را صفحه‌خوان با نامش می‌خواند («چهرهٔ خندان با عرق»)؛ در متن مهم به کار نبرید و هرگز به‌جای واژه.\n- **فقط با رنگ معنا ندهید:** «فیلدهای قرمز» نه؛ «فیلدهایی که خطا دارند».\n- **عدد و تاریخ** را برای صفحه‌خوان کامل بگذارید؛ کامپوننت‌های کاشی متن صفحه‌خوانِ خودشان را دارند (مثلاً «شنبه ۱۶ آبان ۱۴۰۵، تعطیل»)، آن‌ها را با متن کوتاه‌تر عوض نکنید.\n\n## واژه‌نامهٔ محصول\n\nبرای هر محصول یک فهرست کوتاه نگه دارید: هر مفهوم، واژهٔ انتخابی، واژه‌هایی که **نباید** به کار بروند و یک مثال. همین فهرست جلوی «سبد» در یک صفحه و «کارت» در صفحهٔ دیگر را می‌گیرد، و نویسندهٔ تازه را سریع هم‌صدا می‌کند. اگر محصولتان دوزبانه است، معادل انگلیسی را هم کنارش بنویسید و متن‌های [فرهنگ کاشی](/docs/i18n/) را با همان واژه‌ها هم‌خوان کنید.\n\n## فهرست بازبینی\n\nپیش از انتشار هر صفحه:\n\n- همهٔ دکمه‌ها فعل‌اند و بدون خواندن بقیهٔ صفحه هم معنا دارند.\n- هر پیام خطا می‌گوید چه شد و حالا چه باید کرد، و کاربر را سرزنش نمی‌کند.\n- حالت خالی، بارگذاری، خطای اینترنت و موفقیت متن دارند.\n- لحن با موقعیت جور است؛ در پول، خطا و امنیت شوخی نیست.\n- ی و ک فارسی، نیم‌فاصلهٔ «می»، «ها» و «تر»، ارقام فارسی و نشانه‌های فارسی درست‌اند.\n- «می‌باشد»، «نمودن»، «توسط» و «در رابطه با» در متن نیست.\n- دکمه‌های فقط‌آیکون، تصویرها و پیوندها برای صفحه‌خوان نام درست دارند.\n- برای یک مفهوم در کل محصول یک واژه به کار رفته است.\n\n## منابع\n\n- [دستور خط فارسی فرهنگستان؛ خلاصه و فرهنگ املایی «ویراستاران»](https://virastaran.net/khat/)\n- [آروانی نوشتن؛ شیوه‌نامهٔ نگارش ابر آروان](https://www.arvancloud.ir/fa/culture/orthography)\n- [W3C: Arabic & Persian Layout Requirements](https://www.w3.org/TR/alreq/)\n- [لحن برند در راهنمای برند دیجی‌کالا](https://about.digikala.com/guidelines/brand/verbal/brandtone/)\n- [بهبود پیغام‌های خطا در طراحی محصول؛ آکادمی آمانج](https://amanjacademy.com/blog/%D9%85%D9%82%D8%A7%D9%84%D8%A7%D8%AA-ux-writing/improvement-of-error-messages-in-product-design/)\n- [Google Material: مقدمه‌ای بر نوشتن تجربهٔ کاربری](https://codelabs.developers.google.com/codelabs/material-communication-guidance?hl=fa)"}]}