Слой состояния (State Layer)

Источник: ds-docs/component-contracts/foundations/state-layer.md

Слой состояния — полупрозрачное покрытие, которое делает в макете две работы разом: показывает наведение, нажатие и фокус — и обозначает область нажатия компонента. Вторая работа не очевидна, но в Astra DS она главная: слой отвечает на вопрос «по чему именно здесь нажимают — по всей строке или только по значку».

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

Наверх — фундаменты. Рядом: цвет, светлая тема, движение.


Два вида в макете

В файле слой встречается в двух видах, и путать их нельзя.

1. Обойма State layer … — тело компонента

Обычная обойма с раскладкой, внутри которой лежит всё содержимое. Красится токеном по состоянию: в покое заливки нет, в наведении и нажатии появляется. Имя всегда начинается со слов State layer, дальше идёт уточнение вида: State layer Small Text, State layer Medium, State layer Text+Icon.

Так устроены 53 компонента: кнопка, значок-кнопка, чипы, фильтры, вкладки, все поля ввода, флажок, переключатель, ячейки календаря, клавиша клавиатуры.

2. Инстанс ⚒︎ State Layer — область нажатия

Отдельный служебный компонент (28007:16437, страница Color Tokens). Сам по себе прозрачен — в макете его не видно, найти можно только по имени.

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

Но не всякая его вставка — область нажатия. Тот же компонент кладут в поток раскладки просто как крашеный прямоугольник:

  • Button Banner в состоянии загрузки набирает им полосы-заглушки вместо текста (State=Skeleton, размеры 82×12 и 96×10 — под строку) и серую пелену поверх всего баннера (State=Disabled). Это 156 вставок из 159 «в потоке» по всей системе;
  • Image Upload Item кладёт слой в поток, но его родитель абсолютен и слой заполняет его целиком — по действию это всё-таки область нажатия.

Отличать одно от другого просто: position: absolute плюс размер родителя — область нажатия; всё прочее — покраска.

У инстанса своя ось State с пятью значениями:

Defaultпрозрачен — компонент нажимается, но не подсвечен
Hoveredзаливка hover-bg-dark
Pressedзаливка press-bg-dark
Disabledзаливка bg-layer-disabled-02
Skeletonвнутрь кладётся заглушка загрузки Skeleton

В самих компонентах используются только три из пяти: Default (177 вставок), Skeleton (104) и Disabled (52). Hovered и Pressed у инстанса не встречаются ни разу — эти состояния показывают обоймой первого вида.


Как читать пример: строка списка

Basic List Item — образец, на котором видно оба вида сразу:

Basic List Item                     корень, заливка bg-list-primary
  State layer                       ← обойма: тело строки, держит содержимое и поля
    ⚒︎ State Layer   absolute 375×72 ← инстанс: область нажатия во всю строку
    [Left Container] / ✱ Icon Container
    Middle Container / [Text Container] / …
    [Right Container] / Checkbox
  Divider

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

И ещё одно, что видно только по мастерам: инстанс стоит лишь в состоянии Default. В Read Only и Skeleton его нет — строка перестаёт нажиматься. То есть присутствие слоя и есть признак «здесь можно нажать».


Значения по состояниям

Одни и те же для любого фона: слой добавляет прозрачность поверх.

Hover+4 %hover-bg-dark, на тёмном фоне hover-bg-light
Focus+8 %focus-bg
Press+16 %press-bg-dark, на тёмном фоне press-bg-light
Drag+16 %отдельного токена нет

Что стоит за токенами — цвет, светлая тема.

Что из этого проверяет плагин

Правило порядка слоёв («контейнер → слой состояния → контент») относится только к пустой обойме-подкраске: у обоймы с содержимым внутри порядок ни при чём, а служебный инстанс лежит абсолютно поверх нарочно. Плагин проверки так и настроен — LAY-001 в каталоге правил.

Проверяемое отклонение здесь другое и записано ниже: чем слой красится в своём состоянии. Это LAY-002; по базе он находит те же два случая, что нашёл аудит.

Где правило нарушено

