مزايا استخدام "السمات المخصّصة" في أنظمة التصميم ومكتبات المكوّنات
اسمي "ديف" وأعمل مطوّرًا أول للواجهة الأمامية في Nordhealth. أعمل على تصميم وتطوير نظام التصميم Nord، الذي يتضمّن إنشاء Web Components لمكتبة المكوّنات. أردتُ أن أشارك كيف حللنا المشاكل المتعلّقة بتصميم مكوّنات الويب باستخدام خصائص CSS المخصّصة، وبعض المزايا الأخرى لاستخدام الخصائص المخصّصة في أنظمة التصميم ومكتبات المكوّنات.
كيفية إنشاء مكوّنات الويب
لإنشاء مكوّنات الويب، نستخدم Lit، وهي مكتبة توفّر الكثير من الرموز البرمجية النموذجية، مثل الحالة والأنماط المحدودة النطاق والقوالب وغير ذلك. لا يقتصر الأمر على أنّ Lit خفيف الوزن، بل إنّه يستند أيضًا إلى واجهات برمجة تطبيقات JavaScript الأصلية، ما يعني أنّه يمكننا تقديم حزمة رموز برمجية بسيطة تستفيد من الميزات التي يتضمّنها المتصفّح.
import {html, css, LitElement} from 'lit';
export class SimpleGreeting extends LitElement {
static styles = css`:host { color: blue; font-family: sans-serif; }`;
static properties = {
name: {type: String},
};
constructor() {
super();
this.name = 'there';
}
render() {
return html`Hey
${this.name}, welcome to Web Components!`;
}
}
customElements.define('simple-greeting', SimpleGreeting);
لكنّ الميزة الأكثر جاذبية في Web Components هي أنّها تعمل مع أي إطار عمل JavaScript تقريبًا، أو حتى بدون أي إطار عمل على الإطلاق. بعد الإشارة إلى حزمة JavaScript الرئيسية في الصفحة، يصبح استخدام Web Component مشابهًا إلى حد كبير لاستخدام عنصر HTML أصلي. العلامة الوحيدة التي تدل على أنّ العنصر ليس عنصر HTML أصليًا هي الشرطة الثابتة داخل العلامات، وهي معيار يشير إلى المتصفّح بأنّ هذا العنصر هو Web Component.
تغليف الأنماط في Shadow DOM
وكما أنّ عناصر HTML الأصلية تتضمّن Shadow DOM، كذلك تتضمّن Web Components. Shadow DOM هي شجرة مخفية من العُقد داخل أحد العناصر. أفضل طريقة لتصوّر ذلك هي فتح أداة فحص الويب وتفعيل الخيار "عرض شجرة Shadow DOM". بعد إجراء ذلك، جرِّب النظر إلى عنصر إدخال أصلي في "أداة الفحص"، وسيظهر لك الآن خيار فتح عنصر الإدخال هذا والاطّلاع على جميع العناصر داخله. يمكنك حتى تجربة ذلك مع أحد "مكوّنات الويب" لدينا، جرِّب فحص مكوّن الإدخال المخصّص للاطّلاع على Shadow DOM.

