Специальные макеты для выбора

Свойство CSS appearance: base-select позволяет настраивать стиль элемента <select> и его средства выбора (с помощью ::picker(select)) так же, как и обычных элементов HTML. В сочетании с современными методами макета CSS, такими как Grid или Flexbox, вы можете отказаться от традиционного вертикального списка и создать богатый визуальный интерфейс без пользовательского JS. Например, вы можете использовать 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), окно выбора будет видно, даже когда элемент 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 отменяет стандартное поведение браузера, при котором размер элемента select определяется по ширине самого длинного варианта. Чтобы предотвратить смещение макета, может потребоваться задать фиксированную ширину или использовать ограничения flex/grid.
  • Убедитесь, что у элемента <select> есть атрибут name и связанный с ним элемент <label>. Это гарантирует, что даже при наличии пользовательского интерфейса компонент останется доступным для программ чтения с экрана и будет корректно работать со стандартными отправками форм.
  • Используйте стандартные атрибуты value для вариантов, чтобы отправка формы работала так же, как и раньше.

Специальные возможности

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

Чтобы реализовать пространственную навигацию по сетке, вы можете перехватывать keydown и направлять фокус на ближайший вариант в нажатом направлении (например, с помощью getBoundingClientRect()), когда используются клавиши со стрелками. Чтобы определить следующий или предыдущий элемент, используйте методы DOM, которые работают с фактической геометрией (например, getBoundingClientRect()), а не анализируют строки CSS, такие как grid-template-columns или grid-column. Последние могут быть ненадежными и приводить к ошибкам в сложных сетках, подсетках, RTL и других сценариях макета. Однако не усложняйте код без необходимости. Чтобы упростить логику, используйте инварианты, которые точно верны для макета.

Примечание. Это касается только навигации с помощью клавиатуры. Элементы <select> и ::picker() по-прежнему имеют роли по умолчанию (menu или listbox), которые нельзя переопределить. Кроме того, они не могут содержать элемент с атрибутом role="grid". Вложенный контейнер с атрибутом role="grid" внутри виджета выбора работает на практике, но не является допустимым и может удивить пользователей вспомогательных технологий. В некоторых случаях использование <optgroup> для группировки вариантов может обеспечить хорошее взаимодействие с программой чтения с экрана без role="grid". В противном случае вам может потребоваться перейти на собственное решение, использующее Popover API, позиционирование привязки и role="grid", чтобы обеспечить удобство работы для пользователей программ чтения с экрана.

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

Резервные объявления и поддержка браузеров для настраиваемых <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.
  }
});