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

Обратная связь

Баннер

JS Закрытие баннера — слой приложения. Крестика у баннера нет

Сообщение уровня страницы: оно относится ко всему экрану, а не к тому, рядом с чем стоит. Поэтому у баннера есть заливка, рамка и место под действие.

Компонент
Доступ истекает через 3 дня
После 8 августа запуск прогонов остановится.
Разметка
<div class="inst-banner" data-tone="warn">
  <div class="inst-banner-body">
    <div class="inst-banner-title">Доступ истекает через 3 дня</div>
    <div class="inst-banner-text">После 8 августа запуск прогонов остановится.</div>
  </div>
  <div class="inst-banner-actions">
    <button class="inst-btn inst-btn--sm" type="button">Продлить</button>
  </div>
</div>

Контракт

Что Обязательно Почему
inst-banner-body вокруг текста да min-inline-size: 0 — иначе длинное слово распирает баннер
aria-live="polite" или role="alert" да, если баннер появился в ответ на действие Роль не ставится автоматически: библиотека не знает, появился баннер сейчас или был изначально
aria-label у кнопки закрытия да, если баннер закрывается Крестик без имени не называет, что закрывает
inst-banner-text нет Подробность нужна не всегда: заголовок может остаться единственным

Доступность

Появление Баннер, возникший в ответ на действие, обязан попасть в aria-live="polite", а сообщение об отказе — в role="alert". Роль не ставится автоматически: библиотека не знает, появился баннер сейчас или был на странице изначально
Цвет не единственный носитель Тон несёт значок и текст. Иконка появилась именно потому, что warn и error раньше отличались только цветом
Контраст Заголовок берёт --text-primary, подробность --text-secondary — оба на 4.5:1 против --tone-bg в пяти темах
Закрытие Библиотеки оно не касается. Если баннер закрывается, кнопка закрытия — обычная иконочная кнопка с aria-label
Печать Баннер печатается и не рвётся между листами: break-inside: avoid. Сообщение уровня страницы должно попасть в отчёт

Устройство

Класс Работа
inst-banner Контейнер: заливка тона, рамка, значок
inst-banner-body Текстовая часть. min-inline-size: 0 против распирания
inst-banner-title Заголовок. Может быть единственным содержимым
inst-banner-text Подробность одной фразой
inst-banner-actions Действия у дальнего края. Не сжимаются, переносятся при нехватке места

Действия прижимаются к дальнему краю и не сжимаются, поэтому длинный текст переносится, а кнопка остаётся целой.

Значок центрируется по первой строке текста: calc((1lh - var(--size-icon)) / 2). Формула сама подстраивается под кегль и плотность, поэтому баннер с одной строкой и баннер с тремя выглядят одинаково выровненными.

Варианты

Пример
Доступ истекает через 3 дня
После 8 августа запуск прогонов остановится.
Обслуживание хранилища
Артефакты недоступны до 21:00.
Изменения сохранены
Разметка
<div class="inst-banner" data-tone="warn">
  <div class="inst-banner-body">
    <div class="inst-banner-title">Доступ истекает через 3 дня</div>
    <div class="inst-banner-text">После 8 августа запуск прогонов остановится.</div>
  </div>
  <div class="inst-banner-actions">
    <button class="inst-btn inst-btn--sm" type="button">Продлить</button>
  </div>
</div>
<div class="inst-banner" data-tone="error">
  <div class="inst-banner-body">
    <div class="inst-banner-title">Обслуживание хранилища</div>
    <div class="inst-banner-text">Артефакты недоступны до 21:00.</div>
  </div>
</div>
<div class="inst-banner" data-tone="ok">
  <div class="inst-banner-body"><div class="inst-banner-title">Изменения сохранены</div></div>
</div>

Тон — тот же data-tone, что на бейдже и всюду ещё. Он несёт иконку. Значок приходит из общего набора масок — одного на баннер и сноску.

Тон Значок Когда
без атрибута нет Нейтральное сообщение без значка
data-tone="neutral" «i» в круге Информация. Роль info исполняет neutral — синий в библиотеке занят акцентом
data-tone="running" «i» в круге Идёт прямо сейчас: обслуживание, миграция
data-tone="ok" галка в круге Свершилось успешно
data-tone="warn" треугольник Условие, которое скоро станет проблемой
data-tone="error" крест в круге Что-то не работает прямо сейчас

Без data-tone значок не рисуется вовсе (content: none), и баннер остаётся на нейтральной приглушённой поверхности.

JS

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

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

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

Ничего. Крестика у баннера нет: баннер сообщает о состоянии системы, а состояние не исчезает оттого, что его закрыли. Закрытие имеет смысл только там, где приложение знает, что делать с закрытым.

Если закрытие всё-таки нужно

Кнопка — обычная, и её надо добавить самому. Вместе с ней придётся решить, что значит «закрыт»: до перезагрузки, до конца сессии или навсегда.

document.addEventListener('click', (e) => {
  const id = e.target.closest('[data-close]')?.dataset.close;
  if (!id) return;
  document.getElementById(id).hidden = true;
  sessionStorage.setItem('hidden:' + id, '1'); // до конца сессии, а не навсегда
});

hidden, а не удаление узла: баннер вернётся, когда состояние повторится, и воссоздавать разметку заново незачем.

API

Классы
ИмяЧто делает
класс
inst-bannerКонтейнер. Заливка тона, рамка, значок
inst-banner-bodyТекстовая часть. min-inline-size: 0, чтобы длинное слово не распирало баннер
inst-banner-titleЗаголовок, средняя насыщенность
inst-banner-textПодробность, --text-secondary
inst-banner-actionsДействия у дальнего края. Не сжимаются, переносятся при нехватке места
Атрибуты
ИмяЗначениеЧто делает
атрибут
data-tone—neutral running ok warn error. Закрыт
Токены
ИмяЗначениеОткуда
токен
--tone-bgvar(--surface-sunken)база +4:
--tone-inkvar(--accent-text)база +2:
--borderlight-dark(oklch(0 0 0 / 0.12), oklch(1 0 0 / 0.11))одно на все ячейки
--hairline1pxбаза +1: экран
--radius-md8pxбаза +4: масштаб
--pad-panelvar(--space-5)база +14: плотность · масштаб
--space-36pxодно на все ячейки
--space-48pxодно на все ячейки
--gap-inlinevar(--space-4)база +14: плотность · масштаб
--size-icon18pxбаза +4: масштаб
--text-sm0.875remбаза +4: масштаб
--text-secondarylight-dark(var(--n-9), var(--n-5))одно на все ячейки
--weight-medium600одно на все ячейки
--leading-ui1.4одно на все ячейки

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