تا حالا به این فکر کردین چطوری میشه BEM رو به «قابل توسعه ترین» حالت ممکن نوشت؟ من که خیلی بهش فکر کردم و الانم اینجام که کشفیاتم رو باهاتون در میون بذارم!
بِم (BEM) مخفف سه کلمه بلوک (Block)، عنصر (Element) و اصلاحکننده (Modifier) ست. بِم یه سبک استاندارد نوشتن CSS محسوب میشه و تقریبا بعیده اگه دنبال یه راه توسعه پذیر برای نوشتن استایلهای پروژهتون هستین، اسمش رو نشنیده باشین.
هرچند ما قرار نیست اینجا راجعبه اصول بِم صحبت کنیم (هین الان هم کلی مقاله و پست عالی وجود داره تو این زمینه) اما بد نیست اگه قبل از شروع بحث اصلیمون این اصول رو یه بار مرور کنیم.
بلوک (Block)
هر بلوک یه واحد مستقله که به خودی خود معنا داره و میتونه چندین بار استفاده بشه (مثلا: میشه دکمه رو یه بلوک در نظر بگیرید؛ یه دکمه به خودی خود معنا داره و نیازی نیست اون رو به بقیه اجزا صفحه مربوط کنیم تا معنا پیدا کنه)
عنصر (Element)
دقیقا برعکس بلوک، عنصر معنا خاصی نداره. عنصر میتونه بخشی از یه بلوک باشه (مثلا: یه لیست خرید ۵ قلمی رو درنظر بگیرید؛ هر قلم توی اون لیست فقط زمانی معنا پیدا میکنه که جزئی از لیست باشه و به خودی خود معنایی نداره)
اصلاحکننده (Modifier)
اصلاحکنندهها رفتار یه بلوک یا عنصر رو تغییر میدن (مثلا: برای تغییر رنگ دکمه (بلوک) یا هر کدوم از اون اقلام لیست خرید (عنصر) میتونیم از اصلاحکنندهها استفاده کنیم)
خوب! همینقدر برای اینکه بتونیم مقاله رو ادامه بدیم کافیه و دیگه الان آمادهایم یه مثال واقعی بزنیم و قدم به قدم بهترش کنیم. مثلا این کد رو ببینید:
<!-- index.html --> <button class="button button--blue"> <img src="basket-icon.svg" class="button__icon" /> سفارش بده </button>
بریم بلوک، عنصر و اصلاحکنندهها را مشخص کنیم. میدونیم که بلوک نباید وابستگی داشته باشه، پس تگ button (که با کلاس button مشخص شده) اینجا بلوک ماست.
حالا سراغ تگ img (که با کلاس button__icon مشخص شده) میریم. میتونیم فرض کنیم محتوی این تگ آیکون سبد خریده و کنار متن «سفارش بده» نمایش داده میشه؛ از توضیحات میشه فهمید که این آیکون ما به خودی خود با معنی نیست و فقط وقتی معنی داره که توی این دکمه و کنار اون متن نمایش داده بشه؛ پس به همین راحتی عنصر خودمون رو هم پیدا کردیم!
آخر سر هم سراغ کلاس button--blue میریم که حتما متوجه شدین یه اصلاحکنندهست. از قبل میدونیم که اصلاحکنندهها رفتار بلوک یا عنصر رو تغییر میدن؛ و ما هم اینجا از این کلاس استفاده کردیم تا بتونیم رنگ دکمهمون رو عوض کنیم.
استایلی که میشه برای این دکمه نوشت احتمالا یه چیزی شبیه اینه:
/* styles.css */ .button { cursor: pointer; } .button__icon { font-size: 24px; } .button--blue { color: white; background-color: royalblue; }
دقیقا به محض دیدن این کد میشه فهمید که یه چیزی درست نیست! ما مجبور شدیم کلاس button رو سه مرتبه تکرار کنیم که البته شاید بهنظر چیز مهمی نباشه؛ ولی وقتی یکمی تعداد استایلها بیشتر بشه تکرار کردن یه عبارت (هرچند به کوچکی button)، پشت سر هم واقعا عذاب آور میشه.
ضمنا استایلهای ما خیلی جدا از هم بهنظر میرسن و اگه کسی دانش بِم نداشته باشه ممکنه حس کنه سه تا کلاس مختلف برای سه تا کار مختلف ساختیم و اینها اصلا با هم در ارتباط نیستن؛ با اینکه اصلا اینطور نیست و همهشون در واقع مربوط به بلوک button ما هستن و نباید جدا از اون استفاده بشن.
اما خوب راه حل چیه؟ چطوری میشه هم جلوی تکرار رو گرفت و هم مطمئن بشیم استایلهای مربوط به یه بلوک با هم یکپارچه شدن؟ جواب یک کلمهست! Sass!
گفتیم میشه با ترکیب کردن BEM و Sass مسائل قبلی رو حل کرد؛ ولی حالا این Sass چی هست اصلا؟ سَس یه پیش پردازنده (pre-processor) برای زبان سی اس اسه. ولی الان خیلی نمیخواد خودمون رو درگیر معنای پیش پردازنده کنیم؛ سَس برای ما همون سی اس اس با یه سری امکانات ویژهست!
تورفتگی (Nesting)
یکی از امکاناتی که سَس بهمون میده تورفتگیه! سَس بهمون اجازه میده هر چندتا سلکتور رو که دوست داشتیم توی هم قرار بدیم. یه چیزی مثل این:
/* styles.scss */ .button { cursor: pointer; .button__icon { font-size: 24px; } }
این سلکتورهای داخلی بعد از کامپایل به شکل زیر درمیان:
/* styles.css */ .button .button__icon { font-size: 24px; }
کاراکتر `&`
یکی دیگه از امکانات مفید سَس برای ما کاراکتر & هست. ما میتونیم از & استفاده کنیم تا جلوی تکرار سلکتورها رو بگیریم. مثلا اینجا:
/* styles.scss */ .button { cursor: pointer; &__icon { font-size: 24px; } }
بهجای اینکه دوباره button. رو تکرار کنیم از & استفاده کردیم. سَس موقع کامپایل زحمت تکرار کردنشون رو میکشه.
هووم! مسئلههایی که میخواستیم حلشون کنیم رو یادتونه؟ تکرار نه، یکپارچگی آره! و الان دقیقا میدونیم چطوری باید این کارها رو انجام بدیم! کد قبلی رو یادتونه؟ وقتشه به نسخه جدیدش سلام کنیم!
/* styles.scss */ .button { cursor: pointer; &__icon { font-size: 24px; } &--blue { color: white; background-color: royalblue; } }
دیگه خبری از تکرار نیست و همین الان هم این کد نسبت به قبل خودش خیلی یکپارچهتر شده؛ حتی اگه کسی با بِم هم آشنا نباشه متوجه میشه تمام این سلکتورها یه ارتباطی با هم دارن و نباید جدا از هم دیگه استفاده بشن.
خوب! الان دیگه میدونیم حرفهایها چطوری بِم و سَس رو با هم ترکیب میکنن تا مسائلی که دیدیم رو حل کنن؛ ولی این همش نیست! میشه با انجام یه سری ترفندهای کوچیک توسعه پذیری کدهامون رو از اینی که هست هم بهتر کنیم؛ یا به عبارت دیگه از حرفهای ها هم حرفهای تر کد بزنیم!
اولین قانون نانوشتهای که میتونه کدمون رو یکمی بهتر از قبلش کنه ترتیبه! هرچقدر کدی که مینویسیم قابل پیشبینیتر باشه خوانایی اون هم بیشتر میشه. ولی خوب کاربردش برای ما چیه؟ ما به کجای کدمون میتونیم ترتیب بدیم؟
برای پیدا کردن جواب سوالمون، پیشنهاد میکنم دوباره یه نگاهی به استایلهای بلوک button بندازیم ولی این دفعه جزئیتر:
/* styles.scss */ .button { cursor: pointer; &__icon { font-size: 24px; } &--blue { color: white; background-color: royalblue; } }
ترتیبی که اینجا رعایت کردیم اینه: اول از همه بلوک، بعد عناصر (با ترتیبی که توی اچ تی ام ال نوشته شدن) و در آخر هم اصلاحکنندهها
خوبه این ترتیب رو برای همه بلوکهامون رعایت کنیم، اینطوری حتی اگه یه بلوک خیلی بزرگ (مثلا ۴۰۰ ۵۰۰ خطی!) هم داشته باشیم راحت میدونیم کجا دنبال چی بگردیم.
عالی میشه اگه ترتیب عناصر رو همونطوری که توی اچ تی ام ال داریمشون پیاده کنیم؛ یعنی اگه button__icon. اولین عنصری هست که توی اچ تی ام ال اون رو میبینیم، توی استایلها هم همینطور باشه.
تا اینجا هیچ حرفی از ریسپانسیو نزدیم؛ اما الان داریم از سَس استفاده میکنیم و این یعنی هر کجا که دلمون بخواد میتونیم media@ بذاریم و اونجا رو ریسپانسیو کنیم؛ یه چیزی مثل این:
/* styles.scss */ .button { cursor: pointer; &__icon { font-size: 24px; @media (max-width: 1024px) { font-size: 20px; } } }
و میتونیم همین روش رو ادامه بدیم و دونه دونه هر کدوم از عناصرمون رو ریسپانسیو کنیم؛ ولی واقعا مجبوریم دونه دونه به هر کدوم از عناصر media@ بدیم؟ معلومه که نه!
هرچند نمیشه گفت این روش اشتباهه، ولی اینکه مجبوریم هر بار media@ رو تکرار کنیم اصلا جالب نیست و دیگه لازم نیست اضافه کنم ما به عنوان برنامه نویس از تکرار بدمون میاد! از بخت خوب، این بار مسئله پیش رومون اصلا پیچیده نیست!
برای حلش کافیه همه media@ ها رو یه سطح آورد بالاتر و توی بلوک نوشت. به همین سادگی! اینطوری دیگه خبری از تکرار نیست! ولی جدا از اون، اینطوری ریسپانسیو کردن هم خیلی سادهتر میشه (بهم اعتماد کنید دیگه نصف قبل هم ازتون وقت نمیگیره!) این مثال رو ببینید:
/* styles.scss */ .button { cursor: pointer; &__icon { font-size: 24px; } &--blue { color: white; background-color: royalblue; } @media (max-width: 1024px) { &__icon { font-size: 20px; } &--blue { border: 1px solid white; } }
الان برای اینکه بفهمیم این بلوک صفحه نمایشهای زیر ۱۰۲۴ پیکسل چطوری رفتار میکنه کافیه media@ مربوط به اون اندازه رو ببینیم و تمام! دیگه نیازی نیست هر عنصر رو دونه دونه نگاه کنیم تا بفهمیم چی شده.
خوب این هم از این! با دونستن این جزئیات هرچی که لازم بود رو میدونیم! و الان دیگه میتونیم با خیال راحت بریم پیش اون دوستمون که مشغول کار خودشه، و بهش یادآوری کنیم ما بلدیم مثل حرفهایها کد بِم بزنیم! :)