Стилизация select CSS: как оформить поле и стрелку

Стилизация select css начинается со сброса нативного вида через appearance: none. После этого задайте размер, шрифт, рамку и фон, а стрелку добавьте через SVG или обертку. Нативное поле сохраняет клавиатурную навигацию и проверку формы.

Почему select выглядит по-разному в браузерах

Закрытый select рисуют браузер и операционная система. Поэтому одинаковые CSS-правила могут дать разные стрелки, рамки и списки.

CSS хорошо меняет размеры, шрифт, цвет, фон, рамку и отступы закрытого поля.

Открытый список и его пункты частично контролируют движок и система. Оформление select с помощью HTML и CSS выравнивает поле в интерфейсе, но не гарантирует одинаковый вид каждого option.

Общие приемы оформления форм описаны в руководстве MDN.

Как задать размер, шрифт, рамку и фон select

Свяжите поле с подписью через одинаковые значения for и id. Такое сочетание помогает пользователю понять назначение элемента.

<label for="topic">Тема</label>
<select id="topic" name="topic" class="select">
  <option value="">Выберите тему</option>
  <option value="html">HTML</option>
  <option value="css">CSS</option>
</select>

Соберите базовый вид одним правилом. box-sizing: border-box включает рамку и отступы в расчет размера.

.select {
  box-sizing: border-box;
  width: 240px;
  max-width: 100%;
  min-height: 44px;
  padding: 8px 12px;
  font: inherit;
  font-size: 16px;
  color: #1f2937;
  background-color: #fff;
  border: 1px solid #9ca3af;
  border-radius: 6px;
}
  • width задает ширину поля, а max-width удерживает его в контейнере.
  • min-height сохраняет удобный размер при разном тексте.
  • padding отделяет текст от рамки и стрелки.
  • font: inherit передает полю шрифт родительского элемента.
  • background-color, color, border и border-radius меняют внешний вид.

Используйте min-height, если текст может занимать больше места. Фиксированный height требует расчета с учетом padding и рамки.

Мелкий шрифт в iOS Safari может вызвать автоматический зум при фокусе на поле.

В примере для читаемости задан font-size: 16px после наследования шрифта. Не отключайте масштабирование страницы: пользователю может понадобиться увеличить текст самостоятельно.

Как убрать стандартный вид через appearance

Уберите системную стрелку и нативное оформление рамки через appearance: none. Выбор пункта и отправка формы при этом сохраняются.

.select {
  appearance: none;
}

Для современных браузеров используйте стандартное свойство appearance без вендорных префиксов.

Добавьте собственную стрелку после сброса вида. Рамку, фон и отступы задайте явно, как в базовом примере.

Нужный синтаксис и ограничения свойства описаны в документации MDN об appearance.

Чтобы вернуть системное оформление, удалите правило appearance: none. Поле снова получит стандартную стрелку.

Своя стрелка для select

У обычного select нет надежных псевдоэлементов. Стрелку размещают фоном самого поля или рисуют на соседней обертке.

SVG в background-image

Фоновый SVG занимает место справа. Правый отступ защищает длинный пункт от перекрытия.

.select {
  appearance: none;
  padding-right: 36px;
  background-color: #fff;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='8' viewBox='0 0 12 8'%3E%3Cpath d='M1 1l5 5 5-5' fill='none' stroke='%231f2937' stroke-width='2'/%3E%3C/svg%3E");
  background-repeat: no-repeat;
  background-position: right 12px center;
}

Символ # внутри data-URI записан как %23. Цвет стрелки меняется в значении stroke.

Обертка с псевдоэлементом

Обертка принимает ::after, а select остается интерактивным полем. Нажатие проходит к полю благодаря pointer-events: none у стрелки.

<label for="wrapped-topic">Тема</label>
<div class="select-wrap">
  <select id="wrapped-topic" name="topic" class="select">
    <option value="html">HTML</option>
    <option value="css">CSS</option>
  </select>
</div>
.select-wrap {
  position: relative;
  display: inline-block;
  width: 240px;
  max-width: 100%;
  color: #1f2937;
}

