قرارداد مصرف
پنج محدودیت ترکیببندی که هر اپ مصرفکنندهی پرتو باید رعایت کند، مرور سهپرسشی، و دو گیتی که آنها را اجرا میکنند
اصل
پرتو فقط مجموعهای از کامپوننت نیست؛ یک قرارداد ترکیببندی است. کامپوننتها زیبا هستند، اما اگر آنها را روی یک بوم پرنویز، با اندازههای دلخواه و رنگهای hardcode بچینید، محصول دوباره «قالبی» و ناهماهنگ میشود. این صفحه پنج محدودیتی را که ظاهرِ Supabase-grade را تضمین میکنند در یک جا جمع میکند.
این صفحه مرجع است، نه کپی
این قرارداد منبع واحد حقیقت است. اپها باید به همین صفحه ارجاع دهند، نه اینکه یک parto-contract.md محلی برای
خودشان کپی و نگهداری کنند. نسخههای محلی drift میکنند؛ این نسخه با پکیج همگام میماند.
پنج محدودیت ترکیببندی
۱. فقط مقیاس نامدار تایپوگرافی
اندازهی متن همیشه از شش utility معنایی میآید: text-caption · text-label · text-body · text-subheading · text-heading · text-display. هرگز text-[15px] یا leading-[22px] ننویسید. توکنهای text-micro (۱۰px) و text-mini (۱۱px) فقط برای میکرولیبلهای عددی/لاتینِ متراکماند (شمارندهی چیپ، مدت ویدیو، برچسب محور) — هرگز برای نثر فارسی؛ کف خوانایی فارسی ۱۲px (text-caption) است.
۲. بوم خنثی، رنگ فقط روی داده
بوم اپ (پسزمینه، سطوح، متن، حاشیه) خنثی است — از توکنهای background/surface/foreground/border. رنگ اشباع (برند، معنایی، دامنه) را فقط برای داده و کنش اصلی خرج کنید: نمودار، نشان احساس/جریان، وضعیت، شدت، دکمهی primary. رنگ برای تزئین کارت یا نوار کناری نیست.
۳. بدون رنگ hardcode
هر رنگ از توکن میآید. نه #22c55e، نه rgb(...), نه bg-gray-900، نه text-white. توکنها (v2+) رنگ کاملاند، پس مستقیم استفاده کنید (bg-brand, text-foreground/60) و هرگز داخل hsl() نپیچید.
۴. شعاع از توکن
گردی گوشه از utilityهای نامدار rounded-* (نگاشتشده به توکنهای شعاع) میآید، نه rounded-[6px]. این هماهنگی شعاع را در کل محصول حفظ میکند.
۵. عرض کانتینر نامدار
عرض ناحیهی محتوا از عرضهای نامدار (container، max-w-screen-*، max-w-prose) میآید، نه max-w-[1180px]. عددهای جادویی، ریتم چیدمان را میشکنند.
نمونه بصری
✅ درست
نرخ تعامل
۴٫۸٪
صعودی
بوم خنثی؛ رنگ فقط روی داده (نشان احساس).
❌ نادرست
نرخ تعامل
۴٫۸٪
رنگ hardcode، شعاع دلخواه، اندازهی خارج از مقیاس.
قوانین اجباری
<p className="text-body text-foreground">…</p><p className="text-[15px] text-white">…</p><div className="rounded-lg bg-surface-100 border border-default max-w-screen-lg"><div className="rounded-[10px] bg-gray-900 max-w-[1180px]"><Badge className="bg-brand/10 text-brand">…</Badge>style={{ color: '#22c55e' }}مرور سهپرسشی
پیش از merge هر صفحه، سه سؤال بپرسید. اگر پاسخ هر سه «بله» بود، صفحه با قرارداد همراستاست:
- بوم خنثی؟ — پسزمینه، سطوح، متن، و حاشیه فقط از توکنهای خنثی میآیند؟
- رنگ فقط روی داده؟ — رنگ اشباع تنها روی داده، وضعیت، و کنش اصلی است، نه تزئین؟
- همهچیز روی مقیاس نامدار؟ — اندازهی متن، شعاع، و عرض کانتینر همه از مقیاس نامدارند، بدون مقدار دلخواه؟
دو گیت اجراکننده
قرارداد را چشمی رها نمیکنیم؛ پکیج همان دو گیت استاتیکی را که پرتو روی خودش اعمال میکند به شما هم میدهد. هر دو را وصل کنید (جزئیات در نصب و راهاندازی).
| محدودیت | پلاگین ESLint (@partodata/ui/eslint-plugin) | اسکنر parto-design-lint |
|---|---|---|
| مقیاس نامدار تایپوگرافی | قانون «متن خارج از مقیاس» | text-[…] / leading-[…] دلخواه |
| بوم خنثی، رنگ فقط روی داده | «بدون رنگ پالت»، «بدون متن بدنهی محو» | کلاسهای پالت خام |
| بدون رنگ hardcode | «بدون رنگ hardcode» | literalهای hex / rgb() / hsl() |
| شعاع از توکن | — | rounded-[…px] دلخواه |
| عرض کانتینر نامدار | — | max-w-[…px] دلخواه |
پلاگین ESLint 11 قانون flat-config دارد (9 روی error و 2 روی warn در کانفیگ recommended)؛ شامل جفترنگ WCAG، الزام data-slot، بدون CSS فیزیکی، و بدون letter-spacing روی فارسی. اسکنر parto-design-lint رشتههای className و CSS خام را میگیرد که قوانین AST نمیبینند و با کد خروج 2 روی «صفر فایل اسکنشده» از نتیجهی «تمیزِ» دروغین جلوگیری میکند.
موارد استفاده رایج
سه الگوی آمادهی کپیوپیست که قرارداد را رعایت میکنند — بوم خنثی، رنگ فقط روی داده، و همهچیز روی مقیاس نامدار.
چیپ داده با توکن دامنه (فرم متغیر دلخواه): رنگ اشباع تنها روی خودِ نشانگر داده مینشیند، نه بر پسزمینهی کارت.
<span className="inline-flex items-center rounded-full px-2 py-0.5 text-caption bg-[color-mix(in_srgb,var(--sentiment-positive)_15%,transparent)] text-[var(--sentiment-positive)]">
صعودی
</span>پشتهی سطوح (بوم ← کارت ← سطح داخلی): عمق از توکنهای خنثیِ سطح میآید، نه از رنگ یا سایهی دلخواه.
<div className="min-h-screen bg-canvas p-6">
<div className="rounded-lg border border-default bg-surface-100 p-4">
<div className="rounded-md border border-default bg-surface-200 p-3">…</div>
</div>
</div>بلوک متن با مقیاس نامدار: اندازه و line-height از مقیاس تایپوگرافی میآیند، بدون هیچ text-[Npx].
<div>
<p className="text-caption text-light">نرخ تعامل</p>
<p className="text-heading text-foreground">۴٫۸٪</p>
<p className="text-body text-foreground-light">میانگین هفت روز اخیر</p>
</div>چه نکنیم
- رنگ روی تزئین — نوار کناری، هدر کارت، یا پسزمینهی بخش را برند/رنگی نکنید؛ رنگ را برای داده نگه دارید.
- مقدار دلخواه —
text-[Npx]،rounded-[Npx]،max-w-[Npx]؛ همیشه معادل نامدار وجود دارد. - دور زدن گیتها — قانون ESLint یا
parto-design-lintرا با disable-comment خاموش نکنید تا یک نقض عبور کند؛ ریشه را اصلاح کنید. - کپی محلی قرارداد —
parto-contract.mdجداگانه نسازید؛ به همین صفحه لینک دهید.
صفحات مرتبط
- نصب و راهاندازی — گام ۸: وصلکردن هر دو گیت
- توکنهای طراحی — مرجع کامل توکنهای خنثی و دامنه
- تایپوگرافی — مقیاس نامدار متن
- رنگها — بوم خنثی در برابر رنگ داده