Создание компонента кнопки

Базовый обзор того, как создавать адаптивные к цвету, отзывчивые и доступные компоненты <button> .

В этом посте я хочу поделиться своими мыслями о том, как создать адаптивный к цвету, отзывчивый и доступный элемент <button> . Попробуйте демо и просмотрите исходный код

Взаимодействие с кнопками осуществляется с помощью клавиатуры и мыши в светлой и темной темах.

Если вы предпочитаете видео, вот версия этого поста на YouTube:

Обзор

Поддержка браузера

  • 1
  • 12
  • 1
  • ≤4

Источник

Элемент <button> создан для взаимодействия с пользователем. Событие click срабатывает при нажатии клавиатуры, мыши, прикосновении, голосе и т. д., с умными правилами относительно его времени . Он также поставляется с некоторыми стилями по умолчанию в каждом браузере, поэтому вы можете использовать их напрямую без какой-либо настройки. Используйте color-scheme , чтобы выбрать светлые и темные кнопки браузера.

Существуют также различные типы кнопок , каждая из которых показана на предыдущей вставке Codepen. <button> без типа адаптируется к нахождению внутри <form> , меняясь на тип submit.

<!-- buttons -->
<button></button>
<button type="submit"></button>
<button type="button"></button>
<button type="reset"></button>

<!-- button state -->
<button disabled></button>

<!-- input buttons -->
<input type="button" />
<input type="file">

В конкурсе GUI Challenge в этом месяце каждая кнопка получит стили, помогающие визуально различать их назначение. Кнопки сброса будут иметь предупреждающие цвета, поскольку они разрушительны, а кнопки отправки будут выделены синим текстом, поэтому они будут выглядеть немного более рекламными, чем обычные кнопки.

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

Кнопки также имеют псевдоклассы для CSS, которые можно использовать для стилизации. Эти классы предоставляют CSS-хуки для настройки ощущения от кнопки: :hover , когда мышь находится над кнопкой, :active , когда нажимается мышь или клавиатура, и :focus или :focus-visible для помощи в стилизации вспомогательных технологий.

button:hover {}
button:active {}
button:focus {}
button:focus-visible {}
Предварительный просмотр окончательного набора всех типов кнопок в темной теме.
Предварительный просмотр окончательного набора всех типов кнопок в темной теме

Разметка

В дополнение к типам кнопок, предусмотренным спецификацией HTML, я добавил кнопку со значком и кнопку с пользовательским классом btn-custom .

<button>Default</button>
<input type="button" value="<input>"/>
<button>
  <svg viewBox="0 0 24 24" width="24" height="24" aria-hidden="true">
    <path d="..." />
  </svg>
  Icon
</button>
<button type="submit">Submit</button>
<button type="button">Type Button</button>
<button type="reset">Reset</button>
<button disabled>Disabled</button>
<button class="btn-custom">Custom</button>
<input type="file">

Затем для тестирования каждая кнопка помещается внутрь формы. Таким образом, я могу гарантировать, что стили будут обновлены соответствующим образом для кнопки по умолчанию, которая ведет себя как кнопка отправки. Я также переключаю стратегию значков со встроенного SVG на замаскированный SVG, чтобы оба работали одинаково хорошо.

<form>
  <button>Default</button>
  <input type="button" value="<input>"/>
  <button>Icon <span data-icon="cloud"></span></button>
  <button type="submit">Submit</button>
  <button type="button">Type Button</button>
  <button type="reset">Reset</button>
  <button disabled>Disabled</button>
  <button class="btn-custom btn-large" type="button">Large Custom</button>
  <input type="file">
</form>

На данный момент матрица комбинаций довольно обширна. Существует более 20 комбинаций кнопок между типами кнопок, псевдоклассами и нахождением в форме или вне формы. Хорошо, что CSS может помочь нам четко сформулировать каждый из них!

Доступность

Кнопочные элементы естественно доступны, но есть несколько общих улучшений.

Наведите курсор и сфокусируйтесь вместе

Мне нравится группировать :hover и :focus вместе с функциональным псевдоселектором :is() . Это помогает гарантировать, что мои интерфейсы всегда учитывают стили клавиатуры и вспомогательных технологий.

