Стилизация input css: как оформить поля формы

Стилизация input css помогает привести поля формы к единому виду без JavaScript. Вы зададите базовую геометрию, оформите состояния, select, чекбоксы, range, файл и кнопку. Отдельный раздел покажет, как применить эти правила к Gravity Forms в WordPress.

Сброс и базовые стили полей

Ограничьте сброс классом формы.

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

<form class="form-ui" method="post" enctype="multipart/form-data">
  <div class="field">
    <label for="email">Электронная почта</label>
    <input id="email" name="email" type="email" autocomplete="email" required>
  </div>
  <button type="submit">Отправить</button>
</form>
.form-ui,
.form-ui *,
.form-ui *::before,
.form-ui *::after {
  box-sizing: border-box;
}

.form-ui {
  --accent: #1d4ed8;
  --border: #64748b;
  --error: #b91c1c;
  display: grid;
  gap: 1.25rem;
  max-width: 34rem;
  margin: 2rem auto;
  padding: 1rem;
  color: #172033;
  background: #fff;
  font: 1rem/1.5 system-ui, sans-serif;
}

.form-ui :is(input, select, textarea, button) {
  font: inherit;
}

.form-ui .field {
  display: grid;
  gap: 0.375rem;
  min-width: 0;
}

.form-ui label {
  font-weight: 600;
}

.form-ui :where(
  input:not([type]),
  input[type="text"],
  input[type="email"],
  input[type="password"],
  input[type="search"],
  input[type="tel"],
  input[type="url"],
  input[type="number"],
  input[type="date"],
  input[type="datetime-local"],
  input[type="month"],
  input[type="week"],
  input[type="time"],
  select,
  textarea
) {
  display: block;
  width: 100%;
  min-width: 0;
  min-height: 2.75rem;
  padding: 0.625rem 0.75rem;
  border: 1px solid var(--border);
  border-radius: 0.375rem;
  background: #fff;
  color: #172033;
}

.form-ui :is(input, select, textarea, button):focus-visible {
  outline: 3px solid var(--accent);
  outline-offset: 3px;
}

box-sizing: border-box включает рамку и отступы в ширину элемента. Свойство font: inherit передает полям шрифт формы, а переменные собирают цвета в одном месте.

Не задавайте общую ширину голому селектору input. Такое правило заденет чекбоксы, radio и range, которым нужны другие размеры.

Normalize.css и modern-normalize выравнивают базовые различия браузеров. Подключайте один вариант перед собственными правилами, но все равно задавайте стили формы отдельно.

Текстовые поля и плейсхолдер

Постоянная подпись объясняет назначение поля после очистки.

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

<div class="field">
  <label for="person-name">Имя</label>
  <input id="person-name" name="person_name" type="text"
         autocomplete="given-name" placeholder="Анна">
</div>

<div class="field">
  <label for="message">Сообщение</label>
  <textarea id="message" name="message" rows="4"
            placeholder="Опишите задачу"></textarea>
</div>
.form-ui :is(input, textarea)::placeholder {
  color: #64748b;
  opacity: 1;
}

.form-ui textarea {
  min-height: 8rem;
  resize: vertical;
}

Псевдоэлемент ::placeholder меняет подсказку внутри поля. Значение opacity: 1 убирает дополнительную прозрачность.

Свойство resize: vertical оставляет пользователю изменение высоты textarea.

Плейсхолдер показывает пример, но не заменяет label.

Состояния полей: hover, focus-visible, disabled, ошибка и автозаполнение

Состояния должны объяснять, что происходит с полем. Разделяйте наведение, фокус, недоступность и ошибку.

Наведение, фокус и отключенное поле

Добавляйте hover только устройствам с указателем. Сенсорный экран не получает случайное состояние наведения.

@media (hover: hover) {
  .form-ui :is(input, select, textarea):enabled:hover {
    border-color: var(--accent);
  }
}

.form-ui :is(input, select, textarea, button):disabled {
  background-color: #e2e8f0;
  color: #475569;
  cursor: not-allowed;
  opacity: 1;
}

Псевдокласс :enabled исключает отключенные элементы из hover. Атрибут disabled запрещает взаимодействие и исключает значение из отправки.

Не удаляйте outline без замены.

Общее правило :focus-visible выше оставляет заметный фокус при навигации клавиатурой.

Ошибка после взаимодействия и ответ сервера

Селектор :invalid способен подсветить пустое обязательное поле сразу. Для ошибки после действия пользователя используйте :user-invalid, если его поддерживает браузер.

@supports selector(:user-invalid) {
  .form-ui :is(input, select, textarea):user-invalid {
    border-color: var(--error);
    border-width: 2px;
  }
}

