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

Gerald Monaco
Gerald Monaco

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

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

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

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

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

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

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

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

قبل
@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(...) جديدة نوعًا ما. بالنسبة إلى المتصفحات التي لا تتوافق معها، يوفّر رمز polyfill حلاً بديلاً آمنًا وسهلاً: يمكنك عمدًا زيادة خصوصية قواعدك عن طريق إضافة أداة اختيار وهمية :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.
  • وستظلّ دقة القواعد كما هي دائمًا، لأنّ تقنية 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 للعنصر الذي يستخدم الوحدة، وليس العنصر الذي يتم الاستعلام عنه. لإتاحة ذلك، تطبِّق دالة polyfill نمطًا مضمَّنًا على أي عنصر يختلف فيه 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;
}

وتتغيّر هذه السمات كلما تم اكتشافها، ما يسمح للرمز polyfill باللعب بشكل جيد مع ميزات 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. يُرجى عدم التردد في الإبلاغ عن مشكلة في حال واجهت أي مشاكل.

ولا يسعنا الانتظار لرؤية الأشياء الرائعة التي ستنشئها باستخدامها وتجربتها.