button:is(:hover, :focus) {
  …
}
Попробуйте демо !

Интерактивное кольцо фокусировки.

Мне нравится анимировать кольцо фокусировки для пользователей клавиатуры и вспомогательных технологий. Я добиваюсь этого, анимируя контур на расстоянии 5 пикселей от кнопки, но только тогда, когда кнопка неактивна. Это создает эффект, заставляющий кольцо фокусировки сжиматься до размера кнопки при нажатии.

:where(button, input):where(:not(:active)):focus-visible {
  outline-offset: 5px;
}

Обеспечение передачи цветового контраста

Существует как минимум четыре различных цветовых сочетания светлого и темного цвета, которые требуют учета цветового контраста: кнопка, кнопка отправки, кнопка сброса и отключенная кнопка. VisBug используется здесь для проверки и отображения всех результатов одновременно:

Скрытие значков от людей, которые не видят

При создании кнопки со значком значок должен обеспечивать визуальную поддержку текста кнопки. Это также означает, что значок не представляет ценности для людей с потерей зрения. К счастью, браузер предоставляет возможность скрывать элементы от технологии чтения с экрана, поэтому людей с потерей зрения не беспокоят декоративные изображения кнопок:

<button>
  <svg … aria-hidden="true">...</svg>
  Icon Button
</button>
Chrome DevTools показывает дерево доступности кнопки. Дерево игнорирует изображение кнопки, поскольку для параметра aria-hidden установлено значение true.
Chrome DevTools показывает дерево доступности кнопки. Дерево игнорирует изображение кнопки, поскольку для него установлено значение true.

Стили

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

Адаптивная стратегия настраиваемых свойств

Стратегия пользовательских свойств, используемая в этом вызове GUI, очень похожа на ту, которая используется при построении цветовой схемы . Для адаптивной системы светлых и темных цветов для каждой темы определяется пользовательское свойство, которому присваивается соответствующее имя. Затем используется одно настраиваемое свойство для хранения текущего значения темы и присваивается свойству CSS. Позже одно настраиваемое свойство можно обновить, присвоив ему другое значение, а затем обновив стиль кнопки.

button {
  --_bg-light: white;
  --_bg-dark: black;
  --_bg: var(--_bg-light);

  background-color: var(--_bg);
}

@media (prefers-color-scheme: dark) {
  button {
    --_bg: var(--_bg-dark);
  }
}

Что мне нравится, так это то, что светлые и темные темы декларативны и ясны. Косвенность и абстракция выгружаются в пользовательское свойство --_bg , которое теперь является единственным «реактивным» свойством; --_bg-light и --_bg-dark являются статическими. Также ясно, что светлая тема является темой по умолчанию, а темная применяется только условно.

Подготовка к единообразию дизайна

Общий селектор

Следующий селектор используется для выбора всех типов кнопок и поначалу кажется немного сложным. :where() используется, поэтому настройка кнопки не требует каких-либо особенностей. Кнопки часто адаптируются для альтернативных сценариев, а селектор :where() упрощает задачу. Внутри :where() выбирается каждый тип кнопки, включая ::file-selector-button , которую нельзя использовать внутри :is() или :where() .

:where(
  button,
  input[type="button"],
  input[type="submit"],
  input[type="reset"],
  input[type="file"]
),
:where(input[type="file"])::file-selector-button {
  …
}

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

Акцентный цвет кнопки

Кнопки и значки отправки — отличное место для ярких цветов:

--_accent-light: hsl(210 100% 40%);
--_accent-dark: hsl(210 50% 70%);
--_accent: var(--_accent-light);

Цвет текста кнопки

Цвета текста кнопок не белые и не черные, это затемненные или осветленные версии --_accent , использующие hsl() и придерживающиеся оттенка 210 :

--_text-light: hsl(210 10% 30%);
--_text-dark: hsl(210 5% 95%);
--_text: var(--_text-light);

Цвет фона кнопки

Фон кнопок соответствует тому же шаблону hsl() , за исключением кнопок светлой темы — они окрашены в белый цвет, поэтому на их поверхности они отображаются близко к пользователю или перед другими поверхностями:

--_bg-light: hsl(0 0% 100%);
--_bg-dark: hsl(210 9% 31%);
--_bg: var(--_bg-light);

Фон кнопки хорошо

Этот цвет фона предназначен для того, чтобы поверхность отображалась позади других поверхностей, что полезно для фона ввода файла:

--_input-well-light: hsl(210 16% 87%);
--_input-well-dark: hsl(204 10% 10%);
--_input-well: var(--_input-well-light);

Заполнение кнопок

Расстояние вокруг текста в кнопке определяется с помощью единицы измерения ch — относительной длины к размеру шрифта. Это становится критически важным, когда большие кнопки могут просто пропорционально увеличивать font-size и масштаб кнопок:

--_padding-inline: 1.75ch;
--_padding-block: .75ch;

Граница кнопки

Радиус границы кнопки сохраняется в пользовательском свойстве, поэтому входные данные файла могут соответствовать другим кнопкам. Цвета границ соответствуют установленной адаптивной цветовой системе:

--_border-radius: .5ch;

--_border-light: hsl(210 14% 89%);
--_border-dark: var(--_bg-dark);
--_border: var(--_border-light);

Эффект выделения кнопки при наведении

Эти свойства устанавливают свойство размера для перехода при взаимодействии, а цвет выделения соответствует адаптивной цветовой системе. Мы расскажем, как они взаимодействуют позже в этом посте, но в конечном итоге они используются для эффекта box-shadow :

--_highlight-size: 0;

--_highlight-light: hsl(210 10% 71% / 25%);
--_highlight-dark: hsl(210 10% 5% / 25%);
--_highlight: var(--_highlight-light);

Тень текста кнопки

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

--_ink-shadow-light: 0 1px 0 var(--_border-light);
--_ink-shadow-dark: 0 1px 0 hsl(210 11% 15%);
--_ink-shadow: var(--_ink-shadow-light);

Значок кнопки

Иконки имеют размер в два символа благодаря единице относительной длины ch , что помогает масштабировать значок пропорционально тексту кнопки. Цвет значка основан на --_accent-color для адаптивного цвета внутри темы.

--_icon-size: 2ch;
--_icon-color: var(--_accent);

Тень кнопки

Чтобы тени правильно адаптировались к свету и темноте, им необходимо изменить свой цвет и непрозрачность. Светлые тени лучше всего подходят, когда они неяркие и окрашены в цвет поверхности, на которую накладываются. Тени темной темы должны быть темнее и насыщеннее, чтобы они могли перекрывать более темные цвета поверхности.

--_shadow-color-light: 220 3% 15%;
--_shadow-color-dark: 220 40% 2%;
--_shadow-color: var(--_shadow-color-light);

--_shadow-strength-light: 1%;
--_shadow-strength-dark: 25%;
--_shadow-strength: var(--_shadow-strength-light);

С помощью адаптивных цветов и интенсивности я могу собрать тени двух глубин:

--_shadow-1: 0 1px 2px -1px hsl(var(--_shadow-color)/calc(var(--_shadow-strength) + 9%));

--_shadow-2: 
  0 3px 5px -2px hsl(var(--_shadow-color)/calc(var(--_shadow-strength) + 3%)),
  0 7px 14px -5px hsl(var(--_shadow-color)/calc(var(--_shadow-strength) + 5%));

Кроме того, чтобы придать кнопкам немного трехмерный вид, иллюзию создает тень 1px :

--_shadow-depth-light: 0 1px var(--_border-light);
--_shadow-depth-dark: 0 1px var(--_bg-dark);
--_shadow-depth: var(--_shadow-depth-light);

Переходы кнопок

Следуя шаблону адаптивных цветов, я создаю два статических свойства для хранения параметров системы дизайна:

--_transition-motion-reduce: ;
--_transition-motion-ok:
  box-shadow 145ms ease,
  outline-offset 145ms ease
;
--_transition: var(--_transition-motion-reduce);

Все свойства вместе в селекторе

Все пользовательские свойства в селекторе

