منهجية BEM مع SASS
منهجية BEM مع SASS
BEM (Block Element Modifier) هي اصطلاح تسمية قوي لفئات CSS يعزز إعادة الاستخدام وسهولة الصيانة وقابلية التوسع. عندما يتم دمجها مع SASS، تصبح BEM أكثر أناقة وكفاءة. في هذا الدرس الشامل، سنستكشف كيفية تطبيق منهجية BEM بفعالية باستخدام إمكانيات التداخل في SASS.
ما هي BEM؟
BEM تعني Block Element Modifier (كتلة عنصر معدّل). إنها منهجية تسمية تساعدك على إنشاء مكونات قابلة لإعادة الاستخدام وتجعل الكود الخاص بك أسهل في الفهم والصيانة.
المفاهيم الأساسية الثلاثة
بنية BEM
// كتلة: مكون مستقل
.block { }
// عنصر: جزء من كتلة (يستخدم شرطة سفلية مزدوجة __)
.block__element { }
// معدّل: حالة أو تنوع مختلف (يستخدم شرطة مزدوجة --)
.block--modifier { }
.block__element--modifier { }
1. الكتلة (Block)
الكتلة هي مكون مستقل قابل لإعادة الاستخدام. إنها تمثل كيان مستقل ذو معنى.
- أمثلة:
header,menu,button,card,search-form - الكتل يمكن أن تحتوي على كتل أخرى
- الكتل مستقلة ولا تعتمد على عناصر صفحة أخرى
مثال على الكتلة
<!-- كتلة: card -->
<div class="card">
<!-- محتوى البطاقة -->
</div>
<!-- كتلة: button -->
<button class="button">انقر هنا</button>
2. العنصر (Element)
العنصر هو جزء من الكتلة ليس له معنى مستقل. العناصر مرتبطة دلالياً بكتلتها.
- العناصر يتم تمثيلها بشرطة سفلية مزدوجة:
block__element - أمثلة:
card__title,card__image,menu__item - العناصر موجودة ضمن سياق كتلتها
- العناصر لا يمكن أن توجد خارج كتلتها
مثال على العنصر
<div class="card">
<img class="card__image" src="photo.jpg" alt="صورة">
<h3 class="card__title">عنوان البطاقة</h3>
<p class="card__description">نص وصف البطاقة.</p>
<button class="card__button">اقرأ المزيد</button>
</div>
block__element__subelement. بدلاً من ذلك، فكر فيما إذا كان العنصر الفرعي يجب أن يكون عنصراً منفصلاً أو حتى كتلة جديدة.
3. المعدّل (Modifier)
المعدّل يمثل حالة أو تنوع مختلف لكتلة أو عنصر.
- المعدّلات يتم تمثيلها بشرطة مزدوجة:
block--modifier - أمثلة:
button--primary,card--featured,menu__item--active - المعدّلات تغير المظهر أو السلوك أو الحالة
- استخدم المعدّلات للثيمات والأحجام والحالات والتنوعات
مثال على المعدّل
<!-- زر عادي -->
<button class="button">زر عادي</button>
<!-- معدّل زر أساسي -->
<button class="button button--primary">زر أساسي</button>
<!-- معدّل زر كبير -->
<button class="button button--large">زر كبير</button>
<!-- معدّلات مجتمعة -->
<button class="button button--primary button--large">أساسي كبير</button>
لماذا توجد BEM
المشكلة: حروب خصوصية CSS
بدون اصطلاح تسمية، يمكن أن يصبح CSS كابوساً في الصيانة:
CSS إشكالي بدون BEM
/* عام جداً، سيتعارض */
.title { }
.image { }
.button { }
/* محدد جداً، صعب التجاوز */
.sidebar .widget .title { }
#header nav ul li a { }
/* تسمية غامضة */
.primary { }
.red { }
.big { }
حلول BEM
- خصوصية منخفضة: BEM تستخدم محددات الفئات فقط (خصوصية: 0,0,1,0)
- لا تعارضات: أسماء الكتل الفريدة تمنع تصادمات الأنماط
- قابلية القراءة: أسماء الفئات تصف البنية والغرض
- قابلية التنبؤ: تعرف بالضبط أين ستطبق الأنماط
- قابلية التوسع: سهل إضافة مكونات جديدة دون كسر الموجودة
حل BEM
/* واضح، محدد، خصوصية منخفضة */
.card { }
.card__title { }
.card__image { }
.card--featured { }
.article { }
.article__title { } /* لن يتعارض مع .card__title */
.article__image { }
SASS و BEM: مثاليان معاً
محدد الأب & في SASS يجعل كتابة BEM نظيفة وقابلة للصيانة بشكل لا يصدق:
BEM مع تداخل SASS
// بدون SASS - متكرر
.card { }
.card__header { }
.card__body { }
.card__footer { }
.card--featured { }
// مع SASS - DRY ومنظم
.card {
// أنماط الكتلة
background: white;
border: 1px solid #ddd;
border-radius: 4px;
// العناصر (استخدم & للإشارة إلى الأب)
&__header {
padding: 1rem;
border-bottom: 1px solid #ddd;
}
&__body {
padding: 1rem;
}
&__footer {
padding: 1rem;
border-top: 1px solid #ddd;
}
// المعدّلات
&--featured {
border: 2px solid #007bff;
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);
}
&--dark {
background: #333;
color: white;
}
}
& في SASS يتم استبداله بمحدد الأب. لذا &__header داخل .card يتم تجميعها إلى .card__header.
أمثلة BEM من العالم الحقيقي
مثال 1: مكون التنقل
HTML التنقل
<nav class="nav">
<ul class="nav__list">
<li class="nav__item">
<a href="#" class="nav__link nav__link--active">الرئيسية</a>
</li>
<li class="nav__item">
<a href="#" class="nav__link">حول</a>
</li>
<li class="nav__item">
<a href="#" class="nav__link">الخدمات</a>
</li>
<li class="nav__item nav__item--dropdown">
<a href="#" class="nav__link">المنتجات</a>
<ul class="nav__dropdown">
<li class="nav__dropdown-item">
<a href="#" class="nav__dropdown-link">منتج 1</a>
</li>
</ul>
</li>
</ul>
</nav>
SCSS التنقل مع BEM
.nav {
background: #333;
padding: 0 1rem;
&__list {
list-style: none;
margin: 0;
padding: 0;
display: flex;
}
&__item {
position: relative;
&--dropdown {
&:hover .nav__dropdown {
display: block;
}
}
}
&__link {
display: block;
padding: 1rem;
color: white;
text-decoration: none;
transition: background 0.3s;
&:hover {
background: rgba(255, 255, 255, 0.1);
}
&--active {
background: #007bff;
font-weight: bold;
}
}
&__dropdown {
display: none;
position: absolute;
top: 100%;
left: 0;
background: #444;
min-width: 200px;
list-style: none;
margin: 0;
padding: 0;
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.2);
}
&__dropdown-item {
border-bottom: 1px solid #555;
&:last-child {
border-bottom: none;
}
}
&__dropdown-link {
display: block;
padding: 0.75rem 1rem;
color: white;
text-decoration: none;
&:hover {
background: #555;
}
}
}
مثال 2: مكون البطاقة
HTML البطاقة مع BEM
<article class="card card--featured">
<img class="card__image" src="photo.jpg" alt="صورة">
<div class="card__content">
<span class="card__badge card__badge--new">جديد</span>
<h2 class="card__title">عنوان المقال</h2>
<p class="card__description">
وصف المقال يذهب هنا مع بعض النص التمهيدي.
</p>
<div class="card__meta">
<span class="card__author">أحمد محمد</span>
<span class="card__date">15 يناير 2024</span>
</div>
</div>
<footer class="card__footer">
<button class="card__button card__button--primary">اقرأ المزيد</button>
<button class="card__button card__button--outline">حفظ</button>
</footer>
</article>
SCSS البطاقة مع BEM
.card {
background: white;
border-radius: 8px;
overflow: hidden;
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
transition: transform 0.3s, box-shadow 0.3s;
&:hover {
transform: translateY(-4px);
box-shadow: 0 8px 16px rgba(0, 0, 0, 0.15);
}
&__image {
width: 100%;
height: 200px;
object-fit: cover;
display: block;
}
&__content {
padding: 1.5rem;
position: relative;
}
&__badge {
position: absolute;
top: 1rem;
right: 1rem;
padding: 0.25rem 0.75rem;
border-radius: 12px;
font-size: 0.75rem;
font-weight: bold;
text-transform: uppercase;
&--new {
background: #28a745;
color: white;
}
&--hot {
background: #dc3545;
color: white;
}
}
&__title {
margin: 0 0 0.5rem;
font-size: 1.5rem;
color: #333;
}
&__description {
margin: 0 0 1rem;
color: #666;
line-height: 1.6;
}
&__meta {
display: flex;
gap: 1rem;
font-size: 0.875rem;
color: #999;
}
&__author {
font-weight: 500;
}
&__date {
&::before {
content: "•";
margin-right: 0.5rem;
}
}
&__footer {
padding: 1rem 1.5rem;
background: #f8f9fa;
display: flex;
gap: 0.5rem;
}
&__button {
flex: 1;
padding: 0.5rem 1rem;
border: none;
border-radius: 4px;
font-weight: 500;
cursor: pointer;
transition: all 0.3s;
&--primary {
background: #007bff;
color: white;
&:hover {
background: #0056b3;
}
}
&--outline {
background: transparent;
color: #007bff;
border: 1px solid #007bff;
&:hover {
background: #007bff;
color: white;
}
}
}
// معدّل البطاقة: مميزة
&--featured {
border: 2px solid #ffc107;
.card__title {
color: #ffc107;
}
}
// معدّل البطاقة: ثيم داكن
&--dark {
background: #333;
color: white;
.card__title {
color: white;
}
.card__description {
color: #ccc;
}
.card__footer {
background: #222;
}
}
}
مثال 3: مكون النموذج
SCSS النموذج مع BEM
.form {
max-width: 600px;
margin: 0 auto;
padding: 2rem;
background: white;
border-radius: 8px;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
&__group {
margin-bottom: 1.5rem;
&--inline {
display: flex;
gap: 1rem;
.form__field {
flex: 1;
}
}
}
&__label {
display: block;
margin-bottom: 0.5rem;
font-weight: 500;
color: #333;
&--required::after {
content: "*";
color: #dc3545;
margin-left: 0.25rem;
}
}
&__field {
width: 100%;
padding: 0.75rem;
border: 1px solid #ddd;
border-radius: 4px;
font-size: 1rem;
transition: border-color 0.3s, box-shadow 0.3s;
&:focus {
outline: none;
border-color: #007bff;
box-shadow: 0 0 0 3px rgba(0, 123, 255, 0.1);
}
&--error {
border-color: #dc3545;
&:focus {
box-shadow: 0 0 0 3px rgba(220, 53, 69, 0.1);
}
}
&--success {
border-color: #28a745;
&:focus {
box-shadow: 0 0 0 3px rgba(40, 167, 69, 0.1);
}
}
}
&__error {
display: block;
margin-top: 0.25rem;
font-size: 0.875rem;
color: #dc3545;
}
&__hint {
display: block;
margin-top: 0.25rem;
font-size: 0.875rem;
color: #6c757d;
}
&__checkbox {
display: flex;
align-items: center;
gap: 0.5rem;
input {
width: auto;
}
label {
margin: 0;
}
}
&__submit {
width: 100%;
padding: 0.75rem;
background: #007bff;
color: white;
border: none;
border-radius: 4px;
font-size: 1rem;
font-weight: 500;
cursor: pointer;
transition: background 0.3s;
&:hover {
background: #0056b3;
}
&:disabled {
background: #6c757d;
cursor: not-allowed;
}
}
// معدّلات حجم النموذج
&--small {
.form__field,
.form__submit {
padding: 0.5rem;
font-size: 0.875rem;
}
}
&--large {
.form__field,
.form__submit {
padding: 1rem;
font-size: 1.125rem;
}
}
}
مزالق BEM والأخطاء الشائعة
خطأ 1: الإفراط في تداخل العناصر
خطأ: عناصر متداخلة
// لا تفعل هذا
.card__header__title { }
.card__body__content__text { }
// هذا يهزم غرض BEM ويخلق أسماء فئات طويلة
صحيح: بنية مسطحة
// افعل هذا بدلاً من ذلك
.card__header { }
.card__title { } /* حتى لو كانت داخل header */
.card__text { } /* حتى لو كانت متداخلة بعمق */
// العناصر دائماً أطفال مباشرون للكتلة (في التسمية)
خطأ 2: استخدام محددات العناصر
خطأ: الاقتران بعلامات HTML
// لا تفعل هذا
.card h2 { }
.card div.content { }
.card > p { }
// هذا يكسر BEM ويضيف خصوصية غير ضرورية
صحيح: محددات الفئات فقط
// افعل هذا
.card__title { }
.card__content { }
.card__text { }
// استخدم الفئات دائماً، لا تعتمد على بنية HTML
خطأ 3: معدّل بدون فئة أساسية
خطأ: معدّل فقط
<!-- لا تفعل هذا -->
<button class="button--primary">انقر هنا</button>
/* الأنماط الأساسية مفقودة */
صحيح: أساسي + معدّل
<!-- افعل هذا -->
<button class="button button--primary">انقر هنا</button>
/* كلا الفئتين مطلوبتان: أنماط أساسية + أنماط معدّل */
خطأ 4: تداخل SASS عميق جداً
خطأ: تداخل مفرط
// لا تفعل هذا - صعب القراءة والتجاوز
.card {
&__header {
&__title {
&--large {
// 4 مستويات عمق!
}
}
}
}
صحيح: تداخل ضحل
// افعل هذا - حافظ على التداخل ضحلاً
.card {
&__header { }
&__title { }
&__title--large { }
}
// حد أقصى 2-3 مستويات من التداخل في SASS
تنوعات BEM
شرطتان مقابل شرطتان سفليتان (BEM الأصلية)
BEM الأصلية تستخدم فواصل مختلفة:
صيغة BEM الأصلية
/* كتلة */
.block { }
/* عنصر (شرطة سفلية واحدة) */
.block_element { }
/* معدّل (شرطة سفلية واحدة) */
.block_modifier { }
.block_element_modifier { }
/* أقل شيوعاً اليوم، ولكن لا يزال صالحاً */
ABEM (Atomic BEM)
ABEM تضيف فئات أدوات ذرية إلى BEM:
مثال ABEM
<div class="card -mt-3 -p-2">
<!-- BEM للمكونات، أدوات ذرية للمسافات -->
</div>
/* الأدوات الذرية تستخدم بادئة شرطة */
.-mt-3 { margin-top: 1rem; }
.-p-2 { padding: 0.5rem; }
BEMIT (BEM + ITCSS + فضاءات الأسماء)
BEMIT تضيف بادئات فضاء أسماء إلى فئات BEM:
مثال BEMIT
/* فضاء أسماء المكون */
.c-card { }
.c-card__title { }
/* فضاء أسماء الكائن */
.o-layout { }
.o-layout__item { }
/* فضاء أسماء الأداة */
.u-text-center { }
/* فضاء أسماء الاختراق */
.h-clearfix { }
/* فضاء أسماء الثيم */
.t-dark { }
/* فضاء أسماء الحالة */
.is-active { }
.has-dropdown { }
تمرين 1: بناء تنقل BEM
أنشئ قائمة تنقل متجاوبة باستخدام BEM:
- كتلة:
.nav - عناصر:
.nav__list,.nav__item,.nav__link - معدّلات:
.nav__link--active,.nav--mobile - استخدم تداخل SASS مع رمز
& - تضمين حالات التحويم والانتقالات
- معدّل الجوال الذي يكدس العناصر عمودياً
تمرين 2: إنشاء مكون نافذة BEM
ابنِ مربع حوار نافذة باستخدام منهجية BEM:
- كتلة:
.modal - عناصر:
.modal__overlay,.modal__dialog,.modal__header,.modal__body,.modal__footer,.modal__close - معدّلات:
.modal--small,.modal--large,.modal--fullscreen - تضمين رسوم متحركة للفتح/الإغلاق
- اكتب SCSS الكامل مع تداخل BEM المناسب
تمرين 3: إعادة هيكلة كود غير BEM
خذ هذا الكود غير BEM وأعد هيكلته لاستخدام BEM بشكل صحيح:
<div class="product-card featured">
<img class="image" src="product.jpg">
<h3 class="title primary">اسم المنتج</h3>
<p class="description">تفاصيل المنتج</p>
<div class="price">
<span class="amount">$99.99</span>
<span class="old-price">$149.99</span>
</div>
<button class="button add-to-cart">أضف إلى السلة</button>
</div>
أعد الهيكلة لاستخدام تسمية BEM المناسبة مع الكتل والعناصر والمعدّلات.
الخلاصة
في هذا الدرس، أتقنت منهجية BEM مع SASS. أنت الآن تفهم:
- المفاهيم الأساسية الثلاثة: الكتلة، العنصر، والمعدّل
- لماذا توجد BEM والمشاكل التي تحلها (الخصوصية، التعارضات، قابلية الصيانة)
- كيفية استخدام محدد الأب
&في SASS لكتابة كود BEM نظيف - أمثلة من العالم الحقيقي: التنقل، البطاقات، النماذج، والنوافذ
- مزالق BEM الشائعة وكيفية تجنبها
- تنوعات BEM: ABEM، BEMIT، والنهج بفضاءات الأسماء
- أفضل الممارسات لبنية CSS قابلة للتوسع وسهلة الصيانة
BEM مع SASS توفر نهجاً قوياً وقابلاً للتوسع لكتابة CSS. إنها تجعل الكود الخاص بك أكثر قابلية للتنبؤ، وأسهل في الفهم، وتقلل بشكل كبير من صداع الصيانة في المشاريع الكبيرة.