Графики
Мера
Одна величина относительно своего потолка: расход бюджета, доля времени, готовность шага. Один тон, потому что сравнивается одна величина.
Разметка
<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 и больше
ничего о тоне не знает. Тон здесь — исключение, а не приправа: полоса без
атрибута берёт акцент, и это правильный ответ в подавляющем большинстве
случаев. Тон меняется, когда меняется смысл величины — перешли порог,
исчерпали лимит.
Композиции
Строка с подписью и величиной
Разметка
<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 разносит подпись и величину по краям и стоит над полосой:
число слева от полосы съедало бы её длину, а длина здесь и есть данные.
В бюджете прогона
Разметка
<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-state | — | indeterminate — единственное значение у меры. Без атрибута полоса определённая |
data-tone | — | neutral running ok warn error. Закрыт. Без атрибута — акцент |
| токен | ||
--size-meter | 6px | |
--radius-xs | 3px | |
--track | light-dark(oklch(0 0 0 / 0.10), oklch(1 0 0 / 0.16)) | |
--hairline | 1px | |
--border | light-dark(oklch(0 0 0 / 0.12), oklch(1 0 0 / 0.11)) | |
--tone-mark | var(--text-secondary) | |
--accent-mark | light-dark(var(--a-4), var(--a-3)) | |
--text-sm | 0.8125rem | |
--space-3 | 6px | |
--gap-inline | var(--space-4) | |
--dur-3 | 220ms | |
--ease-out | cubic-bezier(0.22, 0.61, 0.36, 1) | |
Почему кит устроен именно так — конституция · Открытый код под MIT, github.com/keshon/instrument