راهنمای طراحی
قواعدی که صفحهای ساختهشده از کامپوننتهای ranui باید رعایت کند تا یک سامانه خوانده شود، نه تلی از قطعه.
موضوع این صفحه قضاوت است: سراغ کدام توکن بروید و پیش از انتشار چه چیزی را بررسی کنید. خودِ فهرست توکنها سیستم طراحی است و تعویض و بازنویسی در زمان اجرا پوستهبندی. اینکه صفحه پیش از همهٔ اینها چه شکلی میگیرد، معماری اطلاعات است. قواعد کامل طراحی در مخزن آمدهاند، در packages/ranui/docs/DESIGN.md.
کجا به کار میآید: وقتی صفحهای را میچینید یا از عناصر
<r-*>کامپوننتی در سطح برنامه میسازید و باید درباره یک رنگ، یک فاصله، یک اندازه متن، یک سایه یا یک مدت حرکت تصمیم بگیرید. پاسخ کوتاه همیشه یکی است: نقش را انتخاب کنید و مقدار را به توکن بسپارید.
اصول
- روشنی پیش از شخصیت. کار اصلی و کنش اصلی باید پیش از هر ملاحظه دیگری بیابهام باشند.
- از کنترلهای موجود استفاده کنید. کنترل بومی با معنای مناسب یا مؤلفههایی مانند
r-button،r-input،r-selectوr-modalرا متناسب با نیاز انتخاب کنید. رفتار فرم، صفحهکلید و دسترسپذیری را حفظ کنید و فقط برای تغییر ظاهر از نو نسازید. - توکن، نه مقدار خام. یک کد شانزدهشانزدهی، یک فاصله
20pxیا سایهای که با دست انتخاب شده، تصمیمی است که از پوسته پیروی نخواهد کرد. - با نقش و وضعیت تصمیم بگیرید، نه با چشم. «این متن چیست؟» (عنوان / برچسب / بدنه / دکمه) پاسخ دارد؛ «چه اندازهای خوب به نظر میرسد؟» ندارد.
- هر وضعیت دستیافتنی را طراحی کنید. پیشفرض، هاور، فعال، فوکوس، غیرفعال، در حال بارگذاری، خالی، خطا: وضعیت پیشفرض یکی از هشت تاست.
- آنچه رسم شده را وارسی کنید. در روشن و تیره، باریک و پهن، با ماوس و با انگشت. بازبینی، سایهای را که دیده نمیشود نمیگیرد.
ترتیب اولویت وقتی دو قاعده به دو سو میکشند: هدف کاربر ← شواهد وارسیشده ← همین راهنما ← الگوهای منتشرشده ← قواعد سرانگشتی عمومی.
انتخاب رنگ
رنگ بر پایه نقش و وضعیت تخصیص مییابد، نه با چشم انتخاب میشود. نردبان از پیش تعیین کرده هاور و فعال چه شکلیاند؛ کار شما نامگذاری نقش است.
| این عنصر… | این را به کار ببرید |
|---|---|
| پسزمینه صفحه یا یک سطح | --ran-color-bg / -bg-subtle / -bg-elevated / -bg-muted |
| زیر نشانگر / در حال فشرده شدن | --ran-color-bg-hover / -bg-active |
| متن | --ran-color-text / -text-secondary / -text-disabled |
| یک کادر | --ran-color-border / -hover / -active |
| همان کنشی که صفحه برایش هست | --ran-color-primary (و روی آن --ran-color-primary-text) |
| یک وضعیت | --ran-color-success / -warning / -danger |
| یک پیوند | --ran-color-link |
هر رنگ تأکیدی تنها یک معنا دارد. primary تکرنگ است (در روشن سیاه روی سفید، در تیره سفید روی سیاه)، پس برایش از آبی استفاده نکنید: آبی از آنِ پیوندها و حلقه فوکوس است. سبز موفقیت است، کهربایی هشدار و قرمز خطر؛ اگر قرمز را برای تأکید مصرف کنید، بعداً راهی برای نشان دادن خطر نمیماند.
سه قاعده که جلوی خرابیِ بیصدا را میگیرد:
- هرگز برای مقداری که باید از پوسته پیروی کند، یک کد شانزدهشانزدهی یا
rgb()را دستی ننویسید. - مقدار جایگزین باید توکنی را نام ببرد که با پوسته میچرخد:
var(--ran-color-text, var(--ran-gray-1000))، نهvar(--ran-color-text, #171717)؛ مقدار ثابتی که فقط برای حالت روشن است در تیره ناپدید میشود. - مقدار جایگزین باید توکنی را نام ببرد که وجود دارد.
var()روی ویژگی اعلامنشده به هیچ حل نمیشود، کل اعلان دور ریخته میشود و عنصر همان چیزی را نگه میدارد که به ارث برده — که معمولاً تقریباً درست به نظر میرسد. (--ran-color-errorوجود ندارد؛ نامش--ran-color-dangerاست.)
فاصله و ریتم
هر فاصله را از مقیاس نهمقداری بردارید و بگذارید خودِ فاصله معنا برساند:
- ۸ پیکسل میان عناصر درون یک گروه.
- ۱۶ پیکسل میان گروهها.
- ۳۲ تا ۴۰ پیکسل میان بخشها.
20px یا 28px اختراع نکنید. ریتم صفحه را همان مجموعه محدود میسازد و یک فاصله بیرون از مقیاس آن را میشکند. ستون فقرات مشترک میان نواحی را حفظ کنید (لبهها، خطهای مبنا و ستونهایی که همراستا میشوند) و همترازی را با پیکسلهای رسمشده بسنجید نه با چشم.
انتخاب تایپوگرافی
بپرسید متن چه نقشی دارد (عنوان، برچسب، بدنه، دکمه، تکعرض) و آنگاه قلم و اندازه و وزن و ارتفاع خط، همه از مقیاس تایپوگرافی درمیآیند. برای هر مورد جداگانه پیکسل خام انتخاب نکنید.
نقش یک ابزار است نه یک قانون: متن تزئینیِ واقعاً یکباره (لایهای که با یک حرکت میدرخشد، کمی وزن بیشتر برای پیوند فعال) با یک توکن کامپوننتیِ خودش بهتر درمیآید تا اینکه به نزدیکترین نقش تحمیل شود.
عمق: سایه و چیدمان لایهها
رده سایه را بر پایه اینکه عنصر چیست انتخاب کنید (سطحی در جریان صفحه، لایه شناور، یا دیالوگی که راه را میبندد) و مطمئن شوید واقعاً دیده میشود. سایهای که دیده نمیشود هیچ نشانهای از عمق نمیدهد، و لایه شناوری که به رده کارت سقوط کند انگار به صفحه سنجاق شده است.
جاسازی لایههای ranui در قاب خودتان. نردبان z-index دقیقاً از ۱۰۰۰ آغاز میشود تا از قاب معمول صفحه بالاتر برود. پس لایهای که پرتال شده به کمک شما نیاز ندارد. اما لایهای با position: fixed که درون Shadow DOM خودش میماند (دیالوگ r-modal) تنها تا نزدیکترین زمینه چیدمانِ نیای خود بیرون میزند؛ پس اگر محتوای جاسازیشده را در چیزی بپیچید که چنین زمینهای میسازد (isolation، opacity < 1، transform، filter، will-change)، باید سطح چیدمان آن پوشش را بالا ببرید تا دیالوگ دوباره رویش بنشیند. این بالا بردن را به زمانی محدود کنید که لایهای واقعاً باز است:
.embed {
isolation: isolate; /* ارزان: z-index مخصوص خودش ندارد، پس چیزی ارتقا نمییابد */
}
/* فقط تا وقتی لایهای واقعی باز است بالا ببرید — هرگز «محض احتیاط» */
.embed:has(r-modal[open]),
.embed:has(r-modal[closing]) {
position: relative;
z-index: 100;
}یک z-index فراگیر روی پوشش، همه چیزِ درونش را (حتی محتوای کاملاً ثابت را) در تمام طول اسکرول بالای سربرگ چسبانتان میبرد. همین اشکال یک بار در همین سایت منتشر شد. علاوه بر open با closing هم تطبیق دهید: ماسک پس از برداشتهشدن open به اندازه گذارش همچنان رسم میشود.
حرکت
هدف و تکرار استفاده تعیین میکنند که حرکت لازم است یا نه. بازخورد پرتکرار هاور، فوکوس و صفحهکلید بهطور پیشفرض فوری است. از توکنهای موجود فقط وقتی استفاده کنید که حرکت به درک تغییر کمک کند: حدود ۱۵۰ میلیثانیه برای گذار کوچک، ۲۰۰ برای منو و ۳۰۰ برای دیالوگ نمونههای اختیاریاند، نه تأخیر اجباری. حرکت باید قابل قطع باشد و prefers-reduced-motion رعایت شود.
هرگز نگذارید ویژگیهای پالت گذار کنند. CSS نمیداند رنگ چرا عوض شده، پس یک transition روی background-color، color، border-color، box-shadow، fill یا stroke هنگام چرخش پوسته هم شلیک میکند و هر عنصر با سرعت خودش محو میشود، در حالی که بقیه صفحه پیشاپیش عوض شده است. بهجایش ویژگیهای حرکتی را متحرک کنید (transform، opacity، هندسه). transition: all و کوتهنوشتهای لخت مثل transition: 0.2s یعنی همهچیز، از جمله ویژگیهای پالت؛ هر دو در استایلهای خودِ ranui ممنوعاند و در استایلهای شما هم فکر بدی هستند.
وضعیتها و متنها
هر وضعیت دستیافتنی بخشی از طراحی است: هاور، فعال، فوکوس، غیرفعال، در حال بارگذاری، خالی، خطا. آنها را روی نردبان بنشانید: هاور ← bg-hover / border-hover؛ فعال ← bg-active؛ غیرفعال ← توکنهای غیرفعال؛ کاهش کدری فقط در صورت نیاز و با حفظ خوانایی؛ فوکوس ← حلقه فوکوس.
هیچ چیزِ غیرتعاملی نباید تعاملی به نظر برسد. r-card تنها با اتریبیوت hoverable به هاور واکنش میدهد؛ برای کارتهایی که کلیک نمیشوند آن را نگذارید.
متن هم بخشی از سامانه است:
- دکمهها نتیجه را نام میبرند. اگر بافت، مفعول را روشن نمیکند «حذف عضو» بنویسید. در دیالوگی که مفعول روشن است «حذف» کافی است؛ از «تأیید» مبهم پرهیز کنید.
- خطاها میگویند چه شد و سپس چگونه درستش کنیم. ✅ «ساخت شکست خورد: باندل از سقف حجم گذشته است. کوچکش کنید یا سقف را بالا ببرید.» ❌ «عملیات ناموفق بود، دوباره تلاش کنید.»
- تأییدها و اعلانها تغییر انجامشده را مشخص میکنند. ✅ «پروژه حذف شد» ❌ «عملیات موفق بود». پیام اتمام را فقط پس از تأیید کامل شدن عملیات نمایش دهید.
- بگذارید بافت، زیادیها را حذف کند: دیالوگی با عنوان «حذف پروژه» به دکمهای با نوشته «حذف کامل و همیشگی پروژه» نیاز ندارد.
دسترسپذیری
- کنتراست متن نسبت به پسزمینهاش را در حد WCAG AA برسانید.
- هرگز وضعیت را تنها با رنگ اعلام نکنید: آن را با آیکن، برچسب یا متن همراه کنید.
- هر عنصر تعاملی یک حلقه فوکوس دیدنی نگه میدارد (
--ran-focus-ring، یاoutline: 2px solid var(--ran-color-primary); outline-offset: 2px). برای مرتبتر شدن ظاهر حذفش نکنید. - همهچیز با صفحهکلید دستیافتنی است. هیچچیز فقط با ماوس نیست.
- به
prefers-reduced-motionوprefers-color-schemeاحترام بگذارید.
ماوس و لمس، باریک و پهن
نه شیوه اشاره و نه اندازه قاب دید، هیچکدام هدف درجه دو نیستند.
- کشیدن، لغزنده و حرکتهای اشارهای با Pointer Events کار میکنند (
pointerdown/pointermove/pointerup/pointercancel)، نه تنها باmouse*، بههمراهtouch-action: noneروی همان سطحی که کشیده میشود. CSSی کهtouch-action: noneرا بدون هندلر اشارهگر پشت آن اعلام کند، یک کنترل خراب است نه یک خط بیآزار. - نشانهای که فقط با هاور پیدا میشود، به جایگزینی برای لمس نیاز دارد.
trigger="hover"رویr-selectیاr-popoverدر دستگاههای لمسی به کلیک برمیگردد؛ هرچه میسازید هم باید همین کار را بکند. - بهجای اختراع نقطه شکست، اندازههای نسبت به قاب دید را ترجیح دهید (
%،min()،max()،clamp()،vw/vh، مثلاًmin(560px, calc(100vw - 32px))). ranui توکن مشترکی برای نقطه شکست ندارد، پس هر نقطه شکستِ سفتوسخت عددی یکباره است که کسی باید نگهش دارد. - هرگز در موبایل تنها راه انجام یک کار را پنهان نکنید. بهجای
display: noneچیدمان را از نو بریزید. - موقعیتی که اندازه گرفتهاید تنها تا بازچینش بعدی درست است. هرچه از
getBoundingClientRect()بیرون آمده، با تغییر اندازه، با بازچینش ظرف و (برای پنلی که پرتال شده) با اسکرول کهنه میشود. در همان رویدادها دوباره اندازه بگیرید، نه فقط در تعاملی که نخستین اندازهگیری را برانگیخت. باز کردن صفحه در عرض باریک، چیدمان اولیه را میآزماید؛ اما تغییر اندازه به آن را نمیآزماید، و این رده اشکال دقیقاً همانجا پیدا میشود.
آنچه کتابخانه ماشینی اجبار میکند
نه تا از این قواعد را pnpm -F ranui verify:design بررسی میکند و CI آن را روی سورس خودِ ranui اجرا میکند: مقدارهای جایگزین رنگ که در تیره ناامناند، مقادیر رنگ خام، مقیاس فاصله، مقیاس اندازه، حلقههای کشیدنِ فقطماوسی، قواعد display روی :host که hidden را میشکنند، مقدارهای جایگزینی که توکن اعلامنشده را نام میبرند، کامپوننتهایی که درخت shadow خودشان را query میکنند، و درختهای shadowی که بیرون از سازنده ساخته شدهاند. تخلفهای شناختهشده در یک فایل مبنا قفل شدهاند، پس نه میشود تخلف تازهای افزود و نه اصلاحی را بیصدا پس گرفت.
این بررسیها فقط مسیرهای کد تنظیمشده برای فرمان را بررسی میکنند و برنامههای استفادهکننده از ranui را خودکار نمیسنجند. این مخزن برای بخشهایی مانند سایت مستندات بررسیهای جداگانه دارد. از بررسیهای برنامهٔ خود استفاده کنید و چیدمان، پوسته و تعامل را در مرورگر تأیید کنید.
نمونههای طراحی
این نمودارها از توکنهای پایهٔ ranui ساخته شدهاند و مؤلفهٔ واقعی نیستند.
ردیفها را همتراز نگه دارید
وقتی محتوایی به یک ردیف اضافه میشود، ابتدای متن و ستون عملیات را با ردیفهای دیگر همتراز نگه دارید.
گوشههای تودرتو را هماهنگ کنید
اگر هر دو مرز داخلی و بیرونی لازماند، فاصلهٔ داخلی را از شعاع بیرونی کم کنید: ۱۶ − ۴ = ۱۲. منحنیها هممرکز میشوند و فاصلهای یکنواخت دارند.
--ran-radius-lg − --ran-space-1 = --ran-radius-mdکلیک روی نمادهای کوچک را آسان کنید
اندازهٔ نماد ۱۶ پیکسل است و ناحیهٔ کلیک آن تا ۳۲ پیکسل گسترش مییابد تا استفاده آسانتر شود. اندازه را متناسب با ماوس یا لمس انتخاب کنید و بین ناحیههای مجاور فاصله بگذارید.
--ran-size-1 · --ran-size-7سیاهه پیش از انتشار رابط کاربری
بررسیها را برای سطح متأثر و رفتار تغییرکرده انتخاب کنید؛ CI مخزن همچنان همهٔ دروازهها را اجرا میکند.
- کار اصلی و کنش اصلی بیابهاماند.
- در روشن و تیره، و در عرض باریک و پهن کار میکند.
- با ماوس و لمس کار میکند؛ هر محرک هاور جایگزین لمسی دارد.
- وضعیتهای متأثر آزموده شد: هاور، فعال، فوکوس، غیرفعال، در حال بارگذاری، خالی، خطا.
- صفحهکلید و فوکوس وارسی شد؛ فوکوس همهجا دیده میشود.
- حالتهای مرزی: متن بلند، عدد بزرگ، زبانهای پشتیبانیشده.
- فاصلهها از مقیاس، تایپوگرافی بر پایه نقش، رنگ از توکنهای معنایی.
- هیچ ویژگی پالتی در
transitionنیست؛ هیچtransition: allنیست. - متن و بافت، کنش و مفعول را روشن میکنند؛ هیچچیز وضعیت را تنها با رنگ اعلام نمیکند.