Контекстные меню и быстрые действия
1) Назначение и сфера применения
Контекстные меню и быстрые действия сокращают путь к часто используемым операциям:- Локальные действия над объектом (карточка, строка таблицы, коэффициент).
- Пакетные действия над выделением.
- Скрытые, но не критичные опции (второстепенные).
- Правило: критичные и первичные действия не прятать только в контекстное меню.
2) Триггеры и варианты
Right-click / Shift+F10 / Menu key — классическое контекстное.
Icon button (kebab `⋮`, meatballs `…`, context) — универсально для тач/десктопа.
Long-press (400–600 мс) — мобильный эквивалент right-click.
Command palette (⇧⌘P / Ctrl+K) — глобальные быстрые команды с поиском.
Swipe-reveal (iOS/Android списки) — открывает ряд быстрых шорткатов.
Рекомендация: обеспечьте минимум два способа вызова (иконка + контекстный клик/клавиша).
3) Содержание и приоритет
Первые 1–3 пункта — частые действия; затем разделитель; далее — реже используемые.
Деструктивные — в конце, помечены цветом/иконкой (и часто — через подтверждение/undo).
Формулировки — глагол + объект («Добавить в избранное», «Скопировать ID»).
Лейбл ≠ только иконка: иконка — усилитель, не замена текста.
4) Группировка и состояние
Разделители для логических блоков (просмотр, редактирование, админ, опасные).
Состояния: `disabled`, `checked` (`menuitemcheckbox`/`menuitemradio`), `destructive`.
Не показываем недоступные по роли если это путает; альтернативно — показываем как `disabled` с подсказкой причины.
5) Доступность (A11y)
Контейнер: `role="menu"`, элементы: `role="menuitem"` / `menuitemcheckbox` / `menuitemradio`.
Roving tabindex: единственный `tabindex="0"` на текущем элементе, остальные `-1`.
Стрелки ↑↓ — перемещение, →/← — вход/выход из подменю. Enter/Space — активация, Esc — закрыть.
Typeahead: печать первой буквы переносит фокус на пункт.
Фокус-кольцо видно; контраст текста и иконок ≥ AA.
Для иконки-кнопки используйте `aria-haspopup="menu"` и `aria-expanded`.
html
<button id="more" aria-haspopup="menu" aria-expanded="false" aria-controls="menu-1">Еще</button>
<ul id="menu-1" role="menu" aria-labelledby="more" hidden>
<li role="menuitem" tabindex="0">Открыть</li>
<li role="menuitem">Скопировать ссылку</li>
<li role="menuitemcheckbox" aria-checked="true">В избранном</li>
<li role="menuitem" data-danger="true">Удалить</li>
</ul>
Навигация (схема roving tabindex):
js const menu = document.getElementById('menu-1');
menu.addEventListener('keydown', e=>{
const items=[...menu.querySelectorAll('[role^="menuitem"]')];
let i=items.findIndex(n=>n.tabIndex===0);
if(e.key==='ArrowDown'){ items[i].tabIndex=-1; items[(i+1)%items.length].tabIndex=0; items[(i+1)%items.length].focus(); e.preventDefault(); }
if(e.key==='ArrowUp'){ items[i].tabIndex=-1; items[(i-1+items.length)%items.length].tabIndex=0; items[(i-1+items.length)%items.length].focus(); e.preventDefault(); }
if(e.key==='Escape'){ menu.hidden=true; document.getElementById('more').focus(); }
});
6) Позиционирование и перформанс
Открывайте меню у источника (клик-точка/иконка), сдвиг 4–8 px; следите за краями экрана (flip/shift).
Портал/слой поверх (`z-index`) с ловлей клика вне.
Рендерите лениво, рециклируйте список в длинных меню (виртуализация не нужна, но избегайте сотен пунктов).
Анимации только `opacity/transform`, длительность 140–200 мс (out быстрее: 100–160 мс).
Подменю открывайте по `ArrowRight` и hover с задержкой 80–120 мс (anti-flicker).
7) Мобильные паттерны
Long-press с хаптикой; тайминг 450±100 мс.
Bottom sheet как форма контекстного меню (thumb-reachable).
Swipe actions в списках: слева — «архив/избранное», справа — «удалить» (подтверждение/undo).
Зоны клика ≥ 44×44, подписи краткие, иконки 20–24 px.
8) Подтверждения, undo и безопасность
Деструктивные действия: либо второе подтверждение (modal / confirm pattern), либо undo 5–10 с.
Финансовые/рисковые — через явный confirm с контекстом последствий.
Показывайте причину `disabled` («Недостаточно прав», «Достигнут лимит»).
9) Быстрые действия без меню
Inline-шорткаты в строке (иконки «», «», «⋯»).
Hover-reveal (десктоп): показывать действия при наведении, но оставлять и явный триггер.
Command palette: поиск по действиям, горячие клавиши в подсказках («⌘S», «Ctrl+Enter»).
10) Копирайтинг и иконки
Глагол + объект, 2–3 слова.
Начинайте с действия («Удалить запись…»), не с существительного («Удаление записи»).
Иконки из единого набора; используйте одинаковые пиктограммы для одинаковых действий во всем продукте.
Поясняющие подсказки (`title`/tooltip) для неоднозначных пунктов.
11) Телеметрия и эксперименты
CTR по пунктам, медиана времени от открытия до клика, частота отмен/undo.
Число misclick’ов (деструктивные → отмененные).
A/B: порядок и группировка пунктов, наличие быстрых шорткатов в списке.
Логи «невидимых» пунктов (никто не использует) — кандидаты на удаление/перенос.
12) Токены дизайн-системы (пример)
json
{
"menu": {
"minWidth": 208,
"itemHeight": 36,
"gap": 4,
"padding": 8,
"radius": 12,
"elev": 8,
"anim": { "inMs": 180, "outMs": 120, "ease": "cubic-bezier(0.2,0,0.2,1)" }
},
"swipe": { "revealThresholdPx": 56, "confirmDestructive": true }
}
CSS-пресеты:
css
.menu{min-width:208px;padding:8px;border-radius:12px;box-shadow:var(--elev-3);background:var(--bg-elevated)}
.menu [role^="menuitem"]{height:36px;padding:0 12px;display:flex;gap:8px;align-items:center;border-radius:10px}
.menu [role^="menuitem"]:hover,
.menu [role^="menuitem"][tabindex="0"]{background:var(--bg-hover)}
.menu [data-danger="true"]{color:var(--role-danger)}
13) Паттерны по компонентам
Карточки/плитки: иконка `⋯` в правом верхнем углу; на hover — показывается, на тач — видна всегда.
Строки таблицы: `⋯` в последней колонке; при множественном выделении — панель пакетных действий сверху/снизу.
Списки чатов/уведомлений: swipe-actions (архив/прочитано/удалить) с undo.
Медиа-галереи: долгий тап → режим мультивыбора + нижняя панель действий.
14) Специфика iGaming
Быстрая ставка (quick bet): в контекстном меню коэффициента — «Поставить X» (преднастройки), «Добавить в купон», «Подписаться на изменение коэффициента». Подтверждение/undo обязательны.
Избранное/подписки: «Добавить провайдера/игру в избранное», «Подписаться на турнир».
Кэш-аут: с инлайн-подтверждением и текущей суммой; доступно только при статусе рынка.
Модерация/репорты: «Пожаловаться», «Заблокировать чат» — безопасные, видны по ролям.
Ответственная игра: «Установить лимит», «Пауза 24ч» — без агрессивных цветов, с ясным описанием последствий.
15) Анти-паттерны
Критичные действия спрятаны только в контекстное меню.
Пункты без текста (одни иконки), особенно в списках.
Случайное закрытие на уход курсора при переходе в подменю (нет задержки/коридора).
Меню перекрывает элемент-источник или уходит за экран (нет flip/shift).
Деструктивные без подтверждения/undo.
Неочевидные права (пункт пропадает без объяснения).
16) QA-чек-лист
Доступность
- `role="menu"`/`menuitem` корректны, roving tabindex и стрелки работают.
- Esc закрывает меню, фокус возвращается к источнику.
- Контраст и фокус-кольца соответствуют AA.
Поведение
- Порядок пунктов отражает частоту и риск; деструктивные внизу.
- Позиционирование учитывает края (flip/shift); анимации быстрые (≤ 200 мс).
- Подменю открывается по ArrowRight и не «дрожит» (hover-intent 80–120 мс).
Мобильные
- Long-press с хаптикой; bottom-sheet удобен большим пальцем.
- Swipe-actions имеют undo; зоны клика ≥ 44×44.
Безопасность
- Подтверждение/undo для опасных действий; причины disabled понятны.
- Нет утечки приватных данных в подсказках/лейблах.
Метрики
- Снимается CTR/время до клика; misclick/undo мониторятся.
17) Реализация: открытие/закрытие и клик-вне
js const btn=document.getElementById('more'), menu=document.getElementById('menu-1');
const open=()=>{ menu.hidden=false; btn.setAttribute('aria-expanded','true'); menu.querySelector('[role^="menuitem"]').focus(); };
const close=()=>{ menu.hidden=true; btn.setAttribute('aria-expanded','false'); btn.focus(); };
btn.addEventListener('click', e=>{ menu.hidden?open():close(); });
document.addEventListener('pointerdown', e=>{ if(!menu.hidden &&!menu.contains(e.target) && e.target!==btn) close(); });
window.addEventListener('blur', close);
18) Документация в дизайн-системе
Компоненты: `ContextMenu`, `MenuItem`, `Submenu`, `BottomSheet`, `SwipeAction`, `CommandPalette`.
Токены размеров/высоты строки/радиуса/анимаций.
Гайды по доступности (ARIA, клавиатура, typeahead).
«Do/Don’t» с примерами группировки, позиционирования и подтверждений.
Краткое резюме
Контекстные меню и быстрые действия ускоряют работу, если они предсказуемы, доступны и безопасны: два пути вызова, ясные лейблы с иконками, разумная группировка, подтверждение/undo для риска, корректная клавиатурная навигация и четкое позиционирование. Зафиксируйте токены и паттерны в дизайн-системе — и пользователи будут действовать быстро, не опасаясь ошибиться.