Перейти к содержимому
EN
Масштаб
Плотность
Тема
Акцент

Диаграммы

Тепловая карта

Сколько чего-то случилось за день, по неделям. Поле вкладов, календарь нагрузки, число прогонов. Единственное показание здесь, у которого ось — интенсивность, а не категория и не исход.

Компонент
Разметка
<div class="inst-heatmap" role="img"
     aria-label="Thirteen weeks of runs: busiest in the last fortnight, at 40 a day; nine quiet days in all">
  <span class="inst-heatmap-cell" data-empty></span>
  <span class="inst-heatmap-cell" data-empty></span>
  <span class="inst-heatmap-cell"></span>
  <span class="inst-heatmap-cell" data-level="1"></span>
  <span class="inst-heatmap-cell" data-level="2"></span>
  <span class="inst-heatmap-cell" data-level="1"></span>
  <span class="inst-heatmap-cell" data-level="3"></span>
  <span class="inst-heatmap-cell"></span>
  <span class="inst-heatmap-cell" data-level="2"></span>
  <span class="inst-heatmap-cell" data-level="4"></span>
  <span class="inst-heatmap-cell" data-level="1"></span>
  <span class="inst-heatmap-cell" data-level="3"></span>
  <span class="inst-heatmap-cell" data-level="2"></span>
  <span class="inst-heatmap-cell"></span>
  <span class="inst-heatmap-cell" data-level="1"></span>
  <span class="inst-heatmap-cell" data-level="4"></span>
  <span class="inst-heatmap-cell" data-level="3"></span>
  <span class="inst-heatmap-cell" data-level="2"></span>
  <span class="inst-heatmap-cell" data-level="1"></span>
  <span class="inst-heatmap-cell"></span>
  <span class="inst-heatmap-cell" data-level="3"></span>
  <span class="inst-heatmap-cell" data-level="4"></span>
  <span class="inst-heatmap-cell" data-level="2"></span>
  <span class="inst-heatmap-cell" data-level="1"></span>
  <span class="inst-heatmap-cell" data-level="3"></span>
  <span class="inst-heatmap-cell"></span>
  <span class="inst-heatmap-cell" data-level="2"></span>
  <span class="inst-heatmap-cell" data-level="4"></span>
  <span class="inst-heatmap-cell" data-level="3"></span>
  <span class="inst-heatmap-cell" data-level="1"></span>
  <span class="inst-heatmap-cell" data-level="2"></span>
  <span class="inst-heatmap-cell" data-level="4"></span>
  <span class="inst-heatmap-cell" data-level="4"></span>
  <span class="inst-heatmap-cell" data-level="3"></span>
  <span class="inst-heatmap-cell" data-level="2"></span>
  <span class="inst-heatmap-cell" data-level="1"></span>
  <span class="inst-heatmap-cell" data-level="4"></span>
  <span class="inst-heatmap-cell" data-level="3"></span>
  <span class="inst-heatmap-cell" data-level="2"></span>
  <span class="inst-heatmap-cell" data-level="4"></span>
  <span class="inst-heatmap-cell" data-level="1"></span>
  <span class="inst-heatmap-cell" data-level="3"></span>
  <span class="inst-heatmap-cell" data-level="4"></span>
  <span class="inst-heatmap-cell" data-level="2"></span>
  <span class="inst-heatmap-cell" data-level="3"></span>
  <span class="inst-heatmap-cell" data-level="4"></span>
  <span class="inst-heatmap-cell" data-level="1"></span>
  <span class="inst-heatmap-cell" data-empty></span>
  <span class="inst-heatmap-cell" data-empty></span>
</div>

Контракт

Что Обязательно Почему
role="img" и aria-label с количеством словами да Девяносто пустых <span> для скринридера — девяносто пустых <span>. Здесь имя и есть всё доступное содержимое: в отличие от бейджа или метки изменения, внутри ячейки нет знака, на который можно опереться
Семь строк да Сетка объявлена в семь строк, ячейки текут по ним колонками. Другое число — другая картинка, и ей нужен другой компонент
Свежие справа да Поле читается как время, а время в этом письме идёт вправо — то же правило, что у истории проверок
Уровень из закрытого набора да 1 · 2 · 3 · 4, без атрибута — пустая земля. Пятое значение молча не сделает ничего
data-empty на дни вне диапазона нет Только когда диапазон начинается или кончается посреди недели

