أنظمة التصميم والتخصيص
مفردات واحدة وأنظمة تصميم كثيرة. غيِّر قرص ضبط، أو أعلن لوحة ألوان، أو أعد توجيه رمز، فتتبعك كل المنصات في إيداع واحد.
بُني Tanrei ليستطيع إعادة إنتاج أي نظام تصميم، بدقة تامة لا بالتقريب، مع بقاء مفرداته هو صغيرة. قاعدتان تجعلان ذلك ممكنًا: أنظمة التصميم تختلف في القيم أولًا، وما لا تستطيع القيمة التعبير عنه تضيفه الطبقة.
المفردات الأساس (core) في Tanrei هي كل حالة يمكن أن يتخذها نظام تصميم واحد داخل منتج واحد. ومن هذا ينتج اختبار لكل رمز (token) مقترح، هو اختبار «المنتج الواحد»:
- يكون رمزًا إذا كان منتج واحد قد يحتاج إلى التمييز، بالمعنى أو بالموضع. الدور
focus-ringمنفصل عنprimaryلأن منتجًا قد يريد لون علامة مشبعًا وحلقة تركيز هادئة. وكون النظام الافتراضي يعطيهما قيمتين متقاربتين مصادفة، لا سبب لدمجهما. وللمكوّن Button أحجام لأن المنتج الواحد يستخدم عدة أحجام. - لا يكون رمزًا إذا كان موجودًا في نظام تصميم واحد فقط، أو في منتج واحد غير مألوف. الدور
tertiaryفي Material Design 3 لوحة ألوان معلنة في الإعداد الجاهز الذي يعيد إنتاجه، وأدوارprimary-fixedفيه امتدادات لذلك الإعداد، لا خانات يحملها الجميع. وعشرون حجمًا للزر من أجل منتج غريب واحد ليست من المفردات الأساس كذلك؛ يضيفها ذلك المنتج إلى طبقته.
وإذا تعذّرت مع ذلك إعادة إنتاج سمة من سمات نظام تصميم، كمادة خلفية أو جدول أحجام نص خاص به، فذلك عيب في Tanrei يُقيَّد في سجل فجوات المحاكاة (docs/governance/mimicry-gaps.json في المستودع).
ينبغي أن يبدو نظام التصميم صحيحًا افتراضيًّا، وأن يتبعك حين تغيّر شيئًا واحدًا. ثمانية أقراص ضبط (dials) رقمية تقع تحت كل شيء:
| القرص | الافتراضي | ما يحرّكه |
|---|---|---|
dial.type.base |
16px | كل أحجام النص |
dial.type.contrast |
1 | مدى تميّز العناوين عن نص المتن |
dial.space.unit |
8px | كل المسافات |
dial.size.unit |
4px | ارتفاعات عناصر التحكم، وأحجام الأيقونات، وأهداف اللمس |
dial.density |
1 | ارتفاع عناصر التحكم والصفوف، والمساحة حول محتواها: أقل من 1 أكثر تقاربًا، وأكثر من 1 أوسع |
dial.shape.unit |
2px | كل الزوايا |
dial.stroke.unit |
1px | الحدود، والحلقات، ومؤشر التركيز |
dial.motion.unit |
50ms | كل المدد |
كل رمز واحد من ثلاثة أنواع:
- مشتق: قاعدة صغيرة، وهي في العادة معامل على قرص.
displayهوtype.base × 3، وspace175هوspace.unit × 1.75. سلالم أحجام النص الحقيقية ليست هندسية، ولذلك لكل رمز معامله الخاص؛ - ثابت: قيمة حرفية لها سبب مذكور، معفاة من الأقراص (
corner.full، أو منحنى تسارع، أو حجم حاشية يفرضه القانون)؛ - اسم بديل (alias): إحالة يمكن إعادة توجيهها (
button.container.gap←space100).
على الويب تعمل الأقراص حيّةً أيضًا: تُكتب الرموز المشتقة تعبيرات calc()، فضبط --tanrei-dial-type-base على أي عنصر يعيد تحجيم كل ما بداخله. ويوجد اختبار يقيّم كل رمز مشتق في متصفح حقيقي تحت عدة إعدادات للأقراص، ويشترط أن يساوي نتيجة البناء.
نظام التصميم ملف JSON صغير يمتدّ من نظام آخر. هو في الغالب يغيّر قيمًا؛ وحيث يلزم يضيف إلى المفردات.
{
"id": "acme",
"extends": "tanrei",
"palettes": {
"brand": { "description": "أحمر Acme تحدده إرشادات العلامة التجارية.", "source": { "seed": "#e60012" }, "roles": "intent" },
"correct": { "description": "الاختبار يعلّم الإجابات الصحيحة، وليس هذا success ولا primary.", "source": { "seed": "#0f766e" }, "roles": "intent" }
},
"tokens": {
"tanrei.dial.type.base": { "$value": { "value": 17, "unit": "px" }, "reason": "جمهورنا يقرأ على بُعد ذراع." },
"tanrei.comp.button.container.padding-inline-start.medium": { "scale": 2 },
"tanrei.comp.button.container.gap.medium": { "$value": "{tanrei.sys.space.space50}" },
"tanrei.sys.typescale.label-small": { "$value": { "fontSize": { "value": 11, "unit": "px" } }, "reason": "حجم الحاشية القانونية ملزم تعاقديًّا." }
}
}| ما تريده | ما تكتبه |
|---|---|
| كل شيء أكبر قليلًا | اضبط قرصًا |
| حشو الزر ضعف الافتراضي، مع بقائه تابعًا لقرص التباعد | "scale": 2 |
| أن يستخدم هذا الرمز درجة أخرى | أعد توجيه الاسم البديل |
| أن تبقى هذه القيمة الواحدة في مكانها مهما فعلت الأقراص | ثبِّت $value مع reason |
لون علامة أو لون خاص بمجالك له دورا on- و -container، مع فحص التباين |
أعلن لوحة ألوان مع "roles": "intent" |
ولأن شيفرة كل منصة مولَّدة من النظام المحلول نفسه، فإن طلب سحب واحدًا يغيّر الويب و SwiftUI و Compose و Flutter و React Native معًا.
القيم لا تكفي دائمًا. في نظام مرجعي أحجام للزر ليست في الأساس؛ وفي منتجك مكوّن تقييم خاص به. لذلك تستطيع الطبقة أيضًا أن توسّع المفردات: تحت vocabulary، ومع rationale دائمًا، وبالإضافة فقط:
"vocabulary": {
"tanrei.sys.space.space1200": { "$type": "dimension", "rationale": "أقسام الواجهة الرئيسية تحتاج إلى درجة إيقاع مقدارها 96px.",
"derive": { "fn": "mul", "of": "{tanrei.dial.space.unit}", "by": 12 } },
"tanrei.comp.button.size.3x": { "from": "large", "scale": 1.5, "rationale": "زر الدعوة إلى الإجراء في شاشة الهبوط." },
"tanrei.comp.button.variant.danger": { "from": "filled", "rationale": "تأكيد الإجراء الهدّام توكيد مستقل هنا." },
"acme.comp.rating.star.size": { "$type": "dimension", "rationale": "نجمة مكوّن التقييم.",
"derive": { "fn": "mul", "of": "{tanrei.dial.size.unit}", "by": 5 } }
}| ما تضيفه | ما يحدث |
|---|---|
درجة في سلسلة: درجة تباعد، أو زاوية، أو دور نص، أو دور لون مع الدور المرسوم فوقه (onRole) |
تأخذ مكانها بجوار جاراتها، وتتبع القرص نفسه، ويُفحص تباينها كأي زوج من الأساس |
| قيمة على محور مكوّن: حجم أو طراز (variant) للمكوّن Button | يكفي سطر واحد. الحجم ينسخ كل أعضاء المحور (الارتفاع، والحشو، ونمط التسمية، والأيقونة) من حجم آخر ويغيّر مقاسها بنسبة، مع بقائها تابعة للأقراص. والطراز ينسخ ألوان وضع السكون؛ وقواعد الأنماط في النظام تعطي رموز التحويم والتركيز والضغط والتعطيل قيمها عنك |
رموزك الخاصة، في نطاق أسماء طبقتك (acme.…) |
لمكوّناتك أنت. يجوز أن تحيل إلى أي رمز tanrei.*، وأن تتبع الأقراص والمخططات اللونية، وتصير --acme-… على الويب وكائنًا محدَّد النوع على كل منصة أخرى |
يقول vocabulary إن شيئًا ما موجود؛ ويعطيه tokens و rules قيمة، تمامًا كما في رموز الأساس، ولذلك تغيّر الطبقة الوليدة الامتداد كما تغيّر أي شيء آخر. الامتدادات تُورَّث، ولا تزيل شيئًا من الأساس ولا تغيّر نوعه أبدًا، ولا تستطيع إضافة حالات تفاعل (فهذه تخص المنصة). وتسرد كل حزمة مولَّدة الامتدادات مع من أعلنها ولماذا.
تقبل المكوّنات ما تضيفه. يعمل <tanrei-button size="3x" variant="danger"> على الويب، وتأخذ الحزم الأصلية المفاتيح نفسها؛ والقيمة التي لا يملكها نظام التصميم الفعّال تُرسم بالقيمة الافتراضية. النظام الذي لا يضيف شيئًا لا يدفع شيئًا: شيفرته المولَّدة لا تذكر الامتدادات إطلاقًا.
هكذا يجتمع مثلان أعليان: تبقى مفردات Tanrei نفسه صغيرة وواضحة الأسماء، ويمكن مطابقة أي نظام تصميم بدقة تامة، لأنه لا يلزم أن يُضاف شيء إلى الأساس من أجل غيره.
لا تحسب المكوّنات مظهر التحويم أو الضغط أبدًا. لكل حالة رموزها (button.filled.hovered.container.color، …)، وقواعد الأنماط في النظام تعطيها قيمها:
{ "match": "tanrei.comp.*.*.hovered.container.color",
"derive": { "fn": "mix", "base": "{tanrei.comp.$1.$2.container.color}", "with": "{tanrei.comp.$1.$2.label.color}", "amount": "{tanrei.sys.state.hover-state-layer-opacity}" } }هذه هي طبقة الحالة في Tanrei. النظام الذي له ألوان صريحة لكل حالة يكتب أسماء بديلة مكانها؛ والذي لا تحويم فيه يجعل الحالة اسمًا بديلًا لقيمة وضع السكون؛ والذي يُعتم عند الضغط يضبط pressed.opacity. المفردات نفسها وقيم مختلفة.
فروق قليلة ليست مقادير أصلًا: ضغط يُستجاب له بتموّج أو بإعتام عنصر التحكم، وتركيز لوحة مفاتيح يُعرض حلقةً أو توهّجًا. هذان رمزان تحت sys.behavior (pressed-feedback و focus-style)، لكل منهما قائمة قصيرة مغلقة من الأسماء، وتختار الطبقة واحدًا منها كأي قيمة أخرى. أما طريقة انحناء الزاوية فمقدار لا اختيار: sys.shape.corner-smoothing في صفحة الشكل. وتشرحها صفحة السلوك. ومجموعة الأيقونات والخطوط مما تغيّره الطبقة كذلك: انظر الأيقونات والطباعة.
العنصر <tanrei-button> نفسه، والترميز نفسه. النظامان m3-like و cupertino موجودان لإثبات الآلية أمام نظامين متعاكسَي الطابع، وليكونا نقطتَي بداية لطبقتك.
الدقة تُقاس ولا تُدَّعى. يحمل m3-like خريطة مطابقة مع الرموز المنشورة في material-web (Apache-2.0): كل دور لون، وكل نمط نص، وكل رمز للمكوّن Button في المرجع يجب أن يكون مساويًا، أو مغطًّى برمز آخر، أو مسرودًا مع فجوة المحاكاة التي تفسّره. يُعرض الرقم مع الإعداد الجاهز أدناه: عدد الرموز المساوية من بين الرموز غير المغطاة برمز آخر، مقروءًا من tokens/generated/<id>/fidelity.json. ويُسمّى باللاحقة -like لأسباب تتعلق بالعلامات التجارية، لا لأنه تقريبي.
tanrei Tanrei
- لوحات الألوان المعلَنة
accent- القيم المغيَّرة
- 152
- قواعد النمط
- 132
- WCAG
- يستوفي كل الثوابت في كل المخططات. أي مخالفة تُفشل البناء.
The default design system. Near-monochrome surfaces, a monochrome primary and one accent. It takes its values from core and leaves out the components and variants it does not draw.
cupertino Cupertino (Apple HIG-inspired)
- يوسّع
core- لوحات الألوان المعلَنة
- لا توجد
- القيم المغيَّرة
- 581
- قواعد النمط
- 15
- توسعات المفردات
- 3
- المطابقة (مقيسة)
- غير مقيسة — المرجع لا ينشر رموزًا مقروءة آليًا
- WCAG
- 113 من قياسات التباين دون عتبات Tanrei (في التباين القياسي فقط). معلومة فقط: معايير إمكانية الوصول في Tanrei لا تنطبق على إعداد جاهز يحاكي نظامًا آخر، في أي مخطط تباين.
An independent approximation of the look of Apple's platforms, expressed purely as values: a saturated system tint, pure white and black canvases, gray fills with tinted labels, rounded rectangles, no hover state and a dimmed pressed state.
Tanrei is not affiliated with, endorsed by, or sponsored by Apple Inc. Apple is a trademark of Apple Inc. The values here are Tanrei's own approximations, not a transcription of Apple's documentation, and no Apple font or symbol is bundled: the system font is referenced by name only.
m3-expressive-like M3 Expressive-like (Material 3 Expressive-inspired)
- يوسّع
m3-like- لوحات الألوان المعلَنة
- لا توجد
- القيم المغيَّرة
- 53
- قواعد النمط
- 0
- توسعات المفردات
- 2
- المطابقة (مقيسة)
- 35 / 55 رموز مرجعية متساوية · 0 مختلفة · 20 بلا مقابل، مع فجوة مسجَّلة — androidx.compose.material3 button size tokensv0_11_0 (Apache-2.0)
- WCAG
- يستوفي كل الثوابت في كل المخططات. معلومة فقط: معايير إمكانية الوصول في Tanrei لا تنطبق على إعداد جاهز يحاكي نظامًا آخر، في أي مخطط تباين.
m3-like with the five button sizes of Material 3 Expressive — extra small to extra large, each with its own paddings, icon size, icon-to-label gap and outline width. Colors, type, shape and states are m3-like's.
Tanrei is not affiliated with, endorsed by, or sponsored by Google. "Material Design" is Google's; this preset is an independent reproduction of published, Apache-2.0 licensed token values.
m3-like M3-like (Material 3-inspired)
- يوسّع
core- لوحات الألوان المعلَنة
tertiary- القيم المغيَّرة
- 711
- قواعد النمط
- 19
- توسعات المفردات
- 18
- المطابقة (مقيسة)
- 273 / 274 رموز مرجعية متساوية · 0 مختلفة · 1 بلا مقابل، مع فجوة مسجَّلة — material-webv0_192 (Apache-2.0)
- WCAG
- يستوفي كل الثوابت في كل المخططات. معلومة فقط: معايير إمكانية الوصول في Tanrei لا تنطبق على إعداد جاهز يحاكي نظامًا آخر، في أي مخطط تباين.
An independent approximation of Material Design 3's baseline look, expressed purely as values over Tanrei's vocabulary: a chromatic primary, tonal surfaces, pill buttons, shadow elevation without a ring.
Tanrei is not affiliated with, endorsed by, or sponsored by Google. "Material Design" is Google's; this preset is an independent approximation derived from publicly available, Apache-2.0 licensed design tokens.
على الويب يكون الإعداد الجاهز ورقة أنماط إضافية واحدة، محصورة في سمة، فتستطيع الأنظمة أن تتجاور في الصفحة نفسها. ويسجّل مدخلُه المكوّناتِ التي يملكها، ومنها ما يتركه النظام الافتراضي (لا يسجّل @tanrei/web إلا مكوّنات النظام الافتراضي):
import '@tanrei/web/tokens.css';
import '@tanrei/web/systems/cupertino.css';
import '@tanrei/web/cupertino'; // المكوّنات التي يملكها cupertino<body data-tanrei-system="cupertino">ما لا تستطيع القيم ولا اختيارات النوع التعبير عنه بعد، كمواد الخلفية والتسميات بالأحرف الكبيرة على سبيل الأسلوب، مسرود عيبًا في Tanrei في docs/governance/mimicry-gaps.json، وليس مخفيًّا.
يمكن للنظام أن يستبعد مكوّنًا، أو قيمة واحدة من قيم variant لمكوّن ما: النظام الافتراضي لا يملك زرًّا مقسَّمًا، ولا حقل نص بنمط filled أو outlined. وتذكر الطبقة ذلك تحت components، مع السبب وما يُستخدم بدلًا منه:
"components": {
"omit": { "carousel": { "reason": "Acme shows one thing at a time.", "insteadUse": ["list"] } },
"variants": { "tabs": { "omit": ["tonal"] } }
}ما يُستبعَد يظل يُعرض ويعمل. وفي بناء التطوير، يُصدر كل عنصر يستخدمه تحذيرًا واحدًا في وحدة التحكم، يذكر نظامك وما يستخدمه بدلًا منه. ويسمّي التحذير المكوّن الذي كتبته: يُبلَّغ عن <tanrei-text-field variant="outlined"> على أنه نمط outlined لحقل النص، لا على أنه حقل الإدخال الذي يرسمه في داخله. والمكوّن الذي يقيم في طبقة نظام آخر، مثل قائمة FAB في m3-like، يُبلَّغ عنه بالطريقة نفسها، مع الطبقة التي يقيم فيها.
لتُفشِل اختباراتك عند ذلك، اجعله يُبلَّغ عنه خطأً، واسأل عمّا استُخدم:
import { setAvailabilityMode, unavailableUses } from '@tanrei/web';
setAvailabilityMode('error'); // في إعداد اختباراتك، قبل أن يُعرض أي شيء
test('المنتج لا يستخدم إلا ما يملكه نظامه', async () => {
// …اعرض شاشات المنتج…
expect(unavailableUses()).toEqual([]);
});تسرد unavailableUses() كل استخدام مرة واحدة، بالترتيب الأبجدي (segmented-button، text-field:outlined)، في أيٍّ من الوضعين. أما بناء الإنتاج فلا يتحقق من شيء، لذا تبقى القائمة فيه فارغة. ويضم @tanrei/web/availability الدالتين نفسيهما دون أن يسجّل أي عنصر.
ينتج عن بناء النظام حزمة DTCG 2025.10 مكتفية بذاتها، أي ملفات رموز بسيطة كاملة القيم مع محلِّل (resolver) قياسي، فتستطيع أي أداة تقرأ رموز التصميم أن تستهلكها من دون Tanrei. وملفات الطبقات التي أنتجتها محمولة داخل الحزمة، فيستطيع Tanrei (أو وكيل ذكاء اصطناعي) إعادة بنائها أو تعديلها.
إمكانية الوصول
عتبات التباين ثوابت لا يستطيع أحد تغييرها. في النظام الافتراضي تُفشل المخالفة البناء. وفي طبقة منتج تكون المخالفة تحذيرًا يُطبع عند البناء ويُكتب في المخرجات المولَّدة، ولا يمرّ بصمت أبدًا. أما الإعداد الجاهز الذي يحاكي نظامًا آخر فيُحكَم عليه بمطابقته لذلك النظام: لا تنطبق عليه معايير إمكانية الوصول في Tanrei، لا في التباين القياسي ولا المتوسط ولا العالي، ونسب التباين التي يقيسها البناء فيه تُسجَّل معلوماتٍ لا تحذيرات. وأما الخلل الوظيفي، مثل عنصر تحكم يتعذّر تشغيله أو اسم مفقود أو حالة مفقودة، فهو عيب في كل نظام، حتى في الإعداد الجاهز الذي يحاكي نظامًا آخر.