Перейти к содержимому
Плотность
Тема

Начало

Установка

Кит подключается двумя тегами и не требует сборки. Ниже — целая страница, которую можно скопировать и открыть.

Один файл

<link rel="stylesheet" href="instrument.min.css">

Возьмите dist/instrument.min.css — 82 КБ, 14 КБ в gzip. Это весь кит: токены, шесть тем, три плотности, все компоненты.

dist/instrument.css — то же самое без сжатия, если хочется читать исходник в инспекторе.

Пакета в npm нет. Скопируйте файл к себе или подключите репозиторий сабмодулем — версия лежит в VERSION и в имени тега.

С иконкой

Иконка рисуется ссылкой на символ: <use href="#i-refresh">. Значит, символы должны быть в документе. Вставьте содержимое assets/sprite.svg первым элементом внутри <body> — один раз на страницу.

<body>
  <svg class="inst-sprite" aria-hidden="true" focusable="false" xmlns="http://www.w3.org/2000/svg">
    <!-- сюда содержимое assets/sprite.svg -->
  </svg>

Без спрайта всё остальное работает, просто иконки не рисуются. Полный список из девяноста символов — на странице иконок.

Поведение с клавиатуры

<script type="module" src="instrument.js"></script>

Отдельный файл и по желанию. Он выполняет то, что кит обещает разметкой: стрелки в списках и вкладках, копирование, снятие тега, перетаскивание оси. Ничего не рисует. Подробности — в поведении.

Целая страница

<!doctype html>
<html lang="ru">
<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <title>Моё приложение</title>
  <link rel="stylesheet" href="instrument.min.css">
  <script type="module" src="instrument.js"></script>
</head>
<body>
  <div class="inst-shell">
    <header class="inst-shell-header">
      <strong>Моё приложение</strong>
      <button class="inst-btn inst-btn--primary" type="button">Запустить</button>
    </header>

    <main class="inst-shell-main">
      <div class="inst-container">
        <div class="inst-panel">
          <div class="inst-panel-header">
            <span class="inst-panel-title">Очередь</span>
          </div>
          <div class="inst-panel-body">
            <div role="listbox" aria-label="Очередь задач">
              <div class="inst-task" role="option" data-state="running"
                   aria-selected="true" tabindex="0">
                <span class="inst-task-gutter"><span class="inst-dot"></span></span>
                <span class="inst-task-main">
                  <span class="inst-task-title">Разбор карты высот</span>
                  <span class="inst-task-sub">terrain/heightmap.ts</span>
                </span>
                <span class="inst-task-meta">2,1 с</span>
              </div>
            </div>
          </div>
        </div>
      </div>
    </main>
  </div>
</body>
</html>

Вот как это выглядит:

В контексте
Очередь
Разбор карты высот terrain/heightmap.ts 2,1 с
Сборка биомов
Разметка
<div class="inst-panel">
  <div class="inst-panel-header">
    <span class="inst-panel-title">Очередь</span>
  </div>
  <div class="inst-panel-body">
    <div role="listbox" aria-label="Очередь задач">
      <div class="inst-task" role="option" data-state="running" aria-selected="true" tabindex="0">
        <span class="inst-task-gutter"><span class="inst-dot"></span></span>
        <span class="inst-task-main">
          <span class="inst-task-title">Разбор карты высот</span>
          <span class="inst-task-sub">terrain/heightmap.ts</span>
        </span>
        <span class="inst-task-meta">2,1 с</span>
      </div>
      <div class="inst-task" role="option" data-state="queued" aria-selected="false" tabindex="-1">
        <span class="inst-task-gutter"><span class="inst-dot"></span></span>
        <span class="inst-task-main">
          <span class="inst-task-title">Сборка биомов</span>
        </span>
        <span class="inst-task-meta">—</span>
      </div>
    </div>
  </div>
</div>

Тема и плотность

Оба — атрибуты, и оба работают на любом поддереве, а не только на документе.

<html data-theme="dark">              <!-- шесть тем; без атрибута — по настройке ОС -->
<section data-density="compact">      <!-- compact · comfortable -->
Тема
light-neutral light-warm light-cool светлые, различаются уклоном
dark-light dark-soft dark тёмные, различаются глубиной поверхностей

Ваши стили выигрывают

Кит целиком лежит в каскадных слоях, а ваши стили — вне их. Поэтому любое ваше правило перебивает кит, и !important не нужен нигде.

.inst-btn { border-radius: 0; }   /* сработает, специфичность неважна */

Требования

Chrome 123+, Safari 17.5+, Firefox 120+ — по light-dark(), oklch(), каскадным слоям, вложенности, :has() и единице lh. Полифилов нет.

Дальше

Готовые экраны — три собранных интерфейса, из которых видно, как компоненты стыкуются. Оттуда быстрее всего копировать.

Оболочка приложения — шапка, боковая панель, основная область.

Конституция — правила, по которым кит устроен. Читать не обязательно, но если правило и компонент разойдутся, прав будет правило.

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