Перейти к содержимому
Плотность
Тема

Основания

Иконки

Inline SVG на сетке 16, обводка currentColor толщиной 1.5. Иконка наследует цвет текста, поэтому в ките нет ни одного иконочного варианта тона.

Компонент
Разметка
<button class="inst-btn" type="button">
  <svg class="inst-icon" aria-hidden="true"><use href="#i-refresh"/></svg>
  Обновить
</button>
<button class="inst-btn inst-btn--icon" type="button" aria-label="Настройки">
  <svg class="inst-icon" aria-hidden="true"><use href="#i-settings"/></svg>
</button>

Использование

Пять требований. Нарушение любого видно сразу — иконка либо съезжает по базовой линии, либо перестаёт менять цвет вместе с текстом.

Требование Почему
Inline SVG (или <use> из спрайта) Иконка обязана менять цвет вместе с текстом. <img> и background-image этого не умеют
stroke: currentColor, fill: none Один файл работает во всех шести темах и во всех тонах без единого варианта
Толщина обводки 1.5 Общая с хайрлайном оптика: 1 читается как царапина, 2 — как жирный шрифт рядом с 400
Сетка 16 (viewBox="0 0 16 16") Совпадает с --size-icon. Другая сетка даёт другую оптическую толщину при том же stroke-width
Цвет не живёт в data-URI Форма рисуется маской, цвет приходит токеном. Иначе тема иконку не догоняет

Класс .inst-icon объявляет это плюс flex: none — иначе иконка сжимается первой, когда подпись рядом не помещается.

.inst-icon {
  flex: none;
  inline-size: var(--size-icon);
  block-size: var(--size-icon);
  fill: none;
  stroke: currentColor;
  stroke-width: 1.5;
  stroke-linecap: round;
  stroke-linejoin: round;
}

Разметка

Случай Разметка
Иконка рядом с подписью aria-hidden="true" на <svg>. Имя даёт подпись; без атрибута скринридер прочитает элемент дважды или невнятно
Иконка вместо подписи aria-hidden="true" на <svg> и aria-label на кнопке. Кнопка без текста иначе не имеет доступного имени
Иконка внутри ссылки То же самое: имя несёт ссылка
Иконка как единственный носитель информации Так не бывает. Добавьте подпись или aria-label — цвет и форма не имеют права быть единственным носителем

Когда использовать

Используйте Возьмите другое
Иконка рядом с подписью — второй носитель смысла Иконка вместо подписи в списке действий — в меню подпись обязательна, см. меню в поповере
Иконка вместо подписи там, где действие общеизвестно и есть aria-label Иконка вместо статуса — статус несёт точку и слово, см. бейдж
Иконка как второй признак тона у сноски Цвет как единственный носитель — закон кита. Тон описан в цвете
<use href="#i-*"> из спрайта Текстовый глиф ⟳ или ▶ — разные метрики в разных шрифтах и риск подстановки эмодзи
.inst-icon--sm в шевроне и мелком контроле Своя ширина инлайном — размер приходит из ролей: --size-icon и --size-chevron

Устройство

Спрайт объявляется один раз на документ, до первого <use>. Сам он невидим и скрыт от скринридера.

<svg class="inst-sprite" aria-hidden="true" focusable="false" xmlns="http://www.w3.org/2000/svg">
  <symbol id="i-refresh" viewBox="0 0 16 16">…</symbol>
</svg>

Использование:

<svg class="inst-icon" aria-hidden="true"><use href="#i-refresh"/></svg>

У <svg> в месте использования нет собственного viewBox: он приходит из <symbol>. Собственный viewBox на внешнем элементе перебивает символьный и обрезает часть глифов.

Доступные id

Набор намеренно маленький: иконка заводится под задачу, а не «на всякий случай». Список печатается из самого спрайта — набранный руками он уже однажды разошёлся с китом на четыре символа.

Плюс 81 символов страниц справочника (i-p-*) — они принадлежат сайту, а не киту.

Размеры

Класс Размер Токен Где
inst-icon 16px --size-icon Умолчание: кнопка, пункт меню, сноска, шапка
inst-icon--sm 10px --size-chevron Шеврон селекта, метка внутри мелкого контрола

Третьего размера нет. Иконка крупнее 16 в этом ките — это либо иллюстрация (её место в пустом состоянии), либо ошибка иерархии: иконка обслуживает данные и обязана быть тише их.

Размеры не реагируют на плотность намеренно — см. плотность: иконка носитель значения, и на 12px обводка 1.5 превращается в кляксу.

Композиции

<div class="inst-menu" role="menu">
  <button class="inst-menu-item" type="button" role="menuitem">
    <svg class="inst-icon" aria-hidden="true"><use href="#i-refresh"/></svg>Перезапустить
  </button>
  <button class="inst-menu-item" type="button" role="menuitem">
    <svg class="inst-icon" aria-hidden="true"><use href="#i-copy"/></svg>Скопировать id
  </button>
</div>

Иконочная кнопка квадратная: ширина берётся из той же высоты контрола, поэтому квадрат сохраняется во всех трёх размерах и во всех плотностях. Прямоугольник 32×28 — самый частый признак самодельного кита.

Доступность

Декоративная иконка aria-hidden="true" всегда, без исключений: имя даёт подпись или aria-label
Кнопка без текста Обязана нести aria-label. Иначе у неё нет доступного имени вовсе
Контраст Иконка наследует цвет текста, поэтому проходит те же пороги. Иконка на --text-faint — декорация и не имеет права нести смысл
Цвет не единственный носитель Иконка сама по себе второй носитель тона (сноска), но не первый: тон обязан быть ещё и в тексте
Режим принудительных цветов Обводка currentColor переживает сброс, потому что цвет берётся от текста, а не задан своим значением
Фокус На <svg> фокуса не бывает: фокусируется кнопка или ссылка вокруг него, focusable="false" у спрайта убирает старый IE-артефакт

Настройка

  1. Нарисуйте на сетке 16×16, только обводкой.
  2. Уберите fill, stroke, stroke-width из самих путей — они придут из .inst-icon.
  3. Скруглите концы и стыки не в файле, а классом: stroke-linecap и stroke-linejoin уже там.
  4. Положите в спрайт как <symbol id="i-…" viewBox="0 0 16 16">.
  5. Проверьте на тёмной теме: обводка на 1.5 в тёмной теме оптически тоньше, и слишком мелкая деталь исчезает.

API

ИмяЗначениеЧто делает
класс
inst-spriteСпрайт в теле страницы. Прячет его: без правила это видимый блок в 150px
inst-iconБазовый. Обязателен всегда
модификатор
inst-icon--smУменьшение до --size-chevron
токен
--size-icon16px
--size-chevron10px
--stroke1px

Почему кит устроен именно так — конституция · Открытый код под MIT, github.com/keshon/instrument