Используйте реальные токены ax из Aksel v8, штатную плотность таблицы и семантическую структуру заголовков. Сортируйте и выбирайте вымышленные записи для последующей обработки, сравнивая светлую и тёмную темы.
Нативная таблица Aksel 8.18.0 CSS и токены с семантической разметкой HTML; локальная логика сортировки и выбора, явный резервный шрифт Arial. · Справочник по реализации
02
Проектная документация дизайн-системы Aksel
Проверены нативная таблица стилей @navikt/ds-css 8.18.0 и актуальная документация v8. Значения светлой и тёмной тем ограничены областью действия; предварительный просмотр заменяет удалённые шрифты на Arial и использует исходные демонстрационные записи.
В Aksel v8 используются токены --ax-* и классы компонентов .aksel. В руководстве по миграции указано, что старые классы .navds больше не работают, а токены для отдельных компонентов удалены. Поэтому обучающий материал на основе старого снимка экрана может выглядеть правдоподобно, но содержать неработающие рекомендации по реализации.
На этой странице зафиксирована нативная CSS версии 8.18.0 и приведена установка соответствующей React. В предварительном просмотре используются проверенные классы нативной таблицы и обычный HTML; приложение отвечает за JavaScript выбора и сортировки. Здесь не утверждается, что загрузка таблицы стилей создаёт экземпляр React Table.
Дизайн-система Aksel: типографические роли и загрузка шрифтов
Для жирного токена используйте 600
В справочнике токенов в качестве стека шрифтов указаны Source Sans 3, Source Sans Pro, Arial и sans-serif. Обычное начертание соответствует 400, а именованный токен жирного начертания — 600. Размеры основного текста включают 14, 16, 18 и 20px; роли заголовков охватывают размеры до 24, 28, 32 и 40px.
В локальном предпросмотре в качестве шрифтового токена задан Arial, а унаследованное использование шрифтов явно переопределено, поэтому удалённые шрифты не загружаются. Это явно обозначенное решение для резервного шрифта, а не утверждение, что Arial — основной шрифт Aksel. Размеры текста компонентов по-прежнему задаёт CSS.
Дизайн-система Aksel: отступы, радиусы и компоновка
Токены с размерами в пикселях преобразуются в rem
--ax-space-16 равен 1rem, а --ax-space-24 — 1.5rem. Синтаксис примитивов v8 соответствует этим именам: padding="space-16" и gap={{xs:"space-8",md:"space-16"}}. Не заменяйте их старым числовым свойством отступа, не проверив рекомендации по миграции.
В документации указаны следующие роли радиусов: 2, 4, 8, 12 и 16px, а также full. В предпросмотре сохранена исходная геометрия таблицы; для её элементов управления приложения используется space-24. Ширина области чтения 64rem выбрана локально, а значения контрольных точек взяты из изученной таблицы стилей.
Дизайн-система Aksel: семантические цвета с учётом темы
Сохраняйте роль, а не отдельное значение hex
В исходной таблице стилей --ax-text-neutral соответствует neutral-1000: в светлой теме это #202733, а в тёмной — #dfe1e5. Значение --ax-bg-default меняется с белого на #0e151f. Если жёстко задать светлый цвет текста для тёмного контейнера, сопоставление ролей перестанет работать.
В предпросмотре переключается класс темы .light/.dark, а полосы таблицы, границы и цвета выбранных строк зависят от официальных токенов. У выбранной строки по-прежнему установлен флажок, и виден счётчик выбранных элементов. Одной окрашенной строки недостаточно для реализации выбора.
Дизайн-система Aksel: семантическая структура таблицы
Заголовки — не обычные ячейки
В исходной таблице есть отдельные классы для заголовка, тела, строки, ячейки заголовка и ячейки данных. Эквивалент в React использует Table.Header, Table.Body, Table.Row, Table.HeaderCell и Table.DataCell. Столбец с идентификатором служит заголовком строки, а для каждого заголовка столбца задано scope=col.
Подпись задаёт название набора данных. Количество дней ожидания — числовое значение, поэтому оно выровнено по правому краю; идентификаторы остаются выровненными по левому. На узких экранах предпросмотр помещает горизонтальную прокрутку в подписанную область, доступную с клавиатуры, вместо того чтобы превращать строки в карточки и нарушать связи между элементами таблицы.
Используйте ячейки заголовков для названий столбцов и идентификаторов строк.
Выравнивайте по правому краю количественные значения, но не почтовые индексы и идентификаторы.
Добавьте подпись, чтобы у таблицы было понятное название.
В изученной исходной версии CSS для ячеек средней плотности используются space-12 по вертикали и space-8 по горизонтали. Для малой плотности по вертикали используется space-4, а для большой — space-16. Переключатель плотности меняет исходный класс модификатора; размер шрифта основного текста не уменьшается ради размещения большего количества информации на экране.
Заголовок Waiting days — кнопка сортировки в стиле исходного компонента. Её обработчик сортирует числовые значения и обновляет aria-sort у этого заголовка. Преобразование выполняет приложение: React API также предоставляет состояние сортировки и onSortChange, а не сортирует произвольные записи предметной области автоматически.
Дизайн-система Aksel: выбор строк с поддержкой клавиатуры
Добавьте для каждой строки отдельный элемент управления
В документации Table отмечено, что одного onRowClick недостаточно для доступности с клавиатуры. Поэтому в примере для каждого идентификатора есть подписанный флажок, а также флажок для выбора всех строк. Если выбрана только часть строк, флажок общего выбора переходит в неопределённое состояние.
При сортировке выбранные строки определяются по стабильному идентификатору записи. Индекс строки в текущем порядке никогда не используется как ключ выбора: иначе при изменении порядка отметка перейдёт к другой записи. Счётчик выбранных элементов обновляется в области статуса с вежливым режимом объявления. Выбранные записи никуда не отправляются.
Дизайн-система Aksel: подключение согласованных версий CSS и React
Используйте одинаковые версии пакетов
Установите @navikt/ds-react и @navikt/ds-css одной версии, затем один раз импортируйте CSS рядом с корнем приложения. Пакет уже содержит токены. Официальное руководство по импорту CSS также описывает подключение отдельных глобальных файлов и файлов компонентов, но базовый набор обязателен; шрифты и токены нужно подключить либо явно заменить.
В Aksel используются слои CSS. Избегайте широких селекторов вне слоёв: они могут непреднамеренно переопределить стили компонентов. Собственную компоновку приложения задавайте своими классами. В автономном предпросмотре используются локальные версии CSS и намеренно заменённый шрифт; пример React — отдельный вариант интеграции, проверенный по исходному коду.
Закрепите одинаковые версии пакетов React и CSS.
Импортируйте таблицу стилей один раз и используйте семантические дочерние элементы Table.
Храните сортировку и выбор в состоянии приложения.
Проверьте переключение темы, управление с клавиатуры и область прокрутки на узком экране.
Полный документ включает разметку, элементы управления и инициализацию. Распакуйте связанный архив примера, чтобы получить нужные относительные пути к ресурсам поставщика, затем запустите его через HTTP-сервер.
html
<!doctype html><html lang="en"><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1"><title>Aksel table workbench</title><link rel="stylesheet" href="vendor/aksel-8.18.0/index.css"><style>:root{--ax-font-family:Arial,sans-serif}body,body *,body *:before,body *:after{font-family:Arial,sans-serif!important}body{background:var(--ax-bg-default);color:var(--ax-text-neutral);font-size:18px;line-height:1.5}main{max-width:64rem;padding:32px 24px;margin:auto}h1{font-size:32px;font-weight:600;line-height:1.25;margin:16px 0 24px}p{margin:16px 0}.toolbar{display:flex;gap:24px;flex-wrap:wrap;padding:24px;background:var(--ax-bg-sunken);margin:24px 0}select{font-size:16px;padding:8px;background:var(--ax-bg-input);color:var(--ax-text-neutral);border:1px solid var(--ax-border-neutral)}label{font-size:16px;display:flex;gap:8px;align-items:center}.scroll{overflow-x:auto}table{min-width:38rem}caption{text-align:left;font-weight:600;margin-bottom:16px}input[type=checkbox]{width:20px;height:20px;accent-color:var(--ax-bg-accent-strong);cursor:pointer}small{font-size:14px;display:block;margin-top:24px;color:var(--ax-text-neutral-subtle)}:focus-visible{outline:3px solid var(--ax-border-focus);outline-offset:3px}@media(max-width:767px){main{padding:24px 16px}.toolbar{padding:16px;gap:16px}h1{font-size:28px}}</style><body class="light"><main><p>AKSEL v8 · NATIVE TABLE CSS</p><h1>Follow-up record workbench</h1><p>Compare density and theme while sorting and selecting fictional records. Selection follows each reference when the rows move.</p><div class="toolbar"><label>Density <select id="density"><option value="medium">Medium</option><option value="small">Small</option><option value="large">Large</option></select></label><label>Theme <select id="theme"><option value="light">Light</option><option value="dark">Dark</option></select></label></div><p id="selection" role="status">0 records selected</p><div class="scroll" tabindex="0" role="region" aria-label="Fictional follow-up records, horizontally scrollable"><table class="aksel-table aksel-table--zebra-stripes" id="records"><caption>Fictional follow-up records</caption><thead class="aksel-table__header"><tr class="aksel-table__row"><th class="aksel-table__header-cell" scope="col"><input id="all" type="checkbox" aria-label="Select all records"></th><th class="aksel-table__header-cell" scope="col">Reference</th><th class="aksel-table__header-cell" scope="col">Next activity</th><th class="aksel-table__header-cell" id="sort-header" scope="col" aria-sort="ascending"><button id="sort" type="button" class="aksel-table__sort-button">Waiting days ↑</button></th></tr></thead><tbody class="aksel-table__body" id="rows"></tbody></table></div><small>OpenDesign specimen using native Aksel 8.18.0 CSS and an explicit Arial fallback. Fictional data, no NAV identity or external actions.</small></main><script>const data=[{id:'DEMO-204',activity:'Confirm appointment preference',days:4},{id:'DEMO-218',activity:'Read the submitted example note',days:12},{id:'DEMO-231',activity:'Arrange an example callback',days:7}];let ascending=true;const selected=new Set(),all=document.querySelector('#all');function render(){const body=document.querySelector('#rows');body.replaceChildren();for(const r of [...data].sort((a,b)=>ascending?a.days-b.days:b.days-a.days)){const tr=document.createElement('tr');tr.className='aksel-table__row'+(selected.has(r.id)?' aksel-table__row--selected':'');const td=document.createElement('td');td.className='aksel-table__data-cell';const c=document.createElement('input');c.type='checkbox';c.setAttribute('aria-label','Select '+r.id);c.checked=selected.has(r.id);c.onchange=()=>{c.checked?selected.add(r.id):selected.delete(r.id);updateSelection();tr.classList.toggle('aksel-table__row--selected',c.checked)};td.append(c);const th=document.createElement('th');th.scope='row';th.className='aksel-table__header-cell';th.textContent=r.id;const activity=document.createElement('td');activity.className='aksel-table__data-cell';activity.textContent=r.activity;const days=document.createElement('td');days.className='aksel-table__data-cell aksel-table__data-cell--align-right';days.textContent=r.days;tr.append(td,th,activity,days);body.append(tr)}updateSelection()}function updateSelection(){all.checked=selected.size===data.length;all.indeterminate=selected.size>0&&selected.size<data.length;document.querySelector('#selection').textContent=selected.size+' records selected'}all.onchange=()=>{selected.clear();if(all.checked)data.forEach(r=>selected.add(r.id));render()};document.querySelector('#sort').onclick=()=>{ascending=!ascending;document.querySelector('#sort-header').setAttribute('aria-sort',ascending?'ascending':'descending');document.querySelector('#sort').textContent='Waiting days '+(ascending?'↑':'↓');render()};document.querySelector('#density').onchange=e=>document.querySelector('table').className='aksel-table aksel-table--zebra-stripes aksel-table--'+e.target.value;document.querySelector('#theme').onchange=e=>document.body.className=e.target.value;render();</script></body></html>
Скачайте полный пример из раздела «Источники». Точная команда локального запуска приведена в README.
Оставьте пути к ресурсам поставщика относительными к этому документу; архив включает все ресурсы, используемые в предварительном просмотре.
04
Настройка агента дизайн-системы Aksel
Отправная точка для реализации
В предпросмотре локально отрисовывается исходный CSS 8.18.0. Композиция React проверена по исходному коду, но отдельно не собиралась. Используйте согласованные версии; не сочетайте классы navds из v7 с CSS v8. ZIP-архив с полностью рабочим примером в разделе Sources также содержит точные зависимости предпросмотра и полный документ. Распакуйте его, выполните python3 -m http.server 8080 в этой директории, затем откройте http://localhost:8080/nav-aksel.html. Не открывайте примеры на основе модулей через file://.
Создайте семантическую таблицу Aksel v8, используя согласованные пакеты CSS/React версии 8.18.0. Добавьте подпись, заголовки столбцов и заголовки строк с идентификаторами. Используйте настоящие токены ax и свойства примитивов space-*. Реализуйте сортировку числовых значений с aria-sort, выбор флажками по стабильному идентификатору и общий выбор с неопределённым состоянием флажка. Не полагайтесь на onRowClick для взаимодействия с клавиатуры. Поддержите ролевые токены светлой и тёмной тем и подписанную область горизонтальной прокрутки на узких экранах.
05
Дизайн-система Aksel: источники и область применения
Самостоятельные примеры записей без идентификации NAV. Используется исходная таблица стилей с явно указанным резервным шрифтом. Сортировка, выбор и компоновка реализованы на уровне приложения; реальные данные о преимуществах или практических случаях не приводятся.