مقدمه
این راهنما مسیرِ یک نسخه را از تثبیتِ شماره تا رسیدن به دستِ کاربران مرور میکند. هر بخش یک ایستگاهِ واقعی است؛ اگر جایی گیر کردید، بخشِ بازگشت به نسخهی قبل همیشه در دسترس است.
آمادهسازیِ نسخه
پیش از هر انتشار، شاخهٔ اصلی باید سبز باشد: آزمونها، بازبینیِ نوعها و بیلدِ ایستا. اگر یکی از این سه قرمز است، انتشار عقب میافتد — نه اینکه با عجله جلو برود.
شمارهٔ نسخه
نسخهبندیِ معنایی: رقمِ نخست برای تغییرِ ناسازگار، دوم برای قابلیتِ تازه و سوم برای اصلاح. عدد را از لاگِ تغییرات بگیرید، نه از حافظه.
یادداشتِ تغییرات
برای هر تغییرِ دیدنی یک خط بنویسید؛ کارهای داخلی لازم نیست. لحن از دیدِ کاربر: چه چیزی برایش بهتر شد.
آزمونِ پیش از انتشار
روی بیلدِ ساختهشده آزمون کنید، نه روی محیطِ توسعه. مسیرهای اصلی را یک بار با شبکهٔ کند و صفحهٔ باریک هم ببینید؛ بعضی خطاها فقط همانجا بیرون میزنند.
انتشار روی محیطِ آزمایشی
همان بیلدِ نهایی، با دادهی نمونه. یک روز آنجا بماند تا رفتارِ واقعی — سرعت، خطاها، بازخوردِ کاربرانِ داخلی — دیده شود.
انتشار برای همه
انتشارِ تدریجی: اول بخشِ کوچکی از کاربران. اگر نرخِ خطا یک پله بالا رفت، همان لحظه متوقف کنید؛ ادامهدادن با امیدِ «خودش درست میشود» گرانترین گزینه است.
بازگشت به نسخهٔ قبل
هر انتشار باید برگشتپذیر باشد: بیلدِ قبلی نگه داشته میشود و بازگشت یک دستور است، نه یک پروژه. اگر بازگشت بیش از یک دقیقه طول میکشد، این بخش را بازنویسی کنید.
کاستومایز
فهرست پروازیRail TOC
فهرستِ مطالبِ کنارِ صفحه با یک ریلِ نقطهچین و یک هواپیمای کاغذی که با اسکرول رویش سفر میکند: نرم جلو میرود، روی زیرسرفصلها میپیچد و اگر برگردید میچرخد و برمیگردد.
وابستگیها
نوع تعامل
با اسکرولِ صفحه (یا ظرفی که با `containerRef` میدهید) هواپیما روی ریل جلو میرود و بخشِ پیمودهشده پر میشود؛ هر سرفصلی که به خطِ دید برسد، ردیفش فعال و پررنگ میشود و `aria-current` هم میگیرد. روی هر ردیف بزنید تا صفحه تا همان سرفصل روان اسکرول شود؛ تا اولین اسکرولِ شما هواپیما روی همان ردیف میماند، چون سرفصلهای نزدیکِ پایان تا لنگرِ بالا نمیآیند. اگر به سمتِ بالا برگردید، هواپیما سرِ ریل میچرخد و روبهبالا برمیگردد — و با `prefers-reduced-motion` سفر و چرخش حذف میشود و هواپیما بیحرکت جابهجا میشود.
پراپها
گزینههایی که میتونی به کامپوننت بدی؛ ستارهدارها اجباریاند.
items*RailTocItem[]پیشفرض: [{ id: "intro", label: "مقدمه" }, { id: "install", label: "نصب", depth: 1 }]سرفصلهای صفحه. هر آیتم `{ id, label, depth }` است: `id` باید عیناً شناسهٔ همان سرفصل در DOM باشد، و `depth` (۰ به بعد) تورفتگیِ زیرسرفصل را میسازد.
containerRefReact.RefObject<HTMLElement | null>ظرفِ اسکرولِ محتوا. ندهید تا ارتفاع و اسکرول از خودِ پنجره خوانده شود؛ در قابِ اسکرولشونده (مثلِ پنلِ مستندات) همین را بدهید و سرفصلها باید داخلِ همان ظرف باشند.
offsetnumberپیشفرض: 96فاصلهٔ «خطِ دید» از بالای ظرف، پیکسل. یعنی سرفصلی که به این ارتفاع میرسد فعال میشود؛ مقدارِ کمتر یعنی زودتر فعال شدن.
indentnumberپیشفرض: 14تورفتگیِ هر پله عمق، پیکسل. صفر یعنی سلسلهمراتبِ تخت و ریلِ بیپیچ.
planeColorstringپیشفرض: currentColorرنگِ هواپیما — همان چیزی که روی ریل بالا و پایین میرود. هر مقدارِ CSS قبول است؛ ندهید تا رنگِ متنِ خودش را بگیرد (رفتارِ مرجع).
titlestringپیشفرض: "در این صفحه"عنوانِ بالای فهرست. همزمان `aria-label` ناوبری هم میشود.
نصب
۱. این آدرس رو یکبار توی components.json بذار تا shadcn مخزن فارسیفای رو بشناسه.
{
"registries": {
"@farsify": "https://farsify.ir/r/{name}.json"
}
}۲. بعد با دستورِ زیر کامپوننت رو نصب کن.
npx shadcn@latest add @farsify/rail-tocپیشنیاز: Tailwind CSS v4 و صفحهٔ راستبهچپ dir="rtl".
نمونه
"use client";
import { useRef } from "react";
import { RailToc } from "@/components/farsify/rail-toc";
const SECTIONS = [
{ id: "intro", label: "مقدمه" },
{ id: "install", label: "نصب" },
{ id: "config", label: "پیکربندی" },
{ id: "theme", label: "رنگ و تم", depth: 1 },
{ id: "faq", label: "پرسشهای پرتکرار" },
];
export function ArticleToc() {
return <RailToc items={SECTIONS} />;
}
// خطِ دید پایینتر، زیرسرفصلهای عمیقتر و عنوانِ دلخواه
<RailToc items={SECTIONS} offset={140} indent={22} title="فهرستِ این راهنما" />
// رنگِ هواپیما — تنها رنگِ تزئینیِ کامپوننت
<RailToc items={SECTIONS} planeColor="#0a84ff" />
// داخلِ پنلی که خودش اسکرول میشود — ظرف را بدهید تا اندازه از همان خوانده شود
const scrollerRef = useRef<HTMLDivElement>(null);
<RailToc items={SECTIONS} containerRef={scrollerRef} />نکتهها
- ریل در راستبهچپ کنارِ لبهٔ راست (سمتِ شروع) مینشیند و تورفتگی به سمتِ چپ میرود؛ آینهسازی روی یک لایهٔ تزئینیِ `-scale-x-100` انجام میشود، پس هیچ مختصاتی را دستی نگردانید و خودِ فهرست هم با `dir` هر دو جهت کار میکند.
- `id` هر آیتم باید همان شناسهٔ سرفصلِ واقعی در صفحه باشد؛ آیتمی که عنصرش پیدا نشود در محاسبهٔ پیشرفت نادیده گرفته میشود و هواپیما رویش نمیایستد.
- برای صفحهای که خودش اسکرول میشود (نه پنجره) `containerRef` را بدهید و مطمئن شوید ظرف `position: relative` یا هر لنگرِ هندسیِ دیگری که لازم دارید را دارد؛ اندازهگیری با `offsetTop`ِ ردیفهای فهرست انجام میشود و به اسکرولِ ظرف کاری ندارد.
- پهنای ستون را خودِ کامپوننت نمیسازد؛ با `className="w-56"` یا هر عرضی که به متنِ فارسیتان جا بدهد ببندید تا برچسبهای بلند سالم بشکنند.
- با `prefers-reduced-motion` سفرِ فنری و چرخش حذف میشوند؛ فهرست، پر شدنِ ریل و `aria-current` سرِ جایشان میمانند.
- `planeColor` تنها رنگِ کامپوننت است و روی خودِ هواپیما مینشیند، نه ریل؛ ریل از رنگِ متنِ تم ساخته میشود تا همنشینیِ دو رنگ در یک ستون شلوغ نشود. برای هواپیمای برند، همان `var(--brand)`ِ پروژهتان را بدهید.
مجوز
کد بعد از نصب مالِ خودته؛ هر تغییری خواستی بده و توی پروژهی خودت نگهش دار. برای پروژههای شخصی و تجاری آزاده، حتی اگه کدت بسته باشه. فقط اسم و لینک فارسیفای رو یه جا توی پروژه نگه دار.