Проверка по всем 284 контрактам базы нашла четыре отклонения:

  • Numkey: в состоянии Focused кладётся hover-bg-dark вместо focus-bg;
  • Tab Item: в состоянии Pressed кладётся focus-bg вместо press-bg-dark;
  • Icon Button: наведение, нажатие и фокус привязаны к переменным 3174:6492, 7468:21293 и 7468:21292, которых нет в выгрузке токенов — имени у них нет;
  • имя состояния пишется по-разному: Hovered и Hover, Pressed, Focused и Focus — в одной системе четыре словаря на три состояния.

Как это попадает в контракт

Оба вида помечаются одинаково — полем foundation: state-layer. Метка говорит разработчику: перед ним не обычная обойма, а слой состояния, и читать его надо по правилам этого документа.

Обойма:

- path: Basic List Item/State layer
  kind: frame
  foundation: state-layer
  layout: {direction: row, mainAlign: start, crossAlign: center, gap: space-m, …}
  size: {width: fill, height: hug}
  fill: none          # в наведении — hover-bg-dark, в нажатии — press-bg-dark

Инстанс:

- path: "Basic List Item/State layer/⚒︎ State Layer"
  kind: instance
  of: state-layer
  foundation: state-layer
  position: absolute
  size: {width: 375, height: 72}
  props: {State: Default}

position: absolute вместе с меткой читается как «область нажатия — весь родитель». Какой именно родитель — видно по пути узла. Без position: absolute метка значит только «здесь тот самый служебный компонент»: областью нажатия он не работает.

Валидатор следит за единообразием: узел, чьё имя начинается с State layer или ⚒︎ State Layer, без метки даёт предупреждение.


Реализация в коде

Одна концепция, две платформы.

React (core-ui)

Смешивание цветов заранее записано в CSS-переменные: у каждого цвета есть -hover и -pressed. Именование: var(--<токен-фона>-hover), var(--<токен-фона>-pressed).

Когда фон прозрачный, а состояния нужны, берутся --hover-bg-dark и --press-bg-dark — те же токены, что кладёт макет на слой.

.secondary {
  background-color: var(--button-secondary-bg);

  @media (hover: hover) and (pointer: fine) {
    &:not(:disabled):hover { background-color: var(--button-secondary-bg-hover); }
  }
  &:not(:disabled):active { background-color: var(--button-secondary-bg-pressed); }
}

/* прозрачный фон → слой состояния */
.ghost {
  background-color: var(--transparent);

  @media (hover: hover) and (pointer: fine) {
    &:not(:disabled):hover { background-color: var(--hover-bg-dark); }
  }
  &:not(:disabled):active { background-color: var(--press-bg-dark); }
}

Область нажатия в код переносится не слоем, а размером самого интерактивного узла: если в макете слой растянут по строке, в коде по строке растянут button или a, а не вложенный значок.

Flutter

Смешивание задаётся обёрткой анимации цвета ButtonColorAnimated: базовый цвет плюс подмешиваемые hover, press, focus.

return ButtonColorAnimated(
  onTap: isLoading ? () {} : onTap,
  unpressedColor: _isDisabled ? colors.disabled : colors.unpressed,
  hoverBlendedColor: colors.hover,
  pressBlendedColor: colors.press,
  focusBlendedColor: colors.focus,
  builder: (Color? color) => DecoratedBox(
    decoration: BoxDecoration(color: color, borderRadius: BorderRadius.circular(borderRadius)),
    // …
  ),
);

Открытые вопросы

  • Q-1 — Flutter: полное описание поведения (источники цветов состояний) — запросить у команды Flutter.
  • Q-2 — у Drag своего токена нет, хотя в правиле он есть: чем его показывать.
  • Q-3 — три переменные Icon Button (3174:6492, 7468:21293, 7468:21292) не найдены в выгрузке токенов: живые ли они.
  • Q-4 — словарь состояний: свести Hover/Hovered, Press/Pressed, Focus/Focused к одному написанию.

Источник

Концепция, значения прозрачности и примеры кода — PDF «Слои состояния» (Astra DS), перенос 06.08.2026. Устройство в макете, перепись по компонентам и отклонения — разбор оригинала eGpqY0OqVR8l8zhmlHdfb9 по контрактам, 07.09.2026. Та же копия документа лежит в старой библиотеке: ../foundations/state-layer.md; на неё ссылаются 90 карточек компонентов.