Отображение данных
Календарь
Статическая сетка дат. Кит отвечает за то, чтобы сетка была сеткой, а сегодняшний день был помечен не только цветом.
Разметка
<div class="inst-calendar" role="grid" aria-label="Август 2026">
<span class="inst-calendar-dow" aria-hidden="true">пн</span>
<span class="inst-calendar-dow" aria-hidden="true">вт</span>
<span class="inst-calendar-dow" aria-hidden="true">ср</span>
<span class="inst-calendar-dow" aria-hidden="true">чт</span>
<span class="inst-calendar-dow" aria-hidden="true">пт</span>
<span class="inst-calendar-dow" aria-hidden="true">сб</span>
<span class="inst-calendar-dow" aria-hidden="true">вс</span>
<span class="inst-calendar-day" role="gridcell" data-outside="true">27</span>
<span class="inst-calendar-day" role="gridcell" data-outside="true">28</span>
<span class="inst-calendar-day" role="gridcell" data-outside="true">29</span>
<span class="inst-calendar-day" role="gridcell" data-outside="true">30</span>
<span class="inst-calendar-day" role="gridcell" data-outside="true">31</span>
<span class="inst-calendar-day" role="gridcell">1</span>
<span class="inst-calendar-day" role="gridcell">2</span>
<span class="inst-calendar-day" role="gridcell">3</span>
<span class="inst-calendar-day" role="gridcell">4</span>
<span class="inst-calendar-day" role="gridcell">5</span>
<span class="inst-calendar-day" role="gridcell">6</span>
<span class="inst-calendar-day" role="gridcell">7</span>
<span class="inst-calendar-day" role="gridcell">8</span>
<span class="inst-calendar-day" role="gridcell">9</span>
<span class="inst-calendar-day" role="gridcell">10</span>
<span class="inst-calendar-day" role="gridcell">11</span>
<span class="inst-calendar-day" role="gridcell" data-today="true">12</span>
<span class="inst-calendar-day" role="gridcell">13</span>
<span class="inst-calendar-day" role="gridcell">14</span>
<span class="inst-calendar-day" role="gridcell">15</span>
<span class="inst-calendar-day" role="gridcell">16</span>
<span class="inst-calendar-day" role="gridcell">17</span>
<span class="inst-calendar-day" role="gridcell" aria-selected="true">18</span>
<span class="inst-calendar-day" role="gridcell">19</span>
<span class="inst-calendar-day" role="gridcell">20</span>
<span class="inst-calendar-day" role="gridcell">21</span>
<span class="inst-calendar-day" role="gridcell" aria-disabled="true">22</span>
<span class="inst-calendar-day" role="gridcell" aria-disabled="true">23</span>
<span class="inst-calendar-day" role="gridcell">24</span>
<span class="inst-calendar-day" role="gridcell">25</span>
<span class="inst-calendar-day" role="gridcell">26</span>
<span class="inst-calendar-day" role="gridcell">27</span>
<span class="inst-calendar-day" role="gridcell">28</span>
<span class="inst-calendar-day" role="gridcell" aria-disabled="true">29</span>
<span class="inst-calendar-day" role="gridcell" aria-disabled="true">30</span>
<span class="inst-calendar-day" role="gridcell">31</span>
<span class="inst-calendar-day" role="gridcell" data-outside="true">1</span>
<span class="inst-calendar-day" role="gridcell" data-outside="true">2</span>
<span class="inst-calendar-day" role="gridcell" data-outside="true">3</span>
<span class="inst-calendar-day" role="gridcell" data-outside="true">4</span>
<span class="inst-calendar-day" role="gridcell" data-outside="true">5</span>
<span class="inst-calendar-day" role="gridcell" data-outside="true">6</span>
</div>
Использование
<div class="inst-calendar" role="grid" aria-label="Август 2026">
<span class="inst-calendar-dow" aria-hidden="true">пн</span>
…
<span class="inst-calendar-day" role="gridcell"
aria-label="2 августа 2026, суббота">2</span>
</div>
| Что | Обязательно | Почему |
|---|---|---|
role="grid" на сетке, role="gridcell" на дне |
да | Без ролей календарь озвучивается как поток из 42 чисел |
aria-label с месяцем и годом на сетке |
да | «2» без месяца не является датой |
aria-label с полной датой на каждом дне |
да | Одно число вне контекста бесполезно |
aria-hidden="true" на днях недели |
да | «пн», «вт» дублируют информацию из полной даты ячейки |
Когда использовать
| Используйте | Возьмите другое |
|---|---|
| Обзор месяца, где важна раскладка по неделям | Ввод одной даты в форме — <input type="date">, он стилизован китом и даёт нативный пикер с клавиатурой |
| Плотность событий по дням видна с одного взгляда | Диапазон дат в фильтре — два нативных поля даты |
| Выбор дня внутри уже открытого месяца | События во времени без сетки — лента |
| — | Длительности параллельных процессов — дорожки |
Состояния
| Атрибут | Что значит |
|---|---|
| нет атрибутов | Обычный день текущего месяца |
data-today="true" |
Сегодня. Подчёркивание и полужирное начертание |
data-outside="true" |
День соседнего месяца, добивающий неделю |
aria-selected="true" |
Выбран |
aria-disabled="true" |
Недоступен. Прозрачность 0.5, нажатие снято |
Сегодня помечено подчёркиванием, а не только цветом. Это выполнение закона
кита: цвет не имеет права быть единственным носителем состояния. Подчёркивание
рисуется внутренней тенью толщиной --size-marker — той же, что несёт полосу
активной вкладки и край текущего пункта навигации.
JS
Подключите модуль один раз на страницу — инициализировать компоненты по отдельности не нужно, кит работает делегированием и видит узлы, пришедшие позже.
<script type="module" src="src/kit.js"></script>
Что делает кит
Ничего. Календарь — это сетка дат, а даты кит не считает: месяц, високосный год, первый день недели и локаль — данные, а не оформление. Кит рисует сетку и состояния дня, разметку строит приложение.
Что должно сделать приложение
| Что | Что для этого есть в ките |
|---|---|
| Построить сетку месяца | Классы дня и aria-label с полной датой |
| Отметить выбранное | aria-selected="true" на дне |
| Переключить месяц | Кнопки шапки — обычные <button> |
grid.addEventListener('click', (e) => {
const day = e.target.closest('[data-date]');
if (!day || day.getAttribute('aria-disabled') === 'true') return;
for (const d of grid.querySelectorAll('[aria-selected="true"]')) {
d.setAttribute('aria-selected', 'false');
}
day.setAttribute('aria-selected', 'true');
onPick(day.dataset.date);
});
Дата хранится в data-date в формате ISO, а не читается из подписи: подпись
зависит от локали, и парсить её обратно — способ получить разъезд на первом же
языке.
Правила
Так Сегодня помечено начертанием
Подчёркивание и полужирное — два признака помимо цвета. Скринридеру это
сообщается словом в aria-label.
Не так Календарь для ввода одной даты
<input type="date"> уже стилизован китом и даёт нативный пикер с
клавиатурой и мобильным колесом.
Так Полная дата в aria-label дня
«2 августа 2026, суббота». Одно число вне контекста ничего не значит.
Не так Сетка без ролей
Без role="grid" и role="gridcell" это поток из 42 чисел.
Доступность
| Роли | role="grid" на сетке, role="gridcell" на дне, role="row" на неделе. Без них календарь озвучивается как поток из 42 чисел |
| Имя | aria-label с месяцем и годом на сетке. «2» без месяца не является датой |
| Дни недели | aria-hidden="true": сокращения «пн», «вт» дублируют информацию, которую скринридер берёт из полной даты ячейки |
| Полная дата | Каждый день несёт aria-label с полной датой («2 августа 2026, суббота»). Одно число вне контекста бесполезно |
| Клавиатура | Стрелки по дням, PageUp/PageDown по месяцам, Home/End по неделе, бегущий tabindex. Поведение — на приложении |
| Сегодня | Не только цвет: подчёркивание плюс начертание. Скринридеру это надо сообщить словом в aria-label |
| Цель нажатия | Высота — --control-h-sm, то есть 26px. В плотности compact — 22px, ниже 24px по WCAG 2.5.8 |
| Табличные цифры | Включены, поэтому колонки чисел стоят ровно независимо от разрядности |
API
| Имя | Значение | Что делает |
|---|---|---|
| класс | ||
inst-calendar | — | Сетка на семь колонок |
inst-calendar-dow | — | Шапка: сокращение дня недели |
inst-calendar-day | — | Ячейка дня |
| атрибут | ||
data-today | true | на inst-calendar-day |
data-outside | true | там же |
aria-selected | true · false | там же |
aria-disabled | true | там же |
| токен | ||
--space-1 | 2px | |
--space-2 | 4px | |
--control-h-sm | 26px | |
--radius-sm | 5px | |
--text-xs | 0.75rem | |
--text-2xs | 0.6875rem | |
--text-muted | light-dark(var(--n-8), var(--n-6)) | |
--surface-hover | light-dark(oklch(0 0 0 / 0.035), oklch(1 0 0 / 0.045)) | |
--surface-selected | color-mix(in oklab, var(--a-4) 14%, transparent) | |
--accent-text | light-dark(var(--a-5), var(--a-3)) | |
--accent-solid | var(--a-4) | |
--size-marker | 2px | |
--weight-medium | 500 | |
Почему кит устроен именно так — конституция · Открытый код под MIT, github.com/keshon/instrument