.select-wrap .select {
  width: 100%;
  padding-right: 36px;
  background-image: none;
}

.select-wrap::after {
  content: "";
  position: absolute;
  top: 50%;
  right: 14px;
  width: 8px;
  height: 8px;
  border-right: 2px solid currentColor;
  border-bottom: 2px solid currentColor;
  transform: translateY(-70%) rotate(45deg);
  pointer-events: none;
}

Стрелка наследует цвет обертки через currentColor. Нажатие по значку открывает поле.

Состояния select

Оформите наведение, фокус, недоступность и ошибку обязательного поля. Правила состояний поставьте после базового оформления.

.select:not(:disabled):hover {
  border-color: #4b5563;
}

.select:focus-visible {
  outline: 2px solid #2563eb;
  outline-offset: 2px;
}

.select:disabled {
  color: #6b7280;
  background-color: #f3f4f6;
  cursor: not-allowed;
}

.select:required:invalid {
  color: #6b7280;
}

.select:user-invalid {
  border-color: #dc2626;
}

Не удаляйте outline без заметной замены. :focus-visible показывает обводку при клавиатурной навигации.

Пустое значение первого пункта запускает проверку обязательного поля.

<label for="required-topic">Тема, обязательно</label>
<select id="required-topic" name="topic" class="select" required>
  <option value="">Выберите тему</option>
  <option value="html">HTML</option>
  <option value="css">CSS</option>
</select>

После выбора значения поле проходит проверку required. Псевдокласс :invalid перестает действовать, и текст получает основной цвет.

Добавьте внешний текст ошибки, чтобы пользователь не зависел только от цвета.

Стилизация option и optgroup

Элемент option дает меньше контроля, чем обычный блок. Движок и операционная система могут игнорировать часть отступов, фона и шрифта.

<label for="technology">Технология</label>
<select id="technology" name="technology" class="select">
  <optgroup label="Верстка">
    <option value="html">HTML</option>
    <option value="css">CSS</option>
  </optgroup>
  <optgroup label="Программирование">
    <option value="js">JavaScript</option>
  </optgroup>
</select>

Атрибут label подписывает группу, а optgroup нельзя выбрать отдельно. На мобильном устройстве список часто рисует система.

Оформление закрытого поля задавайте самому select: его цвет, фон и шрифт не гарантируют такого же оформления раскрытого списка.

В обычных списках Chrome и Firefox размер шрифта может следовать за font-size поля. Однако правило font не гарантирует применения всей гарнитуры, насыщенности и межстрочного интервала к каждому пункту. Системное меню может использовать собственные параметры текста.

Свойства color и background-color у option могут менять текст и фон в настольных Chrome и Firefox. Результат зависит от операционной системы, а подсветка выбранного пункта может перекрывать авторские цвета.

В обычном списке Safari на macOS не рассчитывайте на произвольные фон и шрифт пунктов. Safari на iOS также использует системный интерфейс выбора, который ограничивает оформление option. Сброс appearance: none у закрытого поля эти ограничения не снимает.

В режиме base-select браузер предоставляет выпадающую часть для CSS-оформления, поэтому пункты можно оформлять вместе с контейнером списка.

ПриемChromeFirefoxSafari на macOSSafari на iOS
Свойства обычного optionЦвета и размер шрифта могут применяться; результат зависит от ОСЦвета и размер шрифта могут применяться; результат зависит от ОСОграничены системным оформлениемОграничены системным оформлением
appearance: base-selectС версии 135—С версии 27С версии 27
::picker(select)С версии 135—С версии 27С версии 27
::picker-iconС версии 133—С версии 27С версии 27

Различия обычного и настраиваемого поля описаны в справке MDN об элементе select. Ограничения пунктов перечислены в справке MDN об option.

Сведения о совместимости расширенного режима также доступны на Can I use.

Множественный выбор: multiple и size

Атрибут multiple разрешает выбрать несколько пунктов, а size задает число видимых строк. В таком поле отдельная стрелка обычно не нужна.