:where(
  button,
  input[type="button"],
  input[type="submit"],
  input[type="reset"],
  input[type="file"]
),
:where(input[type="file"])::file-selector-button {
  --_accent-light: hsl(210 100% 40%);
  --_accent-dark: hsl(210 50% 70%);
  --_accent: var(--_accent-light);

--_text-light: hsl(210 10% 30%); --_text-dark: hsl(210 5% 95%); --_text: var(--_text-light);

--_bg-light: hsl(0 0% 100%); --_bg-dark: hsl(210 9% 31%); --_bg: var(--_bg-light);

--_input-well-light: hsl(210 16% 87%); --_input-well-dark: hsl(204 10% 10%); --_input-well: var(--_input-well-light);

--_padding-inline: 1.75ch; --_padding-block: .75ch;

--_border-radius: .5ch; --_border-light: hsl(210 14% 89%); --_border-dark: var(--_bg-dark); --_border: var(--_border-light);

--_highlight-size: 0; --_highlight-light: hsl(210 10% 71% / 25%); --_highlight-dark: hsl(210 10% 5% / 25%); --_highlight: var(--_highlight-light);

--_ink-shadow-light: 0 1px 0 hsl(210 14% 89%); --_ink-shadow-dark: 0 1px 0 hsl(210 11% 15%); --_ink-shadow: var(--_ink-shadow-light);

--_icon-size: 2ch; --_icon-color-light: var(--_accent-light); --_icon-color-dark: var(--_accent-dark); --_icon-color: var(--accent, var(--_icon-color-light));

--_shadow-color-light: 220 3% 15%; --_shadow-color-dark: 220 40% 2%; --_shadow-color: var(--_shadow-color-light); --_shadow-strength-light: 1%; --_shadow-strength-dark: 25%; --_shadow-strength: var(--_shadow-strength-light); --_shadow-1: 0 1px 2px -1px hsl(var(--_shadow-color)/calc(var(--_shadow-strength) + 9%)); --_shadow-2: 0 3px 5px -2px hsl(var(--_shadow-color)/calc(var(--_shadow-strength) + 3%)), 0 7px 14px -5px hsl(var(--_shadow-color)/calc(var(--_shadow-strength) + 5%)) ;

--_shadow-depth-light: hsl(210 14% 89%); --_shadow-depth-dark: var(--_bg-dark); --_shadow-depth: var(--_shadow-depth-light);

--_transition-motion-reduce: ; --_transition-motion-ok: box-shadow 145ms ease, outline-offset 145ms ease ; --_transition: var(--_transition-motion-reduce); }

Кнопки по умолчанию отображаются рядом со светлой и темной темой.

Адаптации темной темы

Значение шаблона статических реквизитов -light и -dark становится ясным, когда установлены реквизиты темной темы:

@media (prefers-color-scheme: dark) {
  :where(
    button,
    input[type="button"],
    input[type="submit"],
    input[type="reset"],
    input[type="file"]
  ),
  :where(input[type="file"])::file-selector-button {
    --_bg: var(--_bg-dark);
    --_text: var(--_text-dark);
    --_border: var(--_border-dark);
    --_accent: var(--_accent-dark);
    --_highlight: var(--_highlight-dark);
    --_input-well: var(--_input-well-dark);
    --_ink-shadow: var(--_ink-shadow-dark);
    --_shadow-depth: var(--_shadow-depth-dark);
    --_shadow-color: var(--_shadow-color-dark);
    --_shadow-strength: var(--_shadow-strength-dark);
  }
}

Это не только хорошо читается, но и потребители этих пользовательских кнопок могут использовать голые реквизиты с уверенностью, что они адаптируются соответствующим образом к предпочтениям пользователя.

Сниженная адаптация к движению

Если для этого посетителя движение нормально, назначьте --_transition для var(--_transition-motion-ok) :

@media (prefers-reduced-motion: no-preference) {
  :where(
    button,
    input[type="button"],
    input[type="submit"],
    input[type="reset"],
    input[type="file"]
  ),
  :where(input[type="file"])::file-selector-button {
    --_transition: var(--_transition-motion-ok);
  }
}

Несколько общих стилей

Для кнопок и полей ввода необходимо настроить inherit шрифтов, чтобы они соответствовали остальным шрифтам страницы; в противном случае они будут стилизованы браузером. Это также относится к letter-spacing . Установка line-height на 1.5 устанавливает размер почтового ящика, чтобы оставить тексту немного места сверху и снизу:

:where(
  button,
  input[type="button"],
  input[type="submit"],
  input[type="reset"],
  input[type="file"]
),
:where(input[type="file"])::file-selector-button {
  /* …CSS variables */

  font: inherit;
  letter-spacing: inherit;
  line-height: 1.5;
  border-radius: var(--_border-radius);
}

Снимок экрана, показывающий кнопки после применения предыдущих стилей.

Стилизация кнопок

Регулировка селектора

Селектор input[type="file"] не является частью кнопки ввода, а псевдоэлемент ::file-selector-button , поэтому я удалил input[type="file"] из списка:

:where(
  button,
  input[type="button"],
  input[type="submit"],
  input[type="reset"],
  input[type="file"]
),
:where(input[type="file"])::file-selector-button {
  
}

Регулировка курсора и сенсорного управления

Сначала я присваиваю курсору стиль pointer , который помогает кнопке указать пользователям мыши, что она интерактивна. Затем я добавляю touch-action: manipulation чтобы при нажатии не нужно было ждать, и наблюдаю за потенциальным двойным щелчком, благодаря чему кнопки начинают работать быстрее:

:where(
  button,
  input[type="button"],
  input[type="submit"],
  input[type="reset"]
),
:where(input[type="file"])::file-selector-button {
  cursor: pointer;
  touch-action: manipulation;
}

Цвета и границы

Затем я настраиваю размер шрифта, фон, текст и цвета границ, используя некоторые из адаптивных пользовательских свойств, установленных ранее:

:where(
  button,
  input[type="button"],
  input[type="submit"],
  input[type="reset"]
),
:where(input[type="file"])::file-selector-button {
  …

  font-size: var(--_size, 1rem);
  font-weight: 700;
  background: var(--_bg);
  color: var(--_text);
  border: 2px solid var(--_border);
}

Снимок экрана, показывающий кнопки после применения предыдущих стилей.

Тени

К кнопкам применено несколько замечательных техник. text-shadow адаптируется к свету и темноте, создавая приятный тонкий вид текста кнопки, красиво расположенного поверх фона. Для box-shadow назначены три тени. Первый, --_shadow-2 , представляет собой обычную тень блока. Вторая тень — это обман зрения, из-за которого кнопка кажется немного скошенной. Последняя тень предназначена для выделения при наведении, первоначально ее размер равен 0, но позже ей будет присвоен размер и изменен переход, поэтому кажется, что она растет от кнопки.

:where(
  button,
  input[type="button"],
  input[type="submit"],
  input[type="reset"]
),
:where(input[type="file"])::file-selector-button {
  …

  box-shadow: 
    var(--_shadow-2),
    var(--_shadow-depth),
    0 0 0 var(--_highlight-size) var(--_highlight)
  ;
  text-shadow: var(--_ink-shadow);
}

Снимок экрана, показывающий кнопки после применения предыдущих стилей.

Макет

Я присвоил кнопке макет flexbox , в частности, inline-flex макет, который соответствует ее содержимому. Затем я центрирую текст и выравниваю дочерние элементы по центру по вертикали и горизонтали. Это поможет значкам и другим элементам кнопок выровняться правильно.

:where(
  button,
  input[type="button"],
  input[type="submit"],
  input[type="reset"]
),
:where(input[type="file"])::file-selector-button {
  …

  display: inline-flex;
  justify-content: center;
  align-items: center;
  text-align: center;
}

Снимок экрана, показывающий кнопки после применения предыдущих стилей.

Расстояние

Для расстояния между кнопками я использовал gap , чтобы братья и сестры не касались друг друга, и логические свойства для заполнения, чтобы расстояние между кнопками работало для всех текстовых макетов.

:where(
  button,
  input[type="button"],
  input[type="submit"],
  input[type="reset"]
),
:where(input[type="file"])::file-selector-button {
  …

  gap: 1ch;
  padding-block: var(--_padding-block);
  padding-inline: var(--_padding-inline);
}

Снимок экрана, показывающий кнопки после применения предыдущих стилей.

Сенсорный интерфейс и мышь

