رویه های مناسب کدنویسی CSS — از صفر تا صد
اجازه دهید در همین ابتدای مقاله اشاره کنیم که نوشتن CSS خوب میتواند کار دشواری باشد. بسیاری از توسعهدهندگان به نوشتن کدهای CSS علاقهای ندارند. این افراد بیان میکنند: «من هر نوع کدی بخواهید مینویسم، اما از من نخواهید CSS بنویسم!» اما واقعیت این است که CSS به این سختی هم نیست. با ما تا انتهای این مقاله همراه باشید تا با رویه های مناسب کدنویسی CSS آشنا شوید. زمانی که اپلیکیشنها را میسازیم، بخش CSS یکی از بخشهایی است که کمترین هیجان را دارد. اما گریزی هم از آن نیست، چون برای خلق یک تجربه کاربری به CSS نیاز داریم و امروزه نمیتوان این بخش را نادیده گرفت.
زمانی که یک پروژه را به تازگی آغاز کردهایم، همه چیز به خوبی کار میکند. ما چند سلکتور معدود CSS به صورت title input #app. داریم که هیچ مشکلی به نظر نمیآید. اما زمانی که اپلیکیشن رفتهرفته برگتر میشود، همه چیز در هم میپیچد. در این زمان، سلکتورهای CSS موجب سردرگمی میشوند و ناگاه متوجه میشوید که در حال نوشتن چیزی مانند زیر هستید:
div#app.list li.item a
شما این کد را بارها و بارها مینویسید و در نهایت همه کدهای CSS را به انتهای فایل میبرید، چون فکر میکنید که CSS خسته کننده و ملال آور است. به این ترتیب 500 خط کد CSS ایجاد میشود که کاملاً غیر قابل نگهداری است هدف این مقاله آن است که شما را با رویههای مناسب کدنویسی CSS آشنا سازد تا وقتی به کدهای قدیمی خود نگاه میکنید، نگویید: «من چگونه توانستهام چنین چیزی را بنویسم.»
شاید فکر کنید که فریمورکهای CSS اساساً به همین منظور ارائه شدهاند و این گفته نادرستی نیست، اما بهرهگیری از این فریمورکها نیز معایبی دارد:
- در اغلب موارد، منجر به یک طراحی معمولی میشود.
- سفارشیسازی آنها دشوار است و غالب نیازمند عبور از فریمورکهای CSS دارد.
- پیش از استفاده، باید آنها را یاد بگیرید.
توجه کنید که ما در این مقاله در خصوص روش طراحی اپلیکیشنهای زیبا صحبت نمیکنیم، بلکه میخواهیم روش نوشتن کد CSS را بیاموزیم که قابلیت نگهداری داشته باشد و بتوان آن را سازماندهی کرد.
SCSS
ما در مثالهای این نوشته از SCSS (+) استفاده خواهیم کرد. SCSS یک پیشپردازشگر CSS است. در واقع SCSS یک سوپرست CSS است و قابلیتهای جالبی مانند متغیرها، تودرتوسازی، ایمپورت و میکسین (Mixin) را معرفی میکند. در ادامه این قابلیتها را بررسی میکنیم.
متغیرها
در SCSS میتوان از متغیرها استفاده کرد. مهمترین مزیت آن ایجاد قابلیت «استفاده مجدد» (Reusability) است. فرض کنید یک پالت برای رنگهای اپلیکیشن دارید و رنگ اصلی شما آبی است. بدین ترتیب رنگ آبی را در هر کجا استفاده میکنید. background-color دکمه، color عنوان، لینکها و غیره همه جاهایی است که رنگ آبی استفاده میشود. اما ناگهان دیگر از رنگ آبی خوشتان نمیآید و رنگ سبز را ترجیح میدهید. در این صورت:
- بدون استفاده از متغیر، باید همه خطوطی که رنگ آبی در آن آمده است را به صورت دستی تغییر دهید.
- با استفاده از متغیرها کافی است یک متغیر را عوض کنید.
1// Declare a variable
2$primary-color: #0099ff;
3// References a variable
4h1 {
5 color: $primary-color;
6}
تودرتوسازی
در هنگام بهرهگیری از SCSS امکان استفاده تودرتو از کد نیز وجود دارد. به قطعه کد زیر توجه کنید:
1h1 {
2 font-size: 5rem;
3 color: blue;
4}
5h1 span {
6 color: green;
7}
کد فوق را میتوان به صورت زیر نیز نوشت:
1h1 {
2 font-size: 5rem;
3 color: blue;
4
5 span {
6 color: green;
7 }
8}
چنان که میبینید روش دوم نوشتن کد، خوانایی بیشتری دارد و با بهرهگیری از «تودرتوسازی» (Nesting) زمان کمتری صرف نوشتن سلکتورهای پیچیده شده است.
تقسیم کد و ایمپورت
زمانی که میخواهیم به قابلیتهای خوانایی و نگهداری دست پیدا کنیم، امکان نگهداری همه کد در یک فایل بزرگ وجود نخواهد داشت. چنین چیزی در زمان ساخت یک اپلیکیشن کوچک یا کار تجربی شاید نیازهای شما را برآورده سازد، اما در سطح حرفهای نباید از این رویه استفاده کنید. خوشبختانه SCSS امکان تقسیم کد و ایمپورت کردن ماژولهای مختلف را میدهد. به این ترتیب میتوانید فایلهای مختلفی را با نامگذاری فایل به روش «زیرخط آغازین» مانند animations.scss ،_base.scss ،_variables.scss_ و غیره ایجاد کنید. به منظور ایمپورت کردن این فایلها نیز باید از دایرکتیو import@ استفاده کنید. برای نمونه میتوانید به صورت زیر عمل کنید:
1// _animations.scss
2@keyframes appear {
3 0% {
4 opacity: 0;
5 }
6 100% {
7 opacity: 1;
8 }
9}
10// header.scss
11@import "animations";
12h1 {
13 animation: appear 0.5s ease-out;
14}
احتمالاً حدس میزنید که اشتباهی در کد فوق رخ داده است و باید از animations.scss_ به جای animations استفاده میشد، اما SCSS به قدر کافی هوشمند است تا بداند که وقتی نام فایلهای مختلف را این گونه تعیین میکنید، منظورتان کدام فایل است.
بدین ترتیب با قابلیتهای متغیرها، تودرتوسازی، تقسیم فایل و ایمپورت کردن در فریمورک SCSS آشنا شدیم. اما این فریمورک قابلیتهای دیگری از قبیل mixin، وراثت، و دیگر دایرکتیوها (for ،@if@ و غیره) نیز دارد که در اینجا بررسی نکردیم. اگر میخواهید در این خصوص اطلاعات بیشتری کسب کنید، به مستندات SCSS (+) مراجعه کنید. این مستندات به زبان آسانی نوشته شده و قابلیت درک بالایی دارد.
روش BEM برای سازماندهی CSS
ما در اغلب موارد نمیدانیم که چیزهای مختلف را چطور نامگذاری بکنیم، اما این موضوع اهمیت زیادی دارد. تصور کنید به دلایلی یک پروژه را برای چند ماه متوقف میکنید و سپس مجدداً به سراغ آن میروید. یا حتی در سناریویی بدتر تصور کنید که ادامه کار روی یک پروژه را بر عهده فرد دیگری میگذارید. اگر کد CSS شما به صورت صحیحی نامگذاری نشده باشد، در نگاه نخست درک آن دشوار خواهد بود.
BEM به حل این مشکل کمک میکند. BEM یک روش نامگذاری است و اختصاری برای عبارت «مادیفایر عنصر بلوک» (Block Element Modifier) محسوب میشود. این روش موجب سازمان یافتن کد ما میشود و به این ترتیب کد ماژولار میشود و قابلیت استفاده مجدد بیشتری پیدا میکند.
بلوکها
بلوکها را میتوان به عنوان یک کامپوننت تصور کرد. برای نمونه بازی لِگو را تصور کنید. برای ساخت یک خانه با استفاده از آجرهای لگو، به یک در، پنجره، دیوارهای کوچک و غیره نیاز داریم. اینها همگی بلوک محسوب میشوند و برای خود معنای خاصی دارند. به این ترتیب در روش BEM بلوکهای مختلف بر اساس کارکردشان به صورت card ،.form ،.post ،.user-navigation. نامگذاری میشوند.
عناصر
در مثال لگو ممکن است پنجرههای شما از عناصری مانند فریم تشکیل یافته باشند که با اتصال هر چهار تای آنها یک پنجره ساخته میشود. بنابراین عناصر، اجزای تشکیلدهنده بلوکها هستند، چون برای ساخت بلوک مورد نیاز هستند. اما خارج از بلوکها کاربردی ندارند. برای نامگذاری عناصر از فرمول زیر استفاده میکنیم:
- نام بلوک + _ + نام عنصر
مثالهایی از روش BEM برای نامگذاری عناصر به صورت post__author ،.post__date ،.post__text. است.
مادیفایرها
اکنون که پنجره خود را ساختهایم، شاید بخواهیم یک پنجره سبز یا کوچک داشته باشیم. اینها مواردی هستند که «مادیفایر» (Modifier) نامیده میشوند. در واقع مادیفایرها، به صورت فلگهایی روی بلوکها یا عناصر تعریف میشوند و رفتار، ظاهر و یا خصوصیات دیگر آنها را تغییر میدهند. فرمول نامگذاری مادیفایرها به صورت زیر است:
- نام بلوک یا عنصر + __ + نام مادیفایر
مثالهایی از نامگذاری مادیفایرها به روش BEM به صورت post--important ،.post__btn—disabled. است.
نکاتی در مورد BEM
- زمانی که از BEM استفاده میکنید، نام به همراه کلاسها میآید و صرفاً از کلاس استفاده میشود. بدین ترتیب هیچ گاه از ID یا تگ استفاده نمیکنیم و فقط و فقط از کلاس استفاده میکنیم.
- بلوکها یا عناصر را میتوان در بلوکها یا عناصر دیگر به صورت تودرتو تعریف کرد، اما آنها در هر صورت کاملاً مستقل از هم هستند. این واژه «مستقل» را همواره در خاطر داشته باشید. بنابراین باید حاشیهای را روی یک دکمه به این جهت قرار دهید که میخواهید آن را زیر یک عنوان قرار دهید، چون در این صورت این دو به هم گره میخورند. به جای این روش از کلاسهای کاربردی بهره بگیرید.
- در زمان استفاده از BEM، فایل HTML شلوغ میشود، اما جای نگرانی نیست، چون این عیب در مقابل مزیتی که BEM به ارمغان میآورد، ناچیز است.
مثالی از BEM
در این بخش یک تمرین ارائه میکنیم. به یکی از وبسایتهای مورد علاقه خود بروید و تلاش کنید تا بلوکها، عناصر و مادیفایرهای آن را تشخیص دهید. برای نمونه گوگلاستور را میتوان به صورت زیر تحلیل کرد:
اینک نوبت شما است. طرز فکر کنجکاوانهای پیشه کنید و در مورد روشهای بهبود انجام کارها تأمل نمایید. شما باید جستجو کرده و با انجام آزمایشهای مختلف، کارهایی که میخواهید انجام دهید را به شیوهای بهینهتر اجرا کنید.
الگوی 7-1 برای سازماندهی فایلهای CSS
در این بخش با روش سازماندهی فایلهای CSS آشنا میشویم. این بخش به افزایش بهرهوری شما کمک زیادی میکند و موجب میشود که بیدرنگ محلی را که باید کد CSS آن را ویرایش بکنید بیابید. به این منظور باید ابتدا با الگوی 7-1 آشنا شوید. این الگو بسیار ساده است و تنها به رعایت 2 قاعده نیاز دارد:
- نوشتن همه فایلها در 7 پوشه مختلف.
- ایمپورت کردن آنها در یک فایل اصلی به نام main.css که در سطح root قرار دارد.
این هفت پوشه که در بخش فوق اشاره کردیم به شرح زیر هستند:
- base – در این پوشه همه کدهای قالبی (boilerplate) را قرار میدهیم. منظور از کد قالبی، همه کدهای CSS است که در آغاز هر پروژه جدید باید آنها را بنویسیم. برای نمونه قواعد تایپوگرافی، انیمیشنها و کلاسهایی مانند margin-right-large ،text-center و غیره شامل کدهای قالبی میشوند.
- components – این نام به صورت صریح استفاده شده است. یعنی این پوشه شامل همه کامپوننتهای مورد استفاده از قبیل دکمهها، فرمها، سوییپرها، پاپآپها و غیره برای ساخت صفحههای وبسایت است.
- layout – از این پوشه برای لیآوت بخشهای مختلف صفحه استفاده میشود. یعنی با استفاده از این پوشه، هدر، فوتر، ناوبری، بخشهای مختلف، گرید سفارشی و غیره را سازماندهی میکنیم.
- pages – گاهی ممکن است برخی صفحهها باشند که استایل خاص خود را نیاز دارند و این استایل متفاوت از استایل عمومی است. این استایل خاص را در پوشه pages قرار میدهیم.
- themes – اگر قالبهای مختلفی مانند قالب تاریک، قالب ادمین و غیره برای وبسایت خود داشته باشید، آنها را باید در این پوشه قرار دهید.
- abstracts – همه تابعها به همراه متغیرها و mixin-ها در این پوشه قرار میگیرند. به طور خاص این پوشه شامل helper-ها است.
- vendors – یک پروژه با اپلیکیشن بدون کتابخانههای خارجی ممکن نیست. بنابراین همه فایلهایی که مربوط به شما نیستند را باید در این پوشه قرار دهید. برای نمونه فایل Font Awesome ،Font Awesome و مواردی از این دست را در پوشه vendors قرار میدهیم.
فایل اصلی
فایل اصلی جایی است که همه فایلهای جزئی در آن ایمپورت میشوند:
1@import abstracts/variables;
2@import abstracts/functions;
3@import base/reset;
4@import base/typography;
5@import base/utilities;
6@import components/button;
7@import components/form;
8@import components/user-navigation;
9@import layout/header;
10@import layout/footer;
11...
شاید این همه ایمپورت در نگاه نخست، سردرگمکننده به نظر برسند، اما این معماری در پروژههای بزرگ و نه اپلیکیشنهای کوچک استفاده میشود، در حالی که در اینجا با یک پروژه با مقیاس کوچک سروکار داریم.
بنابراین ابتدا باید اعلام کنیم که در این مورد نیازی به پوشه vendors نداریم. کافی است همه کد CSS بیرونی را در یک تگ link در هدر قرار دهیم. همچنین نیازی به پوشه themes هم نداریم، چون احتمالاً برای اپلیکیشن خود تنها از یک قالب استفاده میکنید. در نهایت در صفحههای مختلف نیز از استایلهای متفاوتی استفاده نکردهایم و از این رو میتوانیم این مورد را نیز نادیده بگیریم. به این ترتیب در نهایت 4 پوشه خواهیم داشت.
در این حالت دو گزینه پیش روی خود داریم:
- ما میتوانیم کد CSS خود را در الگوی 7-1 سازماندهی کنیم و پوشههای abstracts ،components ،layout و base را همچنان حفظ کنیم.
- و یا میتوانیم یک پوشه بزرگتر داشته باشیم که همه فایلهای جزئی را به همراه فایل main.css در آن قرار داده و به چیزی مانند زیر دست پیدا کنیم:
sass/ _animations.scss _base.scss _buttons.scss _header.scss ... _variables.scss main.scss
این انتخاب به ترجیح شخصی شما بستگی دارد. در بخش بعدی در مورد پشتیبانی مرورگرها از SCSS و روش کامپایل SCSS به CSS صحبت خواهیم کرد.
از SCSS تا CSS
برای کامپایل کردن کد از SCSS به CSS به Node.js و NPM نیاز داریم. ما از یک پکیج به نام node-sass استفاده میکنیم که به ما امکان میدهد فایلهای scss. را به فایلهای css. کامپایل کنیم.
استفاده از CLI این پکیج نسبتاً آسان است:
node-sass <input> <output> [options]
به این منظور چندین گزینه وجود دارند، اما ما تنها از دو مورد استفاده میکنیم:
- w-: یک دایرکتوری یا فایل را رصد میکند. با استفاده از این گزینه، پکیج node-sass منتظر ایجاد تغییرات در کد میماند و زمانی که این تغییر رخ بدهد، به صورت خودکار آن را به CSS کامپایل میکند. این قابلیت در زمان توسعه بسیار مفید است.
- output-style-: این گزینه خروجی فایل CSS را تعیین میکند. مقادیر این گزینه میتواند یکی از موارد nested|expanded|compact|compressed باشد. ما از این گزینه برای ساخت فایل CSS استفاده میکنیم.
برای کسب اطلاعات بیشتر در این خصوص به صفحه مربوطه در مستندات رسمی (+) مراجعه کنید.
اینک میدانیم که باید از چه ابزارهایی استفاده کنیم و از این رو باقی کار دیگر آسان است. کافی است مراحل زیر را طی کنیم:
- پروژه خود را با دستور زیر ایجاد کنید:
mkdir my-app && cd my-app
- آن را با دستور زیر مقداردهی کنید:
npm init
- کتابخانه node-sass را با دستور زیر اضافه کنید:
npm install node-sass --save-dev
پوشههای خود و فایلهای index.html و main.scss را با دستورهای زیر ایجاد کنید:
touch index.html mkdir -p sass/{abstracts،base،components،layout} css cd sass && touch main.scss
- این اسکریپتها را در فایل package.json اضافه کنید:
1{
2 ...
3 "scripts": {
4 "watch": "node-sass sass/main.scss css/style.css -w",
5 "build": "node-sass sass/main.scss css/style.css --output-style compressed"
6 },
7 ...
8}
- لینکی که شامل ارجاعی به فایل کامپایلشده CSS است را در تگ head فایل index.html اضافه کنید:
1<!DOCTYPE html>
2<html lang=”en”>
3<head>
4 <meta charset=”UTF-8">
5 <meta name=”viewport” content=”width=device-width, initial-scale=1.0">
6 <meta http-equiv=”X-UA-Compatible” content=”ie=edge”>
7 <link rel=”stylesheet” href=”css/style.css”>
8 <title>My app</title>
9</head>
10<body>
11 <h1 class=”heading”>My app</h1>
12</body>
13</html>
به این ترتیب کار به پایان میرسد. اینک میتوانید با اجرای دستور زیر فایل index.html را در مرورگر باز کنید:
npm run watch
اگر میخواهید CSS شما به صورت minified نیز درآید باید دستور زیر را اجرا کنید:
npm run build
نکات جانبی
در این بخش برخی نکات جانبی را در خصوص SCSS مطرح میکنیم که گرچه به صورت مستقیم روی استفاده از آن تأثیری ندارد، اما در نهایت به بهینهسازی فرایند کار کمک زیادی میکند.
افزودن قابلیت بارگذاری مجدد سریع
برای این که بهرهوری خود را افزایش دهید، میتوانید به جای این که به صورت دستی فایل لوکال index.html را بارگذاری کنید، آن را به حالت «بارگذاری مجدد زنده» (live reload) دربیاورید. به این منظور باید مراحل زیر را اجرا کنید:
- پکیج live-server را با دستور زیر اجرا کنید:
npm install -g live-server
توجه کنید که این پکیج به صورت global است.
- با اجرای دستور زیر میتوانید همزمان هر تعداد اسکریپت را که میخواهید اجرا کنید:
npm install npm-run-all --save-dev
این اسکریپتها را به فایل package.json اضافه کنید:
1{
2 ...
3 "scripts": {
4 "start": "npm-run-all --parallel liveserver watch",
5 "liveserver": "live-server",
6 "watch": "node-sass sass/main.scss css/style.css -w",
7 },
8 ...
9}
اکنون زمانی که دستور npm run start را اجرا کنید، بیدرنگ تغییرها را بدون نیاز به دست زدن به هیچ چیز مشاهده میکنید.
افزودن قابلیت autoprefixer
اکنون که ابزارهای توسعه را راهاندازی کردهایم، بهتر است در مورد ابزارهای build و به خصوص autoprefixer (+) نیز اطلاعاتی داشته باشیم. Autoprefixer ابزاری است که CSS را تحلیل کرده و پیشوندهای vendor را به قواعد CSS با استفاده از مقادیر خاص (+) اضافه میکند.
در واقع زمانی که یک وبسایت را بیلد میکنیم، ممکن است از قابلیتهای جدیدی استفاده کنیم که هنوز در همه مرورگرها پشتیبانی نمیشوند. از این رو پیشوندهای vendor باید به عنوان یک راهکار به ابتدای این قابلیتها اضافه شوند تا از آنها نیز پشتیبانی شود. در ادامه مثالی از این وضعیت را مشاهده میکنید:
-webkit-animation-name: myAnimation; -moz-animation-name: myAnimation; -ms-animation-name: myAnimation;
همان طور که حدس میزنید، نوشتن این پیشوندها کار دشواری است و به همین جهت است که از autoprefixer برای سازگار ساختن کدهای CSSA با مرورگرهای مختلف و افزودن آن لایه پیچیدگی اضافی بهره میگیریم.
بدین ترتیب در سطح کد برای ساخت CSS به صورت زیر عمل میکنیم:
- همه فایلهای SCSS را در فایل اصلی CSS کامپایل میکنیم.
- به ابتدای فایل CSS یک Autoprefixer اضافه میکنیم.
- فایلهای CSS خود را فشردهسازی میکنیم.
همچنین در گام آخر به صورت زیر عمل میکنیم:
- دو وابستگی postcss-cli و autoprefixer را با دستور زیر اضافه میکنیم:
npm install autoprefixer postcss-cli --save-dev
- اسکریپت build را ویرایش کرده و این اسکریپتها را به فایل package.json اضافه میکنیم:
1{
2 ...
3 "scripts": {
4 "start": "npm-run-all --parallel liveserver watch",
5 "liveserver": "live-server",
6 "watch": "node-sass sass/main.scss css/style.css -w",
7 "compile": "node-sass sass/main.scss css/style.css",
8 "prefix": "postcss css/style.css --use autoprefixer -o css/style.css",
9 "compress": "node-sass css/style.css css/style.css --output-style compressed",
10 "build": "npm-run-all compile prefix compress"
11 ...
12}
اینک زمانی که دستور npm run build را اجرا کنیم، کد CSS فشردهسازی شده و پیشوندهای vendor به آن اضافه میشوند.
سخن پایانی
به این ترتیب به پایان این مقاله با موضوع رویههای مناسب کدنویسی CSS میرسیم. شما با مطالعه این مقاله با روش نوشتن کدهای CSS خوانا، ماژولار و با قابلیت نگهداری بالا آشنا شدید. امیدواریم این مطلب مورد توجه شما قرار گرفته باشد.