Основания
Ритм и форма
Шаг 4px, шкала нарочно разрежена сверху — чтобы «чуть побольше» просто не было в наборе. Компонент берёт отступ не из шкалы, а из яруса ролей: именно он даёт плотности одну точку, через которую она перенастраивает всё сразу.
Разметка
<div class="inst-card inst-stack inst-stack--tight">
<div class="inst-card-title">worldgen-01</div>
<div class="inst-card-sub">Генерация биомов, 3 прохода</div>
<div class="inst-cluster">
<button class="inst-btn inst-btn--sm" type="button">Повторить</button>
<span class="inst-badge" data-tone="ok"><span class="inst-dot"></span>готово</span>
</div>
</div>
Использование
Отступ берётся по назначению: --pad-panel, не --space-5. Роль
перенастраивается плотностью, ступень шкалы — нет.
.my-panel {
padding: var(--pad-panel);
gap: var(--gap-row);
border-radius: var(--radius-lg);
}
| Что | Обязательно | Почему |
|---|---|---|
| Роль, а не ступень | да | --pad-panel меняется вместе с плотностью, --space-5 — нет. Компонент с зашитой ступенью ломается первым |
| Зазор ставит контейнер | да | Отступ у элемента складывается с внутренним отступом родителя по вертикали и не складывается по горизонтали |
| Радиус из словаря | да | Пять ступеней, названных размером контрола. Своё число разъезжается с соседями на стыке |
Хайрлайн через --hairline |
да | На дробном DPR это 0.5px, и линия остаётся линией, а не полосой |
Когда использовать
| Используйте | Возьмите другое |
|---|---|
Роль: --pad-panel, --gap-row, --row-pad-y |
--space-* в компоненте — плотность перенастраивает ярус ролей, а не шкалу. Список ролей целиком — токены |
Зазор между соседями — примитив потока .inst-stack / .inst-cluster / .inst-grid |
Утилита отступа (mt-3, p-2) — их в ките нет и не будет, см. поток |
Шаг зазора именем: обычный, --tight, --loose |
Свой зазор числом — «чуть побольше» не должно быть доступным решением. Три шага описаны в потоке |
Радиус по вложенности: контейнер lg, контрол внутри sm |
Один радиус на всё — внутренний элемент с радиусом контейнера выпирает. Пример — карточка |
--hairline для линии, которая не должна кричать |
1px числом — на 2x-дисплее это вдвое толще нужного, см. хайрлайн |
Шкала
Компонент обращается к ней только через ярус ролей. Прямое использование
--space-* допустимо в приложении и в примитивах потока.
| Токен | Значение |
|---|---|
--space-1 |
2px |
--space-2 |
4px |
--space-3 |
6px |
--space-4 |
8px |
--space-5 |
12px |
--space-6 |
16px |
--space-7 |
24px |
--space-8 |
32px |
--space-9 |
48px |
--space-10 |
64px |
Разрежение сверху — не экономия, а ограничение: между 32 и 48 нет ступени, и это правильно. Шаг, которого нет, нельзя выбрать по ошибке.
Отступы по назначению
Ярус ролей. Значения ниже — для плотности по умолчанию; в compact и
comfortable они другие, см. плотность.
| Токен | По умолчанию | Где |
|---|---|---|
--pad-panel |
--space-5 |
Внутренний отступ панели |
--pad-card |
--space-6 |
Внутренний отступ карточки |
--pad-cell-x |
--space-4 |
Горизонтальный отступ ячейки таблицы |
--row-pad-y |
--space-3 |
Вертикальный отступ строки: задача, ячейка, пункт меню |
--gap-row |
--space-3 |
Зазор между строками в стопке |
--gap-inline |
--space-4 |
Зазор между соседями в ряду |
--control-pad-sm |
--space-4 |
Горизонтальный отступ мелкого контрола |
--control-pad-md |
--space-5 |
Он же у среднего |
--control-pad-lg |
--space-6 |
Он же у крупного |
Зазор ставится между соседями. margin-block на
каждом элементе складывается с внутренним отступом контейнера сверху и снизу,
но не по бокам — и подсветка отступает от рамки по вертикали вдвое дальше, чем
по горизонтали. Правильная форма — X + X { margin-block-start }.
Размеры глифов и жёлобов
Тот же ярус ролей: у каждого размера есть имя, поэтому в компонентах нет констант.
| Токен | По умолчанию | Что это |
|---|---|---|
--size-dot |
6px | Точка состояния |
--size-check |
15px | Чекбокс и радио |
--size-switch-w / --size-switch-h |
30 / 18px | Дорожка свитча |
--size-thumb |
14px | Бегунок |
--size-track |
4px | Дорожка слайдера |
--size-meter |
6px | Толщина меры |
--size-spinner |
13px | Кольцо занятости |
--size-icon |
16px | Иконка — см. иконки |
--size-chevron |
10px | Шеврон, мелкая иконка |
--size-marker |
2px | Несущая метка: полоса вкладки, край сноски, край текущего пункта навигации. Тоньше не видно, толще — спорит с текстом |
--size-gutter |
14px | Левый жёлоб строки задачи |
--size-indent |
14px | Шаг вложенности дерева |
--label-col |
92px | Колонка подписей инспектора |
Радиусы
Пять ступеней, и выбирает их вложенность.
| Токен | Значение | Для чего |
|---|---|---|
--radius-xs |
3px | Инлайн: бейджи, образцы, торцы полос |
--radius-sm |
5px | Контролы внутри карточки или панели |
--radius-md |
7px | Отдельно стоящие контролы: кнопка, поле, поповер |
--radius-lg |
11px | Карточки, панели, модалка |
--radius-full |
999px | Дорожки, бегунки, аватар |
Правило вложенных радиусов
Внутреннему углу нужен радиус меньше внешнего, иначе внутренний элемент выглядит выпирающим из контейнера. Оптически внутренний радиус равен внешнему минус толщина промежутка, и шкала уже подобрана так, чтобы соседняя ступень вниз давала верный результат при отступе из яруса ролей.
<!-- панель lg → контрол внутри sm; отдельно стоящий контрол — md -->
<div class="inst-panel">
<div class="inst-panel-body">
<div class="inst-card"></div>
</div>
</div>
Скругление на односторонней рамке — всегда баг. border-left вместе с
border-radius даёт обрубок дуги: сноска и край текущего пункта навигации
несут прямой угол намеренно.
Хайрлайн
Браузер никогда не рисует рамку тоньше одного физического пикселя. На
1x-дисплее 0.5px и 1px дают одно и то же, а отрицательное поле в
-0.5px при нарисованной рамке в 1px оставляет полпикселя двойной линии.
Поэтому хайрлайн включается только там, где ему есть куда поместиться.
| Токен | Значение | Когда |
|---|---|---|
--hairline |
1px |
Плотность пикселей ниже 1.5dppx |
--hairline |
0.5px |
@media (min-resolution: 1.5dppx) |
--stroke |
1px |
Линия, которая обязана остаться видимой всегда |
Шов группы кнопок сделан снятой рамкой у соседа — по той же причине.
Правила
| Токен | Значение | Что это |
|---|---|---|
--aside-w |
232px | Боковая колонка оболочки |
--container-max |
76rem | Потолок ширины рабочей области. В rem, потому что мера читаемости, а не геометрия |
--split-side |
18rem | Желаемая ширина боковой части сплита |
--split-main |
30rem | Ниже этого основная часть переносится вниз |
--col-min |
260px | Минимальная колонка адаптивной сетки |
Доступность
| Цели нажатия | Отступ контрола участвует в размере цели наравне с высотой. В compact мелкие контролы уходят ниже 24px по WCAG 2.5.8 — см. плотность |
Геометрия в px |
Отступы и радиусы не растут вместе с кеглем: плотный инструментальный вид иначе расползается. Растёт текст, а высоты работают как минимум |
| Разделение групп | Ритм — тоже носитель смысла: четыре метрики без рамок читаются как одна группа именно зазором |
| Хайрлайн | На 1x-дисплее остаётся 1px: линия, которая должна быть видна, видна на любом экране |
API
| Имя | Значение | Что делает |
|---|---|---|
| класс | ||
inst-stack | — | Вертикальный поток, зазор --gap-inline |
inst-cluster | — | Горизонтальный поток с переносом |
inst-cluster-spacer | — | Разрыв в ряду: всё после него уходит вправо |
inst-grid | — | Адаптивная сетка auto-fit по --col-min |
| модификатор | ||
inst-stack--tight | — | Шаг зазора |
inst-stack--loose | — | Шаг зазора |
inst-cluster--tight | — | Шаг зазора |
inst-cluster--loose | — | Шаг зазора |
inst-grid--tight | — | Другая минимальная колонка |
inst-grid--wide | — | Другая минимальная колонка |
| токен | ||
--space-1 | 2px | Шаг шкалы |
--space-2 | 4px | Шаг шкалы |
--space-3 | 6px | Шаг шкалы |
--space-4 | 8px | Шаг шкалы |
--space-5 | 12px | Шаг шкалы |
--space-6 | 16px | Шаг шкалы |
--space-7 | 24px | Шаг шкалы |
--space-8 | 32px | Шаг шкалы |
--space-9 | 48px | Шаг шкалы |
--space-10 | 64px | Шаг шкалы |
--pad-panel | var(--space-5) | Внутренний отступ панели |
--pad-card | var(--space-6) | Внутренний отступ карточки |
--pad-cell-x | var(--space-4) | Горизонтальный отступ ячейки |
--row-pad-y | var(--space-3) | Вертикальный отступ строки |
--gap-row | var(--space-3) | Зазор между строками |
--gap-inline | var(--space-4) | Зазор в ряду |
--control-pad-sm | var(--space-4) | Отступ малого контрола |
--control-pad-md | var(--space-5) | Отступ базового контрола |
--control-pad-lg | var(--space-6) | Отступ крупного контрола |
--radius-xs | 3px | Инлайн: бейджи, образцы, торцы полос |
--radius-sm | 5px | Контролы внутри карточки |
--radius-md | 7px | Отдельно стоящие контролы |
--radius-lg | 11px | Карточки и панели |
--radius-full | 999px | Круг и капсула |
--hairline | 1px | Волосок. На 1x равен пикселю, выше — половине |
--stroke | 1px | Линия, которая обязана быть видна всегда |
--aside-w | 232px | Боковая колонка оболочки |
--container-max | 76rem | Потолок ширины рабочей области |
--split-side | 18rem | Желаемая ширина боковой части сплита |
--split-main | 30rem | Порог переноса основной части |
--col-min | 260px | Минимальная колонка адаптивной сетки |
| Имя | Значение | Что делает |
|---|---|---|
| класс | ||
inst-stack | — | Вертикальный поток, зазор --gap-inline |
inst-cluster | — | Горизонтальный поток с переносом |
inst-cluster-spacer | — | Разрыв в ряду: всё после него уходит вправо |
inst-grid | — | Адаптивная сетка auto-fit по --col-min |
| модификатор | ||
inst-stack--tight | — | Шаг зазора |
inst-stack--loose | — | Шаг зазора |
inst-cluster--tight | — | Шаг зазора |
inst-cluster--loose | — | Шаг зазора |
inst-grid--tight | — | Другая минимальная колонка |
inst-grid--wide | — | Другая минимальная колонка |
| токен | ||
--space-1 | 2px | Шаг шкалы |
--space-2 | 4px | Шаг шкалы |
--space-3 | 6px | Шаг шкалы |
--space-4 | 8px | Шаг шкалы |
--space-5 | 12px | Шаг шкалы |
--space-6 | 16px | Шаг шкалы |
--space-7 | 24px | Шаг шкалы |
--space-8 | 32px | Шаг шкалы |
--space-9 | 48px | Шаг шкалы |
--space-10 | 64px | Шаг шкалы |
--pad-panel | var(--space-5) | Внутренний отступ панели |
--pad-card | var(--space-6) | Внутренний отступ карточки |
--pad-cell-x | var(--space-4) | Горизонтальный отступ ячейки |
--row-pad-y | var(--space-3) | Вертикальный отступ строки |
--gap-row | var(--space-3) | Зазор между строками |
--gap-inline | var(--space-4) | Зазор в ряду |
--control-pad-sm | var(--space-4) | Отступ малого контрола |
--control-pad-md | var(--space-5) | Отступ базового контрола |
--control-pad-lg | var(--space-6) | Отступ крупного контрола |
--radius-xs | 3px | Инлайн: бейджи, образцы, торцы полос |
--radius-sm | 5px | Контролы внутри карточки |
--radius-md | 7px | Отдельно стоящие контролы |
--radius-lg | 11px | Карточки и панели |
--radius-full | 999px | Круг и капсула |
--hairline | 1px | Волосок. На 1x равен пикселю, выше — половине |
--stroke | 1px | Линия, которая обязана быть видна всегда |
--aside-w | 232px | Боковая колонка оболочки |
--container-max | 76rem | Потолок ширины рабочей области |
--split-side | 18rem | Желаемая ширина боковой части сплита |
--split-main | 30rem | Порог переноса основной части |
--col-min | 260px | Минимальная колонка адаптивной сетки |
Почему кит устроен именно так — конституция · Открытый код под MIT, github.com/keshon/instrument