داخل دالة polyfill لاستعلام الحاوية

Gerald Monaco
Gerald Monaco

طلبات الحاوية هي ميزة جديدة في CSS تتيح لك كتابة منطق تصميم يستهدف ميزات عنصر رئيسي (مثل العرض أو الارتفاع) لتصميم عناصره الفرعية. تم مؤخرًا إصدار تحديث كبير لـ polyfill، تزامنًا مع طرح الميزة في المتصفّحات.

في هذه المشاركة، ستتمكّن من الاطّلاع على آلية عمل polyfill، والتحديات التي يواجهها، وأفضل الممارسات عند استخدامه لتوفير تجربة مستخدم رائعة لزوارك.

الخيارات المتقدمة

التحويل البرمجي

عندما يصادف محلل CSS داخل المتصفّح قاعدة at-rule غير معروفة، مثل القاعدة الجديدة تمامًا @container، سيتجاهلها كما لو لم تكن موجودة مطلقًا. لذلك، فإنّ أول وأهم ما يجب أن يفعله العنصر التكميلي هو تحويل طلب بحث @container إلى عنصر لن يتم تجاهله.

الخطوة الأولى في عملية التحويل هي تحويل قاعدة @container من المستوى الأعلى إلى طلب بحث @media. ويضمن ذلك في الغالب بقاء المحتوى مجمّعًا معًا. على سبيل المثال، عند استخدام واجهات برمجة تطبيقات CSSOM وعند عرض مصدر CSS.

قبل
@container (width > 300px) {
  /* content */
}
بعد
@media all {
  /* content */
}

قبل طلبات البحث عن الحاويات، لم تتوفّر في CSS طريقة تتيح للمؤلف تفعيل مجموعات من القواعد أو إيقافها بشكل عشوائي. لتضمين هذا السلوك في polyfill، يجب أيضًا تحويل القواعد داخل طلب بحث الحاوية. يتم منح كل @container معرّفًا فريدًا (على سبيل المثال، 123)، والذي يتم استخدامه لتحويل كل أداة اختيار بحيث لا يتم تطبيقها إلا عندما يحتوي العنصر على سمة cq-XYZ تتضمّن هذا المعرّف. سيضبط العنصر البديل لهذه الميزة هذه السمة أثناء التشغيل.

قبل
@container (width > 300px) {
  .card {
    /* ... */
  }
}
بعد
@media all {
  .card:where([cq-XYZ~="123"]) {
    /* ... */
  }
}

لاحظ استخدام الفئة الزائفة :where(...). يؤدي عادةً تضمين أداة اختيار سمات إضافية إلى زيادة التحديد لأداة الاختيار. باستخدام الفئة الزائفة، يمكن تطبيق الشرط الإضافي مع الحفاظ على النوعية الأصلية. لمعرفة مدى أهمية ذلك، راجِع المثال التالي:

@container (width > 300px) {
  .card {
    color: blue;
  }
}

.card {
  color: red;
}

استنادًا إلى لغة CSS هذه، يجب أن يتضمّن العنصر الذي يحمل الفئة .card دائمًا color: red، لأنّ القاعدة اللاحقة ستلغي دائمًا القاعدة السابقة باستخدام أداة الاختيار والدقة نفسها. وبالتالي، فإنّ تحويل القاعدة الأولى إلى قاعدة أخرى وتضمين أداة اختيار سمة إضافية بدون :where(...) سيؤدي إلى زيادة التحديد، ما يؤدي إلى تطبيق color: blue بشكل خاطئ.

ومع ذلك، فإنّ الفئة الزائفة :where(...) جديدة نسبيًا. بالنسبة إلى المتصفّحات التي لا تتيح ذلك، يقدّم العنصر البديل حلاً بديلاً آمنًا وسهلاً: يمكنك عمدًا زيادة دقة قواعدك من خلال إضافة عنصر اختيار :not(.container-query-polyfill) وهمي يدويًا إلى قواعد @container:

قبل
@container (width > 300px) {
  .card {
    color: blue;
  }
}

.card {
  color: red;
}
بعد
@container (width > 300px) {
  .card:not(.container-query-polyfill) {
    color: blue;
  }
}

.card {
  color: red;
}

ويعود ذلك بعدد من المزايا:

  • تغيّر المحدّد في ملف CSS المصدر، لذا يظهر الفرق في النوع بوضوح. ويُعدّ هذا الإجراء أيضًا مستندًا حتى تعرف ما الذي يتأثر عندما لا يعود عليك استخدام الحلّ البديل أو العنصر البديل.
  • وستظلّ دقة القواعد كما هي دائمًا، لأنّ تقنية polyfill لا تغيّرها.

أثناء عملية التحويل البرمجي، سيحلّ العنصر البديل محلّ هذا العنصر النائب باستخدام أداة اختيار السمات التي لها التحديد نفسه. لتجنُّب أي مفاجآت، يستخدم العنصر القابل للتضمين كلاً من المحدِّدين: يتم استخدام المحدِّد الأصلي للمصدر لتحديد ما إذا كان يجب أن يتلقّى العنصر سمة العنصر القابل للتضمين، ويتم استخدام المحدِّد المُحوَّل لتطبيق الأنماط.

