اصطلاح تسمية BEM وهندسة CSS
لماذا تهم هندسة CSS على نطاق واسع
عندما تبني موقعا شخصيا صغيرا يتكون من صفحات قليلة، يمكنك كتابة CSS بأي طريقة تريدها. قد تضع جميع الأنماط في ملف واحد، وتستخدم أي أسماء فئات تخطر ببالك، وتعتمد على الخصوصية أو التسلسل لترتيب الأمور. لكن بمجرد أن ينمو مشروعك -- عشرات الصفحات، ومطورون متعددون، ومكونات قابلة لإعادة الاستخدام، وتغييرات متكررة في التصميم -- ينهار هذا النهج بشكل مذهل. يصبح CSS الذي كان يمكن إدارته فوضى متشابكة من التجاوزات، والقواعد المكررة، والأنماط التي لا يجرؤ أحد على لمسها خوفا من كسر شيء آخر.
هندسة CSS هي ممارسة تنظيم وتسمية وهيكلة CSS بطريقة مدروسة ومنهجية. توفر اصطلاحات يمكن لكل مطور في الفريق اتباعها، مما يجعل قاعدة الشفرة قابلة للتنبؤ والصيانة والتوسع. بدون هندسة، ستواجه حتما ثلاث مشاكل مدمرة: حروب الخصوصية، وتصادم الأسماء، وCSS الميت. الهندسة الصلبة تمنع الثلاث جميعها.
الكوابيس الثلاث لـ CSS
حروب الخصوصية
تحدث حروب الخصوصية عندما تتنافس محددات متعددة لتنسيق نفس العنصر، ويستمر المطورون في إضافة محددات أكثر تحديدا أو إعلانات !important للفوز بالمعركة. هذا يخلق سباق تسلح حيث يجب أن يكون كل نمط جديد أكثر تحديدا من السابق. في النهاية، تقريبا كل قاعدة تحتوي على !important، وتغيير أي شيء يتطلب تتبعا عبر متاهة من التجاوزات.
حروب الخصوصية في العمل
/* المطور أ يكتب: */
.sidebar .nav .link {
color: blue;
}
/* المطور ب يحتاج لونا مختلفا، يضيف خصوصية أكثر: */
.page .sidebar .nav .link {
color: red;
}
/* المطور ج يشعر بالإحباط، يستخدم !important: */
.sidebar .nav .link {
color: green !important;
}
/* المطور د الآن يحتاج لتجاوز ذلك: */
.page .sidebar .nav .link {
color: purple !important; /* هذا الجنون لا ينتهي */
}
/* أصبح التسلسل غير قابل للتنبؤ تماما */
تصادم الأسماء
يحدث تصادم الأسماء عندما يستخدم مطوران (أو حتى نفس المطور في أوقات مختلفة) نفس اسم الفئة لأغراض مختلفة. بما أن CSS عالمي بشكل افتراضي، فإن فئة تسمى .title في مكون الرأس ستتعارض مع فئة تسمى .title في مكون البطاقة. التعريف الثاني يتجاوز الأول بصمت، مسببا أخطاء يصعب تتبعها.
مثال على تصادم الأسماء
/* في header.css */
.title {
font-size: 2rem;
color: white;
text-transform: uppercase;
}
/* في card.css (يُحمل لاحقا، يفوز بصمت) */
.title {
font-size: 1rem;
color: #333;
font-weight: normal;
}
/* الآن عنوان الرأس يبدو خاطئا،
وليس لديك أي فكرة عن السبب */
CSS الميت
يشير CSS الميت إلى الأنماط التي لم تعد مستخدمة في أي مكان في HTML الخاص بك لكنها تبقى في أوراق الأنماط. مع مرور الوقت، عندما يتم إعادة هيكلة المكونات أو إعادة تصميمها أو إزالتها، لا يجرؤ أحد على حذف CSS المقابل لأنه لا يمكنه التأكد من عدم استخدامه في مكان ما. تنمو ورقة الأنماط بلا نهاية، وتزداد أحجام التنزيل، ويرتبك المطورون الجدد بأنماط لا تخدم أي غرض. بدون اصطلاح تسمية واضح، من المستحيل تتبع أي CSS ينتمي لأي مكون.
تقديم BEM: كتلة، عنصر، معدّل
BEM هو أحد أكثر اصطلاحات تسمية CSS شيوعا، طورته شركة Yandex (محرك البحث الروسي). BEM اختصار لـ Block, Element, Modifier (كتلة، عنصر، معدّل)، ويوفر نمط تسمية بسيط وصارم يزيل حروب الخصوصية، ويمنع تصادم الأسماء، ويجعل CSS الميت سهل التحديد والإزالة.
البنية الأساسية لـ BEM تستخدم شرطتين سفليتين مزدوجتين وشرطتين عاديتين مزدوجتين كفواصل:
نمط بنية BEM
/* الكتلة: مكون مستقل وقابل لإعادة الاستخدام */
.block {}
/* العنصر: جزء من كتلة ليس له معنى بمفرده */
.block__element {}
/* المعدّل: تباين أو حالة لكتلة أو عنصر */
.block--modifier {}
.block__element--modifier {}
/* أمثلة حقيقية: */
.card {} /* كتلة */
.card__title {} /* عنصر */
.card__image {} /* عنصر */
.card--featured {} /* معدّل كتلة */
.card__title--large {} /* معدّل عنصر */
ما هي الكتلة؟
الكتلة هي مكون مستقل ذو معنى يمكن أن يوجد بمفرده. تمثل تجريدا عالي المستوى -- قطعة كاملة من واجهتك. فكر في الكتل كلبنات بناء صفحتك. كل كتلة لها غرض واضح ويمكن إعادة استخدامها في أي مكان. اسم الكتلة يصف غرضها وليس مظهرها. استخدم .error-message وليس .red-box. استخدم .search-form وليس .big-input-area.
أمثلة على الكتل
/* مكون التنقل */
.nav {}
/* مكون نموذج البحث */
.search-form {}
/* مكون البطاقة */
.card {}
/* مكون نافذة الحوار المنبثقة */
.modal {}
/* مكون ملف المستخدم */
.user-profile {}
/* مكون التنبيه */
.alert {}
/* مكون الترقيم */
.pagination {}
ما هو العنصر؟
العنصر هو جزء من كتلة ليس له معنى مستقل. إنه مرتبط دلاليا بكتلته ولا يمكن أن يوجد خارجها. يُشار إلى العناصر بفاصل الشرطة السفلية المزدوجة __. العنصر ينتمي دائما إلى كتلة، وليس إلى عنصر آخر. هذه قاعدة حاسمة: يجب ألا تضع عناصر داخل عناصر (مثلا .block__element1__element2 خاطئ).
أمثلة على العناصر
/* كتلة التنقل مع عناصرها */
.nav {}
.nav__list {}
.nav__item {}
.nav__link {}
.nav__icon {}
/* كتلة البطاقة مع عناصرها */
.card {}
.card__header {}
.card__image {}
.card__title {}
.card__description {}
.card__footer {}
.card__button {}
/* كتلة نموذج البحث مع عناصرها */
.search-form {}
.search-form__input {}
.search-form__button {}
.search-form__icon {}
.nav__link وليس .nav__list__item__link.ما هو المعدّل؟
المعدّل هو علامة على كتلة أو عنصر تغير مظهره أو سلوكه أو حالته. يُشار إلى المعدّلات بفاصل الشرطة العادية المزدوجة --. لا يمكن أن يوجد المعدّل بدون الكتلة أو العنصر الأساسي. في HTML، تقوم دائما بتضمين كل من الفئة الأساسية وفئة المعدّل معا. المعدّلات تصف ما هو مختلف في هذا التباين: حجمه أو لونه أو حالته أو سمته.
أمثلة على المعدّلات
/* معدّلات الكتلة */
.card--featured {}
.card--horizontal {}
.card--dark {}
.button--primary {}
.button--large {}
.button--disabled {}
.alert--success {}
.alert--error {}
.alert--warning {}
/* معدّلات العنصر */
.card__title--highlighted {}
.nav__link--active {}
.nav__link--disabled {}
.form__input--error {}
.form__input--large {}
/* الاستخدام في HTML -- دائما تضمين كل من الأساسي والمعدّل */
<div class="card card--featured">
<h3 class="card__title card__title--highlighted">عرض خاص</h3>
<p class="card__description">هذه البطاقة تبرز.</p>
</div>
أمثلة عملية على BEM
بناء رأس الصفحة باستخدام BEM
لنبنِ رأس موقع كامل باستخدام BEM. الرأس هو كتلة تحتوي على شعار وتنقل وزر دعوة للعمل. لاحظ كيف أن كل اسم فئة يخبرك فورا بأي مكون ينتمي إليه وما هو دوره.
مكون الرأس -- HTML
<header class="header">
<a href="/" class="header__logo">
<img class="header__logo-image" src="logo.svg" alt="الشعار">
</a>
<nav class="header__nav">
<ul class="header__nav-list">
<li class="header__nav-item">
<a class="header__nav-link header__nav-link--active" href="/">الرئيسية</a>
</li>
<li class="header__nav-item">
<a class="header__nav-link" href="/about">حول</a>
</li>
<li class="header__nav-item">
<a class="header__nav-link" href="/contact">اتصل بنا</a>
</li>
</ul>
</nav>
<a class="header__cta" href="/signup">سجّل الآن</a>
</header>
مكون الرأس -- CSS
/* الكتلة */
.header {
display: flex;
align-items: center;
justify-content: space-between;
padding: 1rem 2rem;
background-color: #fff;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
}
/* العناصر */
.header__logo {
display: flex;
align-items: center;
text-decoration: none;
}
.header__logo-image {
height: 40px;
width: auto;
}
.header__nav-list {
display: flex;
list-style: none;
margin: 0;
padding: 0;
gap: 1.5rem;
}
.header__nav-item {
display: inline-block;
}
.header__nav-link {
text-decoration: none;
color: #555;
font-weight: 500;
padding: 0.5rem 0;
border-bottom: 2px solid transparent;
transition: color 0.2s, border-color 0.2s;
}
/* معدّل العنصر */
.header__nav-link--active {
color: #2563eb;
border-bottom-color: #2563eb;
}
.header__cta {
padding: 0.5rem 1.5rem;
background-color: #2563eb;
color: #fff;
border-radius: 6px;
text-decoration: none;
font-weight: 600;
}
بناء مكون البطاقة باستخدام BEM
مكون البطاقة -- HTML و CSS
/* HTML */
<article class="card card--featured">
<div class="card__image-wrapper">
<img class="card__image" src="photo.jpg" alt="صورة المقال">
<span class="card__badge">جديد</span>
</div>
<div class="card__body">
<h3 class="card__title">فهم BEM</h3>
<p class="card__excerpt">تعلم كيف يحول BEM الـ CSS الخاص بك...</p>
<div class="card__meta">
<span class="card__author">سارة أحمد</span>
<time class="card__date">15 أكتوبر 2024</time>
</div>
</div>
<div class="card__footer">
<a class="card__link" href="/article">اقرأ المزيد</a>
</div>
</article>
/* CSS */
.card {
border: 1px solid #e5e7eb;
border-radius: 12px;
overflow: hidden;
background: #fff;
transition: box-shadow 0.3s;
}
.card--featured {
border-color: #2563eb;
box-shadow: 0 4px 16px rgba(37, 99, 235, 0.15);
}
.card__image-wrapper {
position: relative;
overflow: hidden;
}
.card__image {
width: 100%;
height: 200px;
object-fit: cover;
}
.card__badge {
position: absolute;
top: 12px;
right: 12px;
padding: 4px 12px;
background: #2563eb;
color: #fff;
border-radius: 20px;
font-size: 0.75rem;
font-weight: 600;
}
.card__body {
padding: 1.25rem;
}
.card__title {
margin: 0 0 0.5rem;
font-size: 1.25rem;
color: #111;
}
.card__excerpt {
color: #6b7280;
line-height: 1.6;
margin: 0 0 1rem;
}
.card__meta {
display: flex;
justify-content: space-between;
font-size: 0.85rem;
color: #9ca3af;
}
.card__footer {
padding: 1rem 1.25rem;
border-top: 1px solid #f3f4f6;
}
.card__link {
color: #2563eb;
font-weight: 600;
text-decoration: none;
}
بناء نموذج باستخدام BEM
مكون النموذج مع BEM
/* HTML */
<form class="form form--inline">
<div class="form__group">
<label class="form__label" for="email">البريد الإلكتروني</label>
<input class="form__input form__input--error" type="email"
id="email" placeholder="you@example.com">
<span class="form__error-message">يرجى إدخال بريد إلكتروني صالح</span>
</div>
<div class="form__group">
<label class="form__label" for="password">كلمة المرور</label>
<input class="form__input" type="password" id="password">
</div>
<div class="form__actions">
<button class="form__button form__button--primary" type="submit">تسجيل الدخول</button>
<button class="form__button form__button--secondary" type="reset">إلغاء</button>
</div>
</form>
/* CSS */
.form {
max-width: 400px;
padding: 2rem;
}
.form--inline {
display: flex;
flex-wrap: wrap;
gap: 1rem;
max-width: 100%;
}
.form__group {
margin-bottom: 1.25rem;
display: flex;
flex-direction: column;
}
.form__label {
font-weight: 600;
margin-bottom: 0.5rem;
color: #374151;
}
.form__input {
padding: 0.75rem 1rem;
border: 1px solid #d1d5db;
border-radius: 8px;
font-size: 1rem;
transition: border-color 0.2s;
}
.form__input:focus {
outline: none;
border-color: #2563eb;
box-shadow: 0 0 0 3px rgba(37, 99, 235, 0.1);
}
.form__input--error {
border-color: #ef4444;
}
.form__error-message {
color: #ef4444;
font-size: 0.85rem;
margin-top: 0.25rem;
}
.form__button {
padding: 0.75rem 1.5rem;
border: none;
border-radius: 8px;
font-weight: 600;
cursor: pointer;
}
.form__button--primary {
background: #2563eb;
color: #fff;
}
.form__button--secondary {
background: #f3f4f6;
color: #374151;
}
متى تنشئ كتلة مقابل عنصر مقابل معدّل
أحد أكثر الأسئلة شيوعا عند استخدام BEM هو تحديد ما إذا كان شيء ما يجب أن يكون كتلة جديدة أو عنصرا من كتلة موجودة أو معدّلا. إليك إطار عمل واضح للقرار:
أنشئ كتلة جديدة عندما:
- يمكن إعادة استخدام المكون بشكل مستقل في سياقات مختلفة.
- المكون له معنى بمفرده، خارج الحاوي الحالي.
- تريد وضع المكون في مكان آخر على الصفحة أو في مشروع آخر.
- المكون معقد بما يكفي ليحتوي على عناصره الداخلية الخاصة.
أنشئ عنصرا عندما:
- الجزء منطقي فقط داخل الكتلة الأم.
- الجزء لا يمكن استخدامه خارج سياق الكتلة.
- إزالة الكتلة ستجعل الجزء بلا معنى.
أنشئ معدّلا عندما:
- تحتاج إلى تباين لكتلة أو عنصر موجود.
- الاختلاف مرئي (الحجم، اللون، السمة) أو سلوكي (معطل، نشط، موسع).
- بنية المكون الأساسي تبقى كما هي؛ فقط بعض الخصائص تتغير.
.card__body__title__icon)، فهذه إشارة قوية على أن الجزء الداخلي يجب أن يكون كتلة خاصة به. قم بتسطيح التسلسل إما بجعله عنصرا مباشرا للكتلة الأم أو باستخراجه ككتلة منفصلة تماما.BEM مع التداخل (المعالجات المسبقة)
عند استخدام معالجات CSS المسبقة مثل Sass أو Less، يمكنك استخدام التداخل مع محدد الأب & لكتابة BEM بكفاءة أكبر. يقوم المعالج المسبق بتجميع المحددات المتداخلة إلى أسماء فئات BEM مسطحة. هذا يحافظ على تنظيم شفرتك المصدرية دون إضافة خصوصية غير ضرورية.
BEM مع تداخل Sass
/* مصدر Sass */
.card {
border: 1px solid #e5e7eb;
border-radius: 12px;
&__title {
font-size: 1.25rem;
color: #111;
}
&__description {
color: #6b7280;
line-height: 1.6;
}
&__image {
width: 100%;
object-fit: cover;
}
&--featured {
border-color: #2563eb;
box-shadow: 0 4px 16px rgba(37, 99, 235, 0.15);
}
&--horizontal {
display: flex;
.card__image {
width: 40%;
height: auto;
}
}
}
/* مخرجات CSS المجمعة */
.card { border: 1px solid #e5e7eb; border-radius: 12px; }
.card__title { font-size: 1.25rem; color: #111; }
.card__description { color: #6b7280; line-height: 1.6; }
.card__image { width: 100%; object-fit: cover; }
.card--featured { border-color: #2563eb; box-shadow: 0 4px 16px rgba(37, 99, 235, 0.15); }
.card--horizontal { display: flex; }
.card--horizontal .card__image { width: 40%; height: auto; }
&، وليس للمحددات التنازلية.منهجيات هندسة CSS الأخرى
OOCSS (CSS الكائني التوجه)
تم إنشاء OOCSS بواسطة Nicole Sullivan ويستند إلى مبدأين أساسيين: فصل البنية عن المظهر وفصل الحاوي عن المحتوى. البنية تشير إلى خصائص التخطيط مثل العرض والارتفاع والهامش والحشو. المظهر يشير إلى الخصائص المرئية مثل اللون والخلفية والحدود والظل. من خلال فصل هذه الاهتمامات، تنشئ كائنات CSS قابلة لإعادة الاستخدام بدرجة عالية.
مثال OOCSS
/* البنية (كائن تخطيط قابل لإعادة الاستخدام) */
.media {
display: flex;
align-items: flex-start;
gap: 1rem;
}
.media__image {
flex-shrink: 0;
}
.media__body {
flex: 1;
}
/* المظهر (تباينات مرئية) */
.box {
padding: 1.5rem;
border-radius: 8px;
}
.box--light {
background: #f9fafb;
border: 1px solid #e5e7eb;
}
.box--dark {
background: #1f2937;
color: #f3f4f6;
}
/* ادمجها بحرية في HTML */
<div class="media box box--light">
<img class="media__image" src="avatar.jpg">
<div class="media__body">
<p>المحتوى هنا...</p>
</div>
</div>
SMACSS (هندسة قابلة للتوسع والتقسيم لـ CSS)
SMACSS، أنشأها Jonathan Snook، تصنف قواعد CSS إلى خمسة أنواع: أساسي (افتراضيات)، تخطيط (الأقسام الرئيسية)، وحدة (مكونات قابلة لإعادة الاستخدام)، حالة (تجاوزات للحالات المختلفة)، وسمة (تجاوزات مرئية للتنسيق). كل فئة لها اصطلاح تسمية وتنظيم ملفات خاص بها.
فئات SMACSS
/* أساسي -- الافتراضيات، إعادة التعيين، أنماط العناصر */
html, body { margin: 0; font-family: sans-serif; }
a { color: #2563eb; }
img { max-width: 100%; }
/* تخطيط -- بادئة l- */
.l-header { display: flex; padding: 1rem 2rem; }
.l-sidebar { width: 250px; }
.l-main { flex: 1; padding: 2rem; }
.l-footer { padding: 2rem; background: #111; }
/* وحدة -- مكونات قابلة لإعادة الاستخدام */
.card { border: 1px solid #e5e7eb; border-radius: 8px; }
.card-title { font-size: 1.25rem; }
.nav { display: flex; gap: 1rem; }
.nav-link { text-decoration: none; }
/* حالة -- بادئة is- أو has- */
.is-active { color: #2563eb; font-weight: bold; }
.is-hidden { display: none; }
.is-loading { opacity: 0.5; pointer-events: none; }
.has-error { border-color: #ef4444; }
/* سمة -- تجاوزات مرئية */
.theme-dark .card { background: #1f2937; color: #f3f4f6; }
.theme-dark .nav-link { color: #93c5fd; }
ITCSS (CSS المثلث المقلوب)
ITCSS، أنشأها Harry Roberts، تنظم CSS حسب الخصوصية في شكل مثلث مقلوب. الأنماط في أعلى المثلث هي الأوسع والأقل تحديدا (الإعدادات، الأدوات، إعادة التعيين العامة). كلما انتقلت للأسفل، تصبح الأنماط أكثر تحديدا واستهدافا (المكونات، الأدوات المساعدة). هذا الترتيب يضمن أن الخصوصية تزداد بشكل طبيعي ولن تحتاج أبدا لمحاربة التسلسل.
تنظيم طبقات ITCSS
/* 1. الإعدادات -- المتغيرات، التكوين (لا مخرجات CSS) */
$color-primary: #2563eb;
$font-size-base: 1rem;
$spacing-unit: 8px;
/* 2. الأدوات -- المخاليط، الدوال (لا مخرجات CSS) */
@mixin respond-to($breakpoint) { /* ... */ }
/* 3. عام -- إعادة التعيين، التطبيع، حجم الصندوق */
*, *::before, *::after { box-sizing: border-box; }
body { margin: 0; }
/* 4. العناصر -- أنماط عناصر HTML المجردة */
h1, h2, h3 { line-height: 1.2; }
a { color: $color-primary; }
/* 5. الكائنات -- أنماط التخطيط، بدون تجميل */
.o-container { max-width: 1200px; margin: 0 auto; padding: 0 1rem; }
.o-grid { display: grid; gap: 1rem; }
/* 6. المكونات -- مكونات واجهة مصممة */
.c-card { border: 1px solid #e5e7eb; border-radius: 12px; }
.c-button { padding: 0.75rem 1.5rem; border-radius: 8px; }
/* 7. الأدوات المساعدة -- تجاوزات مع !important */
.u-hidden { display: none !important; }
.u-text-center { text-align: center !important; }
.u-mb-0 { margin-bottom: 0 !important; }
CSS الأول بالأدوات (Tailwind)
يأخذ CSS الأول بالأدوات نهجا مختلفا جذريا. بدلا من كتابة فئات مكونات مخصصة، تقوم بتكوين الأنماط مباشرة في HTML باستخدام فئات أدوات صغيرة ذات غرض واحد. Tailwind CSS هو أكثر الأطر شيوعا الذي يتبع هذا النهج. كل فئة أداة تفعل شيئا واحدا بالضبط: .text-center تمركز النص، .p-4 تضيف حشوا، .bg-blue-500 تضبط خلفية زرقاء.
مثال CSS الأول بالأدوات (Tailwind)
/* نهج BEM التقليدي */
<div class="card card--featured">
<h3 class="card__title">العنوان</h3>
<p class="card__description">الوصف</p>
</div>
/* نهج الأدوات أولا (Tailwind) */
<div class="border rounded-xl shadow-lg overflow-hidden bg-white">
<h3 class="text-xl font-bold text-gray-900 px-5 pt-5">العنوان</h3>
<p class="text-gray-600 leading-relaxed px-5 pb-5">الوصف</p>
</div>
/* المزايا: لا تبديل سياق، تطوير سريع جدا،
لا CSS ميت، سهل رؤية جميع الأنماط بنظرة */
/* العيوب: HTML يمكن أن يصبح مطولا جدا،
أصعب في رؤية أنماط المكونات،
يتطلب أداة بناء لتنقية الأدوات غير المستخدمة */
CSS الذري
CSS الذري مشابه للأدوات أولا لكنه يأخذه إلى أقصى حد. كل فئة CSS تطبق إعلانا واحدا بالضبط. اصطلاح التسمية يرتبط مباشرة بالخاصية والقيمة. أطر مثل Atomizer وTachyons تتبع هذا النمط. CSS الذري يولد أصغر حزم CSS ممكنة لأن كل قاعدة تُعرّف مرة واحدة فقط وتُعاد استخدامها في كل مكان.
مثال CSS الذري
/* كل فئة = إعلان CSS واحد */
.d-flex { display: flex; }
.ai-center { align-items: center; }
.jc-between { justify-content: space-between; }
.p-16 { padding: 16px; }
.m-0 { margin: 0; }
.fs-14 { font-size: 14px; }
.fw-bold { font-weight: bold; }
.c-blue { color: #2563eb; }
.bg-white { background-color: #fff; }
.br-8 { border-radius: 8px; }
/* الاستخدام */
<div class="d-flex ai-center jc-between p-16 bg-white br-8">
<span class="fs-14 fw-bold c-blue">مرحبا</span>
</div>
وحدات CSS
وحدات CSS هي حل في وقت البناء يحدد نطاق أسماء الفئات تلقائيا للمكون الذي يستوردها. عندما تكتب وحدة CSS، تقوم أداة البناء (Webpack، Vite، إلخ) بتحويل كل اسم فئة إلى معرف فريد مُجزّأ. هذا يزيل تصادم الأسماء تماما دون الحاجة إلى أي اصطلاح تسمية. وحدات CSS شائعة بشكل خاص في مشاريع React وVue.
مثال وحدات CSS
/* Card.module.css */
.card {
border: 1px solid #e5e7eb;
border-radius: 12px;
}
.title {
font-size: 1.25rem;
color: #111;
}
.featured {
border-color: #2563eb;
}
/* في مكون JavaScript الخاص بك */
import styles from './Card.module.css';
function Card({ featured }) {
return (
<div className={`${styles.card} ${featured ? styles.featured : ''}`}>
<h3 className={styles.title}>بطاقتي</h3>
</div>
);
}
/* المخرجات المجمعة في المتصفح: */
<div class="Card_card__x7f2k Card_featured__a3b1c">
<h3 class="Card_title__q9d4e">بطاقتي</h3>
</div>
/* لا تصادم أسماء ممكن! */
اختيار الهندسة المناسبة
لا توجد هندسة CSS واحدة أفضل. الخيار الصحيح يعتمد على مشروعك وفريقك وأدواتك. إليك دليل لمساعدتك في القرار:
- BEM -- الأفضل للفرق التي تكتب CSS أو Sass تقليدي. بسيط للتعلم، يعمل في كل مكان، لا يتطلب أدوات بناء. رائع لأنظمة التصميم ومكتبات المكونات.
- OOCSS -- الأفضل عندما تحتاج أقصى قابلية لإعادة الاستخدام. مثالي للمشاريع الكبيرة ذات المكونات المتشابهة المظهر التي تتشارك أنماطا مرئية.
- SMACSS -- الأفضل لتنظيم قواعد الشفرة الكبيرة الموجودة. نظام التصنيف الخاص بها يساعد في إحداث النظام في الفوضى.
- ITCSS -- الأفضل للفرق التي تعاني مع الخصوصية. المثلث المقلوب يضمن أن التسلسل يعمل لصالحك وليس ضدك.
- الأدوات أولا (Tailwind) -- الأفضل للنمذجة السريعة والمشاريع الصغيرة إلى المتوسطة. يتطلب أدوات بناء لكنه يزيل CSS الميت تلقائيا.
- وحدات CSS -- الأفضل لأطر JavaScript القائمة على المكونات (React، Vue). نطاق تلقائي بدون جهد تسمية.
تنظيم ملفات CSS الخاصة بك
هيكل الملفات المنظم جيدا يجعل هندسة CSS ملموسة. إليك هيكلا موصى به يجمع بين طبقات ITCSS ومكونات BEM:
هيكل ملفات CSS الموصى به
styles/
|- settings/
| |- _variables.scss /* الألوان، الخطوط، التباعد */
| |- _breakpoints.scss /* نقاط التوقف المتجاوبة */
|
|- tools/
| |- _mixins.scss /* مخاليط Sass قابلة لإعادة الاستخدام */
| |- _functions.scss /* دوال Sass */
|
|- generic/
| |- _reset.scss /* إعادة تعيين CSS أو التطبيع */
| |- _box-sizing.scss /* قواعد حجم الصندوق */
|
|- elements/
| |- _headings.scss /* افتراضيات h1-h6 */
| |- _links.scss /* افتراضيات الروابط */
| |- _lists.scss /* افتراضيات القوائم */
|
|- objects/
| |- _container.scss /* .o-container */
| |- _grid.scss /* .o-grid */
| |- _media.scss /* .o-media */
|
|- components/
| |- _card.scss /* .card, .card__*, .card--* */
| |- _button.scss /* .button, .button--* */
| |- _header.scss /* .header, .header__* */
| |- _nav.scss /* .nav, .nav__* */
| |- _form.scss /* .form, .form__* */
| |- _modal.scss /* .modal, .modal__* */
|
|- utilities/
| |- _text.scss /* .u-text-center, .u-text-right */
| |- _spacing.scss /* .u-mb-0, .u-mt-2, .u-p-1 */
| |- _display.scss /* .u-hidden, .u-block */
|
|- main.scss /* يستورد كل شيء بالترتيب */
إعادة هيكلة CSS القديم إلى BEM
لنتتبع مثال إعادة هيكلة واقعي. إليك نمط شائع قد تجده في CSS القديم، وكيفية تحويله إلى BEM نظيف.
قبل: CSS القديم
/* CSS القديم -- متداخل بعمق، خصوصية عالية */
#main-content .sidebar .widget {
background: #fff;
border: 1px solid #ddd;
padding: 20px;
margin-bottom: 20px;
}
#main-content .sidebar .widget h3 {
font-size: 18px;
color: #333;
border-bottom: 2px solid #2563eb;
padding-bottom: 10px;
}
#main-content .sidebar .widget ul {
list-style: none;
padding: 0;
}
#main-content .sidebar .widget ul li {
padding: 8px 0;
border-bottom: 1px solid #f0f0f0;
}
#main-content .sidebar .widget ul li a {
color: #555;
text-decoration: none;
}
#main-content .sidebar .widget ul li a:hover {
color: #2563eb;
}
#main-content .sidebar .widget.popular {
border-color: #2563eb;
}
#main-content .sidebar .widget.popular h3 {
color: #2563eb;
}
بعد: BEM نظيف
/* CSS BEM المُعاد هيكلته -- محددات مسطحة، خصوصية منخفضة */
.widget {
background: #fff;
border: 1px solid #ddd;
padding: 20px;
margin-bottom: 20px;
}
.widget--popular {
border-color: #2563eb;
}
.widget__heading {
font-size: 18px;
color: #333;
border-bottom: 2px solid #2563eb;
padding-bottom: 10px;
}
.widget--popular .widget__heading {
color: #2563eb;
}
.widget__list {
list-style: none;
padding: 0;
}
.widget__item {
padding: 8px 0;
border-bottom: 1px solid #f0f0f0;
}
.widget__link {
color: #555;
text-decoration: none;
}
.widget__link:hover {
color: #2563eb;
}
/* HTML المُعاد هيكلته */
<div class="widget widget--popular">
<h3 class="widget__heading">المقالات الشائعة</h3>
<ul class="widget__list">
<li class="widget__item">
<a class="widget__link" href="/post-1">المقال الأول</a>
</li>
<li class="widget__item">
<a class="widget__link" href="/post-2">المقال الثاني</a>
</li>
</ul>
</div>
لاحظ الفرق: CSS القديم كان بخصوصية قصوى 1-3-1 (معرّف + ثلاث فئات + عنصر واحد)، بينما CSS BEM المُعاد هيكلته لا يتجاوز أبدا 0-2-0 (فئتان). كل محدد مسطح وقابل للتنبؤ وسهل التجاوز. أسماء الفئات تخبرك بالضبط بأي مكون تنتمي إليه، ويمكنك حذف كتلة CSS الكاملة لـ .widget بأمان إذا أزلت مكون الودجت من HTML الخاص بك.