<label for="skills">Технологии</label>
<p id="skills-help">Для отдельных пунктов удерживайте Ctrl или Command.</p>
<select id="skills" name="skills" class="select" multiple size="4" aria-describedby="skills-help">
  <option value="html">HTML</option>
  <option value="css">CSS</option>
  <option value="js">JavaScript</option>
  <option value="php">PHP</option>
</select>
.select[multiple] {
  height: auto;
  padding: 4px;
  background-image: none;
}

.select[multiple] option:checked {
  background-color: #dbeafe;
  color: #1e3a8a;
}

Выбранные строки остаются отмеченными. Системная подсветка может заменить заданные цвета.

Настраиваемый select с base-select

Включите расширенное оформление через appearance: base-select в браузерах с поддержкой этого режима.

Псевдоэлемент ::picker(select) обращается к выпадающей части, а ::picker-icon – к встроенной стрелке. Включите расширенный режим через @supports, сохранив базовый вариант поля.

.select {
  appearance: none;
}

@supports (appearance: base-select) and selector(::picker(select)) {
  .select:not([multiple]):not([size]),
  .select:not([multiple]):not([size])::picker(select) {
    appearance: base-select;
  }

  .select:not([multiple]):not([size]) {
    background-image: none;
  }

  .select:not([multiple]):not([size])::picker(select) {
    color: #1f2937;
    background-color: #fff;
    border: 1px solid #9ca3af;
    border-radius: 8px;
    padding: 4px;
  }
}

Браузер без нужной поддержки пропустит блок @supports и сохранит базовые правила. Поэтому пользователь получает рабочее поле с обычной стрелкой или SVG.

Настраиваемый select допускает картинки и дополнительную разметку внутри option.

Например, пункт может содержать изображение и текст в отдельном span. CSS задает им размеры, отступы и взаимное расположение. Каждому пункту оставьте понятный текст и явное значение value для отправки формы.

Элемент selectedcontent показывает копию содержимого выбранного пункта в закрытом поле.

Для собственного оформления выбранного значения поместите selectedcontent внутрь button, первым дочерним элементом select. Браузер обновляет его содержимое при выборе другого пункта. Это позволяет показывать в кнопке ту же картинку и подпись, что в списке.

Такая разметка связана с настраиваемым режимом: обычное системное меню не обязано отображать вложенные изображения и элементы. Условие @supports проверяет CSS-возможности, но само по себе не гарантирует поддержку всех новых HTML-элементов. Для простого запасного варианта сохраняйте текстовые названия пунктов.

Общий синтаксис и примеры разметки приведены в материале MDN о customizable select.

Темная тема для select

Свойство color-scheme сообщает браузеру о светлой и темной палитрах. Переменные меняют фон, текст, рамку и цвет подсказки в одном месте.

:root {
  color-scheme: light dark;
  --select-bg: #fff;
  --select-fg: #1f2937;
  --select-border: #9ca3af;
  --select-muted: #6b7280;
}

@media (prefers-color-scheme: dark) {
  :root {
    --select-bg: #1f2937;
    --select-fg: #f3f4f6;
    --select-border: #9ca3af;
    --select-muted: #cbd5e1;
  }
}

.select {
  color: var(--select-fg);
  background-color: var(--select-bg);
  border-color: var(--select-border);
}

.select:required:invalid {
  color: var(--select-muted);
}

Проверьте контраст текста, фокуса и disabled в обеих темах. Для стрелки в data-URI задайте отдельный цвет.

Когда нужен выпадающий список на JavaScript

Нативный select подходит для выбора готовых значений. CSS закрывает задачу размера, рамки, цвета и собственной стрелки.

JavaScript-компонент оправдан для поиска по длинному списку или мультивыбора с тегами.

При замене select вы сами отвечаете за клавиатуру, фокус, роли и состояния. Сохраните связанный label и передавайте выбранное значение в форму. Без этих частей декоративный список сложнее для пользователей вспомогательных технологий.

