Обратная связь
Спиннер
Кольцо, показывающее, что машина занята, когда долю выполненного назвать
нечем. Настоящий SVG из двух окружностей, а не рамка на div.
Разметка
<svg class="inst-spinner" viewBox="0 0 16 16" role="status" aria-label="Загрузка">
<circle class="inst-spinner-track" cx="8" cy="8" r="6.5"/>
<circle class="inst-spinner-arc" cx="8" cy="8" r="6.5"/>
</svg>
Использование
Разметка фиксирована: viewBox="0 0 16 16", две окружности радиуса 6.5 в
центре 8 8. Копируется целиком.
<svg class="inst-spinner" viewBox="0 0 16 16" role="status" aria-label="Загрузка">
<circle class="inst-spinner-track" cx="8" cy="8" r="6.5"/>
<circle class="inst-spinner-arc" cx="8" cy="8" r="6.5"/>
</svg>
| Что | Почему нельзя изменить |
|---|---|
viewBox="0 0 16 16" |
Толщина обводки и stroke-dasharray посчитаны под эту систему координат |
r="6.5" cx="8" cy="8" |
Длина окружности при r=6.5 — около 40.8, и 11 30 даёт ровно четверть на дугу |
role="status" |
Иначе появление спиннера ничего не сообщает |
aria-label |
У role="status" должно быть имя: «Загрузка», «Идёт прогон» |
Обе окружности — прямые потомки. Вложить их глубже нельзя: обводка не применится.
Когда использовать
| Используйте | Возьмите другое |
|---|---|
| Ожидание, длительность которого неизвестна | Известна доля выполненного — мера: спиннер не сообщает, сколько осталось |
| Ожидание в маленьком месте: рядом с подписью, в ячейке | Известна форма будущего содержимого — скелетон |
| Занятость части вида | Занятость кнопки — aria-busy="true" на самой кнопке: у неё своё кольцо на месте подписи |
| Общий индикатор работы системы | Состояние конкретного объекта — точка в бейдже: она называет фазу словом |
Устройство
| Элемент | Работа |
|---|---|
inst-spinner |
<svg>. Размер, толщина обводки, вращение |
inst-spinner-track |
Первая окружность — дорожка, --border-control |
inst-spinner-arc |
Вторая окружность — дуга, --accent-mark. Четверть длины через stroke-dasharray: 11 30 |
Обе окружности одинаковы; отличает их только обводка. Дорожка нужна, чтобы кольцо было видно целиком: одна дуга без неё читается как обрывок.
| Свойство | Значение |
|---|---|
| размер | --size-spinner — 13px, в плотном режиме 12px |
| толщина обводки | 2 |
| концы | stroke-linecap: round |
| оборот | 0.7с, линейно, бесконечно |
--accent-mark, а не --accent-text: у тона два передних плана, и метка без
подписи берёт тот, что проверен на 3:1 против дорожки.
Почему SVG
Круг из border-radius на боксе в 13px при дробном DPR растрируется с центром
на полупикселе, и на вращении это видно как биение. У SVG центр задан в
системе координат viewBox, поворот применяется к боксу целиком,
растеризация происходит после — биения нет ни при каком DPR.
Правила
Так Спиннер со словом
Долгое ожидание сопровождается подписью: «Идёт прогон». Кольцо сообщает, что машина занята, но не чем именно.
Не так Спиннер вместо доли
Если долю выполненного назвать можно — это мера. Спиннер, крутящийся четыре минуты, неотличим от зависшего.
Доступность
| Роль | role="status" + aria-label. Спиннер без роли — просто картинка, и для скринридера ожидание не начинается |
| Не единственный носитель | Долгое ожидание сопровождается словами: «Идёт прогон», а не одним крутящимся кольцом |
| Уменьшенное движение | Оборот замедляется до 2.4с, а не останавливается. Схлопнуть анимацию до 0.01ms — это не сжатие длительности, а остановка: спиннер замер бы в случайной фазе, и кит перестал бы показывать, что машина занята |
| Контраст | Дуга --accent-mark держит 3:1 против дорожки, а не только против фона |
| Печать | На листе не печатается: печать статична |
API
| Имя | Значение | Что делает |
|---|---|---|
| класс | ||
inst-spinner | — | Базовый. Ставится на <svg> |
inst-spinner-track | — | Дорожка. Прямой потомок inst-spinner |
inst-spinner-arc | — | Дуга. Прямой потомок inst-spinner |
| токен | ||
--size-spinner | 13px | |
--border-control | light-dark(oklch(0 0 0 / 0.46), oklch(1 0 0 / 0.38)) | |
--accent-mark | light-dark(var(--a-4), var(--a-3)) | |
Почему кит устроен именно так — конституция · Открытый код под MIT, github.com/keshon/instrument