.form-ui :is(input, select, textarea)[aria-invalid="true"] {
  border-color: var(--error);
  border-width: 2px;
}

.form-ui .field-error {
  margin: 0;
  color: var(--error);
}
<div class="field">
  <label for="account-email">Почта учетной записи</label>
  <input id="account-email" name="account_email" type="email"
         aria-invalid="true" aria-describedby="account-email-error">
  <p id="account-email-error" class="field-error">
    Эта почта уже привязана к учетной записи.
  </p>
</div>

Серверная ошибка получает aria-invalid="true". Атрибут aria-describedby связывает поле с пояснением ошибки.

После исправления сервер убирает атрибут и сообщение.

CSS показывает состояние, но не проверяет данные формы.

Автозаполнение браузером

Автозаполнение может менять фон и цвет текста. Оформите это состояние отдельно через :autofill.

@supports selector(input:autofill) {
  .form-ui input:autofill {
    border-color: var(--accent);
    border-style: dashed;
  }
}

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

Выпадающий список select со своей стрелкой

Для закрытого однострочного списка можно заменить системную стрелку SVG-иконкой. Списки с multiple и size сохраните в нативном виде.

.form-ui select:not([multiple]):not([size]) {
  appearance: none;
  padding-right: 2.75rem;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16' viewBox='0 0 16 16'%3E%3Cpath d='m4 6 4 4 4-4' fill='none' stroke='%23334155' stroke-width='2'/%3E%3C/svg%3E");
  background-repeat: no-repeat;
  background-position: right 0.875rem center;
  background-size: 1rem 1rem;
}

@media (forced-colors: active) {
  .form-ui select:not([multiple]):not([size]) {
    appearance: auto;
    background-image: none;
  }
}

appearance: none убирает системную стрелку. Правый отступ оставляет место для SVG, а %23 кодирует решетку внутри data URI.

Режим принудительных цветов возвращает системный вид. Так пользователь сохраняет контраст и узнаваемое управление.

CSS закрытого select не меняет все части всплывающего меню.

Подробности о свойстве appearance смотрите в справке MDN.

Чекбоксы и радиокнопки: accent-color и полностью свой вид

Выберите один из двух подходов по задаче дизайна. accent-color сохраняет нативное управление, а appearance: none дает контроль над индикатором.

Нативный вид с accent-color

Оставьте input в разметке и свяжите его с label. У radio одной группы должно быть одинаковое значение name.

<div class="choice">
  <input id="updates" name="updates" type="checkbox" value="yes">
  <label for="updates">Получать новости</label>
</div>

<fieldset class="choice-group">
  <legend>Способ связи</legend>
  <div class="choice">
    <input id="contact-email" name="contact" type="radio" value="email">
    <label for="contact-email">Электронная почта</label>
  </div>
  <div class="choice">
    <input id="contact-phone" name="contact" type="radio" value="phone">
    <label for="contact-phone">Телефон</label>
  </div>
</fieldset>
.form-ui .choice {
  display: flex;
  align-items: flex-start;
  gap: 0.625rem;
}

.form-ui .choice input {
  flex: 0 0 auto;
  width: 1.25rem;
  height: 1.25rem;
  margin: 0.125rem 0 0;
  accent-color: var(--accent);
}

.form-ui .choice-group {
  display: grid;
  gap: 0.625rem;
  min-width: 0;
  margin: 0;
  padding: 0.75rem;
  border: 1px solid var(--border);
}

.form-ui .choice input:disabled + label {
  color: #64748b;
}

Свойство accent-color меняет основной цвет нативного индикатора. Браузер продолжает рисовать галочку, кружок и состояния элемента.

Клик по label переключает элемент.

Пробел меняет чекбокс, а стрелки перемещают выбор внутри группы radio.

Собственный индикатор с appearance: none

Добавьте класс custom-choice к обертке. Input остается в потоке, поэтому Tab и пробел продолжают работать.

<div class="choice custom-choice">
  <input id="updates-custom" name="updates-custom" type="checkbox" value="yes">
  <label for="updates-custom">Получать новости</label>
</div>

<div class="choice custom-choice">
  <input id="contact-custom-email" name="contact-custom" type="radio" value="email">
  <label for="contact-custom-email">Электронная почта</label>
</div>
.form-ui .custom-choice input {
  appearance: none;
  position: relative;
  flex: 0 0 auto;
  width: 1.25rem;
  height: 1.25rem;
  margin: 0.125rem 0 0;
  padding: 0;
  border: 2px solid var(--border);
  border-radius: 0.25rem;
  background: #fff;
}

.form-ui .custom-choice input[type="radio"] {
  border-radius: 50%;
}