Следующий раздел в основном предназначен для пользователей сенсорных устройств на мобильных устройствах. Первое свойство user-select предназначено для всех пользователей; он предотвращает выделение текста в тексте кнопки. Это особенно заметно на сенсорных устройствах, когда кнопка нажата и удерживается, а операционная система выделяет текст кнопки.

Обычно я обнаружил, что это не тот пользовательский интерфейс с кнопками во встроенных приложениях, поэтому я отключаю его, устанавливая для параметра user-select значение none. Цвета выделения касания ( -webkit-tap-highlight-color ) и контекстные меню операционной системы ( -webkit-touch-callout ) — это другие веб-ориентированные функции кнопок, которые не соответствуют общим ожиданиям пользователей кнопок, поэтому я удаляю их как хорошо.

:where(
  button,
  input[type="button"],
  input[type="submit"],
  input[type="reset"]
),
:where(input[type="file"])::file-selector-button {
  …

  user-select: none;
  -webkit-tap-highlight-color: transparent;
  -webkit-touch-callout: none;
}

Переходы

Адаптивная переменная --_transition присваивается свойству перехода :

:where(
  button,
  input[type="button"],
  input[type="submit"],
  input[type="reset"]
),
:where(input[type="file"])::file-selector-button {
  …

  transition: var(--_transition);
}

При наведении курсора мыши, пока пользователь не нажимает активно, отрегулируйте размер теневого выделения, чтобы придать ему приятный вид фокуса, который кажется растущим изнутри кнопки:

:where(
  button,
  input[type="button"],
  input[type="submit"],
  input[type="reset"]
):where(:not(:active):hover) {
  --_highlight-size: .5rem;
}

После фокусировки увеличьте смещение контура фокуса от кнопки, также придав ему красивый вид фокуса, который кажется растущим изнутри кнопки:

:where(button, input):where(:not(:active)):focus-visible {
  outline-offset: 5px;
}

Иконки

Для обработки значков в селектор добавлен селектор :where() для прямых дочерних элементов SVG или элементов с настраиваемым атрибутом data-icon . Размер значка задается с помощью настраиваемого свойства с использованием встроенных и блочных логических свойств. Устанавливается цвет обводки, а также drop-shadow , соответствующая text-shadow . flex-shrink установлено значение 0 , поэтому значок никогда не сжимается. Наконец, я выбираю значки с линиями и назначаю здесь эти стили с fill: none , round концами линий и соединениями линий:

:where(
  button,
  input[type="button"],
  input[type="submit"],
  input[type="reset"]
) > :where(svg, [data-icon]) {
  block-size: var(--_icon-size);
  inline-size: var(--_icon-size);
  stroke: var(--_icon-color);
  filter: drop-shadow(var(--_ink-shadow));

  flex-shrink: 0;
  fill: none;
  stroke-linecap: round;
  stroke-linejoin: round;
}

Снимок экрана, показывающий кнопки после применения предыдущих стилей.

Настройка кнопок отправки

Я хотел, чтобы кнопки отправки имели слегка продвинутый вид, и добился этого, сделав цвет текста кнопок акцентным цветом:

:where(
  [type="submit"], 
  form button:not([type],[disabled])
) {
  --_text: var(--_accent);
}

Снимок экрана, показывающий кнопки после применения предыдущих стилей.

Настройте кнопки сброса

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

:where([type="reset"]) {
  --_border-light: hsl(0 100% 83%);
  --_highlight-light: hsl(0 100% 89% / 20%);
  --_text-light: hsl(0 80% 50%);
  --_text-dark: hsl(0 100% 89%);
}

Я также подумал, что было бы неплохо, чтобы цвет контура фокуса соответствовал красному акценту. Цвет текста меняется от темно-красного к светло-красному. Я делаю цвет контура соответствующим этому ключевому слову currentColor :

:where([type="reset"]):focus-visible {
  outline-color: currentColor;
}

Снимок экрана, показывающий кнопки после применения предыдущих стилей.

Настроить отключенные кнопки

Слишком часто отключенные кнопки имеют плохой цветовой контраст при попытке подчинить отключенную кнопку, поэтому она кажется менее активной. Я протестировал каждый набор цветов и убедился, что они прошли проверку, изменяя значение яркости HSL до тех пор, пока оценка не пройдет в DevTools или VisBug.

