Навигация
Навигация
Список разделов приложения в боковой колонке. Текущий пункт помечен не только цветом: у его края стоит несущая полоса.
Разметка
<nav class="inst-nav" aria-label="Разделы">
<div class="inst-nav-group">
<span class="inst-nav-label">Работа</span>
<a class="inst-nav-item" href="#" aria-current="page">
<svg class="inst-icon" aria-hidden="true"><use href="#i-grid"/></svg>Прогоны
<span class="inst-badge inst-nav-count">7</span></a>
<a class="inst-nav-item" href="#">
<svg class="inst-icon" aria-hidden="true"><use href="#i-list"/></svg>Очередь</a>
<a class="inst-nav-item" href="#">
<svg class="inst-icon" aria-hidden="true"><use href="#i-chart"/></svg>Отчёты</a>
</div>
<div class="inst-nav-group">
<span class="inst-nav-label">Настройка</span>
<a class="inst-nav-item" href="#">
<svg class="inst-icon" aria-hidden="true"><use href="#i-users"/></svg>Доступы</a>
<a class="inst-nav-item" href="#">
<svg class="inst-icon" aria-hidden="true"><use href="#i-settings"/></svg>Параметры</a>
</div>
</nav>
Использование
<nav class="inst-nav" aria-label="Разделы">
<div class="inst-nav-group">
<span class="inst-nav-label">Работа</span>
<a class="inst-nav-item" href="/runs" aria-current="page">Прогоны</a>
<a class="inst-nav-item" href="/queue">Очередь</a>
</div>
</nav>
| Что | Обязательно | Почему |
|---|---|---|
<nav> + aria-label |
да | На экране больше одной навигации (боковая, крошки, вкладки, страницы) — без имени они неразличимы в списке ориентиров |
<a href> у пункта |
да | Навигация — это адрес. Кнопка ломает средний клик, «открыть в новой вкладке» и копирование ссылки |
aria-current="page" |
да | Единственный источник текущего состояния. Класса для него нет |
aria-hidden="true" у иконки |
да | Имя пункта даёт подпись |
Когда использовать
| Используйте | Возьмите другое |
|---|---|
| Постоянный список разделов приложения — обычно внутри оболочки | Переключение видов внутри одного раздела — вкладки: у них другая работа и другое место на экране |
| Плоский список ссылок, сгруппированный подписями | Раскрывающаяся иерархия объектов — дерево: у пункта навигации нет уровня и раскрытия |
Пункт со счётчиком незавершённого — inst-nav-count |
Список действий по клику — меню: навигация ведёт на адрес, меню запускает действие |
| Путь до текущего экрана — рядом, в шапке | Только путь, без списка разделов — крошки |
Устройство
inst-nav-group собирает пункты в смысловой блок, inst-nav-label его
подписывает. Зазор между группами ставится между соседями
(.inst-nav-group + .inst-nav-group), а не вокруг каждой, — иначе первая
группа отступила бы от верхнего края колонки вдвое дальше, чем от боковых.
Подпись — обычное предложение кеглем --text-2xs цветом --text-muted. Капса
здесь нет: подпись не имеет права звучать громче пунктов, которые она
подписывает.
Состояния
Разметка
<nav class="inst-nav" aria-label="Пример текущего пункта">
<div class="inst-nav-group">
<a class="inst-nav-item" href="#" aria-current="page">Прогоны</a>
<a class="inst-nav-item" href="#">Очередь</a>
</div>
</nav>
Метка ставится атрибутом aria-current="page", а не классом. Оформление ловит
селектор [aria-current]:not([aria-current="false"]), поэтому строчка
aria-current="false" у остальных пунктов допустима и ничего не подсвечивает.
| Признак текущего | Что даёт |
|---|---|
| Полоса у внутреннего края | box-shadow: inset шириной --size-marker. Носитель, не зависящий от цвета |
| Поверхность | --surface-selected |
| Цвет подписи | --accent-text |
| Начертание | --weight-medium. Веса 600 и 700 в ките нет |
Поведение
В узкой оболочке
Внутри .inst-shell-aside ниже 60rem колонка разворачивается в горизонтальную
полосу: .inst-nav и .inst-nav-group встают в ряд, .inst-nav-label
скрывается, подписи пунктов перестают переноситься. Это единственное место, где
навигация меняет форму, и делает это оболочка, а не
сама навигация.
Композиции
Счётчик в пункте
Разметка
<nav class="inst-nav" aria-label="Пример со счётчиком">
<div class="inst-nav-group">
<a class="inst-nav-item" href="#">Очередь
<span class="inst-badge inst-nav-count">7</span></a>
<a class="inst-nav-item" href="#">История</a>
</div>
</nav>
inst-nav-count — модификатор положения: margin-inline-start: auto прижимает
бейдж к дальнему краю пункта. Сам бейдж остаётся
бейджем и читает те же токены.
Правила
Так Пункт — ссылка
Навигация ведёт на адрес. Средний клик, «открыть в новой вкладке» и копирование работают бесплатно.
Не так Пункт — кнопка с обработчиком
Она ломает всё перечисленное и требует роли, которой у неё нет.
Так Текущий пункт полосой у края
Носитель, не зависящий от цвета. aria-current сообщает то же самое
скринридеру.
Не так Капс в подписи группы
Подпись не имеет права звучать громче пунктов, которые она подписывает. Различают её кегль и цвет.
Доступность
| Клавиатура | Всё от нативных ссылок: Tab — обход, Enter — переход. Кит ничего не перехватывает, бегущий tabindex навигации не нужен |
| Ориентир | <nav aria-label> — пункт назначения для «перейти к ориентиру» в скринридере |
| Цвет не единственный носитель | Текущий пункт несёт полосу у края и aria-current, а не один только оттенок фона |
| Цель нажатия | Высота пункта — --control-h-md, 32px. В плотности compact она уменьшается: применяйте compact к табличным видам, а не к основной навигации |
| Контраст | Подпись --text-secondary держит 4.5:1 на приглушённой поверхности колонки в шести темах |
| Печать | Боковая колонка не печатается: это хром приложения, а не данные |
API
| Имя | Значение | Что делает |
|---|---|---|
| класс | ||
inst-nav | — | Контейнер списка. Колонка с зазором --space-1 |
inst-nav-group | — | Смысловой блок пунктов |
inst-nav-label | — | Подпись группы |
inst-nav-item | — | Пункт. Ссылка, а не кнопка |
inst-nav-count | — | Прижать счётчик к дальнему краю пункта |
| токен | ||
--control-h-md | 32px | |
--radius-sm | 5px | |
--text-sm | 0.8125rem | |
--text-2xs | 0.6875rem | |
--weight-medium | 500 | |
--space-1 | 2px | |
--space-3 | 6px | |
--gap-inline | var(--space-4) | |
--pad-panel | var(--space-5) | |
--size-marker | 2px | |
--surface-hover | light-dark(oklch(0 0 0 / 0.035), oklch(1 0 0 / 0.045)) | |
--surface-selected | color-mix(in oklab, var(--a-4) 14%, transparent) | |
--accent-solid | var(--a-4) | |
--accent-text | light-dark(var(--a-5), var(--a-3)) | |
--text-secondary | light-dark(var(--n-9), var(--n-5)) | |
--text-muted | light-dark(var(--n-8), var(--n-6)) | |
--dur-1 | 80ms | |
--ease-out | cubic-bezier(0.22, 0.61, 0.36, 1) | |
Почему кит устроен именно так — конституция · Открытый код под MIT, github.com/keshon/instrument