كان من الصعب في السابق تنسيق عنصر <select> الأصلي، وكان يمكن أن يحتوي فقط على خيارات نص عادي. توفّر السمة appearance: base-select طريقة تعريفية تستند إلى CSS فقط لتفعيل حالة قابلة للتخصيص للعنصر <select>. يتيح ذلك للمطوّرين تضمين محتوى HTML غني، مثل الصور وملفات SVG والتنسيقات المعقّدة، داخل عناصر <option>، مع الاحتفاظ بإمكانية الوصول إلى لوحة المفاتيح الأصلية ودمج النماذج. استخدِم هذا النمط لاستبدال مكوّنات الاختيار المخصّصة الثقيلة بعناصر عادية ومدمجة.
كيفية الإعداد
لتنفيذ أداة اختيار وسائط تفاعلية متقدّمة باستخدام واجهة برمجة التطبيقات Customizable Select API:
- الموافقة على الأنماط الأساسية: يمكنك تطبيق السمة
appearance: base-selectعلى كل من العنصر<select>وأداة الاختيار الداخلية باستخدام العنصر الزائف::picker(select). يؤدي ذلك إلى تغيير محلِّل HTML اللغوي في المتصفّح للمحتوى داخل<select>. - تحديد محتوى الزر: استخدِم عناصر
<button>و<selectedcontent>العادية داخل<select>لتحديد ما يظهر عند إغلاق أداة الاختيار. يعكس العنصر<selectedcontent>تلقائيًا محتوى الخيار المحدّد. هذا الإجراء مطلوب إذا أردت عرض المحتوى الوافي المحدّد حاليًا في الزر. - استخدام المحتوى الوافي داخل الخيارات: يمكنك الآن وضع الصور وملفات SVG وعلامات HTML الأخرى داخل علامات
<option>. قبل واجهة برمجة التطبيقات هذه، كان المتصفّح يزيل العلامات من علامات<option>ويعرض النص العادي فقط. - تنسيق القائمة المنبثقة: يمكنك تنسيق قائمة الخيارات المنسدلة من خلال استهداف العنصر الزائف
::picker(select). ويتم عرضه في الطبقة العليا، ما يعني أنّك لست بحاجة إلى التعامل معz-index. يتم تحديد موضع الخيارات باستخدام واجهة Anchor Positioning API بشكلٍ أصلي.
مثال على الرمز: أداة اختيار الدور الغني
<label for="role-picker">Select your role</label>
<select class="custom-select" name="role" id="role-picker">
<button>
<selectedcontent></selectedcontent> <!-- Mirrors the selected option's content automatically so you do not need JS to update the button -->
</button>
<!-- Define concise aria-label values on options whose mirrored rich content would otherwise read awkwardly as a concatenated string -->
<option value="frontend" aria-label="Frontend Developer">
<svg aria-hidden="true" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor">
<rect x="2" y="3" width="20" height="14" rx="2" ry="2"></rect>
</svg>
<div class="option-text">
<span class="option-title">Frontend Developer</span>
<span class="option-desc">React, Vue, CSS</span>
</div>
</option>
<option value="backend" aria-label="Backend Developer">
<svg aria-hidden="true" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor">
<rect x="2" y="12" width="20" height="14" rx="2" ry="2"></rect>
</svg>
<div class="option-text">
<span class="option-title">Backend Developer</span>
<span class="option-desc">Node.js, Python</span>
</div>
</option>
</select>
select.custom-select,
select.custom-select::picker(select) {
appearance: base-select; /* MUST opt-in both the select and its picker to enable the customizable state, otherwise browser standard rendering applies */
}
select.custom-select {
display: flex;
align-items: center;
justify-content: space-between;
width: 100%;
padding: 12px;
background-color: #1e293b;
border: 1px solid #334155;
border-radius: 8px;
color: #f1f5f9;
cursor: pointer;
}
select.custom-select::picker(select) {
background-color: #0f172a;
border: 1px solid #334155;
border-radius: 8px;
padding: 8px;
box-shadow: 0 10px 25px -5px rgba(0, 0, 0, 0.5);
width: anchor-size(width); /* Uses Anchor Positioning API to keep the dropdown precisely aligned to the button trigger width */
}
select.custom-select option {
display: flex;
align-items: center;
gap: 12px;
padding: 10px;
border-radius: 4px;
cursor: pointer;
}
select.custom-select option:hover {
background-color: #1e293b;
}
/* Remove standard OS checkmark for base-select */
select.custom-select option::before {
display: none;
}
/* MANDATORY: Provide multiple visual indicators (e.g., prominent background color and bold title font) to communicate the checked state cleanly */
select.custom-select option:checked {
background-color: #3b82f6;
color: #ffffff;
}
select.custom-select option:checked .option-title {
font-weight: 700;
}
التنفيذ الاستراتيجي وأفضل الممارسات
- يُرجى استخدام
appearance: base-selectعند الحاجة إلى تنسيقات خيارات معقّدة (رموز وأوصاف وصور) لم تكن ممكنة في السابق إلا من خلال أُطر عمل واجهة مستخدم JavaScript الثقيلة. - لا تستخدِم عناصر مخصّصة إذا لاحظت تأخّرًا في الأداء، لأنّ المتصفّح يتعامل مع تركيز لوحة المفاتيح بشكلٍ أصلي.
- يجب مراعاة عرض الطبقة العليا. يتم عرض أداة الاختيار في الطبقة العليا، ما يعني أنّها تلغي
z-indexالنسبي لمحتوى الصفحة. - يجب إخفاء التفاصيل الثانوية (مثل الأوصاف) في حالة الزر إذا كانت تشغل مساحة كبيرة، وذلك من خلال تصميم
.custom-select selectedcontent .option-desc { display: none; }. - اختبار سلوك التنسيق يؤدي ضبط السمة
appearance: base-selectإلى إزالة السلوك التلقائي للمتصفّح المتمثّل في تحديد حجم القائمة المنسدلة استنادًا إلى عرض أطول خيار فيها. قد تحتاج إلى ضبط عرض ثابت أو استخدام قيود flex/grid لمنع متغيّرات التصميم. - تأكَّد من أنّ
<select>يتضمّن سمةnameو<label>مرتبطة بها. يضمن ذلك أن يظلّ المكوّن متاحًا لبرامج قراءة الشاشة ويعمل بشكل صحيح مع عمليات إرسال النماذج العادية، حتى مع واجهة مستخدم مخصّصة. - استخدِم سمات
valueالعادية في الخيارات لضمان عمل عملية إرسال النموذج تمامًا كما في السابق.
الاستراتيجيات الاحتياطية
<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.
}
});