Доступность

Цвет здесь единственный носитель, и это честно У интенсивности нет формы, на которую можно отступить: вдвое темнее — не значит другой формы. Всё прочее в ките, что красит метку, даёт ей ещё и знак или слово; здесь этого нельзя, и вся тяжесть переходит на доступное имя
Имя В нём должно стоять количество, а не форма. «Тринадцать недель прогонов, плотнее всего последние две недели, до 40 в день» — имя; «тепловая карта активности» — нет
Принудительные цвета Правила нет, и намеренно. В этом режиме остаётся одна заливка, и сетка одинаковых квадратов продолжала бы утверждать то, чего уже не значит, — то же решение, что у истории проверок
Роль img, не table. Заголовков нет, по ячейкам ничего не читают: это одна картинка с одним именем
Контраст Ступени — метки, а не текст, и порога 4,5:1 на них нет. Важно, чтобы соседние ступени различались, и подмешивание это гарантирует по построению: каждая — четверть пути от земли до акцента

Варианты

Четыре ступени подмешаны, а не объявлены:

background: color-mix(in oklab, var(--accent-solid) 25%, var(--surface-recessed));

--chart-1..6 для этого не годятся и не были взяты. Они категориальные — подобраны так, чтобы ряды различались, — а шкале нужно обратное: ступени, про которые сразу видно, что это один цвет разной силы.

Объявить пять новых токенов означало бы пять значений на пятнадцать ячеек масштаба и плотности и на четыре акцента, и перенастраивать их при каждом сдвиге акцента. Подмешивание от --accent-solid к пустой земле следует за всеми четырьмя акцентами и всеми пятью темами даром, а пустая ячейка берёт --surface-recessed — ту же заливку, на которой стоит плитка метрики.

Ступень Заливка
без атрибута --surface-recessed
data-level="1" 25% акцента
data-level="2" 50% акцента
data-level="3" 75% акцента
data-level="4" --accent-solid

Четыре ступени и не больше. Пять пробовали на экране: средние две перестают различаться на ячейке в 14px. Тепловую карту читают площадью, а площади нужны грубые ступени.

Размеры

Ячейка по умолчанию — --size-icon-sm, 14px против замеренных у образца 14,7, — и едет по лестнице кегля, так что поле растёт вместе с интерфейсом, а не остаётся неподвижным квадратом, пока всё вокруг движется.

<div class="inst-heatmap" style="--cell-size: 10px" role="img" aria-label="…">

Год — это пятьдесят две колонки, и в узкую панель они не встанут. Поле прокручивается со скрытой полосой — тем же приёмом, что полоса вкладок и строка состояния. Оно не переносится: перенесённая тепловая карта — это две картинки.

API

Классы
ИмяЧто делает
класс
inst-heatmapПоле. Семь строк, недели идут по горизонтали, свежие справа
inst-heatmap-cellОдин день. Квадратная метка, внутрь неё ничего не кладут
Атрибуты
ИмяЗначениеЧто делает
атрибут
data-level1 · 2 · 3 · 4Интенсивность дня. Без атрибута — пустая земля: день был, и на нём ничего не насчитали
data-empty—Такого дня нет — диапазон начался или кончился посреди недели. Место остаётся, метки нет
roleimgОбязателен. Поле — картинка данных, и у неё должно быть имя
aria-label—Обязателен, и в нём должно стоять количество словами. В самой картинке цвет — единственный носитель
Токены
ИмяЗначениеОткуда
переменная
--cell-sizeСторона ячейки. Задают на поле, чтобы сделать квадраты крупнее или мельче--size-icon-smзадаёт приложение
токен
--size-icon-smСторона ячейки по умолчанию. У образца она 14,714pxбаза +4: масштаб
--accent-solidВерх шкалы. Ступени ниже подмешиваются к пустой землеvar(--a-4)одно на все ячейки
--surface-recessedПустая ячейка и та земля, в которую подмешана каждая ступеньlight-dark(oklch(0 0 0 / 0.060), oklch(1 0 0 / 0.050))одно на все ячейки
--radius-markvar(--radius-2xs)одно на все ячейки
--space-12pxодно на все ячейки

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