چیدمان‌های انتخاب‌گر سفارشی

ویژگی CSS appearance: base-select امکان سبک‌دهی به عنصر <select> و انتخابگر آن (ازطریق ::picker(select)) را مانند عناصر HTML معمولی باز می‌کند. با ترکیب کردن این ویژگی با تکنیک‌های مدرن چیدمان CSS مانند Grid یا Flexbox، می‌توانید از فهرست عمودی سنتی فاصله بگیرید و تجربه بصری غنی‌ای بدون JS سفارشی ایجاد کنید. برای مثال، می‌توانید از display: grid برای سبک‌بندی گزینه‌ها به‌صورت شبکه‌ای دوبعدی از کارت‌ها استفاده کنید.

این گزینه برای انتخاب‌گرهای رنگ، انتخاب‌گرهای اموجی، یا انواع محصول که در آن‌ها منو دیداری غنی مؤثرتر از فهرست است ایده‌آل است.

نحوه پیاده‌سازی

۱. فعال کردن سبک پایه

appearance: base-select را هم روی عنصر <select> و هم روی شبه‌عنصر انتخاب‌گر داخلی آن select::picker(select) اعمال کنید:

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

۲. سبک‌بندی «ظرف انتخاب‌گر»

‫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) را سبک‌بندی کنید، انتخاب‌گر حتی وقتی انتخاب بسته باشد نمایان می‌شود!

۳. سبک دادن به گزینه‌ها

عناصر <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. */
}

۴. سبک دادن به گزینه انتخاب‌شده

برای سبک‌دهی به گزینه انتخاب‌شده، از 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%);
}

مانند هر کنترل میانای کاربر، وضعیت را فقط با رنگ منتقل نکنید و از چندین نشانگر دیداری (مانند رنگ‌ها، ضخامت حاشیه، علامت تیک) برای نشان دادن گزینه انتخاب‌شده استفاده کنید.

۵. (اختیاری) علامت تیک گزینه انتخاب‌شده را سبک‌بندی کنید

به‌طور پیش‌فرض، مرورگر علامت تیک را به‌عنوان اولین فرزند هر <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 برای این کار استفاده نکنید، زیرا این موارد بسته به حالت نوشتن تغییر می‌کنند، درحالی‌که شکل علامت تیک تغییر نمی‌کند.

‫۶. (اختیاری) سفارشی‌سازی کردن نمایش گزینه انتخاب‌شده

به‌طور پیش‌فرض، <select> نسخه فقط نوشتاری گزینه انتخاب‌شده را نشان می‌دهد. برای نمایش محتوای غنی برای مقدار انتخاب‌شده، از <selectedcontent> در <button> استفاده کنید و/یا گزینه انتخاب‌شده را به‌صورت متفاوتی نمایش دهید (مثلاً پنهان کردن جزئیات خاص).

از NOT برای استفاده از <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> نادیده بگیرند یا آن‌ها را نامعتبر تلقی کنند. اگر نوشتار استاندارد را به‌عنوان جایگزین خوانا درنظر بگیرید، برای ارتقای پیش‌رو به شبیه‌سازهای مرورگر جاوا اسکریپت سنگین نیازی نیست.
document.addEventListener("DOMContentLoaded", () => {
  // Check if browser supports base-select value
  if (!CSS.supports("appearance", "base-select")) {
    // Custom select overrides are not supported natively.
  }
});