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

Сборки

Инспектор

Список объектов слева, свойства выбранного справа. Экран, ради которого кит и затевался: плотность здесь не украшение, а вместимость.

Компонент
Объекты сцены 4
сохранено
terrain
heightmap.ts
biomes.ts
Свойства
Позиция X Y Z
Плотность
Тень
Полигонов
48 210
Обновлён
14:32:07
Разметка
<div class="inst-split" style="--split-side: 20rem">

  <div class="inst-split-main inst-panel">
    <div class="inst-panel-header">
      <span class="inst-panel-title">Объекты сцены</span>
      <span class="inst-badge">4</span>
      <span class="inst-panel-actions">
        <button class="inst-btn inst-btn--sm inst-btn--ghost inst-btn--icon" type="button" aria-label="Обновить">
          <svg class="inst-icon" aria-hidden="true"><use href="#i-refresh"/></svg></button>
      </span>
    </div>
    <div class="inst-toolbar">
      <div class="inst-btn-group">
        <button class="inst-btn inst-btn--sm inst-btn--icon" type="button" aria-label="Переместить">
          <svg class="inst-icon" aria-hidden="true"><use href="#i-move"/></svg></button>
        <button class="inst-btn inst-btn--sm inst-btn--icon" type="button" aria-label="Повернуть">
          <svg class="inst-icon" aria-hidden="true"><use href="#i-rotate"/></svg></button>
        <button class="inst-btn inst-btn--sm inst-btn--icon" type="button" aria-label="Масштаб">
          <svg class="inst-icon" aria-hidden="true"><use href="#i-scale"/></svg></button>
      </div>
      <span class="inst-toolbar-spacer"></span>
      <span class="inst-badge" data-tone="ok"><span class="inst-dot"></span>сохранено</span>
    </div>
    <div class="inst-panel-body inst-panel-body--list">
      <div class="inst-tree" role="tree" aria-label="Объекты сцены">
        <div class="inst-tree-item" role="treeitem" aria-level="1" aria-expanded="true"
             tabindex="-1" style="--depth:0"><span class="inst-tree-twist"></span>terrain</div>
        <div class="inst-tree-item" role="treeitem" aria-level="2" aria-selected="true"
             tabindex="0" style="--depth:1">heightmap.ts</div>
        <div class="inst-tree-item" role="treeitem" aria-level="2"
             tabindex="-1" style="--depth:1">biomes.ts</div>
        <div class="inst-tree-item" role="treeitem" aria-level="1" aria-expanded="false"
             tabindex="-1" style="--depth:0"><span class="inst-tree-twist"></span>audio</div>
      </div>
    </div>
  </div>

  <div class="inst-split-side inst-panel">
    <div class="inst-panel-header"><span class="inst-panel-title">Свойства</span></div>
    <div class="inst-panel-body">
      <div class="inst-props">
        <div class="inst-prop">
          <span class="inst-prop-label" title="Позиция">Позиция</span>
          <span class="inst-prop-control inst-vec">
            <span class="inst-num-field"><span class="inst-num-axis">X</span><input type="number" value="128" aria-label="Позиция X"></span>
            <span class="inst-num-field"><span class="inst-num-axis">Y</span><input type="number" value="0" aria-label="Позиция Y"></span>
            <span class="inst-num-field"><span class="inst-num-axis">Z</span><input type="number" value="-64" aria-label="Позиция Z"></span>
          </span>
        </div>
        <div class="inst-prop">
          <span class="inst-prop-label" title="Плотность растительности">Плотность</span>
          <span class="inst-prop-control">
            <input class="inst-slider" type="range" min="0" max="100" value="72" aria-label="Плотность растительности">
          </span>
        </div>
        <div class="inst-prop">
          <label class="inst-prop-label" title="Материал" for="ins-mat">Материал</label>
          <span class="inst-prop-control">
            <span class="inst-select-wrap">
              <select class="inst-select inst-select--sm" id="ins-mat">
                <option>rock-01</option><option>sand-02</option><option>grass-03</option>
              </select>
            </span>
          </span>
        </div>
        <div class="inst-prop">
          <span class="inst-prop-label" title="Отбрасывать тень">Тень</span>
          <span class="inst-prop-control">
            <label class="inst-switch"><input type="checkbox" checked>Отбрасывать</label>
          </span>
        </div>
      </div>

      <dl class="inst-kv">
        <dt>Полигонов</dt><dd>48 210</dd>
        <dt>Обновлён</dt><dd>14:32:07</dd>
      </dl>
    </div>
  </div>
</div>

Из чего собрано

Часть Чем собрана
Две колонки Сплит. Порог боковой части задан переменной --split-side
Список объектов Дерево: глубина приходит из данных, а не из вложенных обёрток
Полоса инструментов Тулбар с группой кнопок
Строки свойств Список свойствinst-props и inst-prop
Вектор Числовое поле инспектора в inst-vec
Остальные контролы Слайдер, селект, свитч
Итоги только для чтения Список свойств на <dl>

Что здесь видно

Колонка имён одна на два примитива. inst-props и inst-kv берут один и тот же --label-col, поэтому редактируемые и нередактируемые свойства стоят по одной вертикали.

Контрол в строке свойства — sm. Высота 26px против 32px у формы: на экране, где свойств десятки, это разница в целый экран прокрутки. Цена названа честно на странице числового поля: в плотности compact цель нажатия уходит ниже нормы WCAG.

Инлайновые стили здесь — данные. --depth у узла дерева и --split-side у раскладки: глубина приходит из модели, а порог — решение экрана. Оформление целиком в ките.

Панель — контейнер запросов. Сузьте окно: строка свойства перестанет быть двухколоночной, и подпись встанет над контролом. Это @container, а не медиазапрос: реагирует панель, а не окно.

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