اندازه و چگالی

یک نردبان برای همهٔ کنترل‌ها، دو پیش‌فرض، جدول 40/44، کف لمسی — و دلیل هر عدد

اصل

چگالی را سیستم طراحی تعیین می‌کند، نه هر صفحه. همهٔ عددهای این صفحه تصمیم‌اند، نه بازه: صفحه‌ای که فقط با پیش‌فرض‌ها ساخته شود متوازن است و هیچ صفحه‌ای نباید عدد تازه بنویسد.

جهت: ساختار سوپابیس، یک پله راحت‌تر برای فارسی. دلیلش اندازه‌گیری فونت است، نه سلیقه:

  • جعبهٔ محتوای یکان‌بخ 1.55em است (ascent 1000 و descent 550 از 1000)، در برابر 1.21em در Inter فونت سوپابیس. پس با همان عددهای سوپابیس، متن فارسی در جعبه‌ای حدود 30٪ تنگ‌تر می‌نشیند.
  • متن 12 پیکسلی فارسی مثل 11 پیکسل Inter خوانده می‌شود و متن 14 پیکسلی یکان‌بخ هم‌اندازهٔ دیداری 13 پیکسل Inter است.

برای همین هر عدد سوپابیس یک پله بالا رفته است: دکمهٔ 26 با متن 12 ← 30 با 13؛ فیلد 34 با متن 13 ← 38 با 14؛ جدول 40 / حدود 52 با متن 13 ← 40 / 44 با 14. نوارهای ثابت (48) و ریتم صفحه (48 / 24 / 16 / 8) همان سوپابیس است؛ آرامش سوپابیس از همین‌جاست (هندسهٔ صفحه).


نمونه بصری

اندازهدکمهورودی
xs
…
…
sm
…
پیش‌فرض
…
md
…
…
پیش‌فرض
lg
…
…
xl
…
…

قدها از lib/constants.ts می‌آیند و عددِ کنار هر کنترل، ارتفاعِ اندازه‌گیری‌شده از خودِ DOM است — نه عددی که جدولی ادعا کند.


قوانین اجباری

یک نردبان برای همهٔ کنترل‌ها

پلهارتفاعمتنکاربرد
xs26 پیکسل12ردیف‌های خیلی فشرده داخل کارت یا پنل
sm30 پیکسل13همهٔ اقدام‌ها؛ نوار ابزار و ردیف فیلتر
md38 پیکسل14فیلدهای فرم
lg42 پیکسل14صفحه‌های کم‌تراکم (ورود)
xl50 پیکسل16صفحه‌های معرفی

قاب بیرونی هر کنترل دقیقاً همین عدد است، دکمه، فیلد، انتخابگر و کنترل قطعه‌ای (PeriodSelector) یکسان. متن هر پله وسط کنترل است: فاصلهٔ بالا و پایین متن برابر است، و با جعبهٔ خطِ وسط‌چین جوهر حروف فارسی یکان‌بخ در مرکز کنترل می‌نشیند (در Chrome با فونت یکان‌بخ روی گلیف‌های رندرشده اندازه‌گیری شده است؛ حداکثر 0.75 پیکسل فاصله از مرکز). فقط حروف بزرگ لاتین و ارقام یک تا دو پیکسل بالاتر می‌نشینند؛ خط اصلی فارسی است. همهٔ کنترل‌های یک ردیف، دکمه و فیلد و کنترل قطعه‌ای، برچسبشان را روی یک خط کرسی می‌گذارند.

درست — هر دو روی پلهٔ sm (30 پیکسل)
نادرست — ارتفاع دلخواه 34؛ بیرون از نردبان