Ошибки при стилизации select

  • ::after прямо на select не дает надежной стрелки. Используйте соседнюю обертку.
  • Удаленный outline скрывает клавиатурный фокус. Оставьте обводку или задайте свою.
  • Фиксированный height вместе с padding может обрезать текст. Используйте min-height и box-sizing: border-box.
  • Мелкий шрифт может вызвать зум при фокусе в iOS Safari. Увеличьте размер текста поля, не отключая масштабирование страницы.
  • Фоновая стрелка без padding-right перекрывает длинный пункт.
  • SVG-стрелка вместе с ::picker-icon дает два значка. Отключайте фон в расширенном режиме.
  • Запись linear-gradient(top, ...) использует устаревший порядок аргументов. Пишите linear-gradient(to bottom, ...).

Готовый пример оформления select на HTML и CSS

Пример оформления select на HTML и CSS состоит из двух файлов.

Сохраните первый блок как index.html, а второй – как styles.css. HTML подключает таблицу стилей через элемент link.

<!DOCTYPE html>
<html lang="ru">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <title>Стилизованный select</title>
  <link rel="stylesheet" href="styles.css">
</head>
<body>
  <form>
    <label for="topic">Тема, обязательно</label>
    <select id="topic" name="topic" class="select" required>
      <option value="">Выберите тему</option>
      <option value="html">HTML</option>
      <option value="css">CSS</option>
      <option value="js">JavaScript</option>
    </select>
    <button type="submit">Продолжить</button>
  </form>
</body>
</html>
:root {
  color-scheme: light dark;
  --page-bg: #fff;
  --select-bg: #fff;
  --select-fg: #1f2937;
  --select-border: #9ca3af;
  --select-muted: #6b7280;
  --select-focus: #2563eb;
  --select-error: #b91c1c;
}

@media (prefers-color-scheme: dark) {
  :root {
    --page-bg: #111827;
    --select-bg: #1f2937;
    --select-fg: #f3f4f6;
    --select-border: #9ca3af;
    --select-muted: #cbd5e1;
    --select-focus: #93c5fd;
    --select-error: #fca5a5;
  }
}

body {
  margin: 24px;
  font: 16px/1.5 system-ui, sans-serif;
  color: var(--select-fg);
  background-color: var(--page-bg);
}

label {
  display: block;
  margin-bottom: 8px;
}

.select {
  box-sizing: border-box;
  appearance: none;
  width: 280px;
  max-width: 100%;
  min-height: 44px;
  padding: 8px 36px 8px 12px;
  font: inherit;
  font-size: 16px;
  color: var(--select-fg);
  background-color: var(--select-bg);
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='8' viewBox='0 0 12 8'%3E%3Cpath d='M1 1l5 5 5-5' fill='none' stroke='%239ca3af' stroke-width='2'/%3E%3C/svg%3E");
  background-repeat: no-repeat;
  background-position: right 12px center;
  border: 1px solid var(--select-border);
  border-radius: 6px;
}

.select:not(:disabled):hover {
  border-color: var(--select-fg);
}

.select:focus-visible {
  outline: 2px solid var(--select-focus);
  outline-offset: 2px;
}

.select:disabled {
  color: var(--select-muted);
  cursor: not-allowed;
}

.select:required:invalid {
  color: var(--select-muted);
}

.select:user-invalid {
  border-color: var(--select-error);
}

@supports (appearance: base-select) and selector(::picker(select)) {
  .select,
  .select::picker(select) {
    appearance: base-select;
  }

  .select {
    background-image: none;
  }

  .select::picker(select) {
    color: var(--select-fg);
    background-color: var(--select-bg);
    border: 1px solid var(--select-border);
    border-radius: 8px;
    padding: 4px;
  }
}

Откройте файл в браузере. Поле получит рамку и собственную стрелку, а пустая подсказка не пройдет встроенную проверку формы.

Клавишей Tab переведите фокус на поле и выберите тему. Следующее нажатие Tab переведет фокус к кнопке. Расширенный режим применит стили к выпадающей части там, где он доступен.

Чтобы вернуть системное оформление, удалите правила класса select или строку подключения styles.css.

Перед переносом примера в проект добавьте самый длинный пункт вашего списка. Проверьте, что стрелка не перекрывает текст в обеих темах.

Комментарии

Оставьте свой комментарий
Пока никто не оставил комментариев