Инструмент выбора мультимедиа (настраиваемый выбор)

Раньше элемент <select> было сложно стилизовать, и он мог содержать только обычный текст. Свойство appearance: base-select предлагает декларативный способ, основанный только на CSS, для включения настраиваемого состояния элемента <select>. Это позволяет разработчикам включать в элементы <option> сложный HTML-контент, например изображения, SVG и сложные макеты, сохраняя при этом доступность клавиатуры и интеграцию форм. Используйте этот шаблон, чтобы заменить тяжелые, созданные на заказ компоненты выбора стандартными, нативными элементами.

Как реализовать

Чтобы реализовать мультимедийный инструмент выбора с помощью Customizable Select API, выполните следующие действия:

  1. Включить базовые стили. Примените appearance: base-select к элементу <select> и его внутреннему средству выбора с помощью псевдоэлемента ::picker(select). Это правило меняет синтаксический анализатор HTML в браузере для контента в элементе <select>.
  2. Определите контент кнопки. Используйте стандартные элементы <button> и <selectedcontent> внутри <select>, чтобы определить, что будет показываться, когда окно выбора закрыто. Элемент <selectedcontent> автоматически отражает контент выбранного варианта. Это необходимо, если вы хотите, чтобы на кнопке показывались выбранные в данный момент расширенные сведения о товаре.
  3. Использование расширенного контента в параметрах. Теперь вы можете размещать изображения, SVG и другие HTML-теги внутри тегов <option>. До появления этого API браузер удалял теги из тегов <option> и отображал только обычный текст.
  4. Оформление всплывающего окна. Оформите раскрывающийся список вариантов, используя псевдоэлемент ::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 отменяет стандартное поведение браузера, при котором размер элемента select определяется по ширине самого длинного варианта. Чтобы предотвратить смещение макета, может потребоваться задать фиксированную ширину или использовать ограничения flex/grid.
  • Убедитесь, что у элемента <select> есть атрибут name и связанный с ним элемент <label>. Это гарантирует, что даже при наличии пользовательского интерфейса компонент останется доступным для программ чтения с экрана и будет корректно работать со стандартными отправками форм.
  • Используйте стандартные атрибуты value для вариантов, чтобы отправка формы работала так же, как и раньше.

Резервные стратегии

Резервные объявления и поддержка браузеров для настраиваемых <select>

В браузерах, которые пока не поддерживают appearance: base-select, элемент <select> преобразуется в стандартный раскрывающийся список операционной системы.

  • Игнорирование нетекстового контента. Старые браузеры удаляют HTML-теги (например, <svg> или <div>) внутри тегов <option> и отображают только текстовые узлы. Убедитесь, что текстовый контент элемента <option> понятен и имеет смысл сам по себе.
  • Обработка структуры HTML. Стандартные синтаксические анализаторы могут игнорировать теги <button> и <selectedcontent> внутри <select> или считать их недопустимыми. Если вы рассматриваете стандартный текст как резервный вариант, то для прогрессивного улучшения не требуются тяжелые полифилы JavaScript.
document.addEventListener("DOMContentLoaded", () => {
  // Check if browser supports base-select value
  if (!CSS.supports("appearance", "base-select")) {
    // Custom select overrides are not supported natively.
  }
});