.form-ui .custom-choice input:checked {
  border-color: var(--accent);
  background: var(--accent);
}

.form-ui .custom-choice input:checked::before {
  content: "";
  position: absolute;
  inset: 0.2rem;
  border-right: 2px solid #fff;
  border-bottom: 2px solid #fff;
  transform: rotate(45deg) scale(0.7);
}

.form-ui .custom-choice input[type="radio"]:checked::before {
  inset: 0.25rem;
  border: 0;
  border-radius: 50%;
  background: #fff;
  transform: none;
}

.form-ui .custom-choice input:focus-visible {
  outline: 3px solid var(--accent);
  outline-offset: 3px;
}

@media (forced-colors: active) {
  .form-ui .custom-choice input {
    appearance: auto;
  }

  .form-ui .custom-choice input::before {
    content: none;
  }
}

Селекторы совпадают с HTML-примером, поэтому блок можно копировать целиком. Не скрывайте input через display: none, потому что такой элемент исчезает из клавиатурного управления.

Ползунок range

Ползунок состоит из трека и бегунка. Chromium и Safari используют WebKit-псевдоэлементы, Firefox – Mozilla-псевдоэлементы.

<div class="field">
  <label for="volume">Громкость от 0 до 100</label>
  <input id="volume" name="volume" type="range" min="0" max="100" step="1" value="50">
</div>
.form-ui input[type="range"] {
  appearance: none;
  display: block;
  width: 100%;
  height: 2rem;
  margin: 0;
  padding: 0;
  border: 0;
  background: transparent;
  cursor: pointer;
}

.form-ui input[type="range"]::-webkit-slider-runnable-track {
  height: 0.375rem;
  border-radius: 1rem;
  background: #64748b;
}

.form-ui input[type="range"]::-webkit-slider-thumb {
  appearance: none;
  width: 1.5rem;
  height: 1.5rem;
  margin-top: -0.5625rem;
  border: 2px solid #fff;
  border-radius: 50%;
  background: var(--accent);
}

.form-ui input[type="range"]::-moz-range-track {
  height: 0.375rem;
  border-radius: 1rem;
  background: #64748b;
}

.form-ui input[type="range"]::-moz-range-thumb {
  box-sizing: border-box;
  width: 1.5rem;
  height: 1.5rem;
  border: 2px solid #fff;
  border-radius: 50%;
  background: var(--accent);
}

.form-ui input[type="range"]:focus-visible {
  outline: 3px solid var(--accent);
  outline-offset: 3px;
}

.form-ui input[type="range"]:disabled {
  cursor: not-allowed;
  opacity: 0.6;
}

Высота трека равна 0,375 rem, а диаметр бегунка – 1,5 rem. Отрицательный margin-top центрирует бегунок WebKit.

Псевдоэлементы ::-webkit-slider-thumb и ::-moz-range-thumb сохраняют нужные префиксы.

Префиксы IE и устаревшие свойства добавлять не нужно.

Поле загрузки файла

Псевдоэлемент ::file-selector-button оформляет кнопку внутри нативного поля. Имя выбранного файла остается под управлением браузера.

<div class="field">
  <label for="attachment">Приложение к сообщению</label>
  <input id="attachment" name="attachment" type="file" aria-describedby="attachment-help">
  <small id="attachment-help">Выберите документ для задачи.</small>
</div>
.form-ui input[type="file"] {
  display: block;
  width: 100%;
  min-width: 0;
  padding: 0.375rem;
  border: 1px solid var(--border);
  border-radius: 0.375rem;
  background: #fff;
  color: #172033;
}

.form-ui input[type="file"]::file-selector-button {
  margin-right: 0.75rem;
  padding: 0.5rem 0.75rem;
  border: 0;
  border-radius: 0.25rem;
  background: #e2e8f0;
  color: #172033;
  font: inherit;
  cursor: pointer;
}

.form-ui input[type="file"]:disabled::file-selector-button {
  background: #e2e8f0;
  color: #64748b;
  cursor: not-allowed;
}

margin-right отделяет кнопку от имени файла. Свойство font: inherit выравнивает ее текст с остальной формой.

Для передачи файла оставьте у формы method="post" и enctype="multipart/form-data".

CSS меняет внешний вид, но не отправляет файл на сервер.

Кнопка отправки

Укажите кнопке явный тип и текст действия. Ограничьте правило классом формы, чтобы не изменить остальные кнопки сайта.

.form-ui button[type="submit"] {
  justify-self: start;
  min-height: 2.75rem;
  padding: 0.625rem 1.25rem;
  border: 2px solid transparent;
  border-radius: 0.375rem;
  background: var(--accent);
  color: #fff;
  font-weight: 700;
  cursor: pointer;
}

