Графики
Спарклайн
Форма изменения рядом с числом. Намеренно без осей, сетки и подписей: это не график, а вторая строка метрики. SVG, без JS.
Разметка
<svg class="inst-sparkline" viewBox="0 0 120 32" preserveAspectRatio="none" aria-hidden="true">
<path class="inst-sparkline-area" d="M0,27 5,24 10,26 15,21 20,23 25,18 30,20 35,14 40,17 45,11 50,15 55,9 60,13 65,6 70,10 75,5 80,12 85,8 90,11 95,4 100,7 105,3 110,6 115,4 120,2 120,32 0,32Z"/>
<path class="inst-sparkline-line" d="M0,27 5,24 10,26 15,21 20,23 25,18 30,20 35,14 40,17 45,11 50,15 55,9 60,13 65,6 70,10 75,5 80,12 85,8 90,11 95,4 100,7 105,3 110,6 115,4 120,2"/>
</svg>
Контракт
Путь приходит с сервера или из шаблона. Библиотека отвечает за тона и толщины и
ничего не считает: d — это данные, ровно как ширина заливки у меры.
| Что | Обязательно | Почему |
|---|---|---|
viewBox |
да | Без него SVG не масштабируется, и путь уезжает за границы |
aria-hidden="true" или role="img" с aria-label |
да | Третьего не дано: либо картинка декоративна и число рядом несёт значение, либо у неё есть имя |
Прямые дети <svg> |
да | Правила библиотеки написаны через & >: узел, завёрнутый в <g>, потеряет тон и толщину |
inst-sparkline-area и -last |
нет | Заливка и точка последнего значения необязательны |
preserveAspectRatio="none": спарклайн тянется по ширине
контейнера, а высоту держит фиксированной. Именно поэтому штрих объявлен
немасштабируемым — иначе растянутая линия становилась бы толще слева направо.
Доступность
| Имя | Декоративный спарклайн — aria-hidden="true", и тогда значение обязано быть в тексте рядом. Самостоятельный — role="img" плюс aria-label с итогом словами: «Рост с 92 до 184 тысяч за 11 суток» |
| Цвет не единственный носитель | Спарклайн один — цвет ничего не кодирует. Два ряда одним цветом различить нельзя, поэтому двух рядов у спарклайна и нет |
| Контраст | Линия берёт категориальный тон и держит 3:1 против поверхности в пяти темах. Заливка с альфой 0.12 — декоративная и на порог не проверяется: значение несёт линия |
| Толщина | vector-effect: non-scaling-stroke — штрих остаётся 1.5 при любом растяжении, поэтому не истончается ниже видимого |
| Движение | Анимации нет вовсе: график — это состояние, а не процесс |
Устройство
| Узел | Обязателен | Работа |
|---|---|---|
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,27 5,24 10,26 15,21 20,23 25,18 30,20 35,14 40,17 45,11 50,15 55,9 60,13 65,6 70,10 75,5 80,12 85,8 90,11 95,4 100,7 105,3 110,6 115,4 120,2 120,32 0,32Z"/>
<path class="inst-sparkline-line" d="M0,27 5,24 10,26 15,21 20,23 25,18 30,20 35,14 40,17 45,11 50,15 55,9 60,13 65,6 70,10 75,5 80,12 85,8 90,11 95,4 100,7 105,3 110,6 115,4 120,2"/>
<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,27 5,24 10,26 15,21 20,23 25,18 30,20 35,14 40,17 45,11 50,15 55,9 60,13 65,6 70,10 75,5 80,12 85,8 90,11 95,4 100,7 105,3 110,6 115,4 120,2 120,32 0,32Z"/>
<path class="inst-sparkline-line" d="M0,27 5,24 10,26 15,21 20,23 25,18 30,20 35,14 40,17 45,11 50,15 55,9 60,13 65,6 70,10 75,5 80,12 85,8 90,11 95,4 100,7 105,3 110,6 115,4 120,2"/>
</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>
API
| Имя | Что делает |
|---|---|
| класс | |
inst-sparkline | <svg>. Обязателен: остальные классы работают только внутри него |
inst-sparkline-line | Линия ряда, штрих 1.5, немасштабируемый |
inst-sparkline-area | Заливка под линией, альфа 0.12 |
inst-sparkline-last | Точка последнего значения с кольцом поверхности |
| Имя | Значение | Откуда |
|---|---|---|
| переменная | ||
--seriesПриёмник тона: линия, заливка и последняя точка читают её | --accent-mark | задаёт приложение |
| токен | ||
--chart-1Тон ряда №1. Порядок — часть контракта | light-dark(oklch(0.520 0.150 292), oklch(0.700 0.130 292)) | одно на все ячейки |
--chart-2Тон ряда №2. Порядок — часть контракта | light-dark(oklch(0.600 0.150 320), oklch(0.760 0.120 320)) | одно на все ячейки |
--chart-3Тон ряда №3. Порядок — часть контракта | light-dark(oklch(0.550 0.140 355), oklch(0.720 0.120 355)) | одно на все ячейки |
--chart-4Тон ряда №4. Порядок — часть контракта | light-dark(oklch(0.650 0.120 55), oklch(0.800 0.110 55)) | одно на все ячейки |
--chart-5Тон ряда №5. Порядок — часть контракта | light-dark(oklch(0.580 0.130 115), oklch(0.740 0.120 115)) | одно на все ячейки |
--chart-6Тон ряда №6. Порядок — часть контракта | light-dark(oklch(0.620 0.110 190), oklch(0.780 0.100 190)) | одно на все ячейки |
--control-h-mdВысота считается от неё: ×1.25 | 32px | база +14: плотность · масштаб |
--surface-raisedКольцо последней точки — цвет поверхности под ней | light-dark(var(--n-0), var(--n-12)) | база +1: тема |
--size-markerТолщина кольца последней точки | 2px | база +4: масштаб |
Почему кит устроен именно так — конституция · Открытый код под MIT, github.com/keshon/instrument