:where(
  button,
  input[type="button"],
  input[type="submit"],
  input[type="reset"]
)[disabled] {
  --_bg: none;
  --_text-light: hsl(210 7% 40%);
  --_text-dark: hsl(210 11% 71%);

  cursor: not-allowed;
  box-shadow: var(--_shadow-1);
}

Снимок экрана, показывающий кнопки после применения предыдущих стилей.

Настройка кнопок ввода файлов

Кнопка ввода файла представляет собой контейнер для диапазона и кнопки. CSS может немного стилизовать входной контейнер, а также вложенную кнопку, но не диапазон. Контейнеру задается max-inline-size , поэтому он не станет больше, чем нужно, а inline-size: 100% позволит себе сжиматься и вмещать контейнеры меньшего размера, чем он есть. Цвет фона установлен на адаптивный цвет, который темнее, чем у других поверхностей, поэтому он выглядит за кнопкой выбора файла.

:where(input[type="file"]) {
  inline-size: 100%;
  max-inline-size: max-content;
  background-color: var(--_input-well);
}

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

:where(input[type="button"]),
:where(input[type="file"])::file-selector-button {
  appearance: none;
}

Наконец, к inline-end кнопки добавляется поле, чтобы отодвинуть текст диапазона от кнопки, создавая некоторое пространство.

:where(input[type="file"])::file-selector-button {
  margin-inline-end: var(--_padding-inline);
}

Снимок экрана, показывающий кнопки после применения предыдущих стилей.

Особые исключения темной темы

Я придал основным кнопкам действий более темный фон для более контрастного текста, придав им более рекламный вид.

@media (prefers-color-scheme: dark) {
  :where(
    [type="submit"],
    [type="reset"],
    [disabled],
    form button:not([type="button"])
  ) {
    --_bg: var(--_input-well);
  }
}

Снимок экрана, показывающий кнопки после применения предыдущих стилей.

Создание вариантов

Ради развлечения и потому, что это практично, я решил показать, как создать несколько вариантов. Один вариант очень яркий, похожий на то, как часто выглядят основные кнопки. Другой вариант — крупный. Последний вариант имеет значок с градиентной заливкой.

Яркая кнопка

Чтобы добиться такого стиля кнопки, я перезаписал базовые реквизиты синими цветами. Хотя это было быстро и легко, адаптивные реквизиты удалены и выглядят одинаково как в светлой, так и в темной темах.

.btn-custom {
  --_bg: linear-gradient(hsl(228 94% 67%), hsl(228 81% 59%));
  --_border: hsl(228 89% 63%);
  --_text: hsl(228 89% 100%);
  --_ink-shadow: 0 1px 0 hsl(228 57% 50%);
  --_highlight: hsl(228 94% 67% / 20%);
}

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

Большая кнопка

Этот стиль кнопки достигается путем изменения пользовательского свойства --_size . Отступы и другие элементы пространства относятся к этому размеру и масштабируются пропорционально новому размеру.

.btn-large {
  --_size: 1.5rem;
}

Большая кнопка отображается рядом с пользовательской кнопкой, примерно в 150 раз больше.

Кнопка со значком

Этот эффект значка не имеет ничего общего с нашими стилями кнопок, но он показывает, как добиться этого с помощью всего лишь нескольких свойств CSS и насколько хорошо кнопка обрабатывает значки, которые не являются встроенными SVG.

[data-icon="cloud"] {
  --icon-cloud: url("https://api.iconify.design/mdi:apple-icloud.svg") center / contain no-repeat;

  -webkit-mask: var(--icon-cloud);
  mask: var(--icon-cloud);
  background: linear-gradient(to bottom, var(--_accent-dark), var(--_accent-light));
}

Кнопка со значком отображается в светлой и темной темах.

Заключение

Теперь, когда вы знаете, как я это сделал, как бы вы‽ 🙂

Давайте разнообразим наши подходы и изучим все способы разработки в Интернете.

Создайте демо, пришлите мне ссылку в Твиттере , и я добавлю ее в раздел ремиксов сообщества ниже!

Ремиксы сообщества

Здесь пока смотреть нечего.

Ресурсы