العناصر الزائفة

قد تطرح على نفسك السؤال التالي: إذا كان العنصر البديل يضبط بعض سمات cq-XYZ على عنصر لتضمين معرّف الحاوية الفريد 123، كيف يمكن أن تكون العناصر الزائفة متوافقة، علمًا بأنّه لا يمكن ضبط سمات عليها؟

تكون العناصر الزائفة مرتبطة دائمًا بعنصر حقيقي في DOM يُعرف باسم العنصر الأصلي. أثناء عملية التحويل البرمجي، يتم تطبيق العنصر الشرطي على هذا العنصر الحقيقي بدلاً من ذلك:

قبل
@container (width > 300px) {
  #foo::before {
    /* ... */
  }
}
بعد
@media all {
  #foo:where([cq-XYZ~="123"])::before {
    /* ... */
  }
}

وبدلاً من تحويله إلى #foo::before:where([cq-XYZ~="123"]) (وهو غير صالح)، يتم نقل أداة الاختيار الشَرطية إلى نهاية العنصر الأصلي، #foo.

ومع ذلك، هذا ليس كل ما هو مطلوب. لا يُسمح للحاوية بتعديل أي عنصر غير مضمّن داخلها (ولا يمكن أن تكون الحاوية داخل نفسها)، ولكن هذا هو بالضبط ما سيحدث إذا كان #foo هو عنصر الحاوية الذي يتم الاستعلام عنه. سيتم تغيير سمة #foo[cq-XYZ] عن طريق الخطأ، وسيتم تطبيق أي قواعد #foo عن طريق الخطأ.

لتصحيح ذلك، يستخدم العنصر البديل سمتَين: سمة يمكن تطبيقها على عنصر من خلال عنصر رئيسي فقط، وسمة يمكن للعنصر تطبيقها على نفسه. وتُستخدَم السمة الأخيرة للمحدّدات التي تستهدف العناصر الزائفة.

قبل
@container (width > 300px) {
  #foo,
  #foo::before {
    /* ... */
  }
}
بعد
@media all {
  #foo:where([cq-XYZ-A~="123"]),
  #foo:where([cq-XYZ-B~="123"])::before {
    /* ... */
  }
}

بما أنّ الحاوية لن تطبّق السمة الأولى (cq-XYZ-A) على نفسها أبدًا، لن يتطابق المحدّد الأول إلا إذا استوفت حاوية رئيسية مختلفة شروط الحاوية وطبّقتها.

الوحدات النسبية للحاويات

تتضمّن طلبات البحث عن الحاويات أيضًا بعض الوحدات الجديدة التي يمكنك استخدامها في CSS، مثل cqw وcqh لنسبة% 1 من العرض والارتفاع (على التوالي) لأقرب حاوية رئيسية مناسبة. ولتفعيل هذه الميزة، يتم تحويل الوحدة إلى تعبير calc(...) باستخدام خصائص CSS المخصّصة. سيضبط العنصر البديل القيم لهذه السمات من خلال الأنماط المضمّنة في عنصر الحاوية.

قبل
.card {
  width: 10cqw;
  height: 10cqh;
}
بعد
.card {
  width: calc(10 * --cq-XYZ-cqw);
  height: calc(10 * --cq-XYZ-cqh);
}

هناك أيضًا وحدات منطقية، مثل cqi وcqb للحجم المضمّن وحجم الكتلة (على التوالي). هذه العناصر أكثر تعقيدًا قليلاً، لأنّ محورَي "العنصر المضمّن" و"العنصر المجمّع" يتم تحديدهما من خلال writing-mode للعنصر الذي يستخدم الوحدة، وليس العنصر الذي يتم الاستعلام عنه. ولتفعيل هذه الميزة، يطبّق العنصر البديل أسلوبًا مضمّنًا في أي عنصر يختلف writing-mode الخاص به عن العنصر الرئيسي.

/* Element with a horizontal writing mode */
--cq-XYZ-cqi: var(--cq-XYZ-cqw);
--cq-XYZ-cqb: var(--cq-XYZ-cqh);

/* Element with a vertical writing mode */
--cq-XYZ-cqi: var(--cq-XYZ-cqh);
--cq-XYZ-cqb: var(--cq-XYZ-cqw);

الآن، يمكن تحويل الوحدات إلى السمة المخصّصة المناسبة لتنسيق CSS كما في السابق.

أماكن إقامة

تضيف طلبات البحث عن الحاويات أيضًا بعض خصائص CSS الجديدة، مثل container-type وcontainer-name. بما أنّه لا يمكن استخدام واجهات برمجة التطبيقات مثل getComputedStyle(...) مع خصائص غير معروفة أو غير صالحة، يتم أيضًا تحويلها إلى خصائص مخصّصة في CSS بعد تحليلها. إذا تعذّر تحليل خاصية (على سبيل المثال، لأنّها تحتوي على قيمة غير صالحة أو غير معروفة)، يتم تركها ببساطة ليعالجها المتصفّح.

