هندسهٔ صفحه
توکنهای --layout-* — ارتفاع ردیفهای ثابت، عرض منو، حاشیهٔ صفحه، ریتم عمودی و نردبان عرض محتوا
اصل
هندسهٔ صفحه در پرتو یک مجموعه توکن است، نه عددی که هر صفحه یا هر کامپوننت برای خودش بنویسد.
ارتفاع نوار بالا، عرض منو، حاشیهٔ کنار صفحه، فاصلهٔ عنوان تا اولین بلوک، فاصلهٔ بخشها و عرض ستون
محتوا همه از توکنهای --layout-* در globals.css میآیند، و اجزای چیدمان همین توکنها را
میخوانند: ProductFrame، PageContainer، PageHeader، PageHeaderRoot، PageSection، PageToolbar،
FilterBar، NavRail، AppSecondary، SiteHeader و ردیفهای PageBreadcrumbs و PageNav.
نتیجه این است که کد صفحه عدد چیدمان نمینویسد. صفحه اجزای صفحه را به کار میبرد و هندسه را از آنها میگیرد. اگر روزی یکی از این اعداد عوض شود، در یک جا عوض میشود و همهٔ محصولات با هم تغییر میکنند.
مقادیر همان هندسهٔ Supabase Studio هستند: ارتفاع 48 برای ردیفهای ثابت، نردبان عرض 768 / 1200 / 1600، حاشیهٔ 16 / 24 / 40 و ریتم 48 / 24. برای راستبهچپ آینه شدهاند: منو در سمت شروع خط (راست) قرار میگیرد.
| توکن | مقدار | کاربرد | کلاس نامدار |
|---|---|---|---|
--layout-header-height | 3rem (48) | نوار بالای قاب، سرِ منو، ردیفهای PageBreadcrumbs و PageNav | h-layout-header-height |
--layout-rail-width | 3rem (48) | نوار آیکونی جمعشده | w-layout-rail-width |
--layout-menu-width | 16rem (256) | منوی برچسبدار ثابت و پنل ثانویه | w-layout-menu-width |
--layout-page-inset | 1rem ← 1.5rem ← 2.5rem | حاشیهٔ کنار صفحه: 16، از sm 24، از xl 40 | px-layout-page-inset |
--layout-page-top | 3rem (48) | فاصلهٔ بالای عنوان صفحه | pt-layout-page-top |
--layout-section-gap | 3rem (48) | سرِ صفحه تا اولین بلوک، و بخش تا بخش | gap-layout-section-gap |
--layout-block-gap | 1.5rem (24) | بلوک تا بلوک داخل یک بخش | gap-layout-block-gap |
--layout-toolbar-gap | 1rem (16) | نوارابزار تا محتوایی که کنترلش میکند | gap-layout-toolbar-gap |
--layout-control-gap | 0.5rem (8) | بین کنترلهای یک ردیف | gap-layout-control-gap |
--layout-search-width | 16rem (256) | عرض جستوجوی نوارابزار | w-layout-search-width |
--layout-search-width-narrow | 12rem (192) | جستوجوی باریک (searchWidth="narrow") | w-layout-search-width-narrow |
--layout-search-width-wide | 24rem (384) | جستوجوی پهن (searchWidth="wide") | w-layout-search-width-wide |
--layout-tile-min-width | 16rem (256) | کمترین عرض ستون شبکهٔ کاشی: ردیف شاخص داشبورد، شبکهٔ کارتها | فرمول ستونهای شبکه (زیر جدول) |
--layout-state-min-height | 15rem (240) | کمترین ارتفاع بلوک در حالت خطا و خالی (PageState) | min-h-layout-state-min-height |
--layout-chart-min-width | 28rem (448) | کمترین عرض ستون شبکهٔ نمودارهای داشبورد (DashboardSection) | فرمول ستونهای شبکه (زیر جدول) |
--layout-chart-height | 18rem (288) | ارتفاع بدنهٔ کارت نمودار داشبورد (DashboardChart)، با حاشیهٔ داخلی | h-layout-chart-height |
--layout-aside-width | 20rem (320) | ستون کناری: DetailPage aside، کنار فید (ListPage aside)، پنل کناری PagePane | w-layout-aside-width |
--layout-auth-width | 25rem (400) | کارت صفحهٔ ورود (AuthPage) | w-layout-auth-width |
--layout-feed-width | 42.5rem (680) | ستون فید: ردیف و کارت EntityCollection و Post | max-w-layout-feed-width |
--layout-content-narrow | 48rem (768) | فرم، تنظیمات، جریان چندمرحلهای، صفحههای کمکی | max-w-layout-content-narrow |
--layout-content-default | 75rem (1200) | فهرست جدولی و جزئیات | max-w-layout-content-default |
--layout-content-wide | 100rem (1600) | داشبورد، تحلیل، شبکهٔ کارتها | max-w-layout-content-wide |
--layout-content-feed | 42.5rem (680) | اندازهٔ خواندن فید تکستونی (پست، نظر، گزارش پخش)؛ ستون فید هرگز پهنتر نمیشود | max-w-layout-content-feed |
--layout-tile-min-width و --layout-chart-min-width عرض کمینهٔ ستوناند، نه عرض خود کاشی: کاشی با min-w-… از ظرفی باریکتر از خودش بیرون میزند. شبکه آنها را
در فرمول ستونها میخواند و هیچ ستونی پهنتر از ردیف نمیشود:
grid-cols-[repeat(auto-fill,minmax(min(var(--layout-tile-min-width),100%),1fr))] (شبکهٔ کارت؛ ردیف شاخص و شبکهٔ
نمودار با auto-fit). قالبهای صفحه همین را خودشان دارند.
عددها پیکسل CSS هستند. --layout-page-inset تنها توکن واکنشگراست: مقدارش در نقطههای شکست sm
(40rem) و xl (80rem) روی :root عوض میشود. در کد محصول حاشیه را PageContainer میدهد، و جایی که
ظرف خودتان را میسازید px-layout-page-inset؛ کلاس page-inset جزء داخلی خود اجزای صفحه است.
نام هر کلاس از نام توکنش ساخته میشود: پیشوند ویژگی در Tailwind + نام توکن بدون --
(--layout-section-gap ← gap-layout-section-gap). هر توکن یک کلاس دارد و کلاس دیگری در کار نیست، پس
نامی را حدس نزنید؛ نامی که در این جدول نیست هیچ قاعدهٔ CSS نمیسازد و خطایی هم نمیدهد. هر توکن را فقط با
پیشوندی که ستون آخر نشان میدهد به کار ببرید: ارتفاعها با h- (یا min-h-)، عرضها با w-، فاصلهها با
gap-، حاشیه با px-، بالای عنوان با pt-، و سه عرض محتوا فقط با max-w-. ترکیب دیگری مثل
gap-layout-header-height هم کامپایل میشود ولی معنایی ندارد. در CSS خودتان همان توکن را با
var(--layout-…) بخوانید.
ارتفاع نوارابزار از این توکنها نمیآید: ردیف PageToolbar به بلندی کنترلهایش است (34، اندازهٔ sm در
اندازه و چگالی)، نه 48.
نمونه بصری
هر نوار و هر فاصلهٔ این شکل با خود توکن اندازه گرفته شده است، و جدول زیرش مقدار هر توکن را از
همین صفحه میخواند. پس این شکل نمیتواند از globals.css جدا شود.
| توکن | کاربرد | مقدار در همین صفحه |
|---|---|---|
| --layout-header-height | ردیفهای ثابت (قاب، منو، ناوبری صفحه) | … |
| --layout-rail-width | نوار آیکونی | … |
| --layout-menu-width | منوی برچسبدار | … |
| --layout-page-inset | حاشیهٔ صفحه | … |
| --layout-page-top | بالای عنوان | … |
| --layout-section-gap | بین بخشها | … |
| --layout-block-gap | بین بلوکها | … |
| --layout-toolbar-gap | نوارابزار تا محتوا | … |
| --layout-control-gap | بین کنترلها | … |
| --layout-content-narrow | عرض باریک | … |
| --layout-content-default | عرض پیشفرض | … |
| --layout-content-wide | عرض پهن | … |
قوانین اجباری
کد صفحه عدد چیدمان نمینویسد
عرض، حاشیه و فاصلهٔ عمودی صفحه از قالب صفحه میآید. نمونهٔ زیر صفحهای داخل
ProductFrame است، جایی که هر صفحهٔ محصول رندر میشود: قالب عنوان را
--layout-page-top زیر نوار بالای قاب میگذارد و بخشها را --layout-section-gap از هم. قالبها روی
PageContainer، PageHeader و PageSection ساخته شدهاند؛ همان اجزا فقط داخل محتوای یک CustomPage مستقیم به کار
میروند.
// ✅ درست — عرض، حاشیه و ریتم از قالب صفحه
<DetailPage title="گزارش هفتگی" back={{ href: '/reports', label: 'گزارشها' }}>
<DetailSection title="تحلیل">
<Card>تحلیل گفتوگوهای کمپین</Card>
</DetailSection>
</DetailPage>
// ❌ غلط — عرض، حاشیه و فاصله با عدد؛ هر صفحه جواب دیگری میدهد
<div className="mx-auto max-w-[1180px] space-y-5 p-6">
<PageHeader title="گزارشها" />
<Card>تحلیل گفتوگوهای کمپین</Card>
</div>گزارش هفتگی
گزارش هفتگی
عرض از نوع صفحه میآید
| نوع صفحه | عرض | قالب (نام عرض) | در PageContainer |
|---|---|---|---|
| فرم، تنظیمات، صفحههای کمکی (404، 403، اولین استفاده) | 768 | FormPage، SettingsPage، UtilityPage | size="small" |
| فهرست (جدول تا 8 ستون، فید ردیفی یا کارتی، کارتها) و جزئیات | 1200 | ListPage، DetailPage | size="default" |
| داشبورد، تحلیل، جدول بیش از 8 ستون، فیدی که کاشی نشان میدهد | 1600 | DashboardPage؛ ListPage width="wide" | size="large" |
| لاگ و جدولی که افقی پیمایش میشود | تمامعرض | ListPage width="full" | size="full" |
هشدار تازه
هشدار تازه
ردیف ثابت سفارشی با همان ارتفاع
اگر ردیف ثابتی میسازید که باید با نوار بالا و ردیفهای ناوبری همتراز باشد، ارتفاعش توکن است.
// ✅ درست
<div className="flex h-layout-header-height items-center border-t border-default">…</div>
// ❌ غلط — 52 پیکسل، کنار ردیفهای 48 پیکسلی
<div className="flex h-[52px] items-center border-t border-default">…</div>موارد استفاده رایج
- ریتم عمودی یک صفحه، از بالا به پایین: نوار بالای قاب
--layout-header-height(48) · بالای عنوان--layout-page-top(48) · سرِ صفحه تا اولین بلوک و بخش تا بخش--layout-section-gap(48) · بلوک تا بلوک--layout-block-gap(24) · نوارابزار تا جدول--layout-toolbar-gap(16) · بین کنترلهای یک ردیف--layout-control-gap(8). - صفحهٔ فهرست کوتاهتر است: نوارابزار فهرست بخشی از سرِ صفحه است، پس
--layout-block-gap(24) زیر سرِ صفحه مینشیند نه 48؛ توضیح صفحه یک خط است؛ و نوارابزار و سرِ جدول هنگام پیمایش بالای ناحیهٔ محتوا میچسبند. اندازهگیریشده در 1440 (عنوان، یک خط توضیح، نوارابزار): از زیر نوار قاب تا ردیف سرِ جدول 205 ← 181 پیکسل. - عرض از محتوا میآید: جدول با نام (
default1200،wide1600)، فید تکستونی در اندازهٔ خواندن (--layout-content-feed، 680 — ستون خبرخوان فیسبوک؛ X 600 متنمحور است و اینستاگرام 470 رسانهمحور؛ با حاشیه و ستون آواتار کارت، متن فارسی در 14 پیکسل حدود 600 پیکسل میشود، 12 تا 14 واژه در هر خط)، کنار آن ستون کناری دستکم--layout-aside-widthکه باقی صفحهٔ 1200 را میگیرد، و شبکهٔ کارت پهن (1600). نوارابزار و سرِ جدول از 48rem محتوا میچسبند؛ پوستهٔ برنامهای با سرِ ثابت خودش (بیرون ازProductFrame)--page-sticky-offsetرا به ارتفاع آن میدهد. - CSS خودتان: مقدار را با
var(--layout-header-height)بخوانید، نه با عدد. - رابطه با مقیاس فضابندی: مقیاس 4 پیکسلی برای فاصلههای داخل کامپوننتها و بلوکهاست؛ فاصلهها و اندازههای بین قاب، سرِ صفحه، بخشها و بلوکها فقط از این توکنها میآید. ارتفاع کنترلها موضوع جداگانهای است: اندازه و چگالی.
چه نکنیم
نکنید
- عرض و فاصلهٔ صفحه را با عدد ننویسید:
max-w-[1180px]،p-6روی ریشهٔ صفحه،space-y-4یاmt-6بین بخشها. - به ناحیهٔ محتوای
ProductFramepadding یا سقف عرض اضافه نکنید؛ حاشیه و عرض را قالب صفحه یک بار میدهد. - توکنها را در یک صفحه یا یک محصول بازتعریف نکنید. تغییر این اعداد تصمیم سیستم طراحی است و در
globals.cssانجام میشود؛ مقادیر با آزمونsrc/test/layout-tokens.test.tsثابت نگه داشته میشوند. - کلاسهای نامدار (
gap-layout-section-gapو …) را جای اجزای صفحه به کار نبرید. آنها برای ردیفها و قابهای سفارشیای هستند که باید با هندسهٔ سیستم طراحی همتراز شوند، نه برای بازسازیPageSectionدر هر صفحه.