دو پیش‌فرض: دکمه sm، فیلد md

  • دکمه (و کنترل‌های دکمه‌مانند: Toggle، ToggleGroup، PeriodSelector، ViewToggle، دکمه‌های نوار ابزار جدول) بدون size، 30 پیکسل است. دکمهٔ بدون variant هم خنثی (default) است؛ دکمهٔ اصلی همیشه صریح است و در هر نما فقط یکی (دکمه).
  • فیلد فرم (Input، Textarea، SelectTrigger، NativeSelect، SearchInput، MultiSelect، Autocomplete، TagInput، InputOTP و انتخابگرهای تاریخ) بدون size، 38 پیکسل است: فرم فضای خواندن و نوشتن دارد.
درست — فیلد md (38) برای نوشتن، دکمه sm (30)
نادرست — فیلد فشرده و دکمهٔ غول‌پیکر؛ فرم فضای نوشتن ندارد

هر ردیف، یک ارتفاع

ردیفی که فیلد و دکمه را کنار هم دارد باید یک اندازه بگیرد، و آن اندازه را یک جا تعیین کنید:

  • نوار ابزار صفحهٔ فهرست (آنچه ListPage از search و filters می‌سازد؛ درون یک CustomPage خودِ PageToolbar) همهٔ کنترل‌هایش را sm می‌کند، فیلدها را هم؛
  • FilterBar هم بستر فیلتر است: کنترل‌های داخلش sm می‌شوند، مگر Provider بیرونی اندازهٔ دیگری بدهد؛
  • ردیف‌های اقدامِ خود سیستم طراحی هم خودشان sm می‌کنند: سرِ کارت نمودار (ChartCardHeader)، actions و primaryAction سربرگ صفحه (PageHeader، PageHeaderAside)، actions در FormHeader و نوار بالای SiteHeader؛
  • هر ردیف دیگر (ردیف فیلتر یک پنل، ردیف ابزار خودتان) داخل ControlSizeProvider با controlWidth="intrinsic".
// ✅ یک اندازه برای کل ردیف، در یک جا
<ControlSizeProvider size="sm" controlWidth="intrinsic">
  <Input aria-label="نام" />
  <PeriodSelector value={period} onValueChange={setPeriod} />
  <Button>اعمال</Button>
</ControlSizeProvider>

// ❌ فیلد 38 کنار دکمهٔ 30: ردیف دو ارتفاع دارد
<div className="flex items-center gap-2">
  <Input aria-label="نام" />
  <Button>اعمال</Button>
</div>
درست — یک ControlSizeProvider؛ ردیف یک ارتفاع
نادرست — فیلد 38 کنار دکمهٔ 30

دکمهٔ فقط‌آیکون، مربعی روی همان نردبان

دکمهٔ آیکون‌دار بدون متن (icon و aria-label) مربعی به ارتفاع پلهٔ خودش است: 26، 30، 38 و … پس کنار دکمهٔ متنی هم‌قد است. اندازه‌های قدیمی icon (36)، icon-sm (28)، icon-xs (24) و icon-lg (40) هیچ‌وقت با دکمهٔ متنی هم‌قد نمی‌شدند؛ در 5.0 حذف شدند (npx --no parto-migrate-v5 هر کدام را به پله‌ای می‌برد که در 4.x می‌داد: icon ← بدون size یعنی sm، icon-sm و icon-xs ← xs، icon-lg ← md). زنگوله‌های نوار بالا (NotificationCenter، WhatsNewBell)، ThemeToggle و UserMenu هم روی همین نردبان‌اند: یک نوار بالا، یک ارتفاع.

درست — دکمهٔ آیکونی مربع 30، هم‌قد دکمهٔ متنی
نادرست — دکمهٔ آیکونی md (38) کنار دکمهٔ 30

جدول: 40 / 44 / 14، و compact

تراکمسرِ جدولردیف (حداقل)متنسرِ ستون
default40441413 / 500
compact36361312 / 500

