Кнопки - это наиболее важные интерактивные компоненты, используемые пользователями для выполнения действий. Проектирование кнопок означает создание инструментария, позволяющего взаимодействовать с интерфейсом.
В этой заключительной статье нашей серии о системах проектирования мы рассмотрим, как создать систему кнопок в CSS.
Настройка основного стиля кнопки
Отправной точкой проектирования кнопок в CSS является определение базового стиля, который можно применить ко всем компонентам кнопок:
:root {
--btn-font-size: 1em;
--btn-radius: 0.25em;
}
.btn {
display: inline-flex;
position: relative;
white-space: nowrap;
text-decoration: none;
line-height: 1;
padding: var(--space-xs) var(--space-sm);
border-radius: var(--btn-radius);
font-size: var(--btn-font-size);
color: var(--color-link);
}
Основной стиль включает те свойства, которые вы не планируете переписывать, создавая в дальнейшем варианты кнопок, а также стиль сброса, чтобы предотвратить общие проблемы стиля. Например:
position: relative;
используется, если вы хотите создать интерактивные значки, где значок заменяет текстовую метку.white-space: nowrap;
предотвращает перенос текстовой метки на следующую строку.text-decoration: none;
удаляет текстовое украшение, если вы применяете класс .btn
к ссылке.
Абстрагирование размера шрифта и радиуса границы особенно полезно на ранних этапах создания системы проектирования, когда часто происходят изменения. Значения заполнения задаются в соответствии с нашей системой интервалов, так что они автоматически реагируют.
Варианты стиля
Варианты стиля - это разные стили, используемые для передачи значений кнопкам. Они обычно включают:
.btn - primary
→ основной стиль кнопки вызова к действию.
.btn--secondary
→ тонкая версия стиля, используемая для вторичных действий или в комбинации с кнопкой основного / акцент.
.btn--accent
→ используется для привлечения внимания к кнопке (например, деструктивные действия).
.btn [disabled]
→ используется для обратной связи, чтобы определить кнопку, которая отключена.
/* themes */
.btn--primary {
// main button
background-color: var(--btn-primary-bg);
color: var(--btn-primary-label);
@include fontSmooth;
&:visited {
color: var(--btn-primary-label);
}
&:hover {
background-color: var(--btn-primary-hover);
}
&:active {
background-color: var(--btn-primary-active);
}
}
.btn--secondary {
// subtle version, used for secondary actions or in combo with primary/accent button
background-color: var(--btn-secondary-bg);
color: var(--btn-secondary-label);
&:visited {
color: var(--btn-secondary-label);
}
&:active {
background-color: var(--btn-secondary-active);
}
}
.btn--accent {
// used to draw special attention to the button (e.g. destructive actions)
background-color: var(--btn-accent-bg);
color: var(--btn-accent-label);
@include fontSmooth;
&:visited {
color: var(--btn-accent-label);
}
&:hover {
background-color: var(--btn-accent-hover);
}
&:active {
background-color: var(--btn-accent-active);
}
}
/* feedback */
.btn[disabled] {
cursor: not-allowed;
background-color: var(--btn-disabled-bg);
color: var(--btn-disabled-label);
box-shadow: none;
opacity: 0.6;
&:visited {
color: var(--btn-disabled-label);
}
}
Несколько важных замечаний: во-первых, все цвета определены в отдельном файле _colors.scss. Работая над нашей картой, мы решили иметь единственный источник правды, хранящий все цвета и темы. Для получения дополнительной информации вы можете проверить нашу статью по этой теме.
Вот загляните, как цвета кнопок определены в нашем файле _colors.scss:
/* --------------------------------
Colors
-------------------------------- */
:root {
/* main colors */
--color-primary: #286bf4;
--color-primary-light: color-mod(var(--color-primary) tint(15%));
--color-primary-dark: color-mod(var(--color-primary) shade(15%));
--color-primary-bg: color-mod(var(--color-primary) alpha(20%));
--color-accent: #f5414f;
--color-accent-light: color-mod(var(--color-accent) tint(15%));
--color-accent-dark: color-mod(var(--color-accent) shade(10%));
--color-accent-bg: color-mod(var(--color-accent) alpha(20%));
/* shades - generated using chroma.js - 12 steps */
--black: #1d1d21;
--gray-10: #2e2e31;
--gray-6: #7b7a7d;
--gray-4: #a5a5a6;
--gray-3: #bbbbbc;
--gray-2: #d1d0d2;
--gray-1: #e8e7e8;
--white: white;
/* buttons */
--btn-primary-bg: var(--color-primary);
--btn-primary-hover: var(--color-primary-light);
--btn-primary-active: var(--color-primary-dark);
--btn-primary-label: var(--white);
--btn-secondary-bg: var(--gray-1);
--btn-secondary-active: var(--gray-2);
--btn-secondary-label: var(--gray-10);
--btn-accent-bg: var(--color-accent);
--btn-accent-hover: var(--color-accent-light);
--btn-accent-active: var(--color-accent-dark);
--btn-accent-label: var(--white);
--btn-disabled-bg: var(--gray-2);
--btn-disabled-label: var(--gray-10);
}
Смеситель fontSmooth
используется для улучшения рендеринга шрифтов, когда светлый текст используется на темном фоне. В наше время этот стиль применяется ко всем элементам HTML. После некоторых исследований мы решили создать mixin
для ориентации на определенные элементы, а не включать этот стиль в наш глобальный файл _reset.scss.
// edit font rendering -> tip: use for light text on dark backgrounds
@mixin fontSmooth {
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
Поскольку наша инфраструктура должна быть настраиваемой отправной точкой для любых веб-проектов, я старался не включать в нее какой-то несущественный стиль. Например, я не включал тени или эффекты фокусировки, потому что они будут отличаться в каждом проекте. Предоставление стиля по умолчанию заставило бы наших разработчиков инфраструктуры искать и перезаписывать вещи, а не иметь свободу основываться на вещах.
Варианты размеров
В дополнение к изменениям стиля, система кнопок также должна включать изменения размера. Чтобы быть ясным: я не имею в виду кнопки реагирования, которые применяются на разных контрольных точках, но для того, чтобы иметь классы утилит для создания кнопок разных размеров, независимо от размера видового экрана.
Хорошим подходом к созданию таких вариаций является определение переменных размера (с использованием функции CSS calc ()
), а затем применение этих переменных к классам служебных данных размера:
:root {
--btn-font-size: 1em;
--btn-sm: calc(var(--btn-font-size) - 0.2em);
--btn-md: calc(var(--btn-font-size) + 0.2em);
--btn-lg: calc(var(--btn-font-size) + 0.4em);
--btn-radius: var(--radius);
}
/* button size */
.btn--sm {
font-size: var(--btn-sm);
}
.btn--md {
font-size: var(--btn-md);
}
.btn--lg {
font-size: var(--btn-lg);
}
.btn--full-width {
display: flex;
width: 100%;
justify-content: center;
}
Использование значков в кнопках
Мы используем класс .icon-text-aligner
для выравнивания текстовых меток и значков в пределах кнопок:
<button class="btn btn--primary icon-text-aligner">
<span>Button</span>
<svg class="icon"><use href="#icon-arrow-right" xlink:href="#icon-arrow-right"/></svg>
</button>
Так как у нас может быть кнопка, которая не включает текст, а только значок, рекомендуется настроить таргетинг на .icon
внутри базового класса .btn
и убедиться, что значок наследует цвет по умолчанию на ярлыке кнопки (даже если метка не там):
.btn {
display: inline-flex;
position: relative;
white-space: nowrap;
text-decoration: none;
line-height: 1;
padding: var(--space-xs) var(--space-sm);
border-radius: var(--btn-radius);
font-size: var(--btn-font-size);
color: var(--color-link);
transition: .2s;
&:active {
transition: none;
}
.icon {
/* icon inherits color of button label */
color: inherit;
flex-shrink: 0;
}
}
Группы кнопок
Другим классическим вариантом использования в глобальном файле CSS является группа кнопок. К счастью, Flexbox упрощает установку кнопок в простой одномерной компоновке:
/* buttons group */
.btns {
display: flex;
flex-wrap: wrap;
margin-bottom: calc(-1 * var(--space-xs));
> * {
margin-right: var(--space-xs);
margin-bottom: var(--space-xs);
&:last-of-type {
margin-right: 0;
}
}
}
Кнопки реагирования
Если вы до сих пор следовали нашим сериям по системам проектирования, вы знаете, что наш отзывчивый подход основан на двух переменных CSS: --text-base-size
и -space-unit
. Обновление этих переменных с помощью медиа-запросов создает каскадный эффект, который влияет на всю систему проектирования, включая кнопки.
В наших рамках компоненты кнопок, как и все другие компоненты, основаны на единицах, соответствующих единицам. Текущий размер шрифта обновляется переменной -text-base-size
. Поэтому наши кнопки уже реагируют, поэтому нет необходимости создавать один мультимедийный запрос в файле _buttons.scss.
_buttons.scss
Соединяя все это, вот наш глобальный файл SCSS для наших кнопок:
/* --------------------------------
Buttons
-------------------------------- */
:root {
--btn-font-size: 1em;
--btn-sm: calc(var(--btn-font-size) - 0.2em);
--btn-md: calc(var(--btn-font-size) + 0.2em);
--btn-lg: calc(var(--btn-font-size) + 0.4em);
--btn-radius: var(--radius);
}
.btn {
display: inline-flex;
position: relative;
white-space: nowrap;
text-decoration: none;
line-height: 1;
padding: var(--space-xs) var(--space-sm);
border-radius: var(--btn-radius);
font-size: var(--btn-font-size);
color: var(--color-link);
transition: .2s;
&:active {
transition: none;
}
.icon {
/* icon inherits color of button label */
color: inherit;
flex-shrink: 0;
}
}
/* themes */
.btn--primary {
// main button
background-color: var(--btn-primary-bg);
color: var(--btn-primary-label);
@include fontSmooth;
&:visited {
color: var(--btn-primary-label);
}
&:hover {
background-color: var(--btn-primary-hover);
}
&:active {
background-color: var(--btn-primary-active);
}
}
.btn--secondary {
// subtle version, used for secondary actions or in combo with primary/accent button
background-color: var(--btn-secondary-bg);
color: var(--btn-secondary-label);
&:visited {
color: var(--btn-secondary-label);
}
&:active {
background-color: var(--btn-secondary-active);
}
}
.btn--accent {
// used to draw special attention to the button (e.g. destructive actions)
background-color: var(--btn-accent-bg);
color: var(--btn-accent-label);
@include fontSmooth;
&:visited {
color: var(--btn-accent-label);
}
&:hover {
background-color: var(--btn-accent-hover);
}
&:active {
background-color: var(--btn-accent-active);
}
}
/* feedback */
.btn[disabled] {
cursor: not-allowed;
background-color: var(--btn-disabled-bg);
color: var(--btn-disabled-label);
box-shadow: none;
opacity: 0.6;
&:visited {
color: var(--btn-disabled-label);
}
}
/* button size */
.btn--sm {
font-size: var(--btn-sm);
}
.btn--md {
font-size: var(--btn-md);
}
.btn--lg {
font-size: var(--btn-lg);
}
.btn--full-width {
display: flex;
width: 100%;
justify-content: center;
}
/* buttons group */
.btns {
display: flex;
flex-wrap: wrap;
margin-bottom: calc(-1 * var(--space-xs));
> * {
margin-right: var(--space-xs);
margin-bottom: var(--space-xs);
&:last-of-type {
margin-right: 0;
}
}
}