Графики
Спарклайн
Форма изменения рядом с числом. Намеренно без осей, сетки и подписей: это не график, а вторая строка метрики. SVG, без JS.
Разметка
<svg class="inst-sparkline" viewBox="0 0 120 32" preserveAspectRatio="none" aria-hidden="true">
<path class="inst-sparkline-area" d="M0,26 12,22 24,24 36,16 48,18 60,10 72,13 84,7 96,9 108,4 120,6 120,32 0,32Z"/>
<path class="inst-sparkline-line" d="M0,26 12,22 24,24 36,16 48,18 60,10 72,13 84,7 96,9 108,4 120,6"/>
</svg>
Использование
Путь приходит с сервера или из шаблона. Кит отвечает за тона и толщины и
ничего не считает: d — это данные, ровно как ширина заливки у меры.
<svg class="inst-sparkline" viewBox="0 0 120 32" preserveAspectRatio="none" aria-hidden="true">
<path class="inst-sparkline-line" d="M0,26 12,22 24,24 36,16 48,18 60,10 72,13 84,7 96,9 108,4 120,6"/>
</svg>
| Что | Обязательно | Почему |
|---|---|---|
viewBox |
да | Без него SVG не масштабируется, и путь уезжает за границы |
aria-hidden="true" или role="img" с aria-label |
да | Третьего не дано: либо картинка декоративна и число рядом несёт значение, либо у неё есть имя |
Прямые дети <svg> |
да | Правила кита написаны через & >: узел, завёрнутый в <g>, потеряет тон и толщину |
inst-sparkline-area и -last |
нет | Заливка и точка последнего значения необязательны |
preserveAspectRatio="none" — намеренно: спарклайн тянется по ширине
контейнера, а высоту держит фиксированной. Именно поэтому штрих объявлен
немасштабируемым — иначе растянутая линия становилась бы толще слева направо.
Когда использовать
| Используйте | Возьмите другое |
|---|---|
| Тренд одного ряда под числом — направление, а не значения | График, у которого спрашивают значения — как только понадобились оси, это уже полноценный график и он заслуживает своего места, а не строки под метрикой |
| Ряд из десятка-другого точек за понятный период | Доля от целого — мера или кольцо: у спарклайна нет потолка |
| Одна линия | Два и более рядов — им нужна легенда, а легенде нужен график с осями |
| Число уже показано рядом — метрика | Ряд, который читают по строкам — таблица: точные значения читаются цифрами, а не формой |
Устройство
| Узел | Обязателен | Работа |
|---|---|---|
inst-sparkline |
да | <svg> с viewBox. Ширина 100%, высота — четверть сверх контрола |
inst-sparkline-line |
да | Линия. vector-effect: non-scaling-stroke, поэтому растяжение по ширине не утолщает штрих |
inst-sparkline-area |
нет | Заливка под линией. Тот же путь, замкнутый вниз до нижнего края |
inst-sparkline-last |
нет | Точка последнего значения |
Разметка
<svg class="inst-sparkline" viewBox="0 0 120 32" preserveAspectRatio="none" aria-hidden="true">
<path class="inst-sparkline-area" d="M0,26 12,22 24,24 36,16 48,18 60,10 72,13 84,7 96,9 108,4 120,6 120,32 0,32Z"/>
<path class="inst-sparkline-line" d="M0,26 12,22 24,24 36,16 48,18 60,10 72,13 84,7 96,9 108,4 120,6"/>
<circle class="inst-sparkline-last" cx="120" cy="6" r="2.5"/>
</svg>
Порядок узлов внутри SVG — порядок отрисовки: заливка первой, линия поверх, точка последней. Поменяли местами — линия ушла под собственную заливку.
Заливка под линией — единственный разрешённый в ките градиент, и он не
градиент: сплошной тон с альфой 0.12. Настоящий градиент сообщал бы, что
величина меняется по вертикали, чего нет.
Точка последнего значения несёт кольцо цвета поверхности, поэтому остаётся различимой, когда наезжает на собственную линию. Её координаты — те же, что у последней точки пути: расходятся они молча.
Варианты
Разметка
<svg class="inst-sparkline" viewBox="0 0 120 32" preserveAspectRatio="none" aria-hidden="true" style="--series:var(--chart-2)">
<path class="inst-sparkline-area" d="M0,10 12,14 24,11 36,18 48,15 60,21 72,19 84,24 96,22 108,27 120,25 120,32 0,32Z"/>
<path class="inst-sparkline-line" d="M0,10 12,14 24,11 36,18 48,15 60,21 72,19 84,24 96,22 108,27 120,25"/>
</svg>
Все три узла читают --series и без него берут --chart-1. Переменная
ставится один раз на <svg>. Линия, заливка и точка одного ряда не имеют
права разъехаться по цвету.
Номер ряда — часть контракта: первый ряд всегда --chart-1. Правила
категориального цвета целиком — на странице палитры.
/* Ряд задаётся снаружи, а не переписыванием stroke */
.my-chart-2 { --series: var(--chart-2); }
Композиции
Под метрикой
Разметка
<div class="inst-card inst-stack inst-stack--tight">
<div class="inst-metric-label">Токенов в сутки</div>
<div class="inst-metric-value">184<span class="inst-metric-unit"> тыс.</span></div>
<svg class="inst-sparkline" viewBox="0 0 120 32" preserveAspectRatio="none" aria-hidden="true">
<path class="inst-sparkline-area" d="M0,26 12,22 24,24 36,16 48,18 60,10 72,13 84,7 96,9 108,4 120,6 120,32 0,32Z"/>
<path class="inst-sparkline-line" d="M0,26 12,22 24,24 36,16 48,18 60,10 72,13 84,7 96,9 108,4 120,6"/>
</svg>
<div class="inst-metric-label">За 11 суток</div>
</div>
В ячейке таблицы
<td>
<svg class="inst-sparkline" viewBox="0 0 120 32" preserveAspectRatio="none" aria-hidden="true">
<path class="inst-sparkline-line" d="M0,26 24,24 48,18 72,13 96,9 120,6"/>
</svg>
</td>
Правила
Так Спарклайн про направление
Он отвечает на «растёт или падает». Число рядом отвечает на «сколько».
Не так Спарклайн, у которого спрашивают значения
Как только понадобились оси, это полноценный график — и он заслуживает своего места, а не строки под метрикой.
Так --series один раз на svg
Линия, заливка и точка одного ряда не имеют права разъехаться по цвету.
Не так Второй ряд в спарклайне
Два ряда требуют легенды, а легенда требует графика с осями.
Доступность
| Имя | Декоративный спарклайн — aria-hidden="true", и тогда значение обязано быть в тексте рядом. Самостоятельный — role="img" плюс aria-label с итогом словами: «Рост с 92 до 184 тысяч за 11 суток» |
| Цвет не единственный носитель | Спарклайн один — цвет ничего не кодирует. Два ряда одним цветом различить нельзя, поэтому двух рядов у спарклайна и нет |
| Контраст | Линия берёт категориальный тон и держит 3:1 против поверхности в шести темах. Заливка с альфой 0.12 — декоративная и на порог не проверяется: значение несёт линия |
| Толщина | vector-effect: non-scaling-stroke — штрих остаётся 1.5 при любом растяжении, поэтому не истончается ниже видимого |
| Движение | Анимации нет вовсе: график — это состояние, а не процесс |
API
| Имя | Значение | Что делает |
|---|---|---|
| класс | ||
inst-sparkline | — | <svg>. Обязателен: остальные классы работают только внутри него |
inst-sparkline-line | — | Линия ряда, штрих 1.5, немасштабируемый |
inst-sparkline-area | — | Заливка под линией, альфа 0.12 |
inst-sparkline-last | — | Точка последнего значения с кольцом поверхности |
| переменная | ||
--series | --chart-1 | Приёмник тона: линия, заливка и последняя точка читают её |
| токен | ||
--chart-1 | light-dark(oklch(0.520 0.150 280), oklch(0.700 0.130 280)) | Тон ряда №1. Порядок — часть контракта |
--chart-2 | light-dark(oklch(0.600 0.150 320), oklch(0.760 0.120 320)) | Тон ряда №2. Порядок — часть контракта |
--chart-3 | light-dark(oklch(0.550 0.140 355), oklch(0.720 0.120 355)) | Тон ряда №3. Порядок — часть контракта |
--chart-4 | light-dark(oklch(0.650 0.120 55), oklch(0.800 0.110 55)) | Тон ряда №4. Порядок — часть контракта |
--chart-5 | light-dark(oklch(0.580 0.130 115), oklch(0.740 0.120 115)) | Тон ряда №5. Порядок — часть контракта |
--chart-6 | light-dark(oklch(0.620 0.110 190), oklch(0.780 0.100 190)) | Тон ряда №6. Порядок — часть контракта |
--control-h-md | 32px | Высота считается от неё: ×1.25 |
--surface-raised | light-dark(var(--n-0), var(--n-12)) | Кольцо последней точки — цвет поверхности под ней |
--size-marker | 2px | Толщина кольца последней точки |
Почему кит устроен именно так — конституция · Открытый код под MIT, github.com/keshon/instrument