Тесты
Все тесты последнего прогона с описаниями — сгруппированы по компонентам и разделам витрины. A11y-тесты помечены меткой, падения подсвечены.
Сводка прогона
Всего тестов
Прошли
Упали
Компонентов с тестами
Базовые
183CoreButton
13/13- a11y обычная кнопка с текстом — без axe-нарушений
- a11y loading: aria-busy + скрытый спиннер — без axe-нарушений
- a11y disabled и иконочная кнопка с aria-label — без axe-нарушений
- a11y кнопка-ссылка (to) — без axe-нарушений
- клик вызывает родительский onClick — кнопка активна
- disabled рендерит атрибут и не вызывает onClick
- loading тоже делает кнопку disabled (не только visually)
- кнопка-ссылка с disabled/loading: aria-disabled и активация блокируется
- активная кнопка-ссылка не блокирует клик
- to рендерит ссылку вместо button — кликабельная «кнопка-ссылка»
- round даёт круглый модификатор, без round — его нет
- flowBorder включает модификатор перелива, без флага — его нет
- flowBg включает модификатор градиентного фона, независимо от flowBorder
CoreIcon
9/9- рендерит элемент с классом core-icon и именем коллекции
- size задаёт font-size в inline-стиле
- по умолчанию size=16
- size строкой проходит как CSS-длина без px (масштаб от текста)
- label делает иконку смысловой: role=img + aria-label, aria-hidden убирается
- flow вешает градиентный класс на спан
- a11y декоративная по умолчанию (aria-hidden) — без axe-нарушений
- a11y смысловая с label (role=img + имя) — без axe-нарушений
- a11y декоративная и смысловая рядом (иконка в кнопке) — без axe-нарушений
CoreBrandLogo
7/7- рендерит span.core-brand-logo с размером по умолчанию 32
- проп size задаёт сторону квадрата
- size строкой — CSS-длина без px
- без label — декоративный: aria-hidden, без role
- с label — role="img" с доступным именем
- a11y декоративный логотип (aria-hidden) — без axe-нарушений
- a11y смысловое использование (role="img" + label) — без axe-нарушений
CoreCard
32/32- a11y карточка с контентом — без axe-нарушений
- a11y карточка с шапкой (h3) в иерархии h1→h2 — без axe-нарушений
- a11y карточка-ссылка (to) — без axe-нарушений
- рендерит дефолтный слот
- as=article меняет тег карточки
- to рендерит ссылку с href и включает interactive-класс
- to перекрывает as — рендерится ссылка, а не article
- title и description шорткаты рендерят заголовок и описание
- без title/description/слотов шапки — блока header нет
- слот header полностью заменяет title/description
- слот footer рендерится внутри core-card-footer
- карточка только с шапкой получает header-only модификатор
- flowBorder и flowBorderHover включают модификаторы перелива
- flowBg включает модификатор градиентной заливки
- с контентом или футером header-only модификатора нет
- по умолчанию рендерится h3 со слотом
- as меняет тег
- maxLines добавляет кламп-класс и CSS-переменную
- без maxLines нет кламп-класса и переменной
- рендерит слот
- рендерит слот
- рендерит слот
- a11y картинка со смысловым alt — без axe-нарушений
- a11y декоративная картинка с явным пустым alt — без axe-нарушений
- src рендерит img с alt
- ratio задаёт inline aspect-ratio
- дефолтный ratio — 16 / 9
- слот перекрывает собственный img
- без src и без слота — пустой бокс без img
- src без alt — предупреждение валидации: alt обязателен либо явно пустой
- alt="" — осознанный декоративный выбор, предупреждения нет
- слот вместо картинки — alt не нужен, предупреждения нет
CoreTitle
7/7- по умолчанию рендерится h2 со слотом
- as меняет тег
- maxLines добавляет кламп-класс и CSS-переменную
- без maxLines нет клампа
- flow вешает модификатор градиента
- a11y h2 после h1 — без axe-нарушений
- a11y as=h1 — без axe-нарушений
CoreText
6/6- по умолчанию рендерится p со слотом
- as меняет тег
- maxLines добавляет кламп-класс и CSS-переменную
- без maxLines нет клампа
- a11y по умолчанию p — без axe-нарушений
- a11y as=span и maxLines — без axe-нарушений
CoreLink
10/10- внутренний to рендерит ссылку с href
- aria-label пробрасывается на корень — доступное имя иконочных ссылок
- внешний https URL: авто-определение → target=_blank + rel noopener noreferrer
- external=true явно помечает ссылку внешней: a + target=_blank + rel
- external=false для внешнего URL — без _blank
- flow добавляет модификатор и общий класс градиентного текста, без флага — их нет
- flow на внешней ветке: модификатор и на <a> внешней ссылки
- a11y внутренняя ссылка с текстом — без axe-нарушений
- a11y внешняя ссылка (target=_blank + rel) — без axe-нарушений
- a11y иконичная ссылка: скрытая иконка + aria-label — без axe-нарушений
CoreSeparator
8/8- a11y горизонтальный разделитель (role=separator) — без axe-нарушений
- a11y с текстом-разделителем и vertical — без axe-нарушений
- рендерит role=separator с aria-orientation=horizontal по умолчанию
- label-проп рендерится текстом-разделителем
- слот default перекрывает label-проп
- vertical меняет aria-orientation и модификатор
- без label сегменты-линии не рендерятся
- flow даёт модификатор перелива, без флага — его нет
CoreChip
18/18- a11y обычный чип с текстом — без axe-нарушений
- a11y removable: × — именованная кнопка (i18n) — без axe-нарушений
- a11y clickable + selected (role=button, aria-pressed) — без axe-нарушений
- a11y два инстанса на странице — без axe-нарушений
- рендерит label
- рендерит слот default поверх label
- клик по × эмитит remove и не эмитит click
- removable без clickable — обычный span без роли button; имя × из i18n «Убрать: {label}»
- removable без label — имя × без хвоста «Убрать» (i18n removeOnly)
- без removable кнопки × нет; clickable вешает модификатор
- clickable: клик эмитит click
- clickable: Enter эмитит click
- clickable: Space эмитит click (клавиатурный контракт role=button)
- clickable + disabled: клик и Enter не эмитят click
- selected добавляет модификатор и aria-pressed
- removeAriaLabel переопределяет имя кнопки удаления (i18n)
- flow добавляет модификатор flow
- без flow модификатора flow нет
CoreCodeBlock
7/7- a11y подсвеченный блок — без axe-нарушений
- a11y состояние «Скопировано» — без axe-нарушений
- подсвечивает код на клиенте: shiki-вывод заменяет plain-фолбэк
- реалтайм: смена code перерисовывает подсветку без remount
- viewport — регион с tabindex и доступным именем (грамматика в имени)
- кнопка копирования рендерится с непустым именем
- lineNumbers: модификатор и .line-строки фолбэка; без пропа — нет
CoreBadge
12/12- a11y плашка с текстом — без axe-нарушений
- a11y цветной solid-вариант с текстом — без axe-нарушений
- a11y dot с текстовым дублированием статуса (1.4.1) — без axe-нарушений
- a11y два инстанса на странице — без axe-нарушений
- рендерит текст слота
- dot=true рендерит точку-декорацию (aria-hidden) и дублирует статус текстом
- dot без слота и label — без текста (декоративная точка)
- label — fallback слота и в dot-режиме, и в плашке
- не-neutral цвет прокрашивается через --badge-rgb
- flow добавляет модификатор flow
- без flow модификатора flow нет; в режиме dot flow игнорируется
- neutral использует fg/bg токены
CoreAvatar
14/14- a11y пустой аватар-заглушка — без axe-нарушений
- a11y картинка без alt с name — автоподстановка «Фото <name>» — без axe-нарушений
- a11y инициалы (role=img + имя) — без axe-нарушений
- a11y декоративный аватар с явным пустым alt — без axe-нарушений
- src рендерит NuxtImg (img) с src и alt
- без alt при name картинка получает автоподстановку «Аватар <name>» (i18n)
- явный alt перекрывает автоподстановку «Фото <name>»
- инициалы: корень role=img с полным именем вместо крипто-инициалов
- картинка с alt: корень без role — имя объявляет сам img
- одиночное имя даёт одну букву
- без src и name — пустой аватар без img
- size задаёт inline width/height
- size строкой — CSS-длина как есть, инициалы 0.4em
- shape square даёт модификатор, по умолчанию circle
CoreSkeleton
5/5- a11y заглушка rect (aria-hidden) — без axe-нарушений
- a11y формы circle и text — без axe-нарушений
- по умолчанию — rect-модификатор
- shape задаёт модификатор
- скрыт от доступности — aria-hidden
CoreImage
7/7- a11y смысловая картинка с alt — без axe-нарушений
- a11y декоративная картинка с явным пустым alt — без axe-нарушений
- рендерит <img> с исходным src и alt
- дефолт loading="lazy" — без явного пропа
- loading=eager перезаписывает дефолт — для LCP-изображений
- width/height попадают в атрибуты — браузер резервирует место без CLS
- format/quality не ломают рендер
CoreTextShimmer
5/5- a11y span-перелив — без axe-нарушений (текст остаётся реальным контентом)
- a11y заголовок с переливом — без axe-нарушений
- рендерит текст в дефолтном span
- as меняет тег, duration попадает в CSS-переменную
- кастомные from/to перекрывают дефолтные цвета
CoreReveal
7/7- рендерит слот в DOM без ошибок (SSR-совместимый маунт)
- рендерится с direction="none" и кастомным тегом
- SSR-разметка содержит контент и класс core-reveal — CSS-страховка (reduced-motion/no-JS) цепляется за него
- amount: дефолт 0.5, значение и кламп к [0,1] попадают в inViewOptions Motion
- prefers-reduced-motion: контент сразу видим, без opacity:0
- a11y блок с появлением — без axe-нарушений
- a11y интерактивный контент внутри — без axe-нарушений
CoreSortableList
16/16- дефолт: li + ручка + контент из default-слота
- #item: полная замена — li потребителя, дефолтная ручка не рендерится
- live-регион — role=status
- disabled: ручки не рендерятся
- без default-слота — фолбэк: текст itemLabel
- Enter/ArrowDown/Enter переставляет элементы
- Escape отменяет: возврат на позицию захвата
- Enter/ArrowDown/Enter через @keydown="onKeydown"
- grabbed-состояние доходит до потребителя (класс/проп)
- variant=dashed — модификатор на li; solid — нет
- dragByItem: ручки-кнопки нет, li фокусируемый с data-sortable-handle
- dragByItem: клавиатурный реордеринг с фокуса на li
- dragByItem + disabled: li без фокуса и ручек
- заблокированный элемент: затемнён, aria-disabled, без ручки и tabindex
- заблокированный элемент не стартует drag клавиатурой
- входной массив не мутируется — эмитится новый
Данные
122CoreAmount
7/7- форматирует значение с разделителями групп
- currency=USD меняет вывод — появляется код/символ доллара
- currency=EUR даёт другой вывод, отличимый от USD
- слоты prefix/postfix рендерятся по сторонам от значения
- без слотов prefix/postfix блоки не рендерятся
- a11y сумма по умолчанию — без axe-нарушений
- a11y flow-сумма с префиксом/постфиксом — без axe-нарушений
CoreDate
6/6- рендерит <time> с машинным datetime (ISO) и непустым текстом
- принимает ISO-строку и timestamp как дату
- format="time" рендерит время, а не дату — без года
- as=span убирает тег time и datetime-атрибут
- a11y дата по умолчанию (<time datetime>) — без axe-нарушений
- a11y flow-дата с форматом времени — без axe-нарушений
CoreProgress
10/10- конечное значение: aria-valuenow и ширина fill
- value вне диапазона клампится в 0–100 в ширине fill
- undefined — indeterminate: aria-valuenow нет, есть модификатор
- без label имя подставляется из i18n — progressbar всегда назван
- showValue рендерит округлённый процент
- showValue без значения не рендерит процент в indeterminate-режиме
- value=150: aria-valuenow и числовой показатель клампятся к 100
- a11y indeterminate по умолчанию — без axe-нарушений
- a11y indeterminate без label — имя из i18n-фолбэка, без axe-нарушений
- a11y конечный прогресс с видимым процентом — без axe-нарушений
CoreCircularProgress
11/11- конечное значение: aria-valuenow и dashoffset по дуге
- без label имя подставляется из i18n — progressbar всегда назван
- value вне диапазона клампится в 0–100
- undefined — indeterminate: aria-valuenow нет, есть модификатор, дуга короче
- showValue рендерит округлённый процент в центре
- showValue в indeterminate-режиме не рендерится
- flow рендерит градиент и ссылается на него в stroke
- a11y indeterminate по умолчанию — без axe-нарушений
- a11y конечный прогресс с процентом в центре — без axe-нарушений
- a11y indeterminate без label — имя из i18n-фолбэка, без axe-нарушений
- a11y два инстанса на странице — без axe-нарушений (дубликаты id градиента ловятся axe)
CoreRating
13/13- a11y интерактивный слайдер — без axe-нарушений
- a11y readonly role=img — без axe-нарушений
- интерактивный режим рендерит слайдер с aria-значениями и valuetext «3 из 5»
- readonly рендерит role=img без слайдера и aria-label с числами
- клик по 4-й звезде устанавливает модель 4 и valuenow
- повторный клик по текущей последней звезде снимает её (4 → 3)
- стрелки меняют значение: →/↑ +1, ←/↓ −1, valuetext следует
- Home/End — края шкалы, стрелки не выходят за 0..max
- readonly звёзды не имеют модификатора --filled у пустых
- pointerleave сбрасывает hover-превью (звёзды не остаются подсвеченными)
- jsonLd: эмиссит AggregateRating с @id, reviewCount и кастомным max
- значение 0 при переданном jsonLd — script не эмиссится
- без jsonLd script не эмиссится
CoreDataTable
20/20- a11y таблица с caption — без axe-нарушений
- a11y пустая таблица — без axe-нарушений
- a11y выбор + частичный mixed + сортировка + row-заголовок + действия — без axe-нарушений
- заголовки рендерятся из columns в semantic thead
- ячейки берут значения из rows по key, null показывается тире
- кастомный слот ячейки получает row и value
- пустые rows — строка colspan с текстом «Нет данных» (i18n)
- пустые rows со слотом #empty рендерят его содержимое
- caption рендерится как подпись таблицы
- скролл-регион: role=region, tabindex=0 и имя из caption
- ariaLabel именует таблицу и регион, видимого caption нет
- rowHeader-колонка рендерится в tbody как th scope="row"
- сортировка: aria-sort на th, эмит sort переключает направление
- несортируемая колонка не получает aria-sort и кнопку
- выбор: «выбрать все» эмитит все ключи, частичный выбор — mixed (indeterminate)
- выбор: полный выбор — чекбокс отмечен и не mixed, снятие эмитит пустой массив
- действия: именованные кнопки «label: строка», эмит action с row
- пустая строка учитывает колонки выбора и действий в colspan
- loading: indeterminate-прогресс вместо тела, empty не показывается, aria-busy
- без loading — обычное поведение: empty-строка, aria-busy нет
CoreEmptyState
9/9- рендерит title и description
- без icon-пропа CoreIcon не рендерится (только кружок-заглушка)
- с icon-проп рендерится CoreIcon
- слот actions рендерится и кнопка кликабельна
- слот default заменяет description
- слот #title рендерится вместо пропа (раньше молча отбрасывался)
- flow включает перелив у иконки, без флага — его нет
- a11y с иконкой, заголовком и описанием — без axe-нарушений
- a11y с действием в слоте actions — без axe-нарушений
CoreAccordion
17/17- a11y закрытый аккордеон — без axe-нарушений
- a11y открытая секция — без axe-нарушений
- a11y два инстанса на странице — без axe-нарушений (дубликаты id ловятся axe)
- клик по хедеру открывает секцию — aria-expanded и видимость панели
- повторный клик закрывает секцию
- content секции — фолбэк панели, когда слот не передан (MDC-контент)
- слот перекрывает content секции
- multiple=false: клик по второй секции закрывает первую, модель — один ключ
- multiple=true: обе секции открыты одновременно
- disabled-секция не раскрывается
- rotate-icon=false не поворачивает индикатор открытой секции
- по умолчанию индикатор открытой секции повёрнут
- два аккордеона на странице: id панелей уникальны, aria-controls указывает на свою панель
- панель — role=region с именем секции, хедер — button (Enter/Space активируют нативно)
- jsonLd: эмиссит FAQPage из title+слота; disabled и пустые ответы не попадают
- все ответы пустые — script не эмиссится
- без jsonLd script не эмиссится
CoreResizable
8/8- a11y горизонтальный сплит по умолчанию — без axe-нарушений
- a11y вертикальный сплит после клавиатурного изменения — без axe-нарушений
- панель 1 имеет flex-basis из модели, обе панели рендерят слоты
- девайдер — separator с инвертированной aria-orientation и метриками модели
- ArrowRight на девайдере увеличивает модель на 2
- ArrowLeft уменьшает модель на 2
- стрелка за пределы диапазона не превышает max
- Home/End перемещают модель к min/max
CoreCountUp
8/8- a11y счётчик по умолчанию — без axe-нарушений
- a11y flow-перелив числа — без axe-нарушений
- рендерит начальное значение сразу, без анимации с нуля
- анимирует изменение value до нового значения
- учитывает prefix/suffix и decimals
- count-on-mount: после маунта считает с нуля до значения
- count-on-mount: при prefers-reduced-motion значение сразу финальное
- flow включает модификатор перелива, без флага — его нет
CoreImageCompare
4/4- позиция modelValue управляет clip-path и ручкой
- инпут range эмитит update:modelValue и несёт aria-label
- pointer-drag по figure двигает разделитель (тач/мышь)
- рендерит оба изображения с alt-подписями
CoreMarquee
9/9- a11y бегущая строка — без axe-нарушений
- a11y именованная строка (role=region) — без axe-нарушений
- дублирует слот для бесшовного цикла, дубль aria-hidden
- передаёт duration в CSS-переменную и модификаторы трека
- без pauseOnHover трек не получает модификатор static
- прокидывает aria-label на корень
- flowRandom подсвечивает пары копий синхронно и снимает подсветку при unmount
- пара переливается, пока видна хоть одна копия, и снимается, когда обе ушли из кадра
- без flowRandom элементы не подсвечиваются
Формы
262CoreInput
20/20- v-model: setValue обновляет value и эмитит update:modelValue
- label связана с input через for/id
- без label подпись не рендерится
- error показывает текст с role=alert и aria-invalid=true
- без error aria-invalid отсутствует
- disabled блокирует ввод
- size-модификаторы применяются на контроле
- слот #leading рендерит зону-адорнмент и вешает отступ на input
- слот #trailing рендерит зону-адорнмент и вешает отступ на input
- без слотов зоны-адорнменты не рендерятся
- размер sm сужает зону и паддинг
- пустой слот #trailing (рендерит только пустоту) не рисует зону
- autocomplete пробрасывается в атрибут input
- flow вешает класс градиентного поля на контрол
- id и describedBy попадают на нативный input; без id генерируется и связывает label
- автономная ошибка связывается через aria-describedby с внутренним id
- внутренняя ошибка и внешний describedBy объединяются в aria-describedby
- a11y default с label и autocomplete — без axe-нарушений
- a11y error — без axe-нарушений (aria-invalid + связанная role=alert)
- a11y disabled — без axe-нарушений
CoreTextarea
11/11- v-model: setValue обновляет value и эмитит update:modelValue
- label связана с textarea через for/id
- error показывает текст с role=alert и aria-invalid=true
- disabled запрещает редактирование
- size-модификаторы применяются на контроле
- autocomplete пробрасывается в атрибут textarea
- без error aria-invalid и aria-describedby отсутствуют
- автономная ошибка связывается через aria-describedby; внешний describedBy объединяется
- a11y default с label — без axe-нарушений
- a11y error — без axe-нарушений (aria-invalid + связанная role=alert)
- a11y disabled — без axe-нарушений
CoreSelect
10/10- v-model: setValue выбирает option и эмитит update:modelValue
- label связана с select через for/id
- error показывает текст с role=alert и aria-invalid=true
- disabled запрещает выбор
- describedBy прокидывается в aria-describedby нативного select
- ошибка связывается через aria-describedby (описание + id ошибки), как в Input
- FormGroup-контракт: label через labelFor/id, hint — через aria-describedby
- a11y нативный select с label — без axe-нарушений
- a11y ошибка (aria-invalid + role=alert) — без axe-нарушений
- a11y два инстанса на странице — без axe-нарушений (дубликаты id ловятся axe)
CoreCheckbox
11/11- a11y чекбокс в label со слотом-подписью — без axe-нарушений
- a11y indeterminate (aria-checked="mixed") — без axe-нарушений
- a11y группа «выбрать все» (fieldset + indeterminate-родитель) — без axe-нарушений
- клик переключает модель false → true
- клик переключает модель true → false
- disabled не переключает модель
- label-слот рендерится и кликабелен (нативный label оборачивает input)
- indeterminate: aria-checked="mixed" и DOM-свойство indeterminate
- без indeterminate aria-checked не переопределён — работает нативная семантика
- describedBy прокидывается в aria-describedby
- error: aria-invalid + модификатор рамки danger; без error — нет
CoreRadio
9/9- a11y радио в label со слотом-подписью — без axe-нарушений
- a11y группа fieldset+legend с одинаковым name — без axe-нарушений
- a11y ошибка (aria-invalid) — без axe-нарушений
- клик выбирает значение: model = value
- группа с одной моделью: выбор одного снимает другой
- disabled не выбирает значение
- error проставляет aria-invalid на контроле
- name прокидывается на input — одинаковое name в группе даёт нативную навигацию стрелками
- describedBy прокидывается в aria-describedby
CoreSwitch
10/10- клик переключает модель и aria-checked
- label-обёртка: клик по тексту подписи тоже переключает
- disabled не переключает
- ariaLabel именует переключатель без видимой подписи
- ariaLabel именует кнопку и при label-обёртке (переопределяет имя из label)
- size задаёт модификатор размера
- a11y без подписи, с aria-label — без axe-нарушений
- a11y с видимым label — без axe-нарушений
- a11y включённое состояние — без axe-нарушений
- a11y два инстанса на странице — без axe-нарушений
CoreToggle
11/11- a11y выключенный с текстовым слотом — без axe-нарушений
- a11y нажатое состояние — без axe-нарушений
- a11y icon-only с aria-label атрибутом — без axe-нарушений
- a11y два инстанса на странице — без axe-нарушений
- клик переключает модель и aria-pressed
- modelValue прокидывается извне — aria-pressed следует за пропом
- variant-классы применяются (soft по умолчанию, outline по пропу)
- клик по нажатому переключает состояние обратно в pressed-модификаторе
- disabled не переключает
- нативный button type=button (не сабмитит форму)
- aria-label атрибутом прокидывается на кнопку (icon-only переключатель)
CoreToggleGroup
10/10- a11y группа без выбора — без axe-нарушений
- a11y группа с выбранным пунктом — без axe-нарушений
- клик выбирает пункт: aria-pressed и модель обновляются
- повторный клик по активному сохраняет выбор (не снимает)
- block растягивает группу — модификатор на контейнере
- disabled-пункт не выбирается
- roving tabindex: без выбора tab-стоп у первого доступного, остальные -1
- roving tabindex: выбранный пункт становится единственным tab-стопом
- стрелки двигают фокус и выбирают пункт; disabled пропускается
- Home/End прыгают к первому/последнему доступному пункту
CoreRange
12/12- рендерит по одному инпуту на бегунок; дефолтные имена — «от»/«до» (i18n)
- labels задает aria-label бегунков по индексу
- нативные range-инпуты несут min/max/step — слайдер-семантика без ARIA-дублей
- левый бегунок упирается в правый и не толкает его
- правый бегунок упирается в левый и не толкает его — симметрично
- снапит значение к шагу
- средний бегунок учитывает обоих соседей
- нормализует внешнее значение: сортировка, нестандартные min/max, зазоры
- один бегунок: значение меняется, заливка отсутствует
- flow вешает модификатор на заливку
- a11y интервал из двух бегунков — без axe-нарушений
- a11y мульти-бегунки с disabled — без axe-нарушений
CoreFormGroup
11/11- label рендерится и связывается с контролом через for
- без label метка не рендерится — слот работает самостоятельно
- error перекрывает hint и объявляется role=alert
- required добавляет звёздочку к метке
- без required звёздочки нет
- required озвучивается словом: sr-only текст в метке (символ+слово, H90)
- scoped-slot describedBy указывает на id ошибки; контрол получает aria-describedby
- без ошибки describedBy указывает на id подсказки
- a11y label + hint + контрол с describedBy — без axe-нарушений
- a11y required + error — без axe-нарушений
- a11y два инстанса на странице — без axe-нарушений (дубликаты id ловятся axe)
CorePinInput
13/13- ввод «1» в первую ячейку обновляет модель и переводит фокус во вторую
- ввод всех 4 цифр собирает полный код в модели
- Backspace на пустой ячейке переводит фокус на предыдущую и очищает её
- удаление средней цифры очищает ячейку, не сдвигая соседние
- paste в среднюю ячейку раскладывает код с первой ячейки
- ArrowLeft/Right перемещают фокус между ячейками
- paste «99» в первую ячейку заполняет ячейки 1–2 и модель начинается с «99»
- disabled блокирует все ячейки
- первая ячейка — autocomplete one-time-code, остальные — нет
- группа и ячейки имеют aria-label: «Код подтверждения», «Цифра N» (i18n)
- a11y пустые ячейки — без axe-нарушений
- a11y частично заполненный код — без axe-нарушений
- a11y disabled — без axe-нарушений
CoreNumberField
15/15- клик по «+» увеличивает модель на step
- клик по «−» на границе min не уходит ниже — кнопка disabled
- клик по «+» на границе max не уходит выше — кнопка disabled
- ручной ввод «7» при min=0 step=1 обновляет модель числом 7
- ручной ввод выше max ограничивается max
- disabled блокирует input и кнопки
- label связывается с input через id
- error: aria-invalid + композиция describedBy/errorId в aria-describedby + danger-класс
- кнопки шага имеют локализованные имена (aria-label из i18n)
- нативный number-инпут: без дублирующего aria-valuenow, min/max/step — HTML-атрибуты
- flow-зеркало (градиентная цифра-дубль) скрыто от скринридеров
- min=0 не отключает сетку шага: ввод 3 при step=5 округляется к 5
- ввод вне max клампится в поле сразу; рассинхрон дисплея чистится на blur
- a11y поле с label — без axe-нарушений
- a11y flow с зеркалом и disabled — без axe-нарушений
CoreKbd
5/5- a11y клавиша по умолчанию — без axe-нарушений
- a11y flow-чип с размером lg — без axe-нарушений
- рендерит текст клавиши из слота
- применяет модификатор размера core-kbd--sm
- по умолчанию размер md
CoreHotkey
9/9- a11y комбинация по умолчанию — без axe-нарушений
- a11y flow-перелив клавиш — без axe-нарушений
- вызывает action по комбинации mod+k (не mac → ctrl)
- русская раскладка: e.key=к, e.code=KeyK — combo срабатывает
- не вызывает action при лишнем модификаторе
- disabled=true — action не вызывается
- рендерит CoreKbd на каждую клавишу с человекочитаемыми метками
- снимает слушатель при unmount
- flow включает перелив на всех клавишах, без флага — его нет
CoreAutocomplete
23/23- ввод фильтрует items (contains-match, без точного совпадения с моделью)
- выбор варианта по mousedown обновляет модель и закрывает список
- клавиатура: ArrowDown открывает список, Enter выбирает активный вариант
- Escape закрывает список, aria-expanded отражает состояние
- blur закрывает список и сбрасывает aria-expanded
- активная опция связана с input через aria-activedescendant и id
- label связана с input через for/id
- combobox-атрибуты: haspopup=listbox, controls только при открытом списке, ошибка связывается через aria-describedby
- нет совпадений: список закрыт, aria-expanded=false, статус role=status объявляет результат
- Escape скрывает и статус «нет результатов», и открытый список
- клавиатура: Home/End переносят на первую/последнюю опцию
- single: выбор объектного элемента кладёт в модель value, а поле показывает label
- single: повторный фокус после выбора открывает список, а не фильтрует по value
- multiple: выбор двух элементов даёт модель [v1, v2] и рендерит чипы
- multiple: выбранные исключены из выпадающего списка
- multiple: remove чипа убирает value из модели
- multiple: Enter добавляет активный вариант, Backspace в пустом вводе удаляет последний
- multiple: выбор последнего варианта закрывает список тихо — без статуса «нет результатов»
- multiple: строковые items работают, value = сама строка
- a11y закрытый combobox с label — без axe-нарушений
- a11y открытый список (combobox/listbox/option, aria-expanded) — без axe-нарушений
- a11y нет совпадений (role=status вместо списка) — без axe-нарушений
- a11y два инстанса на странице — без axe-нарушений (дубликаты id ловятся axe)
CoreMoneyInput
15/15- a11y поле с подписью и валютой — без axe-нарушений
- a11y ошибка — без axe-нарушений
- a11y два инстанса на странице — без axe-нарушений (дубликаты id ловятся axe)
- ввод 1234567 → модель 1234567, дисплей с группировкой разрядов
- запятая приводится к точке, decimals=1 обрезает дробную часть
- пошаговый ввод: разделитель не съедается («1.» остаётся, «1.5» → 1.5)
- буквы и лишние символы отфильтровываются
- blur зажимает значение к max
- blur зажимает значение к min
- пустое значение → модель null
- error показывает сообщение с role=alert и aria-invalid
- currency-суффикс рендерится справа внутри поля
- label связана с input через for/id
- inputmode=decimal на денежном поле
- валюта доступна программно: id суффикса входит в aria-describedby
CorePhoneInput
18/18- пустая модель достраивается до значения по умолчанию (RU, +7)
- внешний сброс модели (undefined) очищает поле и не пишет значение обратно
- выбор страны обновляет iso/code, сбрасывает value и закрывает дропдаун
- ввод цифр форматируется по AsYouType (RU national)
- international=true форматирует с кодом страны и плюсом
- valid=true на валидном номере, false на мусоре
- countryCodes фильтрует список стран
- ввод с «+» определяет страну по коду и не сбрасывает номер
- после смены страны ввод +7917 переключает на RU
- error рендерится с role=alert и aria-invalid
- disabled блокирует input и кнопку страны
- после выбора страны фокус переходит в поле ввода
- кнопка страны: имя с действием «Выбрать страну», aria-expanded/aria-controls связывают с дропдауном
- input: inputmode=tel, autocomplete зависит от формата (tel-national / tel)
- a11y поле с подписью — без axe-нарушений
- a11y открытый дропдаун стран — без axe-нарушений
- a11y ошибка и disabled — без axe-нарушений
- a11y два инстанса на странице — без axe-нарушений (дубликаты id ловятся axe)
CoreDateField
13/13- a11y поле даты с подписью — без axe-нарушений
- a11y ошибка и блокировка — без axe-нарушений
- a11y type=time с иконкой — без axe-нарушений
- v-model: setValue обновляет модель, ввод шлёт update:modelValue
- label связан с input через for/id
- error рендерит role=alert и aria-invalid
- min/max пробрасываются в атрибуты нативного input
- type рендерит нужный тип нативного input
- disabled рендерит атрибут
- size рендерит модификатор
- describedBy пробрасывается в aria-describedby нативного input
- error связывается: описание + id сообщения (role=alert) в одном aria-describedby
- readonly рендерит атрибут нативного input
CoreFileUpload
15/15- change на input добавляет файл в модель и рендерит имя и размер
- превью — img с objectURL для image-файлов
- max: сверх лимита файлы отклоняются с reason и не растят модель
- maxSize: слишком большой файл отклоняется с reason
- accept="image/*" отклоняет text/plain
- дубликат (name+size+lastModified) не добавляется повторно
- удаление по клику убирает файл из модели
- drop на дропзону добавляет файлы, dragover включается
- label и hint рендерятся, disabled отключает input и дропзону
- дропзона — label нативного input[type=file]: связка даёт имя и активацию
- изменение набора файлов озвучивается живой областью role=status
- кнопка удаления файла называет удаляемый файл
- a11y дропзона с подписью и подсказкой — без axe-нарушений
- a11y с выбранными файлами — без axe-нарушений
- a11y disabled — без axe-нарушений
CoreForm
21/21- рендерит title формы как h3; без title — не рендерит
- рендерит поля всех типов с лейблами и связкой label↔контрол
- лейаут: span >= columns → --full, span < columns → --span-N
- лейаут: 3 колонки — span 1 база, span 2 в 2 колонки, span 3 вся строка
- лейаут: 4 колонки — span 3 в 3 колонки, span 4 вся строка
- группы: fieldset+legend на группу, своя сетка колонок
- без groups — одна неявная группа (единый fieldset, плоские поля)
- groups выигрывают у fields при задаче обоих
- конфиг-строка (JSON) эквивалентна объекту
- динамическое добавление поля в конфиг рендерит его (живое редактирование)
- пустой сабмит: ошибки у required-полей, fetch не вызван, фокус-таргет aria-invalid
- checkbox checked-семантика: unchecked невалиден, checked валиден
- подпись чекбокса: [текст](URL) рендерится ссылкой
- опасная схема в ссылке рендерится plain-текстом без якоря
- успех: POST JSON, success-текст из конфига, reset, emit success
- HTTP 500: error-текст из конфига, emit error со статусом
- сетевая ошибка: error-текст, emit error без статуса
- emit submit происходит до валидации и до fetch
- a11y pristine форма — без axe-нарушений
- a11y после пустого сабмита: ошибки полей — без нарушений, один alert на поле
- a11y после успешного сабмита: статус role="status" — без нарушений
Навигация
101CoreHeader
7/7- рендерит слот внутри header
- header — landmark banner: тег header с классом core-header
- скрытый hide-on-scroll хедер получает inert — уходит из таба (WCAG 2.4.7)
- useHeaderState: после монтирования visible=true (без hideOnScroll)
- blur: класс core-header--blur только при пропе blur
- a11y пустой header — без axe-нарушений
- a11y шапка с nav+aria-label, бургером и icon-only кнопками — без axe-нарушений
CoreTabs
18/18- a11y табы без панели — без axe-нарушений
- a11y табы с панелью (tabpanel + aria-связки) — без axe-нарушений
- a11y два инстанса с панелями на странице — без axe-нарушений (дубликаты id ловятся axe)
- активный таб: aria-selected=true и tabindex=0, остальные -1
- клик по табу меняет модель
- ArrowRight двигает выбор по кругу, пропуская disabled
- Home/End перемещают на первый/последний доступный таб
- после стрелки фокус переезжает на новый активный таб
- без v-model tabindex=0 у первого доступного таба — tablist доступен с клавиатуры
- модель указывает на disabled-таб — tabindex=0 остаётся у первого доступного
- flow включает модификатор перелива на корне, без флага — его нет
- слот панели: tabpanel с aria-labelledby на активный таб, табы — aria-controls на панель
- переключение таба меняет контент панели и aria-labelledby
- именованные слоты по значениям: панель показывает контент активного таба
- named-слот активного таба приоритетнее scoped default
- именованные слоты без default: панель есть, табы ссылаются на неё
- без слота панели нет: tabpanel не рендерится, aria-controls не проставляется
- id табов уникальны между инстансами (useId в рамках одного приложения)
CorePagination
16/16- a11y первая страница — без axe-нарушений
- a11y середина списка с эллипсами — без axe-нарушений
- a11y два инстанса на странице — без axe-нарушений (label различает ориентиры nav)
- начальная страница подсвечена через aria-current
- клик по «Страница 3» эмитит update:modelValue 3
- клик по текущей странице ничего не эмитит
- prev disabled на странице 1, next disabled на последней
- при pageCount > 7 появляются эллипсы, края всегда кликабельны
- клик next увеличивает страницу
- клик prev уменьшает страницу
- pageCount=0: ни страниц, ни эллипса, обе стрелки заблокированы
- рассинхрон: страница 10 при pageCount=5 — «назад» клампится к 5, без мёртвых кликов
- обёртка nav имеет aria-label, prev/next — доступные имена (i18n)
- эллипс — span с aria-hidden, вне tab-последовательности
- label переопределяет aria-label навигации (различимые ориентиры при двух пагинациях)
- flow включает модификатор перелива на корне, без флага — его нет
CoreSlider
9/9- монтируется без ошибок со слотом панелей
- карусель — role=region с roledescription «карусель», именем и tabindex=0
- ArrowRight/ArrowLeft листают панели через next/prev
- декларативные плагины: ленивый импорт и addPlugins на инстансе
- без флагов плагины не применяются
- смена конфига плагинов пересобирает набор на инстансе
- cameraClass садится на камеру с панелями
- a11y карусель с панелями — без axe-нарушений
- a11y карусель с disabled-контентом внутри панелей — без axe-нарушений
CoreStepper
15/15- активный шаг имеет aria-current="step"
- шаги до текущего помечены --done, после — --disabled-future
- done-шаг показывает галочку (CoreIcon success)
- clickable: клик по завершённому шагу эмитит его индекс
- clickable: кнопка завершённого шага называется «Вернуться к шагу N» (i18n)
- clickable: клик по будущему шагу переводит вперёд
- clickable: кнопка будущего шага называется «Перейти к шагу N» (i18n)
- block растягивает степпер на всю ширину
- без clickable завершённые шаги не кликабельны
- description рендерится под label
- flow включает модификатор перелива на корне, без флага — его нет
- jsonLd: эмиссит HowTo с name, шагами из items и position с 1
- без jsonLd script не эмиссится
- a11y обычные шаги — без axe-нарушений
- a11y clickable с завершёнными шагами — без axe-нарушений
CoreBreadcrumbs
12/12- nav с доступным именем и списком уровней
- последний уровень — текст с aria-current="page", не ссылка
- промежуточные уровни — ссылки с href; текущая — без href
- разделитель — псевдоэлемент ::before у li, начиная со второго
- items длиннее maxItems: средние уровни скрыты, на их месте «…»
- меню «…» содержит скрытые уровни ссылками (переданы в CoreDropdownMenu)
- кнопка «…» имеет доступное имя (не пустой символ)
- без сворачивания меню не рендерится вовсе
- последний элемент с to всё равно рендерится текстом (aria-current)
- itemsBefore/itemsAfter управляют диапазоном сворачивания
- a11y полная тропа — без axe-нарушений
- a11y свернутая тропа с меню «…» — без axe-нарушений
CoreScrollRow
8/8- a11y Flicking-режим: карусель-регион с панелями — без axe-нарушений
- после монтирования рендерит CoreSlider (Flicking-режим) с панелями слота
- пробрасывает label в CoreSlider: регион назван
- дефолтные опции: bound=true, align=prev передаются в Flicking
- align из пропа мержится в опции (center перекрывает дефолт)
- доп. опции потребителя мержятся поверх дефолта
- панели слота не теряют класс flicking-panel (контракт Flicking)
- cameraClass уходит в CoreSlider (камера) и на её элемент
CoreFooter
1/1- рендерит слот внутри footer
CoreBottomNavigation
15/15- a11y nav-landmark с пунктами — без axe-нарушений
- a11y активный пункт (aria-current) и flow — без axe-нарушений
- a11y кастомный aria-label у nav — без axe-нарушений
- рендерит все пункты
- пункт с to рендерится ссылкой
- пункты без to — кнопки
- v-model выбирает пункт: активный получает модификатор
- nav — landmark с aria-label; по умолчанию i18n «Основная навигация»
- ariaLabel переопределяет имя nav (i18n на стороне потребителя)
- активный пункт-ссылка — aria-current="page", неактивный — без aria-current
- активный пункт-кнопка (v-model) — aria-current="true"
- клик по пункту с value эмитит update:modelValue
- flow включает модификатор перелива на корне, без флага — его нет
- рендерит иконки пунктов через CoreIcon
- слот center делит пункты на зоны: слева первая половина, справа вторая
Оверлеи
107CoreCommandPalette
8/8- a11y открытая палитра (combobox + listbox/option) — без axe-нарушений
- a11y «нет результатов» — role=status без axe-нарушений
- группировка: заголовки групп в DOM, безгрупповые пункты первыми
- нечёткий фильтр: subsequence-матч оставляет пункт; мусорный запрос — empty-блок
- ArrowDown меняет aria-activedescendant; Enter вызывает onSelect и закрывает палитру
- контракт combobox: aria-controls ведёт к listbox, aria-selected — на активном option
- «нет результатов» — role=status, активация только Enter (не по фокусу)
- hotkey: Mod+K открывает и закрывает палитру
CoreModal
18/18- a11y закрытая модалка (страница с триггером) — без axe-нарушений
- a11y открытая с заголовком и описанием — без axe-нарушений
- a11y открытая с кастомной шапкой — sr-only имя на месте, без axe-нарушений
- v-model=true открывает панель и кладёт запись в стек оверлеев
- v-model=false (после true) закрывает панель и убирает запись из стека
- title рендерится и связывается через aria-labelledby
- Esc (window keydown) закрывает изначально открытую модалку
- Esc закрывает модалку, открытую после монтирования (изменением modelValue)
- открытие через useOverlays эмитит update:modelValue=true
- flowBorder даёт панели модификатор перелива, без флага — его нет
- description рендерится sr-only и связывается через aria-describedby; без description атрибута нет
- кастомная шапка: sr-only копия title держит aria-labelledby (ссылка не повисает)
- initialFocus: открытие ставит фокус на элемент по селектору
- фокус-контракт: открытие — фокус в панель (tabindex=-1), закрытие — возврат на триггер
- Esc закрывает только верхний слой, нижний остаётся открытым
- z-index слоёв следует порядку открытия в стеке
- unmount открытой модалки чистит стек и снимает scroll-lock
- фокус: открытие — внутрь диалога, закрытие — возврат на триггер
CoreDrawer
12/12- a11y закрытая шторка (страница с триггером) — без axe-нарушений
- a11y открытая шторка — role=dialog с именем, кнопка закрытия, без axe-нарушений
- a11y открытая expandable-шторка — полоска-кнопка с aria-expanded, без axe-нарушений
- open(name): панель появляется в портале с направлением bottom
- close(name): панель исчезает
- side=right: атрибут направления right
- expandable: тело капнуто 30dvh, классы панели и полоски
- v-model: modelValue=true открывает в стеке, закрытие из стека эмитит false
- aria-связки диалога выживают после телепорта: role/aria-modal/labelledby→title
- кнопка закрытия: доступное имя и клик закрывают шторку (клавиатурная альтернатива свайпу)
- expandable-полоска — кнопка: role/tabindex/aria-label, Enter и Space переключают разворот
- полоска обычной шторки декоративна: aria-hidden, без роли и табиндекса
CoreSidebar
9/9- a11y закрытый сайдбар отсутствует в DOM (исключён из tab-порядка)
- a11y открытый сайдбар (dialog + nav-контент) — без axe-нарушений
- v-model=true открывает панель (dialog в DOM), v-model=false закрывает
- контракт диалога: role=dialog, aria-modal, aria-labelledby, id панели; открытой панели inert нет
- side=left даёт модификатор панели --left (по умолчанию --right)
- слот footer рендерится в подвале панели
- Esc закрывает изначально открытый сайдбар
- Esc закрывает сайдбар, открытый после монтирования
- side=bottom даёт модификатор --bottom и открывает панель в DOM
CorePopover
13/13- клик по триггеру открывает панель, повторный клик закрывает
- placement даёт модификатор панели
- v-model синхронизируется в обе стороны
- слот content получает close: клик по нему закрывает поповер
- pointerdown вне компонента закрывает открытый поповер
- Esc закрывает открытый поповер
- aria-контракт триггера-кнопки: aria-haspopup/expanded/controls на элементе слота, панель — диалог с именем
- popup="menu": aria-haspopup=menu, панель без роли — семантику даёт контент
- ArrowDown открывает popup="menu" с закрытого триггера; popup="dialog" стрелки не перехватывает
- текстовый слот: обёртка role=button с tabindex и Enter-активацией
- a11y закрытый (кнопка-триггер) — без axe-нарушений
- a11y открытый — role=dialog с именем из триггера, без axe-нарушений
- a11y открытый с текстовым триггером (обёртка role=button) — без axe-нарушений
CoreDropdownMenu
17/17- a11y закрытое меню — без axe-нарушений
- a11y открытое меню (menu/menuitem, roving tabindex) — без axe-нарушений
- a11y текстовый триггер (fallback-кнопка Popover) — без axe-нарушений
- a11y два инстанса на странице — без axe-нарушений (дубликаты id ловятся axe)
- клик по триггеру рендерит пункты; divider — separator, не menuitem
- клик по пункту эмитит select с item и закрывает меню
- disabled-пункт: aria-disabled без нативного disabled, активация блокируется
- открытие ставит фокус на первый пункт; tabindex — roving
- ArrowDown/ArrowUp по кругу, Home/End — первый/последний пункт
- открытие ArrowDown фокусирует первый пункт, ArrowUp — последний
- hover-открытие не крадёт фокус со страницы; стрелка с триггера входит в меню
- type-ahead: буква переводит фокус на следующий пункт с этой буквы
- Esc закрывает меню и возвращает фокус на триггер
- пункты-переключатели: menuitemcheckbox/menuitemradio с aria-checked
- пункт с to рендерится ссылкой с href
- слот content заменяет список пунктов
- v-model=true открывает меню; выбор пункта закрывает и эмитит false
CoreConfirmDialog
11/11- confirm() открывает диалог с title и message
- контракт Modal: role=dialog, aria-modal, aria-labelledby на заголовок
- кнопки всегда с явными именами: дефолт «Отмена»/«Подтвердить»
- фокус при открытии — на кнопке «Отмена» (наименее деструктивное)
- клик «Подтвердить» резолвит true и закрывает диалог
- клик «Отмена» резолвит false
- Esc закрывает диалог и резолвит false (через стек Modal)
- замена: confirm(A) затем confirm(B) — A резолвится false, диалог показывает B
- answer() после ответа — no-op: не бросает и не меняет состояние
- a11y открытый диалог (role=dialog, текстовые кнопки) — без axe-нарушений
- a11y диалог без message (только title) — без axe-нарушений
CoreSplashScreen
19/19- по умолчанию показан: role=status с локализованным именем, сетка aria-hidden
- label-пропс становится видимым подписью и именем статуса
- modelValue=false — экран не рендерится
- v-model: смена показа рендерит/убирает оверлей
- progress задаёт ширину полосы (0–100 клампится)
- paused ставит класс паузы волны
- фоновая вкладка ставит паузу сама, возврат снимает (SC 2.2.2)
- ячейки волны получают диагональные фазы (row+col)
- каскад появления: слоты 0/10/20…мс разданы случайно по ячейкам
- appear=sequence: каскад по порядку (0/10/20…)
- dismissible: Esc эмитит update:modelValue=false
- dismissible=false: Esc игнорируется
- Esc при скрытом экране ничего не эмитит
- слоты #logo и #label заменяют знак и подпись
- базовый слот заменяет весь центр — знак и подпись не рендерятся
- часть ячеек «дышит» масштабом — со своей длительностью и фазой
- вспышка: случайная дышащая ячейка горит 2с, следующая — через 100–300мс
- a11y показанный экран — без axe-нарушений
- a11y без подписи и прогресса — тоже без нарушений (status получает имя из i18n)
Обратная связь
49CoreNotices
14/14- a11y пустой контейнер-live-регион — без axe-нарушений
- a11y стек с success- и error-нотисами — без axe-нарушений
- push добавляет нотис в DOM: title, message и модификатор типа
- ttl=0: нотис живёт — таймер его не удаляет
- ttl>0: нотис удаляется по истечении
- dismiss(id) убирает конкретный нотис
- лимит: старые нотисы вытесняются при превышении
- клик по нотису закрывает его
- hover ставит TTL на паузу, mouseleave возобновляет
- свайп по горизонтали (dx>64) закрывает нотис
- малый сдвиг (dx=20) возвращает нотис на место и не закрывает
- вытеснение по лимиту не снимает TTL выживших: они закрываются по таймеру
- контейнер — постоянный live-регион: error в стеке → role=alert, иначе status
- close-кнопка имеет локализованное имя (aria-label из i18n)
CoreAlert
14/14- рендерит title и текст
- dismissible: клик по кнопке закрытия эмитит dismiss
- variant solid добавляет модификатор core-alert--solid
- dismissAriaLabel перекрывает i18n-дефолт имени кнопки закрытия
- без dismissible кнопки закрытия нет
- роль live-области: danger → alert, остальные цвета → status
- явный role перекрывает выбор по цвету
- danger подставляет авто-иконку error
- neutral не рендерит авто-иконку
- flow: модификатор core-alert--flow есть
- без flow модификатора core-alert--flow нет
- a11y нейтральное уведомление (role=status) — без axe-нарушений
- a11y danger (role=alert) со авто-иконкой — без axe-нарушений
- a11y dismissible с кнопкой закрытия — без axe-нарушений
CoreTooltip
11/11- a11y закрытый (триггер с aria-describedby) — без axe-нарушений
- a11y открытый по фокусу — role=tooltip на панели, без axe-нарушений
- панель скрыта, пока триггер не активировали
- триггер связан с панелью через aria-describedby
- pointerenter открывает панель с text ровно после openDelay
- pointerleave до истечения задержки отменляет показ
- focusin/focusout открывают и закрывают панель — подсказка доступна с клавиатуры
- слот #content перекрывает text
- id панели совпадает с aria-describedby триггера
- Esc скрывает открытую подсказку (клавиатурная альтернатива уводу фокуса/курсора)
- фокус внутри триггера не мигает панелью: focusout с relatedTarget в тот же триггер не закрывает
CoreHoverCard
10/10- pointerenter на триггер открывает панель после openDelay
- элементный триггер несёт aria-haspopup="dialog" и синхронный aria-expanded
- текстовый триггер: aria-haspopup остаётся на обёртке (фолбэк)
- наведение на панель до истечения closeDelay удерживает её открытой
- панель закрывается по closeDelay, когда курсор ушёл и с триггера, и с панели
- Esc закрывает панель
- фокус внутри панели удерживает карточку после ухода фокуса с триггером
- flowBorder даёт панели модификатор перелива, без флага — его нет
- a11y закрытая карточка — без axe-нарушений (aria-атрибуты на триггере)
- a11y открытая карточка (role=dialog на панели) — без axe-нарушений