تنسيقات مخصّصة لأداة اختيار العناصر

تتيح سمة CSS‏ appearance: base-select إمكانية تصميم عنصر <select> وأداة الاختيار الخاصة به (من خلال ::picker(select)) مثل عناصر HTML العادية. من خلال الجمع بين هذه الميزة وأساليب تنسيق CSS الحديثة، مثل Grid أو Flexbox، يمكنك التخلّص من القائمة العمودية التقليدية وإنشاء تجربة مرئية غنية بدون استخدام JavaScript مخصّص. على سبيل المثال، يمكنك استخدام display: grid لتنسيق الخيارات كشبكة ثنائية الأبعاد من البطاقات.

وهذا الخيار مثالي لأدوات اختيار الألوان أو الرموز التعبيرية أو خيارات المنتجات التي تكون فيها القائمة المرئية الغنية أكثر فعالية من القائمة النصية.

كيفية الإعداد

1. تفعيل الأنماط الأساسية

طبِّق السمة appearance: base-select على كل من العنصر <select> والعنصر الزائف لأداة الاختيار الداخلية select::picker(select):

select.grid-picker {
  &, &::picker(select) {
    appearance: base-select;
  }
}

2. تنسيق حاوية "أداة اختيار الصور"

استهدِف select:open::picker(select) وطبِّق display: grid (أو display: flex). حدِّد الأعمدة والفجوات كما تفعل مع أي حاوية.

select.grid-picker:open::picker(select) {
  display: grid;
  grid-template-columns: repeat(2, 1fr);
  gap: 1rem;
}

ملاحظة مهمة: يجب استخدام :open. إذا أضفت نمطًا إلى select::picker(select)، سيظهر منتقي التاريخ حتى عندما يكون عنصر الاختيار مغلقًا.

3- تنسيق الخيارات

استهدِف عناصر <option> لتنسيقها كعناصر شبكة أو بطاقات. ويمكنك إضافة صور أو ملفات SVG أو تنسيقات معقّدة داخلها.

/* Style options as grid cards */
.grid-picker option {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  /* padding, border-radius, background, etc. */
}

4. تنسيق الخيار المحدّد

لتنسيق الخيار المحدّد، استخدِم option:checked. على سبيل المثال:

.grid-picker option:checked {
  position: relative;
  border: 2px solid var(--primary-color);
  background-color: oklab(from var(--primary-color) l a b / 8%);
}

كما هو الحال مع أي عنصر تحكّم في واجهة المستخدم، لا تعتمد على اللون وحده للإشارة إلى الحالة، بل استخدِم مؤشرات مرئية متعددة (مثل الألوان وسُمك الحدود وعلامة الصح) للإشارة إلى الخيار المحدّد.

5- (اختياري) تنسيق علامة الصح للخيار المحدّد

بشكل تلقائي، يعرض المتصفّح علامة صح كأول عنصر ثانوي لكل <option>.

ويعمل كعنصر ::before مع السمة content: "✓" / ""، ويتم عرضه قبل عنصر ::before يوفّره المؤلف، في حال توفّره.

إذا لم يتم تحديد الخيار، يتم إخفاء علامة الاختيار من خلال visibility: hidden.

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

على سبيل المثال، لوضعه في أعلى يسار الخيار، يمكنك تنفيذ ما يلي:

select.grid-picker {
  > option::checkmark {
    /* Hide the checkmark for unselected options */
    display: none;
  }

  > option:checked {
    position: relative;

    &::checkmark {
      position: absolute;
      inset-block-start: -.4em;
      inset-inline-end: -.5em;
      display: flex;
      align-items: center;
      justify-content: center;
      box-sizing: border-box;
      min-inline-size: 1.6em;
      aspect-ratio: 1;
      padding-right: 0.12em;
      padding-left: 0.05em;
      border-radius: 50%;
      background: var(--primary-color);
      color: white;
      font-size: 70%;
      font-weight: bold;
    }
  }
}

ملاحظة: عند رسم حاوية (مثل دائرة) حول علامة الصح، يجب تحريكها قليلاً نحو الأعلى (كما فعلنا هنا مع padding-bottom وpadding-left) لضبط التوسيط البصري الذي يراعي شكلها. لا تستخدِم padding-inline-start أو padding-block-end لهذا الغرض، لأنّهما سيتغيّران حسب وضع الكتابة، في حين أنّ شكل علامة الاختيار لا يتغيّر.

6. (اختياري) تخصيص عرض الخيار المحدّد

سيعرض <select> تلقائيًا نسخة نصية فقط من الخيار المحدّد. لعرض محتوى وافٍ للقيمة المحدّدة، استخدِم <selectedcontent> داخل <button> و/أو لعرض الخيار المحدّد بشكل مختلف (على سبيل المثال، إخفاء تفاصيل معيّنة).

