در این مقاله میخوایم یک ابزاری رو بررسی کنیم که به ما کمک میکنه css های پیچیده را ساده تر مدیریت کنیم. اسمش sass هست. یک css pre-processor که امکاناتی مانند تعریف متغیر، تابع و غیره را در اختیار ما قرار میدهد. وقتی یک فایل با extension sass رو به کامپایلرش بدیم، خروجی pure css بهمون برمیگردونه.
چرا مطالعه sass لازمه؟ چون هم در انگولار استفاده میشه و از اون مهم تر angular material از sass استفاده میکنه.
فهرست مطالب:
برای شروع کار با sass اول باید sass رو نصب کنیم:
با اجرای دستور زیر میتونیم ببینیم چه نسخه ای از sass نصب شده:
بعدش یک دایرکتوری بسازیم مثلا بنام sass-playground و بریم داخلش:
داخلش یک فایل بنام index.scss بسازیم با محتوای زیر:
وقتی دستور زیر رو اجرا کنیم ، خروجی کامپایل شده pure css رو داخل bash می بینیم:
میتونیم موقع اجرای دستور، آدرس فایل خروجی رو به عنوان پارامتر دوم به کامپایلر بدیم تا خروجی بجای bash داخل فایل ذخیره بشه:
نتیجه اجرای این دستور ساخته شدن فایل index.css با متحوای خروجی کامپایلر هست:
برای یک برنامه نویس سخته که هر بار یک فایل index.scss رو تغییر میده، یه دور بره دستور ساخت فایل css رو اجرا کنه. بنابر این از پارامتر watch استفاده میکنیم تا بلافاصله بعد از ذخیره تغییرات فایل index.scss ، به صورت خودکار فایل index.css با محتوای جدید آپدیت بشه:
و اینطوری به ازای ذخیره هر تغییری، فایل index.scss به صورت خودکار کامپایل میشه و خروجیش در فایل index.css قرار میگیره.
کامپایلر sass بجز scss یک extension دیگه ای هم داره که خود sass هست. یعنی بجای فایل index.scss بنویسیم index.sass. در فایل sass با extension sass یک کم مدل نوشتن فرق میکنه:
در این فایل خبری از براکت ({}) نیست. این مدل من رو یاد پایتون میندازه. بگذریم. من از این مدل استفاده نمیکنم. با مدل scss راحت ترم و به نظرم واقعی تره. میزان استفاده از scss در کل خیلی بالاتره و کتابخونه های زیادی باهاش کار میکنند. پیشنهاد میکنم شما هم از scss استفاده کنید :)
یکی از اولین ویژگی های sass اینه که میشه توش از متغیر استفاده کرد. نحوه تعریف و استفاده از متغیر به صورت زیر هست:
خروجی این فایل فرقی با قبل نمیکنه. فقط متغیری به اسم primary-color داریم که مقدارش رو ( رنگ قرمز رو ) میتونیم خیلی جاها استفاده کنیم. اگر مقدار primary-color تغییر کنه و مثلا کد رنگ دیگه ای رو استفاده کنیم، هر جایی که از این متغیر استفاده کردیم، کد رنگش عوض میشه به مقدار جدید.
مثلا من اگر مقدار $primary-color رو به green عوض کنم:
خروجی کامپایلر به شکل زیر تغییر میکنه:
میتونیم متغیری به اسم green-color داشته باشیم و مقدار اون رو به primary-color بدیم. باز هم در نتیجه فرقی نمیکنه:
اسکوپ در متغیرهای sass
موضوع بعدی که باید در مورد متغیرها در sass بدونیم، اینه که متغیرها block scoped هستند.
یعنی اگر متغیری رو بیرون همه بلاک ها تعریف کنیم، general scope داره و همه جا قابل استفاده است. اما اگر یک متعیر رو داخل یک بلاک تعریف کنیم، تعریفش محدود به همون بلاکه و در بلوک دیگه یا اسکوپ general نمیشه ازش استفاده کرد.
در مثال زیر متغیر primary-color رو در بلاک body تعریف کردیم و در بلاک کلاس panel استفاده کردیم. وقتی کد زیر رو کامپایل کنیم با خطا مواجه میشیم:
خطایی که برمیگرده میگه متغیر primary-color در بلاک کلاس panel تعریف نشده:
بنابر این وقتی متغیری در یک بلاک تعریف بشه، جاهای دیگه نمیشه ازش استفاده کرد.
حالا بیایم یک کد دیگه رو تست کنیم. متغیر primary-color رو به صورت global تعریف کنیم و در یک بلاک تغییرش بدیم. ببینیم تغییرش به بلاک های دیگه سرایت میکنه یا خیر:
اگر خروجی رو نگاه کنیم ، می بینیم که مقدار جدید primary-color در بلاک body روی بلاک panel اثری نگذاشته:
بنابر این متغیرهای sass کاملا block scoped رفتار میکنند.
استفاده از !global
یکی از قابلیت های sass اینه که با استفاده از !global اعلام کنیم تغییر متغیری که در بلاک تعریف شده، روی general scope متغیر هم اثر بگذاره داره:
می بینیم که در این حالت، تغییر رنگ primary-color به قرمز در بلاک body روی مقدار این متغیر در بلاک panel هم اثر گذاشته:
دقت کنید
زمانی که از !global برای متغیری که تازه در بلاک ساخته شده استفاده کنیم ( مثل primary-color در مثال پایین که برای اولین بار در بلاک body تعریف شده ) بهمون هشدار میده و میگه بهتره در stylesheet root این متغیر رو تعریف کنیم:
فرق متغیرهای sass و css
یک نکته دیگه در مورد متغیر های sass فرق شون با متغیر های css هست.
به کد زیر یه نگاهی بندازیم:
خروجی این کد به شکل زیر ذخیره میشه داخل فایل index.css:
یعنی متغیر primary-color بعد از اینکه به رنگ قرمز در بیاد ، در ادامه از مقدارش جدیدش استفاده میشه. درحالیکه رفتار متغیرهای css اینطوری نیست. آخرین مقدار متغیرهای css هر چی باشه، همه جا از همون مقدار استفاده میشه. یعنی body هم با بک گراند قرمز رندر میشه. درحالیکه متغیرهای sass اینطوری رفتار نمیکنند. رنگ primary-color از سبز به قرمز عوض شده، ولی بک گراند body همون مقدار سبز رو داره و عوض نشده!
تاثیر مقدار null
نکته بعدی تاثیر مقدار null روی کامپایل کردن sass هستش. کد زیر رو در نظر بگیریم:
در بلاک body ، رول مربوط به background با مقدار null روبرو میشه. در بلاک panel هم ، رول color ( تنها رول موجود در بلاک panel ) با مقدار null روبرو میشه. ببینیم چه اتفاقی برای خروجی افتاده:
رول بک گراند در بلاک body حذف شده. رول color هم در بلاک panel حذف شده. بلاک panel بدون رول ( خالی ) شده و کل بلاک توسط کامپایلر حذف شده.
عملگر concatenate
موضوع بعدی concatenate کردن متغیر ها ( چسباندن متغیرها به اسامی selector ها و rule هاست. کد زیر بیانگر نحوه استفاده از concatenation برای داینامیک کردن selector ها و rule هاست:
خروجی کد بالا در فایل index.css به صورت زیر دیده میشه:
لیست ها در sass مانند آرایه در جاوااسکریپت هستند و به صورت زیر تعریف میشوند:
لیست ها شامل مقادیری هستند که هر کدام یک index دارند.
برای خواندن یک مقدار از لیست به صورت زیر از تابع nth کمک میگیریم:
خروجی فایل بالا به صورت زیر خواهد بود:
نکته : لیست ها در sass برخلاف زبان های برنامه نویسی معمول از یک شروع میشن نه از صفر.
یعنی index شماره یک اولین مقدار لیست را برمیگردونه.
خواندن مقادیر لیست در لوپ
برای خواندن مقادیر لیست در sass از @each مثل کد زیر استفاده میکنیم:
خروجی کد بالا رو با هم ببینیم:
در انتهای قسمت بعد یک ترکیب عالی از get-map و list با هم می بینیم. فعلا تا همینجا رو داشته باشین بریم سراغ hash-map و استفاده از اون:
ساختار داده hash-map در sass مانند Map عمل میکند. یعنی برای هر مقدار یک کلید در نظر میگیریم:
برای خواندن یک مقدار در hash-map از تابع map-get به صورت زیر استفاده میکنیم:
در مثال بالا دو مدل از صدا کردن تابع map-get رو نوشتم. من خودم مدل کوتاه تر رو می پسندم. خروجی کد بالا با توجه به اینکه یک بار width رو تعریف کرده باشیم و مقدار medium رو به تابع map-get پاس داده باشیم، به صورت زیر نمایش داده میشه:
مثال ترکیب hash-map و لیست در sass
قبل از اینکه بریم لوپ زدن روی hash-map رو مرور کنیم، یک ترکیب خوب از hash-map و لیست با هم ببینیم. در مثال زیر میبینیم که هر مقدار لیست، در واقع یک کلید در hash-map هست:
خروجی کد بالا به صورت زیر در میاد:
برای نوشتن مثال بالا اگر بتونیم روی hash-map لوپ بزنیم نیازی به لیست suffixes نداریم. بریم ببینیم چطوری میتونیم روی hash-map لوپ بزنیم:
نتیجه کد بالا مقدار زیر خواهد بود:
در واقع خروجی همون خروجی ترکیب suffixes و map-get در مثال قبلی بود.
برای استفاده از حلقه for در sass می تونیم از from-to و from-through استفاده کنیم. در مدل from-to آخرین عدد حلقه اجرا نمیشه ( exclude میشه ) ولی در حلقه from-through آخرین عدد هم اجرا میشه ( include میشه ):
اینجا از حلقه from-to استفاده کردیم. پس عددهای ۱ و ۲ اجرا میشن ولی عدد ۳ اجرا نمیشه. نتیجه خروجی sass به صورت زیر در میاد:
اما اگر در همین حلقه از from-through استفاده کنیم:
خروجی حلقه علاوه بر عدد های ۱ و ۲، خود عدد سه رو هم شامل میشه:
از حلقه while در sass به صورت زیر میتونیم استفاده کنیم:
خروجی حلقه به صورت زیر نمایش داده میشه:
توابع برای محاسبه مقادیر ابزار قدرتمندی هستند. مثال زیر برای مشاهده تعریف تابع در sass هست. تابعی تعریف میکنیم به اسم custom_color که به عنوان ورودی بهش کد رنگ میدیم و مقدار رنگ با opacity نیم رو میگیریم:
خروجی مثال بالا به صورت زیر مشاهده میشه:
ورودی با مقدار پیش فرض
میتونیم برای تابع مقدار default در نظر بگیریم:
در مثال بالا تابع custom_color رو بدون ورودی صدا زدم تا خروجی رو بر اساس مقدار پیش فرضش برگردونه:
تعریف تعداد نامشخص ورودی ها در تابع
یعنی چی ؟ وقتی مثلا میخوایم چند تا ورودی به تابع بدیم ولی تعدادش ثابت نیست. مثلا چند تا عدد رو بهش پاس بدیم برامون جمع بزنه. حالا دو تا عدده ، یه دونه ست، ده تاست. نمیدونیم! ما رو یاد spread operator در جاوااسکریپت میندازه. در sass با یه کم تفاوت به صورت زیر تعریفش میکنیم:
متغیر ورودی رو به اسم numbers نامگذاری کردیم. داخلش یک متغیر sum تعریف کردیم و مقدار اولیه صفر بهش دادیم. طبق توضیحاتی قبلا در مورد لیست ها خواندیم، از each مشخصه که ورودی ها به صورت لیست به دستمون رسیدند. یک لوپ روی ورودی ها زدیم و تک تک ورودی ها رو با مقدار sum جمع زدیم و مقدار نهایی sum رو برگردوندیم. تابع رو با مقدار های 100px و 200px و 50px صدا کردیم و جواب 350px رو گرفتیم.
خروجی کد بالا به صورت زیر خواهد بود:
نکته : دقت کنید که توابع قبل از استفاده باید تعریف بشن. یعنی اگر کامپایلر ببینه جایی تابعی صدا زده شده ولی تعریفی ازش پیدا نکنه، عین عبارت تابع رو برمیگردونه:
خروجی کد بالا رو ببینیم که تابع custom_color رو همون جوری که صدا زدیم داخل css تحویل میگیریم. چون تابع custom_color بعد از استفاده ش تعریف شده:
استفاده از if در sass خیلی ساده ست و به نظرم با توضیحات قبلی همین الان هم میتونی نحوه استفاده ش رو حدس بزنی. برای همین گذاشتم بعد از تعریف تابع توضیحش بدم که یک مثال بهتر دیده باشیم. مثالی که در بخش تابع توضیح دادیم رو این بار با یک تغییر کوچولو ببینیم که یک بار if رو هم دیده باشیم:
چون تابع custom_color رو بدون ورودی صدا کردیم، ورودی color مقدار پیش فرضش رو میگیره که همون #000 هست. داخل تابع گفتیم اگر مقدار متغیر color برابر باشه با #000 به عنوان خروجی عبارت currentColor رو برگردون. در نتیجه خروجی کد بالا به صورت زیر خواهد بود:
استفاده از mixin ها بخش مهمی در امکانات sass هست. وقتی میخوایم مجموعه ای از property ها یک کلاس ها رو یه جا جمع کنیم و به صورت reusable جاهای مختلف استفاده کنیم.
بریم یک مثال اولیه ببینیم که داخلش یک mixin تعریف کردیم به اسم common:
خروجی کد ما به صورت زیر خواهد بود:
در واقع rule های داخل mixin عینا تکرار شدند.
میتونیم برای mixin ها پارامتر ورودی با مقدار default تعریف کنیم و از این نظر خیلی شبیه توابع هستند:
در مثال بالا می بینیم که یک بار mixin را با مقدار ورودی و یک بار بدون ورودی ( با مقدار پیش فرض پارامتر ورودی ) صدا کردیم. خروجی به صورت زیر خواهد بود:
داخل mixin میتونیم کلاس ها رو هم تعریف کنیم:
خروجی به صورت زیر در خواهد آمد:
فرض کنید یک فایل داریم بنام variables.scss که داخلش همه متغیرها رو تعریف کردیم:
و یک فایل داریم بنام mixins.scss که داخلش همه توابع و mixin ها رو تعریف کردیم:
چطوری از این ماژول ها داخل فایل index.scss استفاده کنیم؟
میتونیم ماژول ها رو به صورت زیر import کنیم داخل فایل index.scss و ازشون استفاده کنیم:
و نتیجه به صورت زیر دیده میشه:
اما مشکلی که هست اینه که نام متغیرها، توابع و میکسین ها مستقیما داخل stylesheet root فایل index.scss تعریف میشن. برای اینکه تداخلی بین شون پیش نیاد، مثلا به طور اتفاقی تو متغیر همنام از دو ماژول مختلف روی همدیگه به اشتباه تعریف نشن و مسائلی از این دست، مجبوریم موقع نامگذاری های داخل ماژول هامون از prefix استفاده کنیم.
راه دیگه ای هم هست که میتونیم ماژول ها رو با namespace های خاصی صدا کنیم و محتوای هر ماژول در namespace مربوط به خودش باشه. این امکان رو use به کمک کلیدواژه as در اختیار ما قرار میده:
نکته: تفاوت دیگه ای که use نسبت به import داره اینه که فقط یک بار میتونیم یک ماژول رو use کنیم و در صورت تکرار با خطا روبرو میشیم. ولی هر چند بار که بخوایم میتونیم ماژول رو import کنیم و خروجی ماژول به تعداد import ها تکرار میشه.
یکی از مزایایی که استفاده از sass برامون به همراه داره استفاده از selector nesting هست. مثال زیر رو مرور کنیم. داخل کلاس header یک المنت h1 داریم و میخوایم وقتی hover شد، background ش تغییر کنه:
وقتی کد بالا رو کامپایل کنیم، خروجی زیر رو تحویل میگیریم:
می بینیم که همه rule های css مربوط به کلاس header بدون اینکه بارها و بارها نوشته بشن، با یک بار صدا زدن تولید میشن. چون همه rule های مربوط به کلاس header داخل بلاک خودش تعریف شده اند و به صورت nesting نسبت به کلاس header در نظر گرفته شده اند.
بیشتر پروژه های انگولار با sass کار میکنند. تسلط روی sass میتونه توانایی ما رو در حوزه فرانت خیلی بالا ببره. امیدوارم این مقاله براتون مفید باشه :)