البحث في الموقع
المحتوى عن 'sass'.
-
المنهجية الأكثر منطقية لتنسيق شيفرة ملفات CSS وSASS المصطلحات التصريح عن قاعدة التصريح عن قاعدة (rule declaration): هو الاسم المختار لمحدّد معين (أو لمجموعة من المحددات) مع مجموعة من الخاصيات المصاحبة. إليك مثالًا لتوضيح الأمر: .listing { font-size: 18px; line-height: 1.2; } المحددات إن المحددات التي رأينها عند التصريح عن قاعدة، هي الجزء الّذي سيُحدد طريقة تنسيق العناصر في شجرة DOM، وذلك بحسب الخاصيات المعرّفة في المحدّد. يمكن أن تُطابقُ المحددات عناصر HTML، أو صنف العنصر (class)، أو معرّف العنصر (ID)، أو أي سِمة من سماته. وهذه بعض الأمثلة عن المحدّدات: .my-element-class { /* ... */ } [aria-hidden] { /* ... */ } الخاصيات أخيرًا، الخاصيات هي ما يمنح العناصر المحددة تنسيقها والتي صرحنا عنها في المحدد. والخاصيات هي أزواج مؤلفة من الخاصية وقيمتها، ويمكن للقاعدة المصرح عنها أن تحتوي على تعريف خاصية أو أكثر. وهكذا ستبدو طريقة التصريح عن الخاصيات: /* بعض المحددات */ { background: #f1f1f1; color: #333; } ملفات CSS التنسيق استخدم الزر (tabs) بمقدار مسافتين (وتسمى أيضًا Soft-Tab نظرًا لأن زر Tab الإفتراضي يكون ثمان مسافات). يفضل استخدام الشرطة العادية (وهي -) بدل تسمية الأصناف بأسلوب سنام الجمل (camelCasing). لا بأس باستخدام الشرطات السفلية والتسمية بأسلوب PascalCasing (وهي طريقة مشابهة لطريقة سنام الجمل إلا أنه يجب أن يكون أول حرف كبير) إن كنت تستخدم BEM ( والّتي سنتطرق إليها لاحقًا في هذا الدليل). لا تستخدم مُحدِّدات المُعرِّفات (ID). عند استخدام محددات متعددة في تصريح واحد لقاعدة ما، امنح كل مُحدّد سطر خاص به. ضع مسافة قبل قوس الافتتاح { عند التصريح عن قاعدة. ضع مسافة بعد : وليس قبله في الخاصيات. ضع أقواس الإغلاق } عند التصريح عن قاعدة في سطر جديد. ضع أسطر فارغة بين تصريحات القواعد. طريقة تنسيق سيئة .avatar{ border-radius:50%; border:2px solid white; } .no, .nope, .not_good { // ... } #lol-no { // ... } طريقة تنسيق جيدة .avatar { border-radius: 50%; border: 2px solid white; } .one, .selector, .per-line { // ... } التعليقات يفضلُ استخدام التعليقات السطرية (وهي // في Sass-land) للتعليقات الكتلية. يفضلُ أن تضع التعليق بسطر خاص به. تجنب التعليقات في نهاية السطر. اكتب تعليقات تفصيلية للشيفرة البرمجية التي لا تشرح نفسها بنفسها: استخدام الخاصية z-index. استخدام المميزات التوافقية أو بعض الخدع المخصصة للمتصفحات. OOCSS و BEM نشجع على استخدام مزيج من OOCSS و BEM وذلك للأسباب التالية: يساعدنا هذا المزيج في إنشاء علاقات واضحة وصارمة بين ملفات التنسيق CSS وملفات HTML. ينشئ مكونات قابلة لإعادة الاستخدام والتركيب. يسمح بتقليل التداخل والتخصيص. يساعد في زيادة قابلية تطوير ملفات التنسيق. OOCSS (اختصارًا "Object Oriented CSS") وهي التنسيق كائني التوجه لملفات التنسيق، هو نهج لكتابة تنسيق CSS يشجعك على التفكير بملفات التنسيق على أنها مجموعة من "الكائنات": أي أنها قابلة لإعادة استخدام أجزاءٍ منها، وقابلة للتكرار، ويمكن استخدامها بشكل مستقل عبر موقع الوِب. لمزيد من المعلومات يمكنك الاطلاع على: توثيق نيكول سوليفان (Nicole Sullivan's) OOCSS wiki. مقال مدخل إلى OOCSS (Introduction to OOCSS). BEM (اختصارًا Block-Element-Modifier) معدلّ عنصر الكتلة وهو عبارة عن اصطلاح تسمية للأصناف في HTML و CSS. طوّر في الأصل من خلال شركة Yandex ووضعوا في حسبانهم الشيفرات البرمجية الكبيرة وقابلية التطوّر والتوسع للشيفرة، ويمكن اعتبارها بمثابة مجموعة قوية من المبادئ التوجيهية لتطبيق OOCSS. لمزيد من المعلومات يمكنك الاطلاع على: خدع CSS دليلك إلى منهجية BEM - CSS مدخل إلى BEM (introduction to BEM). نوصي بالتنويع بأشكال BEM مع "كتل" مسماة بأسلوب PascalCased، والّتي تعمل بطريقة جيدة خصيصًا عند دمجها مع المكونات (مثلما يحدث في React). لا تزال تُستخدم الشُرط السفلية والشرطات للمعدلات وسلالتهم (من سيرثُ منهم). مثال // ListingCard.jsx function ListingCard() { return ( <article class="ListingCard ListingCard--featured"> <h1 class="ListingCard__title">Adorable 2BR in the sunny Mission</h1> <div class="ListingCard__content"> <p>Vestibulum id ligula porta felis euismod semper.</p> </div> </article> ); } /* ListingCard.css */ .ListingCard { } .ListingCard--featured { } .ListingCard__title { } .ListingCard__content { } .ListingCard: وهي عبارة عن "كتلة" وتمثل مكون عالي المستوى. .ListingCard__title: عبارة عن "عنصر" وتمثل سلالة .ListingCard التي تساعد في تكوين الكتلة ككلّ. .ListingCard--featured: وهو عبارة عن "معدلّ" ويمثل الحالات أو التشكيلات المتختلفة لكتلة .ListingCard. محددات المعرفات في حين أنه من الممكن تحديد العناصر حسب المعرّف في CSS، إلا أن هذه المحددات تعدّ أسلوبًا مضادًا للنمط (anti-pattern). تقدم محدّدات المعرّفات مستوى عاليًا من التخصيص غير ضروري للتصريح عن قاعدة، بل ولن يمكننا إعادة استخدام هذه المحدّدات. لمزيد من المعلومات حول هذا الموضوع، أحيلك لهذه المقالة CSS Wizardry's article والّتي تتحدث عن طريقة التعامل مع التخصيص. خطاف الجافاسكربت تجنب ربط نفس الصنف في كلّ من ملفات التنسيق CSS والجافاسكربت. لأنه غالبًا ما سيُؤدي لخلط الاثنين معًا، وبأحسن الأحوال سيؤدي ذلك لإضاعة الوقت أثناء إعادة البناء عندما يتعين على المطوّر العودة لكلّ صنف يغيره، وفي أسوأ الأحوال، سيخشى المطوّرون إجراء تعديلات عليها خوفًا من كسر الوظيفة ما المرتبطة بها. نوصي بإنشاء أصناف مخصصة للربط مع جافاسكربت مسبوقة بـ .js-: الحدود استخدم 0 بدلًا من none لتحديد أن النمط ليس له حدود. الطريقة السيئة .foo { border: none; } الطريقة الجيدة .foo { border: 0; } ملفات SASS الصياغة استخدم الصيغة .scss، ولا تستخدم أبدًا الصيغة .sass الأصلي رتب شيفرة ملفات CSS العادية وعمليات التضمين @include بطريقة منطقي (انظر أدناه) ترتيب طريقة التصريح عن الخاصيات التصريح عن خاصية أدرج جميع التصاريح القياسية للخاصيات، وأي شيء آخر ما عدا عمليات التضمين @include أو المحددات المتشعّية. .btn-green { background: green; font-weight: bold; // ... } تصريح عن عملية تضمين @include إن تجميع ووضع جميع عمليات التضمين @include في نهاية المحدّد سيسهل قراءة المحدد بالكامل. .btn-green { background: green; font-weight: bold; @include transition(background 0.5s ease); // ... } المحددات المتشعّبة يكون ترتيب المحددات المتشعّبة ( إن اضطررنا لاستخدامها) بعد عمليات التضمين ولا شيء بعدها. أضف مسافة بين عمليات التصريح عن القواعد والمحددات المتشعّبة، وكذلك بين المحددات المتشعّبة المتجاورة. طبّق نفس الإرشادات المذكورة سابقًا على المحددات المتشعّبة الخاصة بك. .btn { background: green; font-weight: bold; @include transition(background 0.5s ease); .icon { margin-right: 10px; } } المتغيرات يفضل استخدام أسماء المتغيرات التي تحتوي على شرطة عادية (مثل: $my-variable) بدلًا من أسماء متغيرات مثل أسلوب تسمية سنام الجمل (camelCased) -تكبير أول حرف من كلّ كلمة في اسم المتغير عدا أول كلمة- أو أسلوب snake_cased -جميع حروف اسم المتغير صغيرة-. لا مشكلة بإضافة شرطة سفلية لأسماء المتغيرات الّتي نود استخدامها فقط داخل نفس الملف(هكذا: $_my-variable). المخاليط يجب استخدام المخاليط (mixins) لتصغير شيفرتك البرمجية، أو لتوضيحها، أو لعزل تعقيدها - بطريقة مشابهة تمامًا للدوالّ المسماة جيدًا. يمكن للمخاليط الّتي لا تقبل أي وسطاء أن تكون مفيدة لذلك، ولكن انتبه فأذا لم تستخدم خوارزميات ضغط للملفات المشروع (مثل خوارزمية gzip)، فسيُساهم ذلك في تكرار الشيفرات البرمجية غير الضرورية في التنسيقات الناتجة. توسعة المحدّدات المُركّبة يجب تجنب التوسعة باستخدام التعليمة @extend لأنه يسلك سلوك غير معروف للبعض وغالبًا ما يكون خطير، خاصة عند استخدامه مع محددات متشعّبة. حتى إن توسيع محددات العناصر الأساسية عالية المستوى يمكن أن يسبب مشاكل إذا انتهى الأمر بتغيير ترتيب المحددات في وقت لاحق (فمثلًا إذا كانت هذه المحددات في ملفات أخرى، وتغير ترتيب تحميل الملفات فهذا سيُشكل مشكلة). يجب أن تتعامل خوارزمية الضغط Gzipping مع عمليات تصغير أحجام الملفات الّتي ستحصلُ عليها باستخدام @extend، وستمنحُك المخاليط تصغيرًا جيدًا لملفات التنسيق خاصتك. المحددات المتشعّبة ** لا تنشئ محددات متشعّبة بعمق يزيد عن ثلاثة مستويات!** .page-container { .content { .profile { // STOP! } } } عندما تصبح المحددات طويلة، من المحتمل أنك تضطر أن لتكتب تنسيق لديه المميزات التالية: مقترن بشدة بملفات HTML (وهذا يعدّ نقطة ضعف في التنسيق) -أو محدد للغاية (وهذا يعدّ نقطة قوة في التنسيق) -أو غير قابل لإعادة الاستخدام. مرة أخرى: لا تستخدم نهائيًا محددات المعرّفات المتشعبة! في حال وجب عليك استخدام محدّد المعرّف في البداية (ويجب أن تحاول بأقصى جهدك أن تتجنبه)، فلا يجب أن تكون متشعّبة أبدًا. وإن وقعت بهذه المشكلة، فعاود النظر في الشيفرة البرمجية أو أسأل نفسك لما كلّ هذا التخصيص الشديد؟ وإن أردت كتابة ملفات HTML وCSS ذات بنية جيدة، فيجب عليك ألا تستخدم محدّدات المعرّفات المتشعبة نهائيًا. ترجمة -وبتصرف- لدليل Airbnb لتنسيق ملفات CSS وSASS (Airbnb CSS / Sass Styleguide) الموجود على موقع GitHub
-
هذا المقال تمت كتابته بتعاون بين Joe Richardson و Robin Rendle ومجموعة من فريق موقع CSS-Tricks. أراد Joe إضافة مقال بخصوص BEM التي أحببناها وكان لدى كل منا أفكارًا وآراءً حولها، لذلك قررنا أن نتعاون سويًا لكتابة هذا المقال. تُعدُّ منهجية BEM (وهي اختصار لهذه الكلمات معًا Block و Element و Modifier) مصطلح تسمية شائع يستخدم من أجل الأصناف في HTML و CSS تم تطويره بواسطة فريق في Yandex؛ هدفها هو مساعدة المطورين في فهم العلاقة بين HTML و CSS في مشروع معين بشكل أفضل. فيما يلي مثال حول ما يكتبه مطور CSS في نمط BEM: /* مكون كتلة */ .btn {} /* عنصر يعتمد على كتلة */ .btn__price {} /* مُعدِّل يعدِّل تنسيق الكتلة */ .btn--orange {} .btn--big {} في شيفرة CSS السابقة، الكتلة (Block) عبارة عن عنصر جديد في أعلى مستوى من التجريد (top-level abstraction) على سبيل المثال زر: .btn{}. يجب اعتبار الجملة البرمجية هذه بمثابة أحد الأبوين، إذ يمكن وضع العناصر الأبناء (elements) في الداخل ويتم الإشارة إليها بواسطة شرطتين سفليتين يتبع اسم الكتلة أو الحاوية البرمجية تلك مثل .btn__price{}. أخيرًا، يمكن للمُعدلات (modifiers) معالجة الحاوية البرمجية (Block) بحيث يمكننا تمييز أو تصميم ذلك المكون المحدد دون إحداث تغييرات على بقية الحاويات البرمجية الأخرى يتم ذلك عن طريق إلحاق شرطتين باسم الحاوية البرمجية Block تمامًا مثل btn--orange. تبدو الشيفرة كما في المثال التالي: <a class="btn btn--big btn--orange" href="http://css-tricks.com"> <span class="btn__price">$9.99</span> <span class="btn__text">Subscribe</span> </a> لو كتب مطور آخر هذه الشيفرة ولم يكن لدينا خبرة في CSS، فيجب أن نأخذ فكرة جيدة عن أي الأصناف ومسؤوليتها وكيف تعتمد على بعضها بعضًا. يستطيع المطورون آنذاك بناء مكوناتهم الخاصة وتعديل الحاويات (الكتل) الموجودة إلى المحتوى الذي يريدونه. يحتمل بدون كتاب مثل شيفرة CSS هذه أن يتمكن المطورون من كتابة عدة مجموعات مختلفة من الأزرار ببساطة عبر تعيير صنف في الشيفرة: في البداية، قد تبدو هذه الصياغة أبطأ من بناء صنف جديد لكل نوع من أنواع الأزرار، ولكن هذا غير صحيح لعدة أسباب سنذكرها. لماذا علينا أخذ BEM في الحسبان؟ إذا كنا نريد إنشاء تصميم جديد لعنصر معين، يمكننا أولًا النظر في الحاويات البرمجية لمعرفة أي المُعدِّلات، والعناصرالفرعية موجودة بالفعل. ربما ندرك أننا لسنا بحاجة لكتابة أي تعبير CSS برمجي آخر لأن هناك معدّل موجود مسبقًا يفي بالغرض. إذا كنا نقرأ الوسوم بدلاً من تعابير CSS البرمجية، يجب أن نكون قادرين على الحصول بسرعة على فكرة حول العنصر الذي يعتمد على عنصر آخر؛ في المثال السابق، يمكننا رؤية أن العنصر .btn__price يعتمد على العنصر .btn، حتى لو لم نكن على دراية بعمل أي من تلك العناصر. يمكن للمصممين والمطورين تسمية العناصر لتسهيل الاتصال بين فريق المطورين بمعنى آخر، يوفر BEM لكل مطور في المشروع تسمية صيغة إعلانية للعناصر يمكنه مشاركتها بحيث تكون في نفس الصفحة. حدد Harry Roberts فائدة رئيسية أخرى لاستخدام صيغة برمجية مثل BEM حين كتب التالي عن تحسين ثقة المطور: "هذا هو السبب الرئيسي الذي يجعلنا ننتهي من قواعد الشفرة المتضخمة المليئة بشيفرات برمجية قديمة وغير قابلة للتعديل من CSS. نحن نفتقر إلى الثقة في أن نكون قادرين على العمل مع الأنماط الحالية وتعديلها لأننا نخشى عواقب أن تعمل CSS عالميًا، إذ هي مشهورة بحد ذاتها. تتلخص كل المشكلات تقريبًا المتعلقة بـ CSS على نطاق واسع في الثقة (أو عدم وجودها)، لذلك لا يقومون بإجراء تغييرات لأنهم لا يعرفون ما هي الآثار لهذه التغييرات على المدى البعيد." وبالمثل، يناقش Philip Walton حل لهذه المشكلة وهو إلتزام عدد كاف من المطورين بمبادئ BEM: "على الرغم من أن الشيفرة القابلة للتنبؤ بنسبة 100٪ غير متوافرة، من المهم فهم المفاضلات التي تجريها مع الطريقة المتبعة في كتابة التعابير البرمجية التي تختارها. إذا كنت تتبع طريقة BEM الصارمة، فستكون قادرًا على تحديث تعابير CSS وإضافة الشيفرات البرمجية الخاصة بك إليها في المستقبل بثقة تامة بأن هذه التغيرات لن يكون لها أي آثار جانبية." لذلك، إذا كان بإمكان المطورين العمل في مشروع بطريقة أكثر ثقة، فإنهم على يقين من اتخاذ قرارات أكثر ذكاءً حول كيفية استخدام هذه العناصر المرئية. قد لا تكون هذه الطريقة حلًا مثاليًا لجميع هذه المشاكل، لكنها بالتأكيد تمنح المطورين معيارًا لكتابة جمل برمجية أفضل وأكثر قابلية للصيانة في المستقبل. هناك جانب آخر جيد في منهجية BEM، وهو أن كل مجموعة جمل برمجية خاصة بعنصر معين توجد داخل حاوية خاصة به ولا يوجد شيء متداخل مما يجعل خصوصية CSS سلسة جدًا ومنخفضة وهذا هو المطلوب. أي أنك لن تجهد نفسك فيما يتعلق بخصوصية تعابير CSS البرمجية. دعونا نلقي نظرة على بعض المشاكل مع BEM. مشاكل مع BEM CSS لن يقوم أحد بمعاقبتك بالطبع إذا خرجت عن قواعد BEM. لا يزال بإمكانك كتابة محدد CSS كالتالي: .nav .nav__listItem .btn--orange { background-color: green; } يبدو أن المثال السابق يحتوي أجزاء من منهجية BEM، لكنه ليس كذلك فهو يحتوي على محددات متداخلة، ولا يصف المُعدِّل (modifier) بدقة ما يحصل في هذه الجمل البرمجية. إذا فعلنا ذلك، فسنكون فشلنا في تحقيق مبدأ الخصوصية وهذا مفيد جدا لمنهجية BEM. يجب ألا تَبطل كتلةٌ (مثل .nav) عمل كتلة أخرى أو معدل آخر (مثل .btn --orange) وإلا فإن هذا سيجعل من المستحيل تقريبًا قراءة ملف HTML وفهم عمل هذا العنصر. في هذه العملية، نحن ملزمون الى حد كبير أن نكون أهلًا لثقة مطور آخر في البرنامج، هذا ينطبق على HTML أيضًا. ماذا تتوقع إذا رأيت الشيفرة التالية؟ <a class="btn" href="http://css-tricks.com"> <div class="nav__listItem">Item one</div> <div class="nav__listItem">Item two</div> </a> ربما ما يحدث في المثال السابق، هو أن عنصرًا في الحاوية البرمجية block يشتمل على الشيفرة التي يحتاجها المطور، لكن العناصر الأبناء لا تحتاج إلى الصنف .nav على أنه عنصر أب. هذه المشكلة تجعل البرنامج استثنائيًّا وغير متسق ومتناقض مع نفسه وينبغي تجنبها بأي ثمن. لذلك، يمكننا تلخيص هذه المشكلات في: عدم استخدام المُعدلات في الحاويات البرمجية block غير المترابطة. تجنب صنع عناصر رئيسية غير ضرورية عندما يكون العنصر الفرعي موجودًا بشكل جيد بدون أي مشاكل. المزيد من الأمثلة العملية على BEM قائمة قابلة للطي: في هذا المثال، هناك حاوية برمجية block واحدة وعنصران ومُعدِّل واحد. يمكننا هنا إنشاء مُعدِّل مثل .accordion__copy-open يتيح لنا معرفة أنه يجب ألا نستخدمه في حاوية برمجية block أو عنصر آخر. قائمة تنقل: يحتوي هذا المثال على كتلة block واحدة و 6 عناصر ومُعدِّل واحد. من الجيد تمامًا إنشاء حاويات برمجية blocks بدون معدلات على الإطلاق. يمكن للمطور في مرحلة ما في المستقبل أن يربط هذه الحاوية بمعدلات جديدة طالما بقيت ثابتة. عيوب BEM ربما لا تحب استخدام شرطة مزدوجة. حسنًا، استخدم شيئًا آخر فريدًا تستخدمه باستمرار. هنا رأي آخر: هذه الثلاثة محددات الأخيرة جميعها لها مستويات خصوصية مختلفة. تحتمل أن يكون لها عنصر رئيسي أو لا بدون أي قواعد معمول بها، هل من الممكن أن يكون هذا المثال الصغير جيد بالنسبة لك؟ ربما. ولكن كلما زاد عدد تعابير CSS البرمجية في المشروع، زاد عدد الأشياء الصغيرة مثل هذه، وبالتالي زادت مشاكل الخصوصية والتعقيد. ليس بالضرورة اختيار رأي صموئيل هنا، ولكن آراءه شاركها الكثير من الناس لذلك فهو مثال جيد. بخصوص من يرفضون BEM تمامًا فلا بأس بذلك، لكنني أعتقد أنه سيكون من الصعب القول بأن وجود مجموعة من القواعد التي تساعد في الفهم والحفاظ على CSS قابل للتعديل هو فكرة سيئة. في منهجية SMACSS، من المحتمل أن تجد اسم صنف CSS متكون من ثلاثة أحرف. تتبع المُعدِّلات بعدئذٍ اسم الوحدة النمطية باستخدام شرطة: /* مثال عن وحدة */ .btn { } /* btn معدِّل الصنف */ .btn-primary { } /* مع حالة Btn الوحدة */ .btn.is-collapsed { } هذه مجرد طريقة تسمية مختلفة لنفس المشكلة. إنه مشابه إلى حد ما، لكن تكون أكثر تحديدًا بشأن التبعيات والحفاظ على خصوصية التفاصيل. في OOCSS، الحاويات البرمجية هي عامة بالغالب. /* مثال عن وحدة */ .mod { } /* جزء من الوحدة */ .inner { } /* Talk الوحدة */ .talk { } /* تغيير جزء داخل الوحدة */ .talk .inner { } لذلك، يمكنك استخدام أصناف متعددة في HTML للحالات المختلفة. لم يتم تسمية الجزء الداخلي مثل التابع له، لذلك فهو أقل وضوحًا ولكنه قابل لإعادة استخدامه. ستقوم BEM بعمل .mod__inner و .mod--talk و mod-talk__inner.. هذه مجرد اختلافات في المنهجية. تذكر أن لا أحد يجبرك على استخدامها، فهذه قواعد مفروضة ذاتيًا حيث تأتي القيمة من متابعتها. BEM و Sass لأولئك الذين يستخدمون Sass ويستمتعون بتشعيب (nesting) العناصر كوسيلة لتحديد النطاقات لتنسيقات، لازال بإمكانك أن تكون المسؤول عن الصيغة المتشعبة ولكن يمكنك الحصول على CSS غير متشعب، باستخدام @at-root: ملف Scss: .block { @at-root #{&}__element { } @at-root #{&}--modifier { } } يولد ملف CSS التالي: .block { } .block__element { } .block--modifier { } ويمكنك الحصول على ملخص كما تريد! تحقق من منشئ BEM الذي يخص Danield Guillan أو Anders Schmidt Hansen من أجل BEM التعبيرية. الخلاصة أعتقد أنه من الإنصاف القول إنه على الرغم من أن BEM لن يحل جميع مشاكلنا، إلا أنه مفيد بشكل كبير في بناء واجهات قابلة للتطوير والصيانة حيث يجب أن يكون لدى كل فرد في الفريق فكرة واضحة عن كيفية تطوير تلك الأشياء. ذلك لأن الكثير من التطوير في الواجهة الأمامية لا يتعلق فقط بالخدع اللطيفة التي تحل مشكلة صغيرة واحدة على المدى القصير؛ نحتاج إلى اتفاقات وعقود ملزمة بين المطورين بحيث يمكن لبرنامجنا التكيف مع مرور الوقت وعلى المدى البعيد. بشكل عام، أود أن أفكر في BEM كإجابة على سؤال نيكولاس غالاغر: ترجمة -وبتصرف- للمقال BEM 101 لصاحبها Robin Rendle.
-
SASS و Compass هما أداتان تساعدانك على كتابة شيفرة CSS أفضل وبشكل أسرع، ويوجد بهما ما يسمى بالـmixins وهناك أيضًا الدّوال (functions). الـmixins والدّوال ببساطة هي مجموعة من شيفرة CSS التي تقوم بتعريفها/كتابتها مرة واحدة وبعدها يمكنك استخدامها في أي مكان تريده. الفرق بين الـmixin والدالة هو أن الأولى تقوم بمناداتها باستخدام include@ وبعدها يتم ادخال الشيفرة الموجود بها في المكان الذي تمت مناداتها فيه، أما الدالة فلا تحتاج لكتابة كلمة معينة لمناداتها وتقوم بإرجاع قيمة معينة. يمكنك أن تقرأ المزيد حول الفرق بينهما من هنا. Compass هي مكتبة تحتوي على مجموعة من الـmixins الخاصة بـSASS وبها العديد من الأمور المفيدة مثل border-radius و box-shadow. ولكن يمكنك بكل تأكيد أن تقوم بإنشاء mixins ودوال خاصة بك، وهذه بعضها والتي أقوم باستخدامها في كل مشروع أعمل عليه. تأثير أبيض وأسودلنقل أنك تريد نصًا بلون أبيض وبشفافية 90% على خلفية سوداء بشفافية 15%. في حالة كتابة شيفرة CSS كالمعتاد سيكون لديك شيء كهذا: .my-class{ background:rgba(0,0,0,0.15); color:rgba(255,255,255,0.9); }ولكن مع SASS يمكننا كتابة دالتين مفيدتين كالتالي: @function black($opacity){ @return rgba(0,0,0,$opacity) } @function white($opacity){ @return rgba(255,255,255,$opacity) }الآن كل ما سنحتاج لكتابته هو: .my-class{ background:black(0.15); color:white(0.9); }فكما ترى هذا سيوفر علينا بعض الوقت والجهد في كتابة شيفرة CSS اعتيادية ويمكننا كذلك أن نستخدم تلك الدوال في أي مكان نريده حتى نخرج بنفس النتيجة. تأثيرا Emboss وLetterpressصحيح أن عالم التصميم أصبح يبتعد عن التصاميم المزخرفة التي تجعل الأشياء تبدو شبه حقيقة وذلك بسبب ظهور التصميم المسطح، إلّا أنّك في بعض الأحيان تحتاج إلى بعض الظلال أو شيء من هذا القبيل في تصاميمك. المشكلة الوحيدة هي أنّ الظلال في CSS ليست بتلك الجودة وسهولة الاستخدام، ولذلك فإنّ Compass تحتوي على mixin تحمل الاسم box-shadow لتسهل علينا استخدامها، ومع ذلك أعتقد أنه يمكننا أن نقوم بإنشاء شيء أفضل. إذا قمت بتصفح موقع Dribbble فسوف ترى أن 90% من التصاميم التي تحتوي على ظلال تكون تقريبًا بنفس النوع، بحيث يكون هناك خطين بحجم 1px موضوعين أسفل وأعلى التصميم لإعطاء المستخدم انطباعًا وكأنّ ذلك الجزء من التصميم منقوش/محفور في التصميم. أنظر الى الصورة التالية لتفهم ما أعنيه: وهذه mixin بسيطة يمكنك استخدامها لإنشاء ذلك النوع من التأثيرات بسهولة: @mixin box-emboss($opacity, $opacity2){ box-shadow:white($opacity) 0 1px 0, inset black($opacity2) 0 1px 0; }والآن كل ما عليك فعله للحصول على ذلك التأثير هو أن تقوم بالتالي: .box{ @include box-emboss(0.8, 0.05); }وهناك تأثير مشابه يسمى Letterpress وهو نفس التأثير السابق ولكن عندما يتم تطبيقه على النصوص، فالخط الأبيض لا يقوم فقط بالمساعدة في إنشاء ذلك التأثير وإنما يجعل النص مقروءًا أكثر. وها هي الـmixin التي تقوم بذلك: @mixin letterpress($opacity){ text-shadow:white($opacity) 0 1px 0; } إخفاء النصوص واستبدال الصورإلى الآن كل ما قمنا باستخدامه كان على العناصر والتأثيرات البصرية، ولكن الـmixins يمكنها أيضًا مساعدتنا في القيام ببعض الأمور المخفية والمتعبة في CSS. فمن أحد الامثلة الشائعة هو استبدال النص بصورة باستخدام خاصية background في CSS. عادةً يتم استبدال الشعارات (logos) والأزرار (buttons) باستخدام هذه الطريقة. هذه هي الـmixin التي نريدها: @mixin hide-text{ overflow:hidden; text-indent:-9000px; display:block; }ويمكن استخدامها هكذا: .logo{ background: url("logo.png"); height:100px; width:200px; @include hide-text; }وكما هو الحال في كل شيء متعلق بتطوير الويب فإن المتصفحات تتطور أيضًا وتحدث تغيرات كثيرة بسرعة. فالطريقة الموضحة بالأعلى يمكننا استبدالها بطريقة أفضل ويمكنك قراءة هذه المقالة للتعرف على هذه الطريقة. تخيّل لو أننا أردنا أن نقوم باستبدال الطريقة الأولى بالثانية فقط باستخدام CSS الاعتيادي، فعندها كنا سنحتاج إلى أن نقوم باستبدال جميع الشيفرات يدويًا، أمّا مع الـmixin فإنه يمكننا ببساطة تغيير محتويات الـmixin أو التعديل عليها وسوف ينطبق التعديل/التغيير على جميع الشيفرات وفي جميع الأماكن التي استخدمنا فيها الـmixin. أنظر إلى نفس الـmixin الموجود في الأعلى ولكن بتغيير محتوياته إلى الطريقة الثانية (الموجودة في المقال الذي وضعت رابطه بالأعلى): @mixin hide-text{ font: 0/0 a; text-shadow: none; color: transparent; } قوائم التنقل الأفقية (horizontal navigations)شيء آخر نستخدمه كثيرًا وهو استخدام عناصر ul و li لبناء القوائم الأفقية، وهذا في العادة يحتاج منا أن نقوم بتجريد هذه العناصر من بعض تنسيقاتها لنضع تنسيقات خاصة بنا ومفيدة أكثر وحتى نستطيع أيضًا أن نجعل عناصر القائمة تظهر بجانب بعضها بشكل أفقي. فبدلًا من كتابة الشيفرة في كل مرة ولكل قائمة فإننا نستخدم mixin كالتالي: @mixin navigation-list { list-style-type:none; padding:0; margin:0; overflow:hidden; > li{ display:block; float:left; &:last-child{ margin-right:0px; } } }يمكنك أن ترى من هذه الـmixin مبدئين من مبادئ الـSASS وهما القواعد المتداخلة (nested rules) وإشارة "&". القواعد المتداخلة تعني أنك لن تحتاج إلى كتابة العنصر الأب في كل مرة، فمثلًا الشيفرة التالية: ul{ color:red; } ul li{ font-weight:bold; }يمكن كتابتها هكذا: ul{ color:red; li{ font-weight:bold; } }وإشارة "&" هي عبارة عن اختصار للعنصر الحالي، ففي الشيفرة التالية تدل إشارة "&" على العنصر "a.my-link": a.my-link{ color:red; &:hover{ color:blue; } }وبالعودة إلى مثال القائمة يمكنك أن ترى كيف أنّ mixin واحدة يمكنها التأثير على أكثر من عنصر في نفس الوقت، وعندما تقوم بدمجها مع اشارة "&" فتستطيع عندها القيام بالكثير من الأمور المفيدة والرائعة. ترجمة -وبتصرّف- للمقال Useful SASS Mixins لصاحبه Sacha Greif.
-
إن إنشاء عروض وتصاميم باستخدام أوراق الأنماط المُتتالية CSS ليس بالأمر الجلل بحدّ ذاته، ولكنّها وسيلة جيّدة لاستعراض قدرات وإمكانيات CSS، وتجربة أدوات ومفاهيم جديدة، أو للتدريب على العمل ضمن شروط وقيود مُحدّدة، حيثُ سيُلقي هذا المقال نظرةً على كيفيّة إنشاء تأثير قطرات مطر (raindrops) على نافذة وذلك باستخدام تقنيات HAML و SASS. يُمكن استعراض مثال كامل لفكرة الدرس على موقع CodePen مع الشيفرة الخاصّة به. المعالج التمهيدي (Preprocessor)سيتمّ أوّلًا وقبل كل شيء توضيح لماذا سيتمّ استخدام تقنيتي HAML/SASS بدلًا من الزوج الشائع HTML/CSS، ومع العلم أنّهما يتمتعان بالعديد من المزايا والتسهيلات ولكن السبب في الحاجة إلى معالجات تمهيديّة هنا هو أنها تسمح للمطوّر باستخدام المُتغيّرات، إنشاء حلقات تكراريّة (loops)، وتوليد قيم عشوائيّة، وبذلك لن يتمّ التعامل مع المئات من قطرات المطر بشكل منفصل بل سيتمّ إنشاؤها برمجيًّا. يُمكن الاستزادة حول الإعداد الأوّلي والصياغة (syntax) من خلال زيارة موقع كل تقنيّة سواءً SCSS أو HAML، أو من المُمكن مبدئيًّا تطبيق الدرس على موقع CodePen، من خلال إنشاء pen جديد واختيار SCSS كمُعالج تمهيدي لـِ CSS و HAML من أجل HTML. إنشاء النافذةستكون الخطوة الأولى هي عرض النافذة نفسها. .container .window // صورة الخلفية $image: 'http://i.imgur.com/xQdYC7x.jpg'; // عرض وطول الحاوية $width:100vw; $height:100vh; .container{ position:relative; width:$width; height:$height; overflow:hidden; } .window{ position:absolute; width:$width; height:$height; background:url($image); background-size:cover; background-position:50%; filter:blur(10px); }تمّ في الشيفرة السابقة وبكل بساطة رسم div مع صورة خلفيّة (background image)، وتطبيق مُرشح غشاوة (blur) عليها لكي تُصبح القطرات جليّة أكثر للناظر. يُلاحظ كيف أنّه تمّ تخزين مسار (URL) صورة الخلفيّة في مُتغيّر image$، وذلك لأنه سيتمّ استخدام ذات الصورة للقطرات نفسها كما سيتّضح ذلك فيما بعد. قطرات المطر في الطبيعةسيتمّ إلقاء نظرة على القطرة وكيف تبدو في الحياة الطبيعيّة قبل الشروع في تطبيق التأثير. يعود مصدر الصورة إلى موسوعة ويكيبيديا. ستَقلب القطرة الصورة الّتي خلفها بمقتضى انكسار الضوء، كما سيكون للقطرة حدودًا (border) سوداء عندما يكون شكلها نصف كرويّ كامل أو غير كامل. إنشاء قطرات المطر (Raindrops)سيتمّ في الخطوة التّالية إنشاء قطرة مطر واحدة بعد أن تمّ معرفة الأساسيات. .container .window .raindrop $drop-width:15px; // قطرات المطر لن تكون دائرية تماما لذلك سنقوم بتمديدها قليلا // حتى لا تتمدد الخلفية أيضا transform:scale لن نستخدم $drop-stretch:1.1; $drop-height:$drop-width*$drop-stretch; .raindrop{ position:absolute; top:$height/2; left:$width/2; width:$drop-width; height:$drop-height; // border-radius:100% بدلا من border-radius:100px, حتى يكون شكل قطرات المطر بيضاوي وليس كبسولي border-radius:100%; background-image:url($image); background-size:$width*0.05 $height*0.05; transform:rotate(180deg); }ما تمّ عمله في الشيفرة السابقة هو رسم div على شكل إهليلجي (بيضاوي)، وتطبيق صورة خلفيّة (background image) داخله، وهي نفس الصورة المُستخدمة سابقًا، وبعد ذلك تم تقليص حجم الخلفيّة ومن ثم قلب القطرة رأسًا على عقب. سيتمّ الآن إضافة حدودًا حول القطرة، لتبدو القطرة وكأن لها حجمًا. ... .border .raindrop ... .border{ position:absolute; top:$height/2; left:$width/2; margin-left:2px; margin-top:1px; width:$drop-width - 4; height:$drop-height; border-radius:100%; box-shadow:0 0 0 2px rgba(0,0,0,0.6); }يُلاحظ كيف أنّه لم يتمّ إضافة حدودًا كاملة حول القطرة، بل التعديل على مكانها والضغط على جانبيها قليلًا لتبدو أقرب إلى القطرة الطبيعيّة. سيتمّ في الخطوة التّالية إضافة المئات من هذه القطرات وذلك بعد الانتهاء من استكمال رسم القطرة الأولى على أكمل وجه. ... .raindrops .borders - (1..100).each do .border .drops - (1..100).each do .raindropإن الشيفرة السابقة ما هي إلا شيفرة HAML في صياغة حلقة تكرار (loop)، فكل ما تمّ عمله هو إضافة مئة كائن من raindrop.و مثلها للكائن border. سيتمّ إلقاء نظرة مُفصّلة على شيفرة SASS بما أنّها مُحيّرة بعض الشيء. سيتمّ بدايةً إنشاء الحلقة التكراريّة (loop) ومن ثُمّ اختيار كل عنصر (element) بشكل منفصل: @for $i from 1 through 100{ .raindrop:nth-child(#{$i}){ } .border:nth-child(#{$i}){ } }سيتمّ الآن توليد وتطبيق تَمَوْضُعات (positions) وأحجام عشوائية لقطرات المطر: @for $i from 1 through 200{ //توليد رقم عشوائي من 0 إلى 1 لإختيار التموضع $x:random(); $y:random(); // إختيار حجم وتمديد قطرة المطر عشوائيا // بما أن لكل قطرة مطر حجم مختلف، سنقوم بحساباتنا هنا .raindrop selector $drop-width:5px+random(11); $drop-stretch:0.7+(random()*0.5); $drop-height:$drop-width*$drop-stretch; .raindrop:nth-child(#{$i}){ // ضرب قيمة الموضع العشوائي في حجم الحاوية left:$x * $width; top:$x * $height; width:$drop-width; height:$drop-height; } .border:nth-child(#{$i}){ // سنقوم بنفس الشيء لحدود القطرة left:$x * $width; top:$x * $height; width:$drop-width - 4; height:$drop-height; } } سيتمّ أخيرًا تغيير تموضع خلفيّة كل قطرة بحسب تموضع القطرة، ليكون تأثير الانعكاس أكثر جمالًا. ... .raindrop:nth-child(#{$i}){ ... background-position:percentage($x) percentage($y); } ... سيكون بذلك قد تمّ الانتهاء من إنشاء التأثير الرئيسي، ولكن بالإمكان التعديل والتحسين عليه، وذلك من خلال الاهتمام ببعض التفاصيل الصغيرة، مثل زيادة السطوع (brightness) للقطرات قليلًا لتبدو صافية وذات لمعة، أو تغيير التركيز البصري ليكون على الخلفيّة بدلًا من القطرات، أو رُبّما تغيير صورة الخلفيّة. يُمكن الوصول إلى النسخة النهائيّة للمثال بجانب الشيفرة بشكلها النهائي من خلال موقع CodePen. ترجمة وبتصرّف للمقال CSS-Only Raindrops on Window Effect لصاحبه Lucas Bebber.
-
لعلّ من أقوى ميّزات Sass الدّوالّ (mixins) ﻷنّها تسمح بتحديد نمط مُتكرِّر وعزله في صورة قطعة برمجيّة يمكن إعادة استخدامها مرارًا. مثال ذلك: عزل كلّ الخصائص المسؤولة عن تنسيق زرّ في صفحة الويب ثمّ —بدل الحاجة لتذكّر كل هذه الخصائص— جمعها في قطعة منفصلة يمكن استدعاؤها في مُحدِّد آخر (selector). هذا يحفظ أنماط الزرّ في موضع واحد ممّا يجعل تعديلها وتحديثها أسهل. المشكلة في هذا أنّ كثيرًا من دوّال Sass هذه تُكتب بأسلوب يجعل الخصائص مُكرّرة، وهذا قد يؤدي إلى حدوث أخطاء في عدة مواضع عند تحويلها إلى CSS بالإضافة إلى زيادة حجم الملفّ النّهائي دون طائل. صحيح أنّ استخدام Sass هو خطوةٌ على الطّريق الصّحيح لإنتاج CSS أخفّ حجمًا وأكثر اختصارًا ممّا قد يكتبه مبرمج متوسّط المستوى، لكنّك إن كنت مهووسًا بتحسين الأداء مثلي، فلن ترضى بوجود أدنى مقدار من التّكرار الّذي لا مُبرِّر له. سأدلكّ في هذه المقالة على كيفيّة تحسين مستوى CSS النّاتج عن دوالّ Sass الّتي تكتبها. مقدّمة سريعة عن Sassيمكن وصف Sass بأنّها مرحلة وسيطة بين ورقة الأنماط الّتي يكتبها مُصمّم الموقع وناتج CSS النّهائي الّذي يطلبه المتصفّح، وتفيد Sass في هذه المرحلة بإضافة العديد من الميّزات الّتي تُسهلّ كتابة CSS وصيانتها. واحدة من فوائد Sass أنّها تُساعِد الُمصمّمين في تجنّب التّكرارات في CSS، ممّا يجعل صيانتها أكثر سهولة. اصطلح على مبدأ "لا تُكرِّر نفسك" (Don't Repeat Yourself أو اختصارًا DRY) في كتاب "المُبرمج البراغماتيّ"، والذي نصّ على التّالي: لا تُساعِدنا CSS كثيرًا في تطبيق هذا المبدأ، لأنّها تُضطرّنا إلى تكرار كتابة الكثير من الخصّائص (كما في مثال الأزرار الذي ضربناه). فلو احتجت إلى إنشاء أنماط لثلاثة أنواع من الأزرار، فستضطر إلى كتابة هذه الأنماط مرّة لكلّ نوع من الأزرار، أو تجزئة الأنماط على أكثر من مُحدّد. وهما أمران أحلاهما مُرٌّ. فالأوّل يعني نسخ الخصائص ولصقها (وزيادة حجم الملفّ بالنتيجة) وتكرار مواضع الأخطاء، وغياب مصدر واحدٍ للحقيقة، وبنية هشّة في حصيلة الأمر؛ وأمّا الثاني فيعني غياب تمثيل مُفرد ومُوحَّد لكلّ نوع من الأزرار في النّظام، لأنّه يُجبرك على تجزئة الأنماط على عدّة مُحدِّدات، وهو ما يجعل البنية هشّة أيضًا، لأنّ تكوينها غير واضح. لو أردنا تحرّي المثاليّة، فعلينا إيجاد طريقة لتعريف الأنماط الأساسيّة في موضع واحد دون تكرار، ثمّ تعريف مُحدِّد مُفرد يمكن استخدامه لتطبيق كلّ الأنماط. لماذا قد أرغب باتّباع مبدأ "لا تُكرِّر نفسك"؟قد تسأل نفسك: لم كلّ هذا التّعقيد؟ الجواب باختصار: لأنّ اتّباع مبدأ DRY يُحسّن من أداء الموقع. فعندما تؤسّس هيكل الموقع، سيكون الأداء عاملًا مُهمًّا، بدءًا من صيغ الصّور الّتي نختارها ومرورًا بطريقة كتابة مُحدِّدات CSS. وهذا العاملّ يُصبح أكثر أهمّية عندما نتحدّث عن الأجهزة المحمولة، ففي هذه الأجهزة قد يفرض مُجرّد طلب HTTP بسيط تحدّيات كبيرةً في الأداء، وخطأ الاعتقاد الشّائع بعدم أهمّيّة حجم ملفّ CSS في أداء الموقع يظهر أكثر وضوحًا في الأجهزة المحمولة الّتي لا تسمح بأكثر من 100 ميغابايت من الحجم الكلّي للتّخزين المؤقّت لكلّ المواقع، وعندها يجب استغلال أكبر مقدار ممكن من مساحة التّخزين المؤقّت بأفضل صورة. الهدف من ذلك إذن هو إنشاء مُحدِّدات يمكن صيانتها في Sass وفي HTML، ويمكن تمثيلها في CSS بأقصر أسلوبٍ ممكن لتقليل حجم التّخزين المؤقّت. الدّوال (mixins) والتّوسعة (extends): حلّان غير كاملينتٌقدّم دوالّ Sass حلًّا لإحدى هاتين المُشكلتين، فهي تسمح بإنشاء موضع واحد يمكن فيه تعريف الأنماط الأساسيّة والرّجوع إليها. يمكن لهذه الدّوال أن تقبل مُعاملات (arguments) كذلك، مما يسمح باختلافات طفيفة بين استدعاء وآخر للدّالة وإنشاء أشكال مختلفة لنفس النّمط. لكن الأمر لا يخلو من مُشكلات: فقد تُسبّب هذه الدّوال تكرارًا غير مُبرّر للخصائص لأنّها ستكتب الخصائص الّتي تحويها في كلّ موضع استدعيت فيه. فالدّوالّ إذن تحل مشكلة التُمثيل المُفردّ في مبدأ DRY، لكنّها تترك خصائص متكرّرة في النّاتج النّهائيّ من CSS. ولهذا تُقدّم Sass مفهومًا آخر يُساعِد في تطبق مبدأ DRY: التّوسعة (extends)، والّتي يمكن استخدامها بكتابة الكلمة @extend، والّتي تسمح لمُصمّم CSS أن يقول: "أريد للُمحدّد A أن يظهر كما يظهر المُحدّد B"، وعندها تقوم Sass بإنشاء نمط جديد يجمع بين A وB (مفصول بينهما بفاصلة)، ثمّ كتابة الخواصّ غير المشتركة بالأسلوب العاديّ. لا يمكن تمرير مُعاملات للتّوسعة (خلافًا للدّوالّ)، فهي حلّ من نوع "الكلّ أو لا شيء". .couch { padding: 2em; height: 37in; width: 88in; z-index: 40; } .couch-leather { @extend .couch; background: saddlebrown; } .couch-fabric { @extend .couch; background: linen; } .couch, .couch-leather, .couch-fabric { padding: 2em; height: 37in; width: 88in; z-index: 40; } .couch-leather { background: saddlebrown; } .couch-fabric { background: linen; } تحلّ التّوسعة مشكلة تكرار الخصائص والمُحدِّد المُفرّد في CSS النّاتج، لكنّها لا تُخلّص مُصمّمي المواقع من الحاجة لصيانة مجموعتين منفصلتين من الأنماط في ملفّات Sass، ولا من الحاجة لتذكّر أيّ الخصائص ينطبق على أيّ نوع—فالأمر لا يختلف كثيرًا فيما لو كتبنا محدّدين منفصلين في الأساس. كما نرى، فكلا الحّلين (الدّوالّ والتّوسعة) غير كاملين، ولكن عند جمعهما معًا ثمّ اعتماد بنيّة ذكيّة في تصميم مشروعنا، مع الاستفادة من بعض ميّزات Sass سنحصل على دالّة نهائيّة تخضع لمبدأ DRY تمامًا، وتجمع نصفي الحلّ في صيغة موحّدة سواءٌ في ملفّات Sass الأصليّة أو في ناتج CSS. المكوّنات الأساسيّة لمبدأ "لا تُكرِّر نفسك"تضع المُكوّنات الأربعة التّالية حجر الأساس لبناء دوالّ موافقة لمبدأ DRY في Sass: المُحدّدات بالنّيابة (placeholder selectors)، والجداول (maps)، والكلمة @at-root، والدّالة unique-id(). المُحدّدات بالنّيابةوهي نوعٌ خاصّ من المُحدّدات يُستخدم مع الكلمة @extend في Sass. تُكتب هذه المُحدّدات كما تُكتب الأصناف التّقليديّة (classes)، ولكنّها تبدأ بالرّمز % بدل النّقطة .، وتتصرّف عند توسعتها بصورة عاديّة، ولكنّها لا تُضاف إلى النّاتج النّهائيّ إلّا عند توسعتها. وكما في التّوسعة العاديّة، تُضاف خواصّ المحدّدات في الموضع الّذي عُرّف فيه المُحدّد. %foo { color: red; } .bar { @extend %foo; background: blue; } .baz { @extend %foo; background: yellow; } .bar, .baz { color: red; } .bar { background: blue; } .baz { background: yellow; } الجداول (maps)وهي نوعٌ من أنواع البيانات في Sass 3.3 (مثلها مثل الأرقام والسلاسل النّصيّة والقوائم) تتصرّف بطريقة مُشابهة للكائنات في JavaScript. تتألّف الجداول من أزواج مفتاح/قيمة (key/value)، حيث يمكن للمفتاح والقيمة أن يكونا أي نوع من أنواع البيانات في Sass (بما في ذلك الجداول نفسها). المفاتيح فريدة دومًا ويمكن الوصول إليها باسمها، ممّا يجعلها مثاليّة لتخزين البيانات المُميّزة واسترجاعها. $properties: ( background: red, color: blue, font-size: 1em, font-family: (Helvetica, arial, sans-serif) ); .foo { color: map-get($properties, color); }الكلمة @at-rootقُدّمت هذه الكلمة في Sass 3.3، وهي تقوم بنقل الخواصّ المُعرّفة ضمنها إلى جذر ورقة الأنماط (أعلى مستوىً فيها)، بغض النّظر عن الموضع الّذي استخدمت فيه. الدّالّة unique-id()قدّمت في Sass 3.3 كذلك، وهي تُعيد مُعرّف CSS يُضمن كونُه فريدًا في كلّ عمليّة تحويل من Sass إلى CSS. كتابة دالّة بسيطةيتطلّب تحويل نمط مُتكرِّر إلى دالّة النّظرَ في الأنماط الأساسيّة المُكوّنة لها ثم تحديد ما المشترك بينها وما الذي يختلف بحسب مُدخَلات المستخدم. سنستخدم زرًّا بسيطًا كمثال في حالتنا: .button { background-color: #b4d455; border: 1px solid mix(black, #b4d455, 25%); border-radius: 5px; padding: .25em .5em; &:hover { cursor: pointer; background-color: mix(black, #b4d455, 15%); border-color: mix(black, #b4d455, 40%); } }لتحويل هذا إلى دالّة، اختر الخصائص الّتي يتحكّم بها المستخدم (الديناميكيّة)، والخصائص الثّابتة. يُتحكّم بالخصائص الدّيناميكيّة عبر مُعامِلات تُمرّر إلى الدّالة، أمّا الخصائص الثّابتة فتكتب بالأسلةب العاديّ. في حالة الزّرّ الذي نُصمّمه، لا نريد سوى أن يتغيّر اللّون، ولهذا نستدعي الدّالّة بمُعامل اللّون، لُينتَج CSS كما نتوقّع: @mixin button($color) { background-color: $color; border: 1px solid mix(black, $color, 25%); border-radius: 5px; padding: .25em .5em; &:hover { cursor: pointer; background-color: mix(black, $color, 15%); border-color: mix(black, $color, 40%); } } .button { @include button(#b4d455); }لا بأس بهذا الحلّ، ولكنّه سيؤدّي إلى تكرار خصائص كثيرة، افترض مثلًا أنّنا نريد إنشاء زرّ آخر بلون مختلف، عندها ستبدو شيفرة Sass كما يلي (دون تعريف الدّالّة): .button-badass { @include button(#b4d455); } .button-coffee { @include button(#c0ffee); }وستبدو شيفرة CSS كما يلي: .button-badass { background-color: #b4d455; border: 1px solid #879f3f; border-radius: 5px; padding: .25em .5em; } .button-badass:hover { cursor: pointer; background-color: #99b448; border-color: #6c7f33; } .button-coffee { background-color: #c0ffee; border: 1px solid #90bfb2; border-radius: 5px; padding: .25em .5em; } .button-coffee:hover { cursor: pointer; background-color: #a3d8ca; border-color: #73998e; }هناك الكثير من الخصائص المُكرَّرة هنا، لا نريد هذا! لذا سنلجأ إلى استخدام المُحدِّدات بالنّيابة. إزالة التّكرار من دالّةإزالة التّكرار من الدّالة يقتضي تجزئتها إلى أجزاء ثابتة وأخرى ديناميكيّة. الأجزاء الدّيناميكيّة هي ما سيستدعيه المستخدم، وأمّا الثّابتة فتحوي الأجزاء الّتي ستكون مُكرّرة فيما لو لم نجمعها في دالّة. @mixin button($color) { @include button-static; background-color: $color; border-color: mix(black, $color, 25%); &:hover { background-color: mix(black, $color, 15%); border-color: mix(black, $color, 40%); } } @mixin button-static { border: 1px solid; border-radius: 5px; padding: .25em .5em; &:hover { cursor: pointer; } }الآن وقد فصلنا دالّتنا على جزأين، نريد أن نُوسِّع العناصر في button-static لتجنّب التّكرار. يمكن إنجاز ذلك باستخدام مُحدّد بالنّيابة بدل الدّالة، ولكن هذا يعني نقل المُحدّدات في ورقة الأنماط، لذا سنقوم بإنشاء مُحدّد بالنّيابة في الموضع ذاته ديناميكيًّا، بحيث يُنشأ حالما يُحتاج إليه ويبقى بترتيبه الأصليّ كما نتوقّع. لفعل ذلك، سنقوم أوّلًا بإنشاء مُتغيّر في النّطاق العامّ لحفظ أسماء المُحدّدات الدّيناميكيّة: $Placeholder-Selectors: ();ثمّ نتحرّى وجود مفتاح يُطابق محدّدنا في button-static، وسندعو هذا المفتاح "button" في الوقت الحالي. باستخدام الدّالّة map-get، سنحصل على قيمة المفتاح المطلوب أو القيمة null إن لم يُوجد، وفي الحالة الأخية سنُعيّن قيمته إلى مُتغيّر فريد (unique ID) باستخدام map-merge. سنستخدم الوسم !global كوننا نريد كتابة قيمة متغيّر عُرِّف في النّطاق العامّ: $Placeholder-Selectors: (); // ... @mixin button-static { $button-selector: map-get($Placeholder-Selectors, 'button'); @if $button-selector == null { $button-selector: unique-id(); $Placeholder-Selectors: map-merge($Placeholder-Selectors, ('button': $button-selector)) !global; } border: 1px solid; border-radius: 5px; padding: .25em .5em; &:hover { cursor: pointer; } }بعد معرفة وجود مُعرّف لمُحدّدنا، نُريد إنشاء هذا المُحدّد، ونفعل هذا باستخدام الكلمة @at-root وصياغة تعويض القيم في النّصوص (#{}) لإنشاء مُحدّد بالنّيابة في جذر ورقة الأنماط اسمه هو المُعرِّف الفريد الّذي حصلنا عليه. محتويات هذا المُحدِّد ستكون استدعاء للدّالة الثّابتة (لاحظ الاستدعاءات المتداخلة! يا للرّوعة!). ثمّ نُوسّع هذا المُحدّد بالنّيابة ذاته، ممّا يُفعّله ويؤديّ لكتابة خواصّه إلى CSS. باستخدام مُحدّد بالنّيابة في هذه الحالة بدل توسعة مُحدّد تقليديّ كصنف (class)، فإنّ محتويات المُحدّد ستُضاف إلى CSS النّهائي فقط إن وُسِّع المُحدِّد، ممّا يسمح بتقليص حجم الملفّ. وباستخدام التّوسعة بدل كتابة الخصائص مباشرةً، نكون قد تجنّبنا تكرار الخصائص في الوقت ذاته. في النّهاية سيمنع هذا هشاشة النّاتج النّهائي من CSS، لأنّه في كلّ مرّة تُستدعى هذه الدّالة، فستكون الخصائص المُشتركة ضمنها مُشاركة بالفعل ضمن ناتج CSS، وليس فقط مرتبطة مع بعضها في مرحلة المُعالجة المسبقة لـCSS فحسب. $Placeholder-Selectors: (); // ... @mixin button-static { $button-selector: map-get($Placeholder-Selectors, 'button'); @if $button-selector == null { $button-selector: unique-id(); $Placeholder-Selectors: map-merge($Placeholder-Selectors, ('button': $button-selector)) !global; @at-root %#{$button-selector} { @include button-static; } } @extend %#{$button-selector}; border: 1px solid; border-radius: 5px; padding: .25em .5em; &:hover { cursor: pointer; } }لكن مهلًا، فلم ننتهِ بعدُ. ستبقى هناك محتويات مُكرّرة في النّاتج النّهائي، وهذا شيء لا نريده (حيث سنخصل على مُحدّد يوسّع نفسه، وهذا لا نريده أيضًا). لتجنّب هذا، سُنضيف مُعاملًا إلى button-static يُحدّد إن كان يجب المُضيّ بعمليّة التّوسعة أم لا. سُنضيفه هذا إلى دالّتنا الدّيناميكيّة أيضًا، ونمرّره إلى دالّتنا الثّابتة، وفي النّهاية ستكون لدينا الدّوالّ التّالية: $Placeholder-Selectors: (); @mixin button($color, $extend: true) { @include button-static($extend); background-color: $color; border-color: mix(black, $color, 25%); &:hover { background-color: mix(black, $color, 15%); border-color: mix(black, $color, 40%); } } @mixin button-static($extend: true) { $button-selector: map-get($Placeholder-Selectors, 'button'); @if $extend == true { @if $button-selector == null { $button-selector: unique-id(); $Placeholder-Selectors: map-merge($Placeholder-Selectors, ('button': $button-selector)) !global; @at-root %#{$button-selector} { @include button-static(false); } } @extend %#{$button-selector}; } @else { border: 1px solid; border-radius: 5px; padding: .25em .5em; &:hover { cursor: pointer; } } }بعد كلّ هذا العناء، أنشأنا لأنفسنا طريقة لتسهيل صيانة أنماط Sass وتوفير مُحدِّد مُفرد في HTML، وإبقاء حجم CSS في أدنى الحدود. فهمها بلغ عدد استدعاءات دالّة button في شيفرتنا، لن تُكرَّر الخصائص الثّابتة فيها. عند استدعاء دالّتنا لأوّل مرّة، سُتنشأ الأنماط الّتي فيها ضمن CSS في الموضع الّذي استدعيت فيه، ممّا يُحافظ على تراكب الأنماط بالتّرتيب المُتوقّع، ويقلّل هشاشة البنية. وبما أنّنا نسمح بعدّة استدعاءات للدّالّة نفسها، فبإمكاننا إنشاء "نكهات" مُختلفة بسهولة وصيانتها في كلا Sass وHTML. في هذه المرحلة سيكون لدينا مصدر Sass وناتج CSS كما يلي: .button-badass { @include button(#b4d455); } .button-coffee { @include button(#c0ffee); } .button-decaff { @include button(#decaff); } .button-badass { background-color: #b4d455; border-color: #879f3f; } .button-badass, .button-coffee, .button-decaff { border: 1px solid; border-radius: 5px; padding: .25em .5em; } .button-badass:hover, .button-coffee:hover, .button-decaff:hover { cursor: pointer; } .button-badass:hover { background-color: #99b448; border-color: #6c7f33; } .button-coffee { background-color: #c0ffee; border-color: #90bfb2; } .button-coffee:hover { background-color: #a3d8ca; border-color: #73998e; } .button-decaff { background-color: #decaff; border-color: #a697bf; } .button-decaff:hover { background-color: #bcabd8; border-color: #857999; } لاحظ كيف يُفصل بين الخصائص الثّابتة بفواصل في موضع تعريفها، ممّا يجعل تتبّع الأخطاء أسهل، ويحافظ على ترتيب النّصّ المصدريّ، ويقلّل حجم ملف CSS، وبحيث لا تُنشأ مُحدّدات جديدة إلّا للخصائص الّتي تتغيّر. أليس هذا رائعًا؟ المتابعةالحقيقة أنّ كتابة النّمط نفسه لكلّ دالّة لا يتوافق مع مبدأ DRY على الإطلاق؛ بل على العكس تمامًا فهو WET (اختصارًا لـWrite Everything Twice، هل لاحظت ظرافة المبرمجين؟!). لا نريد هذا، بل يجب أن نُفكّر في كتابة دالّة لتوليد المُحدّدات بالنّيابة، يمكننا استدعاءها بدل ذلك. أو في حال استخدام إضافة Toolkit لـSass (إما من خلال Bower أو كإضافة لـCompass)، فيمكن في هذه الحالة استخدام الدّالة dynamic-extend لإيجاد وإنشاء وتوسعة مُحدّد ديناميكيّ بالنّيابة. كلّ ما عليك فعله إرسال سلسلة نصيّة للبحث عنها (مثل "button"): @import "toolkit"; @mixin button($color, $extend: true) { @include button-static($extend); background-color: $color; border-color: mix(black, $color, 25%); &:hover { background-color: mix(black, $color, 15%); border-color: mix(black, $color, 40%); } } @mixin button-static($extend: true) { $button-selector: map-get($Placeholder-Selectors, 'button'); @if $extend == true { @include dynamic-extend('button') { @include button-static(false); } } @else { border: 1px solid; border-radius: 5px; padding: .25em .5em; &:hover { cursor: pointer; } } }وهكذا يمكن القضاء على أي تكرار في دالّتنا، مما يقلّل حجم ملفّات Sass الأصلية وملفّات CSS النّهائيّة معًا، ممّا يدفعنا خطوة على طريق إعادة استخدام مكوّناتنا في مشاريع أخرى. ترجمة (بشيء من التصرف) للمقال DRY-ing out Your Sass Mixins لصاحبه Sam Richard.