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

Графики

Мера

Одна величина относительно своего потолка: расход бюджета, доля времени, готовность шага. Один тон, потому что сравнивается одна величина.

Компонент
Рендер18,2 с
Разметка
<div class="inst-meter-row"><span>Рендер</span><span class="inst-meter-value">18,2 с</span></div>
<div class="inst-meter" role="progressbar" aria-label="Рендер"
     aria-valuenow="43" aria-valuemin="0" aria-valuemax="100">
  <div class="inst-meter-fill" style="inline-size:43%"></div>
</div>

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

<div class="inst-meter" role="progressbar" aria-label="Расход токенов"
     aria-valuenow="74" aria-valuemin="0" aria-valuemax="100">
  <div class="inst-meter-fill" style="inline-size:74%"></div>
</div>
Что Обязательно Почему
role="progressbar" на inst-meter да Без роли значение существует только в пикселях ширины и недоступно ни скринридеру, ни тесту
aria-valuenow aria-valuemin aria-valuemax да, у определённой Определённая полоса обязана сообщать число, а не картинку
aria-label или aria-labelledby да У полосы нет своего текста. Подпись из inst-meter-row связывается через aria-labelledby, если она видима
отсутствие aria-valuenow у indeterminate да Ровно так объявляется «занято, длительность неизвестна»
inst-meter-fill внутри да Дорожка сама ничего не заливает

style="inline-size:43%" на заливке — не оформление, а значение: величина приходит с сервера и физически не может жить в статическом CSS. Всё остальное — высота, радиус, дорожка, цвет — берётся из кита. Заливка едет с переходом (--dur-3), поэтому обновление значения читается как движение, а не как скачок.

Протяжённость дорожки показывает внутренний хайрлайн. Сама дорожка при этом остаётся светлой, и заливка держит на ней 3:1.

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

Используйте Возьмите другое
Доля от известного целого: 43 из 100, 184 320 из 250 000 Сравнение нескольких рядов между собойлегенда и категориальная палитра: четыре меры разного цвета врут о различии, которого нет
Расход исчерпаемого ресурса — бюджет прогона Доля в компактном месте, где нужен кругкольцо
Незавершённая работа неизвестной длительности — data-state="indeterminate" Форма изменения во времениспарклайн: у меры нет истории
Полоса под числом в карточке — метрика Ожидание, у которого нет величины вовсескелетон или спиннер

Варианты

Определённая и неопределённая

Это два разных компонента. Определённая мера обещает остаток, неопределённая — только факт работы.

Пример
Разметка
<div class="inst-meter" role="progressbar" aria-label="Расход токенов"
     aria-valuenow="74" aria-valuemin="0" aria-valuemax="100">
  <div class="inst-meter-fill" style="inline-size:74%"></div>
</div>
<div class="inst-meter" data-state="indeterminate" role="progressbar" aria-label="Индексация">
  <div class="inst-meter-fill"></div>
</div>
Определённая Неопределённая
Атрибут без data-state data-state="indeterminate"
Ширина заполнения приходит из данных задаёт кит — 35%, и она метётся
aria-valuenow обязателен отсутствует, и это сообщение
Что обещает читателю остаток и темп что работа идёт

Отсутствие aria-valuenow — не упущение разметки, а её содержание: по нему скринридер объявляет «занято» вместо ложного процента. Поставить valuenow неопределённой полосе — то же самое, что нарисовать её застрявшей на 90%.

Тон

Пример
Разметка
<div class="inst-meter" role="progressbar" aria-label="Расход"
     aria-valuenow="62" aria-valuemin="0" aria-valuemax="100">
  <div class="inst-meter-fill" style="inline-size:62%"></div>
</div>
<div class="inst-meter" data-tone="warn" role="progressbar" aria-label="Расход у порога"
     aria-valuenow="88" aria-valuemin="0" aria-valuemax="100">
  <div class="inst-meter-fill" style="inline-size:88%"></div>
</div>
<div class="inst-meter" data-tone="error" role="progressbar" aria-label="Лимит превышен"
     aria-valuenow="100" aria-valuemin="0" aria-valuemax="100">
  <div class="inst-meter-fill" style="inline-size:100%"></div>
</div>

data-tone ставится на inst-meter, заливка читает --tone-mark и больше ничего о тоне не знает. Тон здесь — исключение, а не приправа: полоса без атрибута берёт акцент, и это правильный ответ в подавляющем большинстве случаев. Тон меняется, когда меняется смысл величины — перешли порог, исчерпали лимит.