@media (hover: hover) {
  .form-ui button[type="submit"]:enabled:hover {
    background: #1e40af;
  }
}

.form-ui button[type="submit"]:enabled:active {
  background: #1e3a8a;
}

.form-ui button[type="submit"]:disabled {
  background: #e2e8f0;
  color: #475569;
  cursor: not-allowed;
}

Постоянная прозрачная рамка сохраняет размер кнопки между состояниями. Общее правило :focus-visible добавляет видимый фокус.

Добавьте required к полю почты и отправьте пустую форму. Браузер остановит отправку и покажет встроенную подсказку.

Стили форм Gravity Forms в WordPress

Gravity Forms выводит собственную разметку.

Ограничьте CSS идентификатором одной формы, чтобы правила не затронули остальные формы WordPress.

Куда добавить CSS и как выбрать форму

Откройте страницу с формой и инструменты разработчика браузера. Выберите поле и найдите внешний контейнер с классом gform_wrapper и идентификатором формы.

  1. Скопируйте значение id внешнего контейнера.
  2. Добавьте CSS в style.css дочерней темы или в область дополнительных стилей темы.
  3. Замените #gform_wrapper_1 в примере идентификатором своей формы.
  4. Сохраните CSS и обновите страницу с формой.

Если поля получили новые рамки и шрифт, селектор работает. Чтобы вернуть прежнее оформление, удалите добавленный блок CSS.

#gform_wrapper_1,
#gform_wrapper_1 *,
#gform_wrapper_1 *::before,
#gform_wrapper_1 *::after {
  box-sizing: border-box;
}

#gform_wrapper_1 :is(input, select, textarea, button) {
  font: inherit;
}

#gform_wrapper_1 :is(
  input[type="text"],
  input[type="email"],
  input[type="tel"],
  input[type="url"],
  input[type="number"],
  textarea,
  select
) {
  width: 100%;
  min-width: 0;
  min-height: 2.75rem;
  padding: 0.625rem 0.75rem;
  border: 1px solid #64748b;
  border-radius: 0.375rem;
  background: #fff;
  color: #172033;
}

#gform_wrapper_1 :is(input, select, textarea, button):focus-visible {
  outline: 3px solid #1d4ed8;
  outline-offset: 3px;
}

#gform_wrapper_1 :is(input[type="checkbox"], input[type="radio"]) {
  accent-color: #1d4ed8;
}

#gform_wrapper_1 :is(input[type="submit"], button[type="submit"]) {
  min-height: 2.75rem;
  padding: 0.625rem 1.25rem;
  border: 2px solid transparent;
  border-radius: 0.375rem;
  background: #1d4ed8;
  color: #fff;
  font-weight: 700;
  cursor: pointer;
}

Как уточнить селектор по классам поля

В текущей разметке Gravity Forms встречаются gfield, классы вида gfield--type-* и контейнеры ginput_container_*. Используйте их для точечного правила.

#gform_wrapper_1 .gfield--type-multiselect select {
  background-image: none;
}

#gform_wrapper_1 select[multiple] {
  min-height: 8rem;
  background-image: none;
}

Селектор select[multiple] работает по атрибуту. Идентификатор формы делает правило специфичнее общих правил плагина.

Когда отключать стили плагина

Отключайте встроенный CSS только после подготовки собственных правил для сетки, отступов, ошибок и составных полей. Откройте официальную документацию Gravity Forms и найдите актуальный способ отключения CSS для установленной версии.

Если отключение не подходит, оставьте стили плагина и переопределите нужные элементы через #gform_wrapper_N. После изменения пройдите форму клавишей Tab и отправьте пустое обязательное поле.

Ошибки при стилизации полей формы

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

ОшибкаЧто ломаетсяКак исправить
outline: none без заменыПропадает видимый фокусЗадайте контрастный outline через :focus-visible
display: none у чекбоксаЭлемент пропадает из клавиатурного управленияОставьте input доступным и примените accent-color или appearance: none
Ширина у любого inputЧекбокс, radio и range получают размеры текстаПеречислите текстовые типы отдельно
Только красная рамкаПричина ошибки остается непонятнойДобавьте пояснение и свяжите его через aria-describedby
Разные шрифты формы и страницыРазмеры текста расходятсяНазначьте font: inherit полям и кнопкам
Устаревшие префиксыCSS становится длиннее без пользыОставьте префиксы у специальных псевдоэлементов range
Постоянный :invalidПустая форма выглядит ошибочной до вводаИспользуйте :user-invalid или серверное состояние

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

Совет от нашей редакции: сначала настройте клавиатурный фокус и связь label с input, затем меняйте цвета, размеры и системный вид элементов.

Комментарии

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