ارتفاع ردیف حداقل است: سلولی که به خط بعد می‌رود ردیف را بلند می‌کند و بریده نمی‌شود. compact فقط برای جدول‌های پرتراکم منشن (جدول). یک ردیف، یک ارتفاع: ردیف default کنترل‌های sm و آواتار تا 32 پیکسل را جا می‌دهد و ردیف compact کنترل‌های xs و نشان‌های 24 پیکسلی را (فاصلهٔ بالا و پایین سلول 4 پیکسل است). تراکم مال خود جدول است؛ صفحه یا کارتِ بالادست آن را عوض نمی‌کند.

حسابمنشن
فروشگاه آفتاب1,240
کافه نارنج860
نشر پرتو415
درست — تراکم مال خود جدول: ردیف 44، متن 14
حسابمنشن
فروشگاه آفتاب1,240
کافه نارنج860
نشر پرتو415
نادرست — ارتفاع ردیف دستی؛ فشرده و ناخوانا

لمس: حداقل 38 با ناحیهٔ 44

روی اشاره‌گر درشت (هر گوشی و تبلت، حتی تبلتی که عرض دسکتاپ دارد) یا زیر data-touch هر کنترل روی نردبان دست‌کم 38 پیکسل است و دکمه‌ها، انتخابگرها و کلیدها ناحیهٔ لمس 44 پیکسلی هم دارند؛ فیلدهای متنی فقط ارتفاع را می‌گیرند. کنترل قطعه‌ای کلاً 38 پیکسل می‌شود (گزینه‌هایش 32). این یک حداقل است، پس sm نوار ابزار یا ردیف فرم آن را پایین نگه نمی‌دارد و کل ردیف با هم بالا می‌رود. روی دسکتاپ همان 30 پیکسل می‌ماند. برای لمس، اندازه را روی تک‌تک کنترل‌ها ننویسید.

  • data-touch را روی <html> بگذارید (مینی‌اپ پیام‌رسان). روی عنصر دربرگیرندهٔ یک بخش فقط همان بخش را می‌گیرد: Dialog، Sheet، Select و منوها در <body> باز می‌شوند و بیرون آن می‌مانند. data-touch="false" (همان چیزی که React برای data-touch={false} می‌نویسد) ناحیهٔ لمسی نیست.
  • خاموش کردن: data-touch="off" روی یک بخش (یا روی <html>) کف لمسی را در همان بخش خاموش می‌کند، روی اشاره‌گر درشت هم. فقط برای صفحهٔ پرتراکمی که واقعاً دسکتاپی است؛ هیچ size و هیچ کلاس ارتفاعی کنترل را زیر کف نگه نمی‌دارد.
  • بیرون از کف (با اندازهٔ خودشان): Checkbox، RadioGroupItem، Switch، دکمه‌های منوی نوار کناری، Item و Card روی پیوند، TabsTrigger، گزینه‌های منو و InputOTP. دکمهٔ داخل یک کنترل دیگر (دکمهٔ نمایش Input kind="password"، دکمهٔ حذف تراشهٔ MultiSelect و TagInput، دکمهٔ آدورنمنت Input) نه ارتفاع 38 را می‌گیرد نه ناحیهٔ 44 را؛ تابع ظرف خود است و ظرف بالا می‌رود.
  • ردیف جدول با کنترل درونش بلند می‌شود: ردیف compact با دکمهٔ xs از 36 به 46 و ردیف default با دکمهٔ sm از 44 به 46 می‌رسد (کنترل 38 به‌اضافهٔ 4 پیکسل بالا و پایین سلول).
  • دکمه‌های چسبیده (ButtonGroup، و شماره‌های صفحه‌بندی با فاصلهٔ 4 پیکسل) لبهٔ ناحیهٔ لمس را با هم شریک‌اند و کنترل بعدی هم‌پوشانی را می‌گیرد.
  • کنترلی که ::before خودش را دارد (کلاس before: یا hit-area-*) آن را نگه می‌دارد و فقط ارتفاع 38 را می‌گیرد.
  • هر کنترل نردبان نشانگر data-ladder را دارد (button یا field) و کف لمسی همین نشانگر را می‌خواند، نه data-slot را که یک Trigger با asChild عوض می‌کند (فقط دکمه‌های پای AlertDialog با data-slot خودشان شناخته می‌شوند). آن را روی عنصرهای خودتان نگذارید؛ کنترل خودتان را از Button و Input بسازید.