قبل
.card {
  container-name: card-container;
  container-type: inline-size;
}
بعد
.card {
  --cq-XYZ-container-name: card-container;
  --cq-XYZ-container-type: inline-size;
}

ويتم تحويل هذه السمات عند اكتشافها، ما يسمح لعملية التعويض بالتوافق مع ميزات CSS الأخرى، مثل @supports. وتشكل هذه الوظيفة أساس أفضل الممارسات لاستخدام polyfill، كما هو موضّح أدناه.

قبل
@supports (container-type: inline-size) {
  /* ... */
}
بعد
@supports (--cq-XYZ-container-type: inline-size) {
  /* ... */
}

يتم اكتساب السمات المخصّصة في CSS تلقائيًا، ما يعني على سبيل المثال أنّ أي عنصر فرعي من .card سيحصل على قيمة --cq-XYZ-container-name و--cq-XYZ-container-type. هذه ليست بالتأكيد طريقة عمل المواقع الأصلية. لحلّ هذه المشكلة، ستُدرج تقنية polyfill القاعدة التالية قبل أي أنماط مستخدم، ما يضمن أن يتلقّى كل عنصر القيم الأولية، ما لم يتم استبدالها عن قصد بقاعدة أخرى.

* {
  --cq-XYZ-container-name: none;
  --cq-XYZ-container-type: normal;
}

أفضل الممارسات

على الرغم من أنّه من المتوقّع أن يستخدم معظم الزوّار متصفحات تتضمّن ميزة "طلبات بحث الحاوية" المضمّنة في وقت قريب، لا يزال من المهم تقديم تجربة جيدة للزوّار المتبقّين.

أثناء التحميل الأولي، هناك الكثير من الإجراءات التي يجب أن تتم قبل أن يتمكّن العنصر البديل من تنسيق الصفحة:

  • يجب تحميل الpolyfill وإعداده.
  • يجب تحليل جداول الأنماط وتحويلها. بما أنّه لا تتوفّر أي واجهات برمجة تطبيقات للوصول إلى المصدر الأوّلي لملف أسلوب خارجي، قد يكون من الضروري إعادة جلبه بشكل غير متزامن، ولكن من الأفضل أن يكون من ذاكرة التخزين المؤقت للمتصفّح فقط.

إذا لم يتمّ معالجة هذه المخاوف بعناية من خلال الpolyfill، قد يؤدي ذلك إلى تراجع مؤشرات Core Web Vitals.

لتسهيل تقديم تجربة ممتعة للزوّار، تم تصميم الpolyfill لمنح الأولوية لمهلة الاستجابة لأوّل إدخال (FID) ولمتغيّرات التصميم التراكمية (CLS)، وقد يكون ذلك على حساب سرعة عرض أكبر محتوى مرئي (LCP). على وجه التحديد، لا تضمن تقنية polyfill تقييم طلبات البحث عن الحاوية قبل عرض المحتوى لأول مرة. وهذا يعني أنّه للحصول على أفضل تجربة مستخدم، يجب التأكّد من إخفاء أي محتوى سيتأثّر حجمه أو موضعه باستخدام طلبات بحث الحاوية إلى أن يتم تحميل الpolyfill وتحويل ملف CSS. تتمثل إحدى طرق تحقيق ذلك في استخدام قاعدة @supports:

@supports not (container-type: inline-size) {
  #content {
    visibility: hidden;
  }
}

ننصحك بدمج ذلك مع صورة متحركة لتحميل CSS، يتم وضعها تمامًا فوق المحتوى (الخفي) لإعلام الزائر بأنّ هناك عملية جارية. يمكنك العثور على عرض توضيحي كامل لهذا النهج هنا.

وننصحك باستخدام هذا الأسلوب لعدة أسباب:

  • يعمل أداة تحميل CSS الخالصة على تقليل الوقت المستغرَق في تنفيذ المهام للمستخدمين الذين يستخدمون المتصفّحات الأحدث، مع تقديم ملاحظات خفيفة الوزن للمستخدمين الذين يستخدمون المتصفّحات القديمة والشبكات البطيئة.
  • من خلال الجمع بين موضع التحميل المطلق وvisibility: hidden، يمكنك تجنُّب تغيير التنسيق.
  • بعد تحميل الpolyfill، سيتوقف شرط @supports عن المرور، وسيتم عرض المحتوى الخاص بك.
  • في المتصفّحات التي تتضمّن ميزة مدمجة لطلبات بحث الحاوية، لن يتم استيفاء الشرط مطلقًا، وبالتالي سيتم عرض الصفحة في أول عملية رسم كما هو متوقّع.

الخاتمة

إذا كنت مهتمًا باستخدام طلبات بحث الحاوية على المتصفّحات القديمة، جرِّب polyfill. يُرجى عدم التردد في الإبلاغ عن مشكلة في حال واجهت أي مشاكل.

ونتطلّع إلى رؤية المحتوى الرائع الذي ستنشئه باستخدامها.