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

Gerald Monaco
Gerald Monaco

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

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

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

الترجمة

عندما يواجه محلّل CSS داخل المتصفّح قاعدة غير معروفة، مثل قاعدة @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 لا يغيّرها.

أثناء التحويل، سيستبدل الرمز polyfill هذه البيانات الوهمية بأداة اختيار السمة بنفس الدقة. ولتجنّب حدوث أي مفاجآت، تستخدم دالة polyfill كلا أداتَي الاختيار: يتم استخدام محدِّد المصدر الأصلي لتحديد ما إذا كان يجب أن يتلقّى العنصر سمة 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 بشكل خاطئ.

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

قبل
@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 المخصَّصة. ستضبط دالة polyfill قيم هذه السمات عبر أنماط مضمّنة في عنصر الحاوية.

قبل
.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 وإعداده.
  • يجب تحليل أوراق الأنماط وترجمتها. نظرًا لعدم وجود أي واجهات برمجة تطبيقات للوصول إلى المصدر الأولي لورقة أنماط خارجية، قد تحتاج إلى إعادة جلبها بشكل غير متزامن، ولكن من الأفضل أن تكون من ذاكرة التخزين المؤقت للمتصفح فقط.

وإذا لم تعالج رموز polyfill هذه المشاكل بعناية، قد يؤدي ذلك إلى تراجع أداء مؤشرات أداء الويب الأساسية.

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

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

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

يُنصح باتّباع هذا الأسلوب لعدة أسباب:

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

الخاتمة

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

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

شكر وتقدير

صورة رئيسية من تصميم دان كريستيان بادوريت على Un التصميم