لا تستخدِم <selectedcontent> بدون <button> حوله، لأنّ <selectedcontent> سيصبح جزءًا من أداة الاختيار، مثل أي عنصر محتوى آخر.

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

  • اختبار سلوك التنسيق يؤدي ضبط السمة appearance: base-select إلى إزالة السلوك التلقائي للمتصفّح المتمثّل في تحديد حجم القائمة المنسدلة استنادًا إلى عرض أطول خيار فيها. قد تحتاج إلى ضبط عرض ثابت أو استخدام قيود flex/grid لمنع متغيّرات التصميم.
  • تأكَّد من أنّ <select> يتضمّن سمة name و<label> مرتبطة بها. يضمن ذلك أن يظلّ المكوّن متاحًا لبرامج قراءة الشاشة ويعمل بشكل صحيح مع عمليات إرسال النماذج العادية، حتى مع واجهة مستخدم مخصّصة.
  • استخدِم سمات value العادية في الخيارات لضمان عمل عملية إرسال النموذج تمامًا كما في السابق.

تسهيل الاستخدام

يتعامل عنصر <select> الأصلي فقط مع التنقّل الخطي باستخدام السهمَين المتّجهَين للأعلى وللأسفل، لذا لا يؤدي السهمان المتّجهان لليمين ولليسار إلى أي إجراء في أداة الاختيار المفتوحة، ويستمر السهمان المتّجهان للأعلى وللأسفل في التنقّل بين العناصر بشكل تسلسلي، بغض النظر عن الصفوف والأعمدة.

لتنفيذ التنقّل المكاني الذي يتّبع الشبكة، يمكنك اعتراض keydown وتوجيه التركيز إلى أقرب خيار في الاتجاه الذي تم الضغط عليه (على سبيل المثال، باستخدام getBoundingClientRect()) عند استخدام مفاتيح الأسهم. لتحديد العنصر التالي أو السابق المناسب، استخدِم طرق DOM التي تعمل على الشكل الهندسي الفعلي (مثل getBoundingClientRect()) بدلاً من تحليل سلاسل CSS مثل grid-template-columns أو grid-column، والتي يمكن أن تكون غير مستقرة وعرضة للأخطاء في الشبكات المعقدة والشبكات الفرعية واللغات التي تُكتب من اليمين إلى اليسار وسيناريوهات التنسيق الأخرى. ومع ذلك، لا تبالغ في التصميم بلا داعٍ، بل استخدِم الثوابت التي تعرف أنّها صحيحة بشأن التنسيق لتبسيط هذا المنطق.

ملاحظة: ينطبق هذا فقط على التنقّل باستخدام لوحة المفاتيح. لا يزال العنصر <select> وعنصره ::picker() يحملان دورَيهما التلقائيَين (menu أو listbox)، ولا يمكن تجاوزهما، ولا يُسمح لهما باحتواء عنصر يحمل السمة role="grid". من الناحية العملية، يمكن دمج برنامج تضمين مع role="grid" داخل أداة الاختيار، ولكن هذا الإجراء غير صالح وقد يفاجئ مستخدمي التكنولوجيا المساعدة. في بعض الحالات، قد يؤدي استخدام <optgroup> لتجميع الخيارات إلى توفير تجربة مستخدم جيدة لقارئ الشاشة بدون role="grid". بخلاف ذلك، قد يكون من الضروري في النهاية التبديل إلى حلّ مخصّص باستخدام واجهة برمجة التطبيقات popover وتحديد موضع الارتساء وrole="grid" لتحقيق تجربة المستخدم المطلوبة لمستخدمي قارئ الشاشة.

استراتيجية الإجراءات الاحتياطية

<select> القابل للتخصيص: البدائل والمتصفّحات المتوافقة

بالنسبة إلى المتصفّحات التي لا تتيح استخدام appearance: base-select بعد، يتم الرجوع إلى استخدام عنصر <select> كقائمة منسدلة عادية في نظام التشغيل.

  • تجاهل المحتوى غير النصي: تزيل المتصفحات القديمة علامات HTML (مثل <svg> أو <div>) داخل علامات <option> وتعرض فقط عُقد النص. تأكَّد من أنّ المحتوى النصي لـ <option> واضح ومفهوم بحد ذاته.
  • التعامل مع بنية HTML: قد تتجاهل المحللات اللغوية العادية علامتَي <button> و<selectedcontent> داخل <select> أو تعتبرهما غير صالحتَين. لا يلزم استخدام polyfills لـ JavaScript ثقيلة من أجل التحسين التدريجي إذا كنت تعرض نصًا عاديًا كبديل قابل للقراءة.
document.addEventListener("DOMContentLoaded", () => {
  // Check if browser supports base-select value
  if (!CSS.supports("appearance", "base-select")) {
    // Custom select overrides are not supported natively.
  }
});