@kashi-ui/mcp 0.22.0 → 0.22.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 +1 -1
- package/package.json +4 -4
package/dist/docs.json
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":"0.22.0","generated":"2026-11-02","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>) محتوای بخشهای بسته را هم پیدا و باز میکند."},{"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- آیکون تزئینی است و از صفحهخوان پنهان است؛ عنوان باید معنا را کامل برساند."},{"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- وضعیت فقط با رنگ نمایش داده نمیشود؛ متن آن برای صفحهخوان در دسترس است."},{"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` اعلام کنید."},{"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` استفاده کنید."},{"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\"` دارد و لینک نیست. جداکنندهها تزئینیاند و در راستبهچپ آینه میشوند. «…» یک دکمهٔ واقعی با نام «نمایش مسیر کامل» است."},{"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 است و حلقهٔ فوکوس روی دکمهٔ کناری نمیرود."},{"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\">`) استفاده کنید، نه دکمه."},{"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`) قرار دهید."},{"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` اعلام میشود."},{"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 فوکوس میگیرد («آبان: ۱۸۴»)؛ در نمودار خطی، با فوکوس روی نمودار کلیدهای ←→ بین نقطهها حرکت میکنند و مقدار همهٔ سریها اعلام میشود."},{"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>` استفاده کنید."},{"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` است. حالت روشن با تیک، رنگ و حاشیه نشان داده میشود، نه فقط رنگ."},{"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 پنل را میبندد و فوکوس را به مربع رنگ برمیگرداند."},{"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` دارند."},{"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> بسته میشود و فوکوس به دکمهای که پالت را باز کرد برمیگردد."},{"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> روی عنصر فوکوسشده هم باز میشود و این راهنما به ناحیه وصل است. همهٔ عملهای منو باید از راه دیگری (دکمه یا منوی کشویی) هم در دسترس باشند."},{"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 در دسترس است."},{"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- برای تأیید عملیات مخرب، دکمهٔ «انصراف» را اول بگذارید و نتیجهٔ کار را در توضیح صریح بنویسید."},{"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» در متن فارسی برعکس نشود."},{"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/) استفاده کنید."},{"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` بدهید."},{"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` اعلام میشوند."},{"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حالت نمایش یک دکمه با نام کامل است («نام پروژه: بازطراحی فروشگاه، ویرایش»). پس از ثبت یا انصراف، فوکوس به همان دکمه برمیگردد."},{"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- پیام خطا باید بگوید چه اشتباهی رخ داده و چطور درست شود، نه فقط «نامعتبر»."},{"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` کامل («۸٬۶۰۰ از ۱۰٬۰۰۰، ۸۶٪، نزدیک به سقف»). وضعیت فقط با رنگ نشان داده نمیشود."},{"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` اعلام میشود."},{"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 بیروناند (کلیدهای جهت همان کار را میکنند) و در مرز محدوده غیرفعال میشوند."},{"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- «قبلی» در صفحهٔ اول و «بعدی» در صفحهٔ آخر غیرفعالاند. فوکوس کیبورد پس از تغییر صفحه حفظ میشود."},{"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`، صفحهخوان این راهنما را همراه فیلد رمز میخواند. نوار رنگی تزئینی است و متن سطح همیشه نمایش داده میشود."},{"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> میبندد و فوکوس را به دکمه برمیگرداند. کلیک بیرون یا رفتن فوکوس به بیرون هم پنجره را میبندد."},{"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` انیمیشنها کند یا متوقف میشوند."},{"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/) استفاده کنید."},{"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> امتیاز را عوض میکند و هر ستاره نام «۳ از ۵» دارد. حالت فقطخواندنی یک تصویر با نام کامل است («امتیاز: ۴٫۵ از ۵») تا صفحهخوان ستارهها را یکییکی نخواند."},{"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` بدهید."},{"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\" | ارتفاع |"},{"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` حفظ میشود."},{"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` درخشش متوقف میشود."},{"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` مقدار قالببندیشده را میخواند («۲٬۰۰۰٬۰۰۰ تومان») و در بازه، هر دستگیره نام جدا دارد: «حداقل قیمت» و «حداکثر قیمت»."},{"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> به کمینه و بیشینه میرود. دوبار کلیک اندازهٔ اولیه را برمیگرداند."},{"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جهت روند فقط با رنگ و فلش نشان داده نمیشود: صفحهخوان متن کامل را میخواند («۱۲٫۴٪ افزایش»). رنگ روند بر اساس خوب یا بد بودن است، نه جهت."},{"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\"` دارد و صفحهخوان وضعیت هر مرحله را میخواند («ارسال، مرحلهٔ فعلی»، «سبد خرید، انجام شده»). وضعیت فقط با رنگ نمایش داده نمیشود: مراحل انجامشده تیک دارند."},{"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- مسیر خاموش کنتراست حداقل ۳ به ۱ با پسزمینه دارد."},{"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) قابلاسکرول است."},{"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- برای ناوبری بین صفحهها از زبانه استفاده نکنید؛ لینکها مناسبترند."},{"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` اعلام میشوند و پیام خطا به فیلد وصل است."},{"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>` با تاریخ دقیق است."},{"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- اطلاعات حیاتی را فقط در توست نگذارید؛ ممکن است کاربر آن را نبیند."},{"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` لازم دارد. اطلاعات ضروری را در آن پنهان نکنید؛ کاربران لمسی آن را نمیبینند."},{"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> میبندد، ←→ بین مرحلهها حرکت میکند و پس از پایان فوکوس به جای قبلی برمیگردد."},{"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). زیرشاخههای بسته رندر نمیشوند."},{"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 تعریف شده و هر فریمورک لایهٔ نازکی روی آن است.\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\n## وضعیت پروژه\n\nکاشی در نسخهٔ `0.x` است؛ یعنی API ممکن است پیش از نسخهٔ `1.0` تغییر کند. تغییرات ناسازگار همیشه در یادداشت انتشار ذکر میشوند."},{"slug":"installation","url":"https://kashi.sepantapouya.com/docs/installation/","title":"نصب","description":"کاشی را در پروژهٔ React یا هر پروژهٔ وب دیگری راهاندازی کنید.","group":"شروع","components":[],"markdown":"## React\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 سازگار است.\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 ساده، فقط پکیج CSS لازم است:\n\n```bash\nnpm i @kashi-ui/css\n```\n\nیا بدون نصب، از CDN:\n\n```html\n<link rel=\"stylesheet\" href=\"https://cdn.jsdelivr.net/npm/@kashi-ui/css/dist/kashi.css\">\n```\n\nسپس از کلاسها استفاده کنید. نمونهٔ HTML هر کامپوننت در صفحهٔ همان کامپوننت آمده است.\n\n```html\n<button class=\"k-btn k-btn--primary\">ثبتنام</button>\n```\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```"},{"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برای صفحهخوان، نقشه یک تصویر با جمع کل است و یک جدول پنهان جمع هر ماه را میگوید؛ ۳۶۵ خانه یکییکی خوانده نمیشوند. مقدار هر روز با نگهداشتن موس نمایش داده میشود."},{"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` با نام کامل است، مثل «کارت بانک ملت، مختوم به ۵۶۷۸»، تا صفحهخوان ۱۶ رقم را جدا جدا نخواند. لوگو تزئینی است. رنگها تقریبیاند و رسمی نیستند."},{"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\"` دارد که صفحهخوان آن را هر ثانیه اعلام نمیکند. نام دسترسپذیر متن کامل است («زمان باقیمانده تا ارسال دوباره: ۲ دقیقه و ۳۴ ثانیه»)، پس کاربر هر وقت به آن برسد زمان را میشنود."},{"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```"},{"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> بدون ثبت تغییر میبندد."},{"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` استاندارد است تا موتورهای جستجو و ابزارها تاریخ را بفهمند."},{"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\n\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\n\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\n\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\n\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\n\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\"` است تا صفحهخوان آن را اعلام کند و دکمهٔ آن با کیبورد در دسترس است. پس از جایگزینی، فوکوس به فیلد برمیگردد."},{"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) (مالکیت عمومی) گرفته و ساده شدهاند؛ برای کارهای نقشهبرداری دقیق یا حقوقی مناسب نیستند."},{"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` نگه میدارد. صفحهخوان به جای ارقام جداشده، مبلغ را به حروف میخواند («یک میلیون و دویست و پنجاه هزار تومان»). رنگ مثبت و منفی همیشه همراه علامت + و − است."},{"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` اعلام میشود."},{"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 | — | نام فیلد مخفی فرم |"},{"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؛ تعداد نتیجهها پس از هر تایپ اعلام میشود."},{"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` است که عنوانش نام آن است. وضعیت با آیکون، رنگ و متن عنوان بیان میشود، نه فقط با رنگ. دکمهٔ کپی کد پیگیری پس از کپی «کپی شد» را اعلام میکند."},{"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` را هم میپذیرد."},{"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```"},{"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` صادر میشوند."}]}
|
|
1
|
+
{"version":"0.22.1","generated":"2026-11-02","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>) محتوای بخشهای بسته را هم پیدا و باز میکند."},{"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- آیکون تزئینی است و از صفحهخوان پنهان است؛ عنوان باید معنا را کامل برساند."},{"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- وضعیت فقط با رنگ نمایش داده نمیشود؛ متن آن برای صفحهخوان در دسترس است."},{"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` اعلام کنید."},{"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` استفاده کنید."},{"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\"` دارد و لینک نیست. جداکنندهها تزئینیاند و در راستبهچپ آینه میشوند. «…» یک دکمهٔ واقعی با نام «نمایش مسیر کامل» است."},{"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 است و حلقهٔ فوکوس روی دکمهٔ کناری نمیرود."},{"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\">`) استفاده کنید، نه دکمه."},{"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`) قرار دهید."},{"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` اعلام میشود."},{"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 فوکوس میگیرد («آبان: ۱۸۴»)؛ در نمودار خطی، با فوکوس روی نمودار کلیدهای ←→ بین نقطهها حرکت میکنند و مقدار همهٔ سریها اعلام میشود."},{"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>` استفاده کنید."},{"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` است. حالت روشن با تیک، رنگ و حاشیه نشان داده میشود، نه فقط رنگ."},{"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 پنل را میبندد و فوکوس را به مربع رنگ برمیگرداند."},{"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` دارند."},{"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> بسته میشود و فوکوس به دکمهای که پالت را باز کرد برمیگردد."},{"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> روی عنصر فوکوسشده هم باز میشود و این راهنما به ناحیه وصل است. همهٔ عملهای منو باید از راه دیگری (دکمه یا منوی کشویی) هم در دسترس باشند."},{"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 در دسترس است."},{"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- برای تأیید عملیات مخرب، دکمهٔ «انصراف» را اول بگذارید و نتیجهٔ کار را در توضیح صریح بنویسید."},{"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» در متن فارسی برعکس نشود."},{"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/) استفاده کنید."},{"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` بدهید."},{"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` اعلام میشوند."},{"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حالت نمایش یک دکمه با نام کامل است («نام پروژه: بازطراحی فروشگاه، ویرایش»). پس از ثبت یا انصراف، فوکوس به همان دکمه برمیگردد."},{"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- پیام خطا باید بگوید چه اشتباهی رخ داده و چطور درست شود، نه فقط «نامعتبر»."},{"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` کامل («۸٬۶۰۰ از ۱۰٬۰۰۰، ۸۶٪، نزدیک به سقف»). وضعیت فقط با رنگ نشان داده نمیشود."},{"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` اعلام میشود."},{"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 بیروناند (کلیدهای جهت همان کار را میکنند) و در مرز محدوده غیرفعال میشوند."},{"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- «قبلی» در صفحهٔ اول و «بعدی» در صفحهٔ آخر غیرفعالاند. فوکوس کیبورد پس از تغییر صفحه حفظ میشود."},{"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`، صفحهخوان این راهنما را همراه فیلد رمز میخواند. نوار رنگی تزئینی است و متن سطح همیشه نمایش داده میشود."},{"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> میبندد و فوکوس را به دکمه برمیگرداند. کلیک بیرون یا رفتن فوکوس به بیرون هم پنجره را میبندد."},{"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` انیمیشنها کند یا متوقف میشوند."},{"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/) استفاده کنید."},{"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> امتیاز را عوض میکند و هر ستاره نام «۳ از ۵» دارد. حالت فقطخواندنی یک تصویر با نام کامل است («امتیاز: ۴٫۵ از ۵») تا صفحهخوان ستارهها را یکییکی نخواند."},{"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` بدهید."},{"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\" | ارتفاع |"},{"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` حفظ میشود."},{"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` درخشش متوقف میشود."},{"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` مقدار قالببندیشده را میخواند («۲٬۰۰۰٬۰۰۰ تومان») و در بازه، هر دستگیره نام جدا دارد: «حداقل قیمت» و «حداکثر قیمت»."},{"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> به کمینه و بیشینه میرود. دوبار کلیک اندازهٔ اولیه را برمیگرداند."},{"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جهت روند فقط با رنگ و فلش نشان داده نمیشود: صفحهخوان متن کامل را میخواند («۱۲٫۴٪ افزایش»). رنگ روند بر اساس خوب یا بد بودن است، نه جهت."},{"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\"` دارد و صفحهخوان وضعیت هر مرحله را میخواند («ارسال، مرحلهٔ فعلی»، «سبد خرید، انجام شده»). وضعیت فقط با رنگ نمایش داده نمیشود: مراحل انجامشده تیک دارند."},{"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- مسیر خاموش کنتراست حداقل ۳ به ۱ با پسزمینه دارد."},{"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) قابلاسکرول است."},{"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- برای ناوبری بین صفحهها از زبانه استفاده نکنید؛ لینکها مناسبترند."},{"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` اعلام میشوند و پیام خطا به فیلد وصل است."},{"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>` با تاریخ دقیق است."},{"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- اطلاعات حیاتی را فقط در توست نگذارید؛ ممکن است کاربر آن را نبیند."},{"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` لازم دارد. اطلاعات ضروری را در آن پنهان نکنید؛ کاربران لمسی آن را نمیبینند."},{"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> میبندد، ←→ بین مرحلهها حرکت میکند و پس از پایان فوکوس به جای قبلی برمیگردد."},{"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). زیرشاخههای بسته رندر نمیشوند."},{"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 تعریف شده و هر فریمورک لایهٔ نازکی روی آن است.\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\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```bash\nnpx shadcn@latest add https://kashi.sepantapouya.com/r/iran-map.json\n```\n\n```tsx\nimport { IranMap } from \"@/components/kashi/IranMap\";\n```\n\nروی هر کامپوننت بزنید تا دستور نصبش کپی شود:\n\nبرای دستور کوتاهتر، رجیستری کاشی را یک بار در `components.json` معرفی کنید:\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 ساده، فقط پکیج CSS لازم است:\n\n```bash\nnpm i @kashi-ui/css\n```\n\nیا بدون نصب، از CDN:\n\n```html\n<link rel=\"stylesheet\" href=\"https://cdn.jsdelivr.net/npm/@kashi-ui/css/dist/kashi.css\">\n```\n\nسپس از کلاسها استفاده کنید. نمونهٔ HTML هر کامپوننت در صفحهٔ همان کامپوننت آمده است.\n\n```html\n<button class=\"k-btn k-btn--primary\">ثبتنام</button>\n```\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":"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برای صفحهخوان، نقشه یک تصویر با جمع کل است و یک جدول پنهان جمع هر ماه را میگوید؛ ۳۶۵ خانه یکییکی خوانده نمیشوند. مقدار هر روز با نگهداشتن موس نمایش داده میشود."},{"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` با نام کامل است، مثل «کارت بانک ملت، مختوم به ۵۶۷۸»، تا صفحهخوان ۱۶ رقم را جدا جدا نخواند. لوگو تزئینی است. رنگها تقریبیاند و رسمی نیستند."},{"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\"` دارد که صفحهخوان آن را هر ثانیه اعلام نمیکند. نام دسترسپذیر متن کامل است («زمان باقیمانده تا ارسال دوباره: ۲ دقیقه و ۳۴ ثانیه»)، پس کاربر هر وقت به آن برسد زمان را میشنود."},{"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```"},{"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> بدون ثبت تغییر میبندد."},{"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` استاندارد است تا موتورهای جستجو و ابزارها تاریخ را بفهمند."},{"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\n\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\n\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\n\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\n\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\n\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\"` است تا صفحهخوان آن را اعلام کند و دکمهٔ آن با کیبورد در دسترس است. پس از جایگزینی، فوکوس به فیلد برمیگردد."},{"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) (مالکیت عمومی) گرفته و ساده شدهاند؛ برای کارهای نقشهبرداری دقیق یا حقوقی مناسب نیستند."},{"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` نگه میدارد. صفحهخوان به جای ارقام جداشده، مبلغ را به حروف میخواند («یک میلیون و دویست و پنجاه هزار تومان»). رنگ مثبت و منفی همیشه همراه علامت + و − است."},{"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` اعلام میشود."},{"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 | — | نام فیلد مخفی فرم |"},{"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؛ تعداد نتیجهها پس از هر تایپ اعلام میشود."},{"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` است که عنوانش نام آن است. وضعیت با آیکون، رنگ و متن عنوان بیان میشود، نه فقط با رنگ. دکمهٔ کپی کد پیگیری پس از کپی «کپی شد» را اعلام میکند."},{"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` را هم میپذیرد."},{"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```"},{"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` صادر میشوند."}]}
|