درست — data-touch یک بار؛ همهٔ کنترل‌ها دست‌کم 38 با ناحیهٔ لمس 44
نادرست — دکمهٔ 26 پیکسلی در مینی‌اپ لمسی؛ انگشت از هدف بزرگ‌تر است

یک کارت، یک قرص، یک نوار

خانواده‌هایی که شبیه هم دیده می‌شدند ولی جدا کشیده می‌شدند، حالا از یک مقیاس می‌خوانند:

خانوادهقاعده
هر جزء کارت‌مانند (Card، ChartCard، MetricCard، کارت‌های دامنه)یک قاب: rounded-lg، حاشیهٔ دیدنی، زمینهٔ surface-100 و سایهٔ کوچک؛ یک نقش عنوان (text-subheading، 14 / 600)؛ یک نقش عدد اصلی (.text-stat، 24 / 400) در همهٔ اندازه‌ها
نشان‌ها و چیپ‌ها (Badge، SeverityBadge، SentimentBadge، EmotionBadge، FlowBadge، ActionTypeChip، LabelChip)یک قرص برای هر اندازه: xs 20، sm 24، md 28، lg 32، xl 36 پیکسل ارتفاع، با فاصلهٔ داخلی 6 تا 14 پیکسل
نوارهای پیشرفت و سنجه (Progress، QuotaProgressBar، BotDetectionMeter، نوار EngagementRate)یک ارتفاع برای هر اندازه: xs 4، sm 6، md 8، lg 12 پیکسل

موارد استفاده رایج

  • صفحهٔ فهرست: ListPage (نوار ابزارش همه sm)، جدول default، اقدام اصلی primaryAction قالب.
  • فرم و تنظیمات: فیلدها md (پیش‌فرض)، دکمه‌های پای فرم sm (پیش‌فرض)، variant="primary" روی تأیید (در FormPage و SettingsSection قالب دکمهٔ ذخیره را خودش می‌گذارد).
  • داشبورد: سرِ کارت نمودار (ChartCardHeader) خودش sm است، بی size روی کنترل‌ها؛ عدد شاخص با نقش .text-stat (تایپوگرافی).
  • جدول پرتراکم منشن: size="compact".
  • جدول پژوهشی 10 تا 12 ستونی: تراکم سومی (حدود 32 / 32 / 12) هنوز در سیستم طراحی نیست. تا تصمیم آن compact را به کار ببرید و نیاز را به مسئول سیستم طراحی بگویید.

اندازهٔ آیکون

آیکونِ داخل دکمه با پلهٔ دکمه بزرگ می‌شود: 14 · 16 · 18 · 20 · 24 پیکسل برای xs تا xl. توکن‌های آیکون برای جاهای دیگر:

توکنمقدار
--icon-xs12px
--icon-sm16px
--icon-md18px
--icon-lg24px
--icon-xl32px

چه نکنیم

  • ارتفاع دستی. h-[34px] یا h-9 روی یک کنترل یعنی آن کنترل دیگر با هیچ کنترلی هم‌قد نیست؛ 34 دیگر پله نیست.
  • size روی تک‌تک کنترل‌های یک ردیف. ردیف را یک جا اندازه بدهید (PageToolbar، FilterBar، ControlSizeProvider).
  • دکمهٔ آیکونی با آیکون در children. آیکون را با icon بدهید تا مربع روی نردبان شود.
  • جدول sm/md/lg. منسوخ‌اند؛ default یا compact.
  • ارتفاع ثابت برای ردیف جدول. ردیف باید بتواند با متن بلند رشد کند.

صفحات مرتبط