Композиции

Строка с подписью и величиной

Пример
Физика11,5 с
Индексациядлительность неизвестна
Разметка
<div>
  <div class="inst-meter-row"><span>Физика</span><span class="inst-meter-value">11,5 с</span></div>
  <div class="inst-meter" role="progressbar" aria-label="Физика"
       aria-valuenow="27" aria-valuemin="0" aria-valuemax="100">
    <div class="inst-meter-fill" style="inline-size:27%"></div>
  </div>
</div>
<div>
  <div class="inst-meter-row"><span>Индексация</span><span class="inst-meter-value">длительность неизвестна</span></div>
  <div class="inst-meter" data-state="indeterminate" role="progressbar" aria-label="Индексация">
    <div class="inst-meter-fill"></div>
  </div>
</div>

inst-meter-row разносит подпись и величину по краям и стоит над полосой: число слева от полосы съедало бы её длину, а длина здесь и есть данные.

В бюджете прогона

В контексте
Токены 184 320 из 250 000
При текущем темпе хватит ещё примерно на 3 прогона.
Разметка
<div class="inst-budget">
  <div class="inst-budget-row">
    <span>Токены</span>
    <span class="inst-budget-value">184 320 <span class="inst-budget-of">из 250 000</span></span>
  </div>
  <div class="inst-meter" role="progressbar" aria-label="Расход токенов"
       aria-valuenow="74" aria-valuemin="0" aria-valuemax="100">
    <div class="inst-meter-fill" style="inline-size:74%"></div>
  </div>
  <div class="inst-budget-note">При текущем темпе хватит ещё примерно на 3 прогона.</div>
</div>

Правила

Так Неизвестная длительность — indeterminate

Полоса метётся, aria-valuenow отсутствует, рядом стоят слова «длительность неизвестна».

Не так Неизвестный прогресс определённой полосой

Полоса, которая врёт про остаток, хуже честного «неизвестно»: первую пользователь ждёт, вторую — планирует.

Так Тон по смыслу величины

Перешли порог — warn, исчерпали лимит — error. Без атрибута полоса берёт акцент, и это верно почти всегда.

Не так Тон по номеру полосы в списке

Четыре меры разного цвета сообщают различие, которого нет. Сравнение рядов — это легенда и категориальная палитра.

Доступность

Роль progressbar — в примере, а не в примечании: aria-valuenow без роли невалиден
Имя aria-label обязателен всегда: полоса — это заливка без текста
Неопределённость Отсутствие aria-valuenow объявляется как «занято». Слова «длительность неизвестна» рядом дублируют это для тех, кто смотрит
Цвет не единственный носитель Величина продублирована числом в inst-meter-value. Полоса, у которой значение есть только в цвете и длине, недоступна дальтонику и невоспроизводима на печати
Контраст Заливка держит 3:1 против дорожки. Дорожка не несущая, поэтому 3:1 к поверхности с неё не спрашивается
Уменьшенное движение Метение неопределённой полосы замедляется до 3 с, а не гаснет: остановленный индикатор сообщает «ничего не происходит», когда происходит
Режим принудительных цветов Дорожка получает рамку CanvasText, заливка переживает сброс (forced-color-adjust: none), потому что несёт значение
Печать Метение на бумаге бессмысленно: заливка неопределённой полосы не печатается, остаётся пустая дорожка и подпись

API

ИмяЗначениеЧто делает
класс
inst-meterДорожка. Носитель роли и значений
inst-meter-fillЗаливка. Обязательный единственный ребёнок
inst-meter-rowСтрока «подпись — величина» над полосой
inst-meter-valueВеличина в этой строке, приглушённым цветом
атрибут
data-stateindeterminate — единственное значение у меры. Без атрибута полоса определённая
data-toneneutral running ok warn error. Закрыт. Без атрибута — акцент
токен
--size-meter6px
--radius-xs3px
--tracklight-dark(oklch(0 0 0 / 0.10), oklch(1 0 0 / 0.16))
--hairline1px
--borderlight-dark(oklch(0 0 0 / 0.12), oklch(1 0 0 / 0.11))
--tone-markvar(--text-secondary)
--accent-marklight-dark(var(--a-4), var(--a-3))
--text-sm0.8125rem
--space-36px
--gap-inlinevar(--space-4)
--dur-3220ms
--ease-outcubic-bezier(0.22, 0.61, 0.36, 1)

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