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

Навигация

Навигация

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

Компонент
Разметка
<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-md32px
--radius-sm5px
--text-sm0.8125rem
--text-2xs0.6875rem
--weight-medium500
--space-12px
--space-36px
--gap-inlinevar(--space-4)
--pad-panelvar(--space-5)
--size-marker2px
--surface-hoverlight-dark(oklch(0 0 0 / 0.035), oklch(1 0 0 / 0.045))
--surface-selectedcolor-mix(in oklab, var(--a-4) 14%, transparent)
--accent-solidvar(--a-4)
--accent-textlight-dark(var(--a-5), var(--a-3))
--text-secondarylight-dark(var(--n-9), var(--n-5))
--text-mutedlight-dark(var(--n-8), var(--n-6))
--dur-180ms
--ease-outcubic-bezier(0.22, 0.61, 0.36, 1)

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