مزايا استخدام الخصائص المخصّصة في أنظمة التصميم ومكتبات المكوّنات
اسمي "ديف" وأنا مطوّر واجهة أمامية أول في Nordhealth. أعمل على تصميم وتطوير نظام التصميم Nord، والذي يتضمن إنشاء مكوّنات ويب لمكتبة المكوّنات لدينا. أردتُ مشاركة كيفية حلّ المشاكل المتعلّقة بتصميم مكوّنات الويب باستخدام خصائص 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);
لكنّ الميزة الأكثر جاذبية في "مكوّنات الويب" هي أنّها تعمل مع أي إطار عمل JavaScript تقريبًا، أو حتى بدون أي إطار عمل على الإطلاق. بعد الإشارة إلى حزمة JavaScript الرئيسية في الصفحة، يشبه استخدام مكوّن ويب إلى حد كبير استخدام عنصر HTML أصلي. العلامة الوحيدة التي تدل على أنّ العنصر ليس عنصر HTML أصليًا هي الواصلة المتسقة داخل العلامات، وهي معيار يشير إلى المتصفّح بأنّ هذا العنصر هو مكوّن ويب.
تغليف نمط Shadow DOM
تمامًا مثل عناصر HTML الأصلية التي تحتوي على Shadow DOM، تحتوي مكوّنات الويب أيضًا على ذلك. Shadow DOM هي شجرة مخفية من العُقد داخل عنصر معيّن. أفضل طريقة لتصوّر ذلك هي فتح أداة فحص الويب وتفعيل خيار "عرض شجرة نموذج العناصر في المستند (DOM) الظل". بعد إجراء ذلك، حاوِل الاطّلاع على عنصر إدخال أصلي في أداة الفحص، وسيظهر لك الآن خيار فتح عنصر الإدخال هذا والاطّلاع على جميع العناصر بداخله. يمكنك تجربة ذلك مع أحد مكوّنات الويب، مثل مكوّن الإدخال المخصّص للاطّلاع على Shadow DOM الخاص به.
من مزايا (أو عيوب، حسب وجهة نظرك) Shadow DOM هي تغليف الأنماط. إذا كتبت CSS داخل مكوّن الويب، لن تتسرّب هذه الأنماط وتؤثر في الصفحة الرئيسية أو العناصر الأخرى، بل ستكون مضمّنة بالكامل داخل المكوّن. بالإضافة إلى ذلك، لا يمكن أن يتسرّب CSS المكتوب للصفحة الرئيسية أو مكوّن ويب رئيسي إلى مكوّن الويب الخاص بك.
هذا التغليف للأنماط هو ميزة في مكتبة المكونات لدينا. ويمنحنا ذلك ضمانًا أكبر بأنّه عندما يستخدم أحد الأشخاص أحد مكوّناتنا، سيظهر بالشكل الذي نريده، بغض النظر عن الأنماط المطبَّقة على الصفحة الرئيسية. وللتأكّد أكثر، نضيف all: unset; إلى جذر جميع مكوّنات الويب أو "مضيفها".
:host {
all: unset;
display: block;
box-sizing: border-box;
text-align: start;
/* ... */
}
ومع ذلك، ماذا لو كان لدى شخص يستخدم مكوّن الويب الخاص بك سبب مشروع لتغيير أنماط معينة؟ ربما هناك سطر من النص يحتاج إلى مزيد من التباين بسبب سياقه، أو يحتاج الحد إلى أن يكون أكثر سمكًا؟ إذا لم تتمكّن أي أنماط من الوصول إلى المكوّن، كيف يمكنك فتح خيارات الأنماط هذه؟
وهنا يأتي دور الخصائص المخصّصة في 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
عندما نطور مكوّنًا لنظام التصميم لدينا، فإننا نتبع نهجًا مدروسًا في 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-space-l);
}
لكن الميزة الأهم هي أنه عندما نحدد هذه الخصائص المخصصة السياقية على أحد المكونات، فإننا ننشئ نوعًا من واجهة برمجة تطبيقات CSS المخصصة لكل مكون من مكوناتنا، والتي يمكن لمستخدم هذا المكون الاستفادة منها.
<nord-tab-group label="Title">
<!-- ... -->
</nord-tab-group>
<style>
nord-tab-group {
--n-tab-group-padding: var(--n-space-xl);
}
</style>
يعرض المثال السابق أحد مكوّنات الويب مع خاصية مخصّصة سياقية تم تغييرها باستخدام أداة اختيار. نتيجة هذا النهج بأكمله هي مكوّن يوفّر مرونة كافية في التصميم للمستخدم مع الحفاظ على معظم الأنماط الفعلية تحت السيطرة. بالإضافة إلى ذلك، كميزة إضافية، يمكننا كمطوّري مكوّنات اعتراض هذه الأنماط التي يطبّقها المستخدم. إذا أردنا تعديل إحدى هذه السمات أو توسيعها، يمكننا ذلك بدون أن يحتاج المستخدم إلى تغيير أي من الرموز البرمجية.
نجد هذا النهج قويًا للغاية، ليس فقط بالنسبة لنا كمبدعين لمكونات نظام التصميم لدينا، ولكن أيضًا لفريق التطوير لدينا عندما يستخدمون هذه المكونات في منتجاتنا.
الاستفادة من الخصائص المخصّصة بشكل أكبر
في وقت كتابة هذه المقالة، لم نكشف فعليًا عن هذه الخصائص المخصّصة السياقية في مستنداتنا، ولكننا نخطّط لذلك حتى يتمكّن فريق التطوير الأوسع نطاقًا من فهم هذه الخصائص والاستفادة منها. يتم تجميع مكوّناتنا على npm باستخدام ملف بيان يحتوي على كل ما يجب معرفته عنها. بعد ذلك، نستخدم ملف البيان كبيانات عند نشر موقعنا الإلكتروني الخاص بالمستندات، ويتم ذلك باستخدام Eleventy وميزة Global Data. نخطّط لتضمين هذه الخصائص المخصّصة السياقية في ملف بيانات البيان هذا.
من الجوانب الأخرى التي نريد تحسينها هي كيفية اكتساب هذه الخصائص المخصّصة السياقية للقيم. الآن، على سبيل المثال، إذا أردت تعديل لون مكوّنَي فاصل، عليك استهداف هذين المكوّنَين تحديدًا باستخدام أدوات الاختيار، أو تطبيق الخاصية المخصّصة مباشرةً على العنصر باستخدام سمة النمط. قد يبدو هذا جيدًا، ولكن سيكون من المفيد أكثر إذا تمكّن المطوّر من تحديد هذه الأنماط على عنصر يحتوي على هذه الأنماط أو حتى على مستوى الجذر.
<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>
السبب في أنّه عليك ضبط قيمة الخاصية المخصّصة مباشرةً على المكوّن هو أنّنا نحدّدها على العنصر نفسه باستخدام أداة اختيار مضيف المكوّن. إنّ رموز التصميم العامة التي نستخدمها مباشرةً في المكوّن تمرّ مباشرةً بدون أن تتأثر بهذه المشكلة، ويمكن حتى اعتراضها في العناصر الرئيسية. كيف يمكننا الاستفادة من أفضل ما في العالمين؟
الخصائص المخصّصة الخاصة والعلنية
الخصائص المخصّصة الخاصة هي ميزة جمعتها "ليا فيرو"، وهي خاصية مخصّصة "خاصة" سياقية في المكوّن نفسه ولكن تم ضبطها على خاصية مخصّصة "عامة" مع خيار احتياطي.
: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>
على الرغم من أنّه يمكن القول إنّ هذه الطريقة ليست "خاصة" حقًا، نعتقد أنّها حلّ رائع لمشكلة كنّا قلقين بشأنها. عندما تتاح لنا الفرصة، سنعالج هذه المشكلة في مكوّناتنا ليتمكّن فريق التطوير لدينا من التحكّم بشكل أكبر في استخدام المكوّنات مع الاستفادة من الإجراءات الوقائية التي نطبّقها.
نأمل أن تكون هذه النظرة المفصّلة حول كيفية استخدامنا لمكوّنات الويب مع خصائص CSS المخصّصة مفيدة لك. يُرجى إطلاعنا على رأيك، وإذا قررت استخدام أي من هذه الطرق في عملك، يمكنك العثور عليّ على Twitter على الحساب @DavidDarnes. يمكنك أيضًا العثور على Nordhealth على Twitter من خلال @NordhealthHQ، بالإضافة إلى بقية أعضاء فريقي الذين عملوا بجدّ لإنشاء نظام التصميم هذا وتنفيذ الميزات المذكورة في هذا المستند: @Viljamis و@WickyNilliams و@eric_habich.