دليل إستراتيجي لخصائص CSS المخصصة
نشرت: 2022-03-10خصائص CSS المخصصة (المعروفة أحيانًا باسم "متغيرات CSS") مدعومة الآن في جميع المتصفحات الحديثة ، ويبدأ الناس في استخدامها في الإنتاج. هذا رائع ، لكنها تختلف عن المتغيرات في المعالجات الأولية ، وقد رأيت بالفعل العديد من الأمثلة لأشخاص يستخدمونها دون التفكير في المزايا التي يقدمونها.
تتمتع الخصائص المخصصة بإمكانية هائلة لتغيير طريقة كتابتنا وهيكلنا لـ CSS وبدرجة أقل ، كيف نستخدم JavaScript للتفاعل مع مكونات واجهة المستخدم. لن أركز على بناء الجملة وكيفية عملها (لذلك أوصيك بقراءة "حان الوقت لبدء استخدام الخصائص المخصصة"). بدلاً من ذلك ، أود إلقاء نظرة أعمق على استراتيجيات تحقيق أقصى استفادة من خصائص CSS المخصصة.
كيف تشبه المتغيرات في المعالجات المسبقة؟
الخصائص المخصصة تشبه إلى حد ما المتغيرات في المعالجات الأولية ولكن لها بعض الاختلافات المهمة. الاختلاف الأول والأكثر وضوحًا هو النحو.
مع SCSS
نستخدم رمز الدولار للإشارة إلى متغير:
$smashing-red: #d33a2c;
في أقل نستخدم رمز @
:
@smashing-red: #d33a2c;
تتبع الخصائص المخصصة اصطلاحات مشابهة وتستخدم البادئة --
:root { --smashing-red: #d33a2c; } .smashing-text { color: var(--smashing-red); }
أحد الاختلافات المهمة بين الخصائص المخصصة والمتغيرات في المعالجات الأولية هو أن الخصائص المخصصة لها صيغة مختلفة لتعيين قيمة واسترداد تلك القيمة. عند استرداد قيمة خاصية مخصصة ، نستخدم وظيفة var()
.
الاختلاف التالي الأكثر وضوحًا هو الاسم. يطلق عليها "خصائص مخصصة" لأنها بالفعل خصائص CSS. في المعالجات الأولية ، يمكنك التصريح عن المتغيرات واستخدامها في أي مكان تقريبًا ، بما في ذلك كتل التصريح الخارجية ، أو في قواعد الوسائط ، أو حتى كجزء من المحدد.
$breakpoint: 800px; $smashing-red: #d33a2c; $smashing-things: ".smashing-text, .cats"; @media screen and (min-width: $breakpoint) { #{$smashing-things} { color: $smashing-red; } }
قد تكون معظم الأمثلة المذكورة أعلاه غير صالحة باستخدام الخصائص المخصصة.
الخصائص المخصصة لها نفس القواعد حول مكان استخدامها كخصائص CSS عادية. من الأفضل بكثير اعتبارها خصائص ديناميكية أكثر من اعتبارها متغيرات. هذا يعني أنه لا يمكن استخدامها إلا داخل كتلة إعلان ، أو بعبارة أخرى ، ترتبط الخصائص المخصصة بمحدد. يمكن أن يكون هذا :root
، أو أي محدد صالح آخر.
:root { --smashing-red: #d33a2c; } @media screen and (min-width: 800px) { .smashing-text, .cats { --margin-left: 1em; } }
يمكنك استرداد قيمة خاصية مخصصة في أي مكان كنت ستستخدم فيه قيمة في إعلان الخاصية. هذا يعني أنه يمكن استخدامها كقيمة واحدة ، كجزء من بيان مختصر أو حتى داخل معادلات calc()
.
.smashing-text, .cats { color: var(--smashing-red); margin: 0 var(--margin-horizontal); padding: calc(var(--margin-horizontal) / 2) }
ومع ذلك ، لا يمكن استخدامها في استعلامات الوسائط أو المحددات بما في ذلك :nth-child()
.
ربما يكون هناك الكثير الذي تريد معرفته حول بناء الجملة وكيفية عمل الخصائص المخصصة ، مثل كيفية استخدام القيم الاحتياطية وهل يمكنك تعيين متغيرات إلى متغيرات أخرى (نعم) ، ولكن يجب أن تكون هذه المقدمة الأساسية كافية لفهم بقية المفاهيم الواردة في هذا المقال. لمزيد من المعلومات حول تفاصيل كيفية عمل الخصائص المخصصة ، يمكنك قراءة "حان الوقت لبدء استخدام الخصائص المخصصة" الذي كتبه Serg Hospodarets.
ديناميكي مقابل ثابت
بغض النظر عن الاختلافات التجميلية ، فإن الاختلاف الأكثر أهمية بين المتغيرات في المعالجات الأولية والخصائص المخصصة هو كيفية تحديد نطاقها. يمكننا الإشارة إلى المتغيرات على أنها إما ذات نطاق ثابت أو ديناميكي. المتغيرات في المعالجات الأولية ثابتة ، في حين أن الخصائص المخصصة ديناميكية.
عندما يتعلق الأمر بـ CSS ، فإن الثابت يعني أنه يمكنك تحديث قيمة متغير في نقاط مختلفة في عملية الترجمة ، ولكن هذا لا يمكن أن يغير قيمة الكود الذي جاء قبله.
$background: blue; .blue { background: $background; } $background: red; .red { background: $background; }
النتائج في:
.blue { background: blue; } .red { background: red; }
بمجرد تقديم هذا إلى CSS ، تختفي المتغيرات. هذا يعني أنه يمكننا قراءة ملف .scss
وتحديد مخرجاته دون معرفة أي شيء عن HTML أو المتصفح أو المدخلات الأخرى. هذا ليس هو الحال مع الخصائص المخصصة.
تمتلك المعالجات المسبقة نوعًا من "نطاق الكتلة" حيث يمكن تغيير المتغيرات مؤقتًا داخل محدد أو وظيفة أو مزيج. هذا يغير قيمة المتغير داخل الكتلة ، لكنه لا يزال ثابتًا. هذا مرتبط بالكتلة ، وليس المحدد. في المثال أدناه ، تم تغيير المتغير $background
داخل كتلة .example
. يتغير مرة أخرى إلى القيمة الأولية خارج الكتلة ، حتى لو استخدمنا نفس المحدد.
$background: red; .example { $background: blue; background: $background; } .example { background: $background; }
سينتج عن ذلك:
.example { background: blue; } .example { background: red; }
تعمل الخصائص المخصصة بشكل مختلف. عندما يتعلق الأمر بالخصائص المخصصة ، فإن النطاق الديناميكي يعني أنها تخضع للوراثة والتتالي. ترتبط الخاصية بمحدد وإذا تغيرت القيمة ، فإن هذا يؤثر على جميع عناصر DOM المطابقة تمامًا مثل أي خاصية CSS أخرى.
يعد هذا أمرًا رائعًا لأنه يمكنك تغيير قيمة خاصية مخصصة داخل استعلام وسائط ، باستخدام محدد زائف مثل hover ، أو حتى باستخدام JavaScript.
a { --link-color: black; } a:hover, a:focus { --link-color: tomato; } @media screen and (min-width: 600px) { a { --link-color: blue; } } a { color: var(--link-color); }
لا يتعين علينا تغيير مكان استخدام الخاصية المخصصة - فنحن نغير قيمة الخاصية المخصصة باستخدام CSS. هذا يعني باستخدام نفس الخاصية المخصصة ، يمكن أن يكون لدينا قيم مختلفة في أماكن أو سياق مختلف في نفس الصفحة.
عالمي مقابل محلي
بالإضافة إلى كونها ثابتة أو ديناميكية ، يمكن أيضًا أن تكون المتغيرات عامة أو محلية. إذا كتبت JavaScript ، فستكون على دراية بهذا. يمكن تطبيق المتغيرات على كل شيء داخل التطبيق ، أو يمكن أن يقتصر نطاقها على وظائف محددة أو كتل من التعليمات البرمجية.
CSS مشابه. لدينا بعض الأشياء التي يتم تطبيقها على الصعيد العالمي وبعض الأشياء الأكثر محلية. تعد ألوان العلامة التجارية والتباعد الرأسي والطباعة كلها أمثلة على الأشياء التي قد ترغب في تطبيقها عالميًا وبشكل متسق عبر موقع الويب أو التطبيق الخاص بك. لدينا أيضًا أشياء محلية. على سبيل المثال ، قد يحتوي أحد مكونات الزر على متغير صغير وكبير. لن ترغب في تطبيق الأحجام من هذه الأزرار على جميع عناصر الإدخال أو حتى كل عنصر في الصفحة.
هذا شيء نعرفه في CSS. لقد قمنا بتطوير أنظمة تصميم واصطلاحات تسمية ومكتبات JavaScript ، وكل ذلك للمساعدة في عزل المكونات المحلية وعناصر التصميم العالمية. توفر الخصائص المخصصة خيارات جديدة للتعامل مع هذه المشكلة القديمة.
يتم تحديد خصائص CSS المخصصة افتراضيًا محليًا على المحددات المحددة التي نطبقها عليها. لذا فهي نوعًا ما مثل المتغيرات المحلية. ومع ذلك ، فإن الخصائص المخصصة موروثة أيضًا ، لذلك في العديد من المواقف تتصرف مثل المتغيرات العامة - خاصة عند تطبيقها على :root
. هذا يعني أننا بحاجة إلى التفكير مليًا في كيفية استخدامها.
تُظهر العديد من الأمثلة الخصائص المخصصة التي يتم تطبيقها على :root
وعلى الرغم من أن هذا أمر جيد بالنسبة للعرض التوضيحي ، إلا أنه يمكن أن يؤدي إلى نطاق عالمي فوضوي ومشكلات غير مقصودة مع الوراثة. لحسن الحظ ، لقد تعلمنا هذه الدروس بالفعل.
تميل المتغيرات العالمية إلى أن تكون ثابتة
هناك بعض الاستثناءات الصغيرة ، ولكن بشكل عام ، فإن معظم الأشياء العالمية في CSS ثابتة أيضًا.
لا تميل المتغيرات العالمية مثل ألوان العلامة التجارية والطباعة والتباعد إلى التغيير كثيرًا من مكون إلى آخر. عندما يتغيرون ، فإن هذا يميل إلى أن يكون تغييرًا عالميًا للعلامة التجارية أو تغييرًا مهمًا آخر نادرًا ما يحدث على منتج ناضج. لا يزال من المنطقي أن تكون هذه الأشياء متغيرات ، يتم استخدامها في العديد من الأماكن ، والمتغيرات تساعد في الاتساق. لكن ليس من المنطقي بالنسبة لهم أن يكونوا ديناميكيين. قيمة هذه المتغيرات لا تتغير بأي طريقة ديناميكية.
لهذا السبب ، أوصي بشدة باستخدام المعالجات الأولية للمتغيرات العالمية (الثابتة). هذا لا يضمن فقط أنها ثابتة دائمًا ، ولكنها تدل عليها بشكل مرئي داخل الكود. هذا يمكن أن يجعل CSS أكثر قابلية للقراءة وأسهل في الصيانة.
المتغيرات الثابتة المحلية جيدة (في بعض الأحيان)
قد تعتقد أنه نظرًا للموقف القوي من المتغيرات العالمية التي تكون ثابتة ، فمن خلال الانعكاس ، قد تحتاج جميع المتغيرات المحلية إلى أن تكون ديناميكية. في حين أنه من الصحيح أن المتغيرات المحلية تميل إلى أن تكون ديناميكية ، إلا أن هذا لا يقترب من قوة الميل إلى أن يكون المتغير العالمي ثابتًا.
المتغيرات الثابتة محليًا جيدة تمامًا في العديد من المواقف. أنا أستخدم متغيرات المعالجات الأولية في ملفات المكونات في الغالب كوسيلة راحة للمطور.
ضع في اعتبارك المثال الكلاسيكي لمكون الزر مع اختلافات متعددة في الحجم.
قد يبدو scss
الخاص بي مثل هذا:
$button-sml: 1em; $button-med: 1.5em; $button-lrg: 2em; .btn { // Visual styles } .btn-sml { font-size: $button-sml; } .btn-med { font-size: $button-med; } .btn-lrg { font-size: $button-lrg; }
من الواضح أن هذا المثال سيكون أكثر منطقية إذا كنت أستخدم المتغيرات عدة مرات أو اشتق قيم الهامش والحشو من متغيرات الحجم. ومع ذلك ، قد تكون القدرة على إنشاء نماذج أولية لأحجام مختلفة سببًا كافيًا.
نظرًا لأن معظم المتغيرات الثابتة عالمية ، فأنا أحب التفريق بين المتغيرات الثابتة التي يتم استخدامها فقط داخل المكون. للقيام بذلك ، يمكنك أن تسبق هذه المتغيرات باسم المكون ، أو يمكنك استخدام بادئة أخرى مثل c-variable-name
للمكون أو l-variable-name
للغة المحلية. يمكنك استخدام أي بادئة تريدها ، أو يمكنك إضافة بادئة إلى المتغيرات العامة. مهما كان اختيارك ، من المفيد التمييز خاصةً إذا كنت تقوم بتحويل قاعدة بيانات موجودة لاستخدام خصائص مخصصة.
متى يجب استخدام الخصائص المخصصة
إذا كان من المناسب استخدام المتغيرات الثابتة داخل المكونات ، فمتى يجب استخدام الخصائص المخصصة؟ عادةً ما يكون تحويل متغيرات ما قبل المعالج الحالية إلى خصائص مخصصة أمرًا غير منطقي. بعد كل شيء ، سبب الخصائص المخصصة مختلف تمامًا. تكون الخصائص المخصصة منطقية عندما تكون لدينا خصائص CSS تتغير بالنسبة إلى شرط في DOM - خاصةً الشرط الديناميكي مثل :focus
، :hover
، استعلامات الوسائط أو باستخدام JavaScript.
أظن أننا سنستخدم دائمًا شكلاً من أشكال المتغيرات الثابتة ، على الرغم من أننا قد نحتاج إلى عدد أقل في المستقبل ، حيث تقدم الخصائص المخصصة طرقًا جديدة لتنظيم المنطق والتعليمات البرمجية. حتى ذلك الحين ، أعتقد أننا في معظم الحالات سنعمل مع مجموعة من متغيرات المعالج المسبق والخصائص المخصصة.
من المفيد معرفة أنه يمكننا تعيين متغيرات ثابتة للخصائص المخصصة. سواء كانت عالمية أو محلية ، فمن المنطقي في العديد من المواقف تحويل المتغيرات الثابتة إلى خصائص مخصصة ديناميكية محليًا.
ملاحظة : هل تعلم أن $var
قيمة صالحة لخاصية مخصصة؟ تتعرف الإصدارات الحديثة من Sass على هذا ، وبالتالي نحتاج إلى استيفاء المتغيرات المعينة للخصائص المخصصة ، مثل هذا: #{$var}
. هذا يخبر Sass أنك تريد إخراج قيمة المتغير ، بدلاً من $var
فقط في ورقة الأنماط. هذا مطلوب فقط في مواقف مثل الخصائص المخصصة ، حيث يمكن أن تكون أسماء المتغيرات أيضًا CSS صالحة.
إذا أخذنا مثال الزر أعلاه وقررنا أن جميع الأزرار يجب أن تستخدم الاختلاف الصغير على الأجهزة المحمولة ، بغض النظر عن الفئة المطبقة في HTML ، فهذا موقف أكثر ديناميكية الآن. لهذا ، يجب أن نستخدم خصائص مخصصة.
$button-sml: 1em; $button-med: 1.5em; $button-lrg: 2em; .btn { --button-size: #{$button-sml}; } @media screen and (min-width: 600px) { .btn-med { --button-size: #{$button-med}; } .btn-lrg { --button-size: #{$button-lrg}; } } .btn { font-size: var(--button-size); }
هنا أقوم بإنشاء خاصية مخصصة واحدة: --button-size
. يتم تحديد نطاق هذه الخاصية المخصصة مبدئيًا لجميع عناصر الأزرار باستخدام فئة btn
. ثم قمت بتغيير قيمة --button-size
فوق 600 بكسل للفئات btn-med
و btn-lrg
. أخيرًا ، أقوم بتطبيق هذه الخاصية المخصصة على جميع عناصر الأزرار في مكان واحد.
لا تكن ذكيًا جدًا
تسمح لنا الطبيعة الديناميكية للخصائص المخصصة بإنشاء بعض المكونات الذكية والمعقدة.
مع إدخال المعالجات الأولية ، أنشأ الكثير منا مكتبات ذات تجريدات ذكية باستخدام mixins والوظائف المخصصة. في حالات محدودة ، لا تزال أمثلة مثل هذه مفيدة اليوم ، ولكن بالنسبة للجزء الأكبر ، كلما طالت مدة عملي مع المعالجات الأولية ، قلت الميزات التي أستخدمها. اليوم ، أستخدم المعالجات الأولية بشكل حصري تقريبًا للمتغيرات الثابتة.
لن تكون الخصائص المخصصة (ولا ينبغي) محصنة ضد هذا النوع من التجارب ، وأنا أتطلع إلى رؤية العديد من الأمثلة الذكية. ولكن على المدى الطويل ، ستفوز الشفرة القابلة للقراءة والصيانة دائمًا على الأفكار المجردة الذكية (على الأقل في الإنتاج).
لقد قرأت مقالًا ممتازًا حول هذا الموضوع في Free Code Camp Medium مؤخرًا. كتبه بيل سرور بعنوان "لا تفعل ذلك في وقت التشغيل. افعل ذلك في وقت التصميم ". بدلاً من إعادة صياغة حججه ، سأدعك تقرأها.
يتمثل أحد الاختلافات الرئيسية بين متغيرات المعالج المسبق والخصائص المخصصة في أن الخصائص المخصصة تعمل في وقت التشغيل. هذا يعني أن الأشياء التي قد تكون مقبولة الحدودية ، من حيث التعقيد ، مع المعالجات الأولية قد لا تكون فكرة جيدة مع الخصائص المخصصة.
أحد الأمثلة التي أوضحت هذا بالنسبة لي مؤخرًا كان هذا:
:root { --font-scale: 1.2; --font-size-1: calc(var(--font-scale) * var(--font-size-2)); --font-size-2: calc(var(--font-scale) * var(--font-size-3)); --font-size-3: calc(var(--font-scale) * var(--font-size-4)); --font-size-4: 1rem; }
هذا يولد مقياس معياري. المقياس النمطي عبارة عن سلسلة من الأرقام التي ترتبط ببعضها البعض باستخدام نسبة. غالبًا ما يتم استخدامها في تصميم الويب وتطويره لتعيين أحجام الخطوط أو التباعد.
في هذا المثال ، يتم تحديد كل خاصية مخصصة باستخدام calc()
، عن طريق أخذ قيمة الخاصية المخصصة السابقة وضربها في النسبة. بالقيام بذلك ، يمكننا الحصول على الرقم التالي في المقياس.
هذا يعني أن النسب يتم حسابها في وقت التشغيل ويمكنك تغييرها عن طريق تحديث قيمة الخاصية --font-scale
فقط. علي سبيل المثال:
@media screen and (min-width: 800px) { :root { --font-scale: 1.33; } }
هذا ذكي وموجز وأسرع بكثير من حساب جميع القيم مرة أخرى إذا كنت تريد تغيير المقياس. إنه أيضًا شيء لن أفعله في كود الإنتاج.
على الرغم من أن المثال أعلاه مفيد للنماذج الأولية ، إلا أنني أفضل أن أرى شيئًا كهذا في الإنتاج:
:root { --font-size-1: 1.728rem; --font-size-2: 1.44rem; --font-size-3: 1.2em; --font-size-4: 1em; } @media screen and (min-width: 800px) { :root { --font-size-1: 2.369rem; --font-size-2: 1.777rem; --font-size-3: 1.333rem; --font-size-4: 1rem; } }
على غرار المثال الوارد في مقالة بيل ، أجد أنه من المفيد معرفة القيم الفعلية. نقرأ الكود عدة مرات أكثر مما نكتبه والقيم العالمية مثل مقاييس الخطوط تتغير بشكل غير منتظم في الإنتاج.
المثال أعلاه لا يزال غير مثالي. إنه ينتهك القاعدة السابقة بأن القيم العالمية يجب أن تكون ثابتة . أفضل استخدام متغيرات المعالج المسبق وتحويلها إلى خصائص مخصصة ديناميكية محليًا باستخدام التقنيات الموضحة سابقًا.
من المهم أيضًا تجنب المواقف التي ننتقل فيها من استخدام خاصية مخصصة واحدة إلى خاصية مخصصة مختلفة. يمكن أن يحدث هذا عندما نسمي خصائص مثل هذا.
تغيير القيمة ليس المتغير
يعد تغيير القيمة وليس المتغير أحد أهم الاستراتيجيات لاستخدام الخصائص المخصصة بشكل فعال.
كقاعدة عامة ، لا يجب أبدًا تغيير الخاصية المخصصة المستخدمة لأي غرض منفرد. من السهل القيام بذلك لأن هذا هو بالضبط كيف نفعل الأشياء مع المعالجات الأولية ، ولكن لا معنى له مع الخصائص المخصصة.
في هذا المثال ، لدينا خاصيتان مخصصتان يتم استخدامهما في مثال مكون. أقوم بالتبديل من استخدام قيمة --font-size-small
إلى --font-size-large
اعتمادًا على حجم الشاشة.
:root { --font-size-small: 1.2em; --font-size-large: 2em; } .example { font-size: var(--font-size-small); } @media screen and (min-width: 800px) { .example { font-size: var(--font-size-large); } }
أفضل طريقة للقيام بذلك هي تحديد خاصية مخصصة واحدة يتم تحديد نطاقها للمكون. ثم باستخدام استعلام وسائط ، أو أي محدد آخر ، قم بتغيير قيمتها.
.example { --example-font-size: 1.2em; } @media screen and (min-width: 800px) { .example { --example-font-size: 2em; } }
أخيرًا ، في مكان واحد ، أستخدم قيمة هذه الخاصية المخصصة:
.example { font-size: var(--example-font-size); }
في هذا المثال وغيره قبله ، تم استخدام استعلامات الوسائط فقط لتغيير قيمة الخصائص المخصصة. قد تلاحظ أيضًا وجود مكان واحد فقط حيث يتم استخدام عبارة var()
، ويتم تحديث خصائص CSS العادية.
هذا الفصل بين الإعلانات المتغيرة وإعلانات الممتلكات مقصود. هناك العديد من الأسباب لذلك ، ولكن الفوائد تكون أكثر وضوحًا عند التفكير في التصميم سريع الاستجابة.
تصميم سريع الاستجابة بخصائص مخصصة
تتمثل إحدى الصعوبات في التصميم سريع الاستجابة عندما يعتمد بشكل كبير على استعلامات الوسائط في أنه بغض النظر عن كيفية تنظيم CSS ، فإن الأنماط المتعلقة بمكون معين تصبح مجزأة عبر ورقة الأنماط.
قد يكون من الصعب جدًا معرفة خصائص CSS التي ستتغير. ومع ذلك ، يمكن أن تساعدنا خصائص CSS المخصصة في تنظيم بعض المنطق المتعلق بالتصميم سريع الاستجابة وتسهيل العمل مع استعلامات الوسائط كثيرًا.
إذا تغير ، فهو متغير
الخصائص التي تتغير باستخدام استعلامات الوسائط ديناميكية بطبيعتها وتوفر الخصائص المخصصة الوسائل للتعبير عن القيم الديناميكية في CSS. هذا يعني أنك إذا كنت تستخدم استعلام وسائط لتغيير أي خاصية CSS ، فيجب عليك وضع هذه القيمة في خاصية مخصصة.
يمكنك بعد ذلك نقل هذا ، جنبًا إلى جنب مع جميع قواعد الوسائط ، أو حالات التمرير أو أي محددات ديناميكية تحدد كيفية تغير القيمة ، إلى أعلى المستند.
منطق منفصل عن التصميم
عند القيام بذلك بشكل صحيح ، فإن الفصل بين المنطق والتصميم يعني أن استعلامات الوسائط تُستخدم فقط لتغيير قيمة الخصائص المخصصة . هذا يعني أن كل المنطق المتعلق بالتصميم سريع الاستجابة يجب أن يكون في الجزء العلوي من المستند ، وحيثما نرى عبارة var()
في CSS لدينا ، فإننا نعلم على الفور أن هذه الخاصية تتغير. مع الأساليب التقليدية لكتابة CSS ، لم تكن هناك طريقة لمعرفة ذلك في لمحة.
أصبح الكثير منا جيدًا جدًا في قراءة وتفسير CSS في لمحة بينما نتتبع في رؤوسنا الخصائص التي تغيرت في المواقف المختلفة. لقد سئمت من هذا ، ولا أريد أن أفعل هذا بعد الآن! توفر الخصائص المخصصة الآن رابطًا بين المنطق وتنفيذه ، لذلك لا نحتاج إلى تتبع ذلك ، وهذا مفيد للغاية!
طية المنطق
إن فكرة إعلان المتغيرات في أعلى المستند أو الوظيفة ليست فكرة جديدة. إنه شيء نقوم به في معظم اللغات ، وهو الآن شيء يمكننا القيام به في CSS أيضًا. تؤدي كتابة CSS بهذه الطريقة إلى إنشاء تمييز مرئي واضح بين CSS في أعلى المستند وأسفله. أحتاج إلى طريقة للتمييز بين هذه الأقسام عندما أتحدث عنها وفكرة "طي المنطق" هي استعارة بدأت في استخدامها.
الجزء المرئي من الصفحة يحتوي على جميع متغيرات المعالج المسبق والخصائص المخصصة. يتضمن هذا جميع القيم المختلفة التي يمكن أن تحتوي عليها خاصية مخصصة. يجب أن يكون من السهل تتبع كيفية تغيير خاصية مخصصة.
CSS الموجود في الجزء السفلي غير المرئي من الصفحة هو واضح ومباشر للغاية ويسهل قراءته. يبدو الأمر وكأنه CSS قبل استعلامات الوسائط والتعقيدات الضرورية الأخرى لـ CSS الحديثة.
ألقِ نظرة على مثال بسيط حقًا لنظام شبكة Flexbox ذي ستة أعمدة:
.row { --row-display: block; } @media screen and (min-width: 600px) { .row { --row-display: flex; } }
تم تعيين الخاصية المخصصة --row-display
في البداية على block
. فوق 600 بكسل ، يكون وضع العرض مضبوطًا على مرن.
قد يبدو الجزء السفلي غير المرئي من الصفحة كما يلي:
.row { display: var(--row-display); flex-direction: row; flex-wrap: nowrap; } .col-1, .col-2, .col-3, .col-4, .col-5, .col-6 { flex-grow: 0; flex-shrink: 0; } .col-1 { flex-basis: 16.66%; } .col-2 { flex-basis: 33.33%; } .col-3 { flex-basis: 50%; } .col-4 { flex-basis: 66.66%; } .col-5 { flex-basis: 83.33%; } .col-6 { flex-basis: 100%; }
نعلم على الفور أن --row-display
هي قيمة تتغير. في البداية ، سيتم block
، لذلك سيتم تجاهل القيم المرنة.
هذا المثال بسيط إلى حد ما ، ولكن إذا قمنا بتوسيعه ليشمل عمود عرض مرن يملأ المساحة المتبقية ، فمن المحتمل أن تكون قيم flex-grow
flex-shrink
flex-basis
بحاجة إلى أن يتم تحويلها إلى خصائص مخصصة. يمكنك تجربة هذا أو إلقاء نظرة على مثال أكثر تفصيلاً هنا.
خصائص مخصصة للسمن
لقد جادلت في الغالب ضد استخدام الخصائص المخصصة للمتغيرات الديناميكية العالمية ونأمل أن يكون إرفاق خصائص مخصصة بـ :root
يعتبر ضارًا في كثير من الحالات. لكن كل قاعدة لها استثناء ، وبالنسبة للخصائص المخصصة ، فهي مخصصة لهم.
يمكن أن يؤدي الاستخدام المحدود للخصائص المخصصة العالمية إلى جعلها أسهل كثيرًا.
يشير التصميم عمومًا إلى السماح للمستخدمين بتخصيص واجهة المستخدم بطريقة ما. قد يكون هذا شيئًا مثل تغيير الألوان على صفحة الملف الشخصي. أو قد يكون شيئًا أكثر محلية. على سبيل المثال ، يمكنك اختيار لون الملاحظة في تطبيق Google Keep.
يتضمن التصميم عادةً تجميع ورقة أنماط منفصلة لتجاوز القيمة الافتراضية مع تفضيلات المستخدم ، أو تجميع ورقة أنماط مختلفة لكل مستخدم. يمكن أن يكون كلاهما صعبًا ويكون لهما تأثير على الأداء.
باستخدام الخصائص المخصصة ، لا نحتاج إلى تجميع ورقة أنماط مختلفة ؛ نحتاج فقط إلى تحديث قيمة الخصائص وفقًا لتفضيلات المستخدم. نظرًا لأنها قيم موروثة ، إذا قمنا بذلك على عنصر الجذر ، فيمكن استخدامها في أي مكان في تطبيقنا.
استفد من الخصائص الديناميكية العالمية
الخصائص المخصصة حساسة لحالة الأحرف ونظرًا لأن معظم الخصائص المخصصة ستكون محلية ، إذا كنت تستخدم الخصائص الديناميكية العامة ، فمن المنطقي استخدامها بأحرف كبيرة.
:root { --THEME-COLOR: var(--user-theme-color, #d33a2c); }
غالبًا ما تشير الكتابة بالأحرف الكبيرة للمتغيرات إلى الثوابت العالمية. بالنسبة لنا ، سيشير هذا إلى أن الخاصية تم تعيينها في مكان آخر في التطبيق وأنه ربما لا ينبغي لنا تغييرها محليًا.
تجنب الإعداد المباشر للخصائص الديناميكية العامة
تقبل الخصائص المخصصة قيمة احتياطية. قد يكون من المفيد تجنب الكتابة مباشرة فوق قيمة الخصائص المخصصة العامة والاحتفاظ بقيم المستخدم منفصلة. يمكننا استخدام القيمة الاحتياطية للقيام بذلك.
يعيّن المثال أعلاه قيمة --THEME-COLOR
إلى قيمة --user-theme-color
إذا كانت موجودة. إذا لم يتم تعيين --user-theme-color
(لون سمة المستخدم) ، فسيتم استخدام قيمة #d33a2c
. بهذه الطريقة ، لا نحتاج إلى توفير احتياطي في كل مرة نستخدم --THEME-COLOR
.
قد تتوقع في المثال أدناه أنه سيتم تعيين الخلفية إلى green
. ومع ذلك ، لم يتم تعيين قيمة --user-theme-color
على عنصر الجذر ، لذلك لم تتغير قيمة --THEME-COLOR
.
:root { --THEME-COLOR: var(--user-theme-color, #d33a2c); } body { --user-theme-color: green; background: var(--THEME-COLOR); }
يؤدي تعيين الخصائص الديناميكية العامة بشكل غير مباشر مثل هذا إلى حمايتها من الكتابة فوقها محليًا ويضمن أن يتم دائمًا توريث إعدادات المستخدم من عنصر الجذر. هذه اتفاقية مفيدة لحماية قيم السمة الخاصة بك وتجنب الميراث غير المقصود.
إذا أردنا كشف خصائص معينة للوراثة ، فيمكننا استبدال :root
بمحدد *
:
* { --THEME-COLOR: var(--user-theme-color, #d33a2c); } body { --user-theme-color: green; background: var(--THEME-COLOR); }
الآن يتم إعادة حساب قيمة --THEME-COLOR
لكل عنصر وبالتالي يمكن استخدام القيمة المحلية لـ --user-theme-color
. بمعنى آخر ، سيكون لون الخلفية في هذا المثال green
.
يمكنك رؤية بعض الأمثلة الأكثر تفصيلاً لهذا النمط في القسم الخاص بمعالجة اللون بخصائص مخصصة.
تحديث الخصائص المخصصة بجافا سكريبت
إذا كنت ترغب في تعيين خصائص مخصصة باستخدام JavaScript ، فهناك واجهة برمجة تطبيقات بسيطة إلى حد ما وتبدو كما يلي:
const elm = document.documentElement; elm.style.setProperty('--USER-THEME-COLOR', 'tomato');
هنا أقوم بتعيين قيمة --USER-THEME-COLOR
على عنصر المستند ، أو بعبارة أخرى ، العنصر :root
حيث سيتم توريثه بواسطة جميع العناصر.
هذه ليست API جديدة. إنها نفس طريقة JavaScript لتحديث الأنماط على عنصر. هذه أنماط مضمنة لذا سيكون لها خصوصية أعلى من CSS العادي.
هذا يعني أنه من السهل تطبيق التخصيصات المحلية:
.note { --note-color: #eaeaea; } .note { background: var(--note-color); }
هنا قمت بتعيين قيمة افتراضية لـ --note-color
ونطاق هذا لمكون .note
. أبقي إعلان المتغير منفصلاً عن إعلان الملكية ، حتى في هذا المثال البسيط.
const elm = document.querySelector('#note-uid'); elm.style.setProperty('--note-color', 'yellow');
ثم أستهدف مثيلًا محددًا لعنصر .note
وقم بتغيير قيمة الخاصية المخصصة --note-color
لهذا العنصر فقط. سيكون لهذا الآن دقة أعلى من القيمة الافتراضية.
يمكنك أن ترى كيف يعمل هذا مع هذا المثال باستخدام React. يمكن حفظ تفضيلات المستخدم هذه في التخزين المحلي أو ، ربما في حالة تطبيق أكبر ، في قاعدة بيانات.
معالجة اللون بخصائص مخصصة
بالإضافة إلى القيم السداسية والألوان المسماة ، تحتوي CSS على وظائف ألوان مثل rgb()
و hsl()
. تسمح لنا هذه بتحديد المكونات الفردية للون مثل تدرج اللون أو الخفة. يمكن استخدام الخصائص المخصصة جنبًا إلى جنب مع وظائف الألوان.
:root { --hue: 25; } body { background: hsl(var(--hue), 80%, 50%); }
هذا مفيد ، ولكن بعض الميزات الأكثر استخدامًا للمعالجات الأولية هي وظائف ألوان متقدمة تسمح لنا بمعالجة اللون باستخدام وظائف مثل التفتيح أو التعتيم أو إزالة التشبع:
darken($base-color, 10%); lighten($base-color, 10%); desaturate($base-color, 20%);
قد يكون من المفيد وجود بعض هذه الميزات في المتصفحات. إنهم قادمون ، ولكن إلى أن نحصل على وظائف أصلية لتعديل اللون في CSS ، يمكن للخصائص المخصصة أن تملأ بعضًا من هذه الفجوة.
لقد رأينا أنه يمكن استخدام الخصائص المخصصة داخل وظائف الألوان الحالية مثل rgb()
و hsl()
ولكن يمكن استخدامها أيضًا في calc()
. هذا يعني أنه يمكننا تحويل رقم حقيقي إلى نسبة مئوية بضربها ، على سبيل المثال calc(50 * 1%)
= 50%
.
:root { --lightness: 50; } body { background: hsl(25, 80%, calc(var(--lightness) * 1%)); }
سبب رغبتنا في تخزين قيمة الخفة كرقم حقيقي هو أنه يمكننا معالجتها calc
قبل تحويلها إلى نسبة مئوية. على سبيل المثال ، إذا كنت أريد أن أغمق لونًا بنسبة 20%
، يمكنني مضاعفة الإضاءة بمقدار 0.8
. يمكننا أن نجعل هذا الأمر أسهل للقراءة عن طريق فصل حساب الخفة إلى خاصية مخصصة محلية النطاق:
:root { --lightness: 50; } body { --lightness: calc(var(--lightness * 0.8)); background: hsl(25, 80%, calc(var(--lightness) * 1%)); }
يمكننا حتى تجريد المزيد من العمليات الحسابية وإنشاء شيء مثل وظائف تعديل اللون في CSS باستخدام الخصائص المخصصة. من المحتمل أن يكون هذا المثال معقدًا للغاية بالنسبة لمعظم الحالات العملية للتخصيص ، ولكنه يوضح القوة الكاملة للخصائص المخصصة الديناميكية.
تبسيط التخطيط
تتمثل إحدى مزايا استخدام الخصائص المخصصة في القدرة على تبسيط عملية التخصيص. لا يحتاج التطبيق إلى معرفة كيفية استخدام الخصائص المخصصة. بدلاً من ذلك ، نستخدم JavaScript أو كود من جانب الخادم لتعيين قيمة الخصائص المخصصة. يتم تحديد كيفية استخدام هذه القيم بواسطة أوراق الأنماط.
هذا يعني مرة أخرى أننا قادرون على فصل المنطق عن التصميم. إذا كان لديك فريق تصميم فني ، فيمكن للمؤلفين تحديث أوراق الأنماط وتحديد كيفية تطبيق الخصائص المخصصة دون تغيير سطر واحد من JavaScript أو التعليمات البرمجية الخلفية.
تسمح الخصائص المخصصة أيضًا بنقل بعض تعقيد التضمين إلى CSS ويمكن أن يكون لهذا التعقيد تأثير سلبي على قابلية صيانة CSS ، لذلك تذكر أن تبقيها بسيطة قدر الإمكان.
استخدام الخصائص المخصصة اليوم
حتى إذا كنت تدعم IE10 و 11 ، يمكنك البدء في استخدام الخصائص المخصصة اليوم. معظم الأمثلة في هذه المقالة لها علاقة بكيفية كتابة وهيكلة CSS. الفوائد كبيرة من حيث قابلية الصيانة ، ومع ذلك ، فإن معظم الأمثلة تقلل فقط ما يمكن القيام به باستخدام كود أكثر تعقيدًا.
أستخدم أداة تسمى متغيرات postcss-css لتحويل معظم ميزات الخصائص المخصصة إلى تمثيل ثابت لنفس الكود. تتجاهل الأدوات المماثلة الأخرى الخصائص المخصصة داخل استعلامات الوسائط أو المحددات المعقدة ، وتعامل الخصائص المخصصة مثل متغيرات المعالج المسبق.
ما لا تستطيع هذه الأدوات فعله هو محاكاة ميزات وقت التشغيل للخصائص المخصصة. هذا يعني عدم وجود ميزات ديناميكية مثل تغيير الخصائص أو تغييرها باستخدام JavaScript. قد يكون هذا على ما يرام في كثير من المواقف. اعتمادًا على الموقف ، يمكن اعتبار تخصيص واجهة المستخدم تحسينًا تدريجيًا ويمكن أن يكون السمة الافتراضية مقبولة تمامًا للمتصفحات القديمة.
تحميل ورقة الأنماط الصحيحة
هناك العديد من الطرق التي يمكنك من خلالها استخدام postCSS. أستخدم عملية gulp
لتجميع أوراق أنماط منفصلة للمتصفحات الأحدث والأقدم. تبدو نسخة مبسطة من مهمة gulp
كما يلي:
import gulp from "gulp"; import sass from "gulp-sass"; import postcss from "gulp-postcss"; import rename from "gulp-rename"; import cssvariables from "postcss-css-variables"; import autoprefixer from "autoprefixer"; import cssnano from "cssnano"; gulp.task("css-no-vars", () => gulp .src("./src/css/*.scss") .pipe(sass().on("error", sass.logError)) .pipe(postcss([cssvariables(), cssnano()])) .pipe(rename({ extname: ".no-vars.css" })) .pipe(gulp.dest("./dist/css")) ); gulp.task("css", () => gulp .src("./src/css/*.scss") .pipe(sass().on("error", sass.logError)) .pipe(postcss([cssnano()])) .pipe(rename({ extname: ".css" })) .pipe(gulp.dest("./dist/css")) );
ينتج عن هذا ملفان CSS: ملف عادي بخصائص مخصصة ( styles.css
) والآخر للمتصفحات القديمة ( styles.no-vars.css
). أريد أن يتم تقديم أنماط IE10 و 11 إلى styles.no-vars.css
والمتصفحات الأخرى للحصول على ملف CSS العادي.
عادةً ما أؤيد استخدام استعلامات الميزات ولكن IE11 لا يدعم استعلامات الميزات وقد استخدمنا خصائص مخصصة على نطاق واسع بحيث يكون تقديم ورقة أنماط مختلفة أمرًا منطقيًا في هذه الحالة.
إن تقديم ورقة أنماط مختلفة بذكاء وتجنب وميض المحتوى غير المصمم ليس بالمهمة السهلة. إذا لم تكن بحاجة إلى الميزات الديناميكية للخصائص المخصصة ، فيمكنك التفكير في تقديم جميع أنماط المتصفح styles.no-vars.css
واستخدام الخصائص المخصصة ببساطة كأداة تطوير.
إذا كنت ترغب في الاستفادة الكاملة من جميع الميزات الديناميكية للخصائص المخصصة ، أقترح استخدام تقنية CSS مهمة. باتباع هذه الأساليب ، يتم تحميل ورقة الأنماط الرئيسية بشكل غير متزامن بينما يتم تقديم CSS الهامة بشكل مضمّن. قد يبدو عنوان صفحتك كما يلي:
<head> <style> /* inlined critical CSS */ </style> <script> loadCSS('non-critical.css'); </script> </head>
يمكننا توسيع هذا لتحميل إما styles.css
أو styles.no-vars.css
اعتمادًا على ما إذا كان المتصفح يدعم الخصائص المخصصة. يمكننا الكشف عن دعم مثل هذا:
if ( window.CSS && CSS.supports('color', 'var(--test)') ) { loadCSS('styles.css'); } else { loadCSS('styles.no-vars.css'); }
خاتمة
إذا كنت تكافح من أجل تنظيم CSS بكفاءة ، أو كنت تواجه صعوبة في استخدام المكونات سريعة الاستجابة ، أو ترغب في تنفيذ السمات من جانب العميل ، أو ترغب فقط في البدء بالطريقة الصحيحة بخصائص مخصصة ، فيجب أن يخبرك هذا الدليل بكل ما تحتاج إلى معرفته.
يتعلق الأمر بفهم الفرق بين المتغيرات الديناميكية والثابتة في CSS بالإضافة إلى بعض القواعد البسيطة:
- منطق منفصل عن التصميم ؛
- إذا تغيرت خاصية CSS ، ففكر في استخدام خاصية مخصصة ؛
- تغيير قيمة الخصائص المخصصة ، وليس الخاصية المخصصة المستخدمة ؛
- المتغيرات العامة عادة ما تكون ثابتة.
If you follow these conventions, you will find that working with custom properties is a whole lot easier than you think. This might even change how you approach CSS in general.
قراءة متعمقة
- “It's Time To Start Using Custom Properties,” Serg Hospodarets
A general introduction to the syntax and the features of custom properties. - “Pragmatic, Practical, And Progressive Theming With Custom Properties,” Harry Roberts
More useful information on theming. - Custom Properties Collection, Mike Riethmuller on CodePen
A number of different examples you can experiment with.