Перейти к содержимому
EN
Масштаб
Плотность
Тема
Акцент

Отображение данных

Код и копируемое значение

JS Копирование в буфер — делает `instrument.js`

Блок кода и однострочное значение, которое хочется скопировать: путь, хеш, идентификатор.

Страница про оба: первый показывают, второй забирают.

Компонент
go -C tools run ./cmd/contrast
a4f7c2e91b0d5537
Разметка
<div class="inst-code">go -C tools run ./cmd/contrast
  <button class="inst-copy" type="button" aria-label="Скопировать команду">
    <svg class="inst-icon" aria-hidden="true"><use href="#i-copy"/></svg>
  </button>
</div>
<span class="inst-copyable">a4f7c2e91b0d5537
  <button class="inst-copy" type="button" aria-label="Скопировать a4f7c2e91b0d5537">
    <svg class="inst-icon" aria-hidden="true"><use href="#i-copy"/></svg>
  </button>
</span>

Контракт

Что Обязательно Почему
tabindex="0" и role="region" с именем да, если внутри нет фокусируемого Блок прокручивается по горизонтали, и до прокрутки надо добраться с клавиатуры
aria-label со значением у кнопки копирования да Ряд кнопок «Скопировать» на слух неразличим: «Скопировать a4f7c2e»
Ничего для озвучивания копирования — instrument.js заводит общую живую область [data-inst-live] сам и говорит в неё словом. Приложению здесь делать нечего

Переноса строк в блоке кода нет: перенос в команде меняет её смысл. Отсюда горизонтальная прокрутка, а scrollbar-gutter зарезервирован, чтобы появление полосы не сдвигало код на пиксель при каждом обновлении.

Доступность

Прокрутка Блок прокручивается по горизонтали, поэтому обязан быть достижим с клавиатуры: tabindex="0" и role="region" с именем, если внутри нет фокусируемого
scrollbar-gutter Зарезервирован, чтобы появление полосы не сдвигало код на пиксель при каждом обновлении
Имя кнопки копирования aria-label со значением: «Скопировать a4f7c2e». Ряд кнопок «Скопировать» на слух неразличим
Результат копирования instrument.js меняет вид кнопки и говорит словом: смена глифа для скринридера — событие без содержания. Живая область у кита одна на документ, заводится при первом сообщении и переиспользуется
Перенос строк В блоке кода его нет: перенос в команде меняет её смысл. Отсюда горизонтальная прокрутка
Кегль --text-xs — 12,5px. Ниже --text-2xs кегль не опускается

Варианты

Копируемое значение

Однострочное значение с кнопкой — хеш, идентификатор, путь.

Пример
a4f7c2e
Разметка
<span class="inst-copyable">a4f7c2e
  <button class="inst-copy" type="button" aria-label="Скопировать a4f7c2e">
    <svg class="inst-icon" aria-hidden="true"><use href="#i-copy"/></svg>
  </button>
</span>

Кнопка стоит в потоке, с просветом, и видна всегда. Абсолют в углу с проявлением по наведению — приём блока кода: там угол свободен и текста много. У однострочного значения угла нет, и значок ложился на последние символы, а на касании его не было вовсе, потому что наведения на касании не бывает.

Область нажатия у кнопки растёт до нормы невидимым ::before — тем же приёмом, что у флажка: растёт область, а не значок, и строка остаётся прежней высоты.

Копируемое значение в строке

Самое частое его место и меньше всего похожее на блок кода: колонка идентификаторов в таблице, машинное значение в списке свойств. Разметка — тот же inst-copyable, таблица в ней ничего не меняет.

Пример
ПрогонИдентификаторСостояние
nightly-4127 2f8a1c04-9b7e-4d31-a5f0-c6e2 идёт
audio-pass 7c31b9de-2a44-4e08-8f1b-30d7 упало
Разметка
<table class="inst-table">
  <thead><tr><th>Прогон</th><th>Идентификатор</th><th>Состояние</th></tr></thead>
  <tbody>
    <tr><td>nightly-4127</td>
      <td><span class="inst-copyable"><code>2f8a1c04-9b7e-4d31-a5f0-c6e2</code>
        <button class="inst-copy" type="button" aria-label="Скопировать 2f8a1c04-9b7e-4d31-a5f0-c6e2">
          <svg class="inst-icon" aria-hidden="true"><use href="#i-copy"/></svg>
        </button></span></td>
      <td><span class="inst-badge" data-tone="running"><span class="inst-dot"></span>идёт</span></td></tr>
    <tr><td>audio-pass</td>
      <td><span class="inst-copyable"><code>7c31b9de-2a44-4e08-8f1b-30d7</code>
        <button class="inst-copy" type="button" aria-label="Скопировать 7c31b9de-2a44-4e08-8f1b-30d7">
          <svg class="inst-icon" aria-hidden="true"><use href="#i-copy"/></svg>
        </button></span></td>
      <td><span class="inst-badge" data-tone="error"><span class="inst-dot"></span>упало</span></td></tr>
  </tbody>
</table>

Три вещи про строку, в которых легко ошибиться, — и в двух из них справочник ошибался сам, пока этот раздел не был написан:

Что Почему
Кнопка видна всегда, а не по наведению на строку У колонки идентификаторов на тачскрине наведения нет, и кнопки, которая им вызывается, там просто не существует. Справочник прятал её так в собственных таблицах API
Цвет — --text-muted, а не --text-faint Faint рисуется по порогу 3:1 и предназначен линиям и разделителям. Контрол читают, значит ему причитаются 4.5:1 у muted
В aria-label стоит значение Колонка из десяти кнопок «Скопировать» на слух — одно слово десять раз. «Скопировать 2f8a1c04» говорит, какая это строка

Размер значка здесь не написан и написан быть не должен: .inst-copy — это --size-icon-sm везде, где он стоит, потому что значок рядом со значением обслуживает значение. Места, которые выбирали сами, выбирали по-разному — именно так в одном ките оказались кнопка копирования на 18 пикселей и на 10 на одной странице.

Листинг с номерами строк

Пример
41const size = 256;
42let seed = Date.now();
43return generate(size, seed);
Разметка
<div class="inst-code inst-code--numbered">
  <div class="inst-code-line"><span class="inst-code-num">41</span><span class="inst-code-src">const size = 256;</span></div>
  <div class="inst-code-line"><span class="inst-code-num">42</span><span class="inst-code-src">let seed = Date.now();</span></div>
  <div class="inst-code-line"><span class="inst-code-num">43</span><span class="inst-code-src">return generate(size, seed);</span></div>
</div>

Номер не выделяется мышью — user-select: none: копируют код, а не страницу. Колонка номеров ровно 5ch, и это не запас на глаз: пятизначный номер строки встречается в настоящих файлах, а колонка, растущая от содержимого, сдвигала бы код при каждой подгрузке.

inst-code inst-copyable
Сколько строк Сколько угодно Одна
Прокрутка По горизонтали Нет
Кнопка Нет Есть
Пример Команда, конфиг Хеш, путь, идентификатор

JS

Подключите модуль один раз на страницу — инициализировать компоненты по отдельности не нужно, instrument.js работает делегированием и видит узлы, пришедшие позже.

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

Что делает instrument.js

Примеры на этой странице живые: наведите на блок кода и нажмите кнопку копирования — значок сменится на галку.

Копирование целиком: текст в буфер, цветной ответ на кнопке и объявление результата вслух. Ответ обязателен — буфер обмена ничего не показывает, и без него человек жмёт кнопку второй раз.

Источник — текст ближайшего .inst-code или .inst-copyable без самой кнопки. Читается textContent, а не innerText: второй у свёрнутого блока возвращает пустую строку, и копирование ушло бы в ничто беззвучно.

Опции

Атрибут Что делает
data-copy Копировать это значение, а не текст блока. Нужно, когда видно сокращение, а копировать надо полное
data-copied-label Своя фраза для скринридера вместо «Copied»
data-failed-label То же для неудачи. По умолчанию «Could not copy»
<span class="inst-copyable">a4f7c2e
  <button class="inst-copy" type="button" data-copy="a4f7c2e91b0d5537"
          aria-label="Скопировать полный хеш"></button>
</span>

События

inst:copy всплывает с { text } и отменяем: preventDefault() означает «копирую сам», и instrument.js не трогает буфер.

document.addEventListener('inst:copy', (e) => {
  analytics.track('copy', { length: e.detail.text.length });
});

Чего instrument.js не делает

Подсветку синтаксиса. Она требует разбора языка, и это работа приложения.

API

Классы
ИмяЧто делает
класс
inst-codeБлок кода. Прокручивается по горизонтали
inst-code-lineСтрока листинга. Только внутри --numbered
inst-code-numНомер. --text-muted, потому что его читают. Не выделяется мышью
inst-code-srcСам код. Единственное место, где живёт overflow-wrap: anywhere
inst-copyableОднострочное значение с кнопкой
inst-copyКнопка копирования. Работу выполняет [instrument.js](../../foundations/behavior.md). Внутри .inst-code встаёт в дальний верхний угол сама
модификатор
inst-code--numberedЛистинг с колонкой номеров строк
Атрибуты
ИмяЗначениеЧто делает
атрибут
data-copy—Копировать это значение, а не текст блока
data-copied-label—Своя фраза для скринридера вместо «Copied»
data-failed-label—То же для неудачи. По умолчанию «Could not copy»
data-copiedtrue · falseОтвет кнопки. Ставит и снимает instrument.js
Токены
ИмяЗначениеОткуда
токен
--surface-sunkenlight-dark(var(--n-2), var(--n-14))база +1: тема
--border-subtlelight-dark(oklch(0 0 0 / 0.07), oklch(1 0 0 / 0.06))одно на все ячейки
--hairline1pxбаза +1: экран
--radius-md8pxбаза +4: масштаб
--space-48pxодно на все ячейки
--pad-panelvar(--space-5)база +14: плотность · масштаб
--text-xs0.78125remбаза +4: масштаб
--font-monoui-monospace, " , " , " , Consolas, " , monospaceодно на все ячейки

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