إحدى مزايا (أو عيوب، حسب نظرتك) Shadow DOM هي تغليف الأنماط. إذا كتبت CSS ضمن Web Component، لا يمكن أن تتسرّب هذه الأنماط وتؤثر في الصفحة الرئيسية أو العناصر الأخرى، بل يتم احتواؤها بالكامل داخل المكوّن. بالإضافة إلى ذلك، لا يمكن أن يتسرّب CSS المكتوب للصفحة الرئيسية أو أحد عناصر Web Component الرئيسية إلى Web Component.
يُعدّ تغليف الأنماط هذا ميزة في مكتبة المكوّنات. يمنحنا ذلك ضمانًا أكبر بأنّ المكون سيبدو كما أردنا، بغض النظر عن الأنماط المطبّقة على الصفحة الرئيسية. وللتأكّد من ذلك، نضيف all: unset;
إلى الجذر أو "المضيف" لجميع "مكوّنات الويب".
:host {
all: unset;
display: block;
box-sizing: border-box;
text-align: start;
/* ... */
}
ومع ذلك، ماذا لو كان لدى مستخدم Web Component سبب وجيه لتغيير أنماط معيّنة؟ ربما هناك سطر نص يحتاج إلى تباين أكبر بسبب سياقه، أو ربما يحتاج إطار إلى أن يكون أكثر سمكًا. إذا لم يكن بإمكان أي أنماط الوصول إلى المكوّن، كيف يمكنك إتاحة خيارات التنسيق هذه؟
وهنا يأتي دور "خصائص CSS المخصّصة".
خصائص CSS المخصّصة
الخصائص المخصّصة هي خصائص CSS يمكنك تسميتها بالكامل وتطبيق أي قيمة مطلوبة. الشرط الوحيد هو أن تبدأها بشرطتين. بعد تعريف السمة المخصّصة، يمكن استخدام القيمة في CSS باستخدام الدالة var()
.
:root {
--n-color-accent: rgb(53, 89, 199);
/* ... */
}
.n-color-accent-text {
color: var(--n-color-accent);
}
في ما يتعلق بالتوريث، يتم توريث جميع السمات المخصّصة، وهو ما يتوافق مع السلوك العادي لسمات وقيم CSS العادية. يمكن استخدام أي سمة مخصّصة يتم تطبيقها على عنصر رئيسي أو على العنصر نفسه كقيمة لسمات أخرى. نستخدم "الخصائص المخصّصة" بشكل كبير لرموز التصميم من خلال تطبيقها على العنصر الجذر عبر إطار عمل CSS، ما يعني أنّه يمكن لجميع العناصر على الصفحة استخدام قيم الرموز هذه، سواء كان ذلك مكوّن ويب أو فئة مساعدة CSS أو مطوّر يريد استخراج قيمة من قائمة الرموز.
تتيح إمكانية توريث السمات المخصّصة، باستخدام الدالة var()
، اختراق Shadow DOM لمكوّنات الويب ومنح المطوّرين تحكّمًا أكثر دقة عند تصميم مكوّناتنا.
الخصائص المخصّصة في أحد مكونات Nord Web
عندما ننشئ أحد عناصر نظام التصميم، نتبع نهجًا مدروسًا في ما يخصّ CSS، إذ نسعى إلى استخدام تعليمات برمجية بسيطة ولكن يسهل صيانتها. يتم تحديد رموز التصميم التي لدينا على أنّها خصائص مخصّصة ضمن إطار عمل CSS الرئيسي في العنصر الجذر.
:root {
--n-space-m: 16px;
--n-space-l: 24px;
/* ... */
--n-color-background: rgb(255, 255, 255);
--n-color-border: rgb(216, 222, 228);
/* ... */
}
بعد ذلك، تتم الإشارة إلى قيم الرموز المميزة هذه داخل مكوّناتنا. في بعض الحالات، سنطبّق القيمة مباشرةً على سمة CSS، ولكن في حالات أخرى، سنحدّد سمة مخصّصة جديدة مرتبطة بالسياق ونطبّق القيمة عليها.
:host {
--n-tab-group-padding: 0;
--n-tab-list-background: var(--n-color-background);
--n-tab-list-border: inset 0 -1px 0 0 var(--n-color-border);
/* ... */
}
.n-tab-group-list {
box-shadow: var(--n-tab-list-border);
background-color: var(--n-tab-list-background);
gap: var(--n-space-s);
/* ... */
}
سنجرّد أيضًا بعض القيم الخاصة بالمكوّن ولكن غير المتوفّرة في الرموز المميزة ونحوّلها إلى سمة مخصّصة سياقية. توفّر لنا السمات المخصّصة التي تتعلّق بالسياق الخاص بالمكوّن فائدتَين رئيسيتَين. أولاً، يعني ذلك أنّه يمكننا استخدام CSS بشكل أكثر "جفافًا" لأنّه يمكن تطبيق هذه القيمة على سمات متعددة داخل المكوّن.
.n-tab-group-list::before {
/* ... */
padding-inline-start: var(--n-tab-group-padding);
}
.n-tab-group-list::after {
/* ... */
padding-inline-end: var(--n-tab-group-padding);
}
ثانيًا، يتيح إجراء تغييرات على حالة المكوّن وخياراته بشكل سلس للغاية، إذ إنّ الخاصية المخصّصة هي فقط التي يجب تغييرها لتعديل كل هذه الخصائص عند تصميم حالة التمرير أو الحالة النشطة أو خيار المنتج في هذه الحالة.
:host([padding="l"]) {
--n-tab-group-padding: var(--n-sp
ace-l);
}
لكنّ الميزة الأقوى هي أنّه عند تحديد "الخصائص المخصّصة" السياقية هذه على أحد المكوّنات، فإنّنا ننشئ نوعًا من واجهة CSS المخصّصة لكل مكوّن من مكوّناتنا، ويمكن للمستخدم الاستفادة من هذه الواجهة.
<nord-tab-group label="T>itl<e"
>!<-- ... --
/nord>-t<ab-gr>oup
style
nord-tab-group {
--n-tab-group-padding: var(--n-space<-xl);
>
}
/style
يعرض المثال السابق أحد "مكوّنات الويب" مع تغيير "خاصية مخصّصة" سياقية من خلال أداة اختيار. نتيجةً لهذه الطريقة بأكملها، نحصل على مكوّن يوفّر للمستخدم مرونة كافية في التصميم مع الحفاظ على معظم الأنماط الفعلية تحت السيطرة. بالإضافة إلى ذلك، يمكننا كمطوّري مكوّنات اعتراض الأنماط التي يطبّقها المستخدم. إذا أردنا تعديل إحدى هذه السمات أو تمديدها، يمكننا إجراء ذلك بدون أن يحتاج المستخدم إلى تغيير أي من الرموز البرمجية.
نرى أنّ هذا الأسلوب فعّال للغاية، ليس بالنسبة إلينا كصنّاع لمكوّنات نظام التصميم فحسب، بل أيضًا لفريق التطوير عند استخدام هذه المكوّنات في منتجاتنا.
الاستفادة بشكل أكبر من الخصائص المخصّصة
في وقت كتابة هذا المقال، لم نكن قد كشفنا عن هذه "الخصائص المخصّصة" السياقية في مستنداتنا، ولكنّنا نخطّط لذلك لكي يتمكّن فريق التطوير الأوسع من فهم هذه الخصائص والاستفادة منها. يتم تجميع مكوّناتنا في حِزم على npm مع ملف بيان يحتوي على كل المعلومات المتعلّقة بها. بعد ذلك، نستخدم ملف البيان كبيانات عند نشر موقعنا الإلكتروني الخاص بالمستندات، ويتم ذلك باستخدام Eleventy وميزة "البيانات العامة". ونخطّط لتضمين هذه السمات المخصّصة السياقية في ملف بيانات البيان هذا.
هناك مجال آخر نريد تحسينه وهو طريقة اكتساب "السمات المخصّصة" السياقية للقيم. في الوقت الحالي، إذا أردت مثلاً تعديل لون مكوّنَين من مكوّنات الفواصل، عليك استهداف هذين المكوّنَين تحديدًا باستخدام أدوات الاختيار، أو تطبيق السمة المخصّصة مباشرةً على العنصر باستخدام سمة النمط. قد يبدو هذا جيدًا، ولكن سيكون من الأفضل لو كان بإمكان المطوّر تحديد هذه الأنماط على عنصر حاوٍ أو حتى على مستوى الجذر.
<nord-divider></nord-divider>
<section>
<nord-divider></nord-divider>
<!-- ... -->
</section>
<style>
nord-divider {
--n-divider-color: var(--n-color-status-danger);
}
section {
padding: var(--n-space-s);
background: var(--n-color-surface-raised);
}
section nord-divider {
--n-divider-color: var(--n-color-status-success);
}
</style>
يجب ضبط قيمة السمة المخصّصة مباشرةً على المكوّن لأنّنا نحدّدها على العنصر نفسه من خلال أداة اختيار مضيف المكوّن. إنّ رموز التصميم العامة التي نستخدمها مباشرةً في المكوّن تمرّ مباشرةً بدون أن تتأثر بهذه المشكلة، ويمكن حتى اعتراضها على العناصر الرئيسية. كيف يمكننا الاستفادة من مزايا كلتا الطريقتين؟
الخصائص المخصّصة الخاصة والعامة
السمات المخصّصة الخاصة هي شيء جمعته Lea Verou، وهي سمة مخصّصة "خاصة" سياقية على المكوّن نفسه، ولكن تم ضبطها على سمة مخصّصة "عامة" مع قيمة احتياطية.
:host {
--_n-divider-color: var(--n-divider-color, var(--n-color-border));
--_n-divider-size: var(--n-divider-size, 1px);
}
.n-divider {
border-block-start: solid var(--_n-divider-size) var(--_n-divider-color);
/* ... */
}
إنّ تحديد "السمات المخصّصة" السياقية بهذه الطريقة يعني أنّه لا يزال بإمكاننا تنفيذ جميع الإجراءات التي كنا ننفّذها من قبل، مثل توريث قيم الرموز المميزة العامة وإعادة استخدام القيم في رمز المكوّن، ولكن سيتيح المكوّن أيضًا توريث التعريفات الجديدة لهذه السمة بسلاسة على المكوّن نفسه أو أي عنصر رئيسي.
<nord-divider></nord-divider>
<section>
<nord-divider></nord-divider>
<!-- ... -->
</section>
<style>
nord-divider {
--n-divider-color: var(--n-color-status-danger);
}
section {
padding: var(--n-space-s);
background: var(--n-color-surface-raised);
--n-divider-color: var(--n-color-status-success);
}
</style>
على الرغم من أنّه يمكن القول إنّ هذه الطريقة ليست "خاصة" تمامًا، إلا أنّنا ما زلنا نعتقد أنّها حلّ أنيق إلى حدّ ما لمشكلة كنا قلقين بشأنها. عندما تتاح لنا الفرصة، سنعالج هذه المشكلة في مكوّناتنا ليتمكّن فريق التطوير من التحكّم بشكل أكبر في استخدام المكوّنات مع الاستفادة في الوقت نفسه من الضوابط التي وضعناها.
نأمل أن تكون هذه النظرة المتعمقة حول كيفية استخدام Web Components مع CSS Custom Properties مفيدة لك. يُرجى إطلاعنا على رأيك، وإذا قررت استخدام أي من هذه الطرق في عملك، يمكنك التواصل معي على Twitter @DavidDarnes. يمكنك أيضًا العثور على Nordhealth @NordhealthHQ على Twitter، بالإضافة إلى بقية فريقي الذين عملوا بجدّ على تجميع نظام التصميم هذا وتنفيذ الميزات المذكورة في هذه المقالة: @Viljamis و@WickyNilliams و@eric_habich.
الصورة الرئيسية من Dan Cristian Pădureț