614 lines
48 KiB
Markdown
614 lines
48 KiB
Markdown
# Luma UI Kit — единый стандарт Web, Desktop и Android
|
||
|
||
Версия: 1.0. Дата: 14 октября 2026 года. Статус: целевой стандарт интерфейса.
|
||
|
||
Связанный документ: [техническое задание](tz_clear.md).
|
||
|
||
## Содержание
|
||
|
||
- [1. Назначение](#1-назначение)
|
||
- [2. Дизайн-принципы](#2-дизайн-принципы)
|
||
- [3. Единицы и токены](#3-единицы-и-токены)
|
||
- [4. Палитра](#4-палитра)
|
||
- [5. Типографика](#5-типографика)
|
||
- [6. Отступы, размеры и формы](#6-отступы-размеры-и-формы)
|
||
- [7. Поверхности, границы и elevation](#7-поверхности-границы-и-elevation)
|
||
- [8. Адаптивная компоновка](#8-адаптивная-компоновка)
|
||
- [9. Иконки](#9-иконки)
|
||
- [10. Компонент Avatar](#10-компонент-avatar)
|
||
- [11. Компонент ConversationRow](#11-компонент-conversationrow)
|
||
- [12. SearchField](#12-searchfield)
|
||
- [13. MessageRow и Bubble](#13-messagerow-и-bubble)
|
||
- [14. CompactReply и ThreadFocus](#14-compactreply-и-threadfocus)
|
||
- [15. ReactionChip и EmojiPicker](#15-reactionchip-и-emojipicker)
|
||
- [16. Composer](#16-composer)
|
||
- [17. MediaIsland и viewer](#17-mediaisland-и-viewer)
|
||
- [18. AttachmentPreview](#18-attachmentpreview)
|
||
- [19. LocationPicker](#19-locationpicker)
|
||
- [20. CallScene и CallHistoryCard](#20-callscene-и-callhistorycard)
|
||
- [21. Auth, Settings, Encryption и Lock](#21-auth-settings-encryption-и-lock)
|
||
- [22. Feedback, dialogs и слои](#22-feedback-dialogs-и-слои)
|
||
- [23. Анимации и жесты](#23-анимации-и-жесты)
|
||
- [24. Accessibility и ввод](#24-accessibility-и-ввод)
|
||
- [25. Каталог состояний и приёмка UI](#25-каталог-состояний-и-приёмка-ui)
|
||
- [26. Развитие стандарта](#26-развитие-стандарта)
|
||
|
||
## 1. Назначение
|
||
|
||
UI Kit определяет визуальный язык, токены, компоненты, состояния и адаптацию Luma для Web, Desktop и Android. Документ применяется независимо от UI framework и не требует конкретной реализации нативных материалов, шрифтов или системных иконок.
|
||
|
||
Цель — узнаваемый продукт с одинаковой иерархией и понятным поведением на разных устройствах. Совпадение физического пикселя между ОС не требуется; соответствие ролям, пропорциям и состояниям обязательно.
|
||
|
||
Документ нормативный: значения токенов реализуются централизованно, компоненты показываются в отдельном каталоге состояний. Примеры токенов и размеров не обозначают уже написанную библиотеку.
|
||
|
||
## 2. Дизайн-принципы
|
||
|
||
**UI-001.** Контент и общение находятся на первом плане. Декор не конкурирует с текстом, avatar и основным действием.
|
||
|
||
**UI-002.** Основной accent — синий. Outgoing сообщения синие, incoming нейтральные. Направление различается также выравниванием, а состояние — иконкой/подписью.
|
||
|
||
**UI-003.** Элементы округлые, поверхности спокойные, navigation и forms следуют привычкам платформы. Нет обязательного копирования внешнего вида одной ОС на другую.
|
||
|
||
**UI-004.** Полупрозрачность поддерживает иерархию, но не ухудшает чтение. При отсутствии blur/пониженной прозрачности используется определённый opaque fallback.
|
||
|
||
**UI-005.** Состояния loading/empty/error/disabled/selected являются частью компонента, а не отдельной импровизацией каждого экрана.
|
||
|
||
**UI-006.** Жест ускоряет действие, но для значимого действия есть видимый/доступный альтернативный control.
|
||
|
||
**UI-007.** Пользовательские строки — русские, короткие и точные. «Отправлено», «Доставлено» и «Прочитано» не заменяют друг друга.
|
||
|
||
**UI-008.** Маленькое окно, клавиатура, увеличение текста и reduced motion поддерживаются системно, а не исправляются после завершения дизайна.
|
||
|
||
## 3. Единицы и токены
|
||
|
||
| Величина | Web | Android | Desktop |
|
||
| --- | --- | --- | --- |
|
||
| Layout unit `u` | CSS px при стандартном масштабе | dp | Логическая единица UI, independent от device pixels |
|
||
| Размер текста | rem/em с role mapping | sp с font scale | Системный scaling / logical font size |
|
||
| Border | 1 CSS px или адаптированный hairline | 1 dp по роли | Логическая линия с корректным pixel snapping |
|
||
| Анимация | ms | ms | ms |
|
||
| Цвет | sRGB HEX + alpha | Эквивалент sRGB | Эквивалент sRGB |
|
||
|
||
При изменении device pixel ratio компонент не должен физически уменьшаться. Растровые assets поставляются в нужных масштабах; logo и функциональные icons предпочтительно в vector.
|
||
|
||
### 3.1. Обязательные группы токенов
|
||
|
||
`color.*`, `type.*`, `space.*`, `radius.*`, `size.*`, `border.*`, `shadow.*`, `motion.*`, `layer.*`, `breakpoint.*`.
|
||
|
||
Каждый токен имеет light/dark значение или единое значение, назначение и допустимое применение. Локальная константа не заменяет существующий токен тем же смыслом.
|
||
|
||
## 4. Палитра
|
||
|
||
### 4.1. Semantic colors
|
||
|
||
| Токен | Light | Dark | Назначение |
|
||
| --- | --- | --- | --- |
|
||
| `color.brand.accent` | #248FF5 | #68B5FF | Выбор, active icon, ссылка по проверенному контрасту |
|
||
| `color.action.fill` | #0969C8 | #0969C8 | Filled primary button с белым текстом |
|
||
| `color.action.onFill` | #FFFFFF | #FFFFFF | Текст primary action |
|
||
| `color.text.primary` | #172B40 | #F3F6FA | Основной текст |
|
||
| `color.text.secondary` | #526174 | #B8C3CF | Вторичный текст |
|
||
| `color.text.muted` | #5E6C7E | #A7B4C4 | Метаданные при достаточном контрасте |
|
||
| `color.surface.page` | #F4F7FA | #101821 | Фон приложения |
|
||
| `color.surface.base` | #FFFFFF | #18232F | Основные панели |
|
||
| `color.surface.raised` | #FFFFFF | #213040 | Dialog/card/popover |
|
||
| `color.surface.incoming` | #E7ECF2 | #293847 | Incoming bubble |
|
||
| `color.border.subtle` | #D7DFE8 | #3C4B5D | Divider/декоративная рамка |
|
||
| `color.border.control` | #7A899A | #8794A3 | Значимая граница control |
|
||
| `color.state.success` | #16713B | #6DDA91 | Успех/online/verified |
|
||
| `color.state.successFill` | #16713B | #16713B | Filled accept/success control, белый текст |
|
||
| `color.state.warning` | #8A4B00 | #FFC46B | Plaintext/предупреждение |
|
||
| `color.state.error` | #B4232F | #FF9199 | Ошибка/destructive text |
|
||
| `color.state.errorFill` | #B4232F | #B4232F | Destructive filled control |
|
||
| `color.state.onErrorFill` | #FFFFFF | #FFFFFF | Текст destructive filled control |
|
||
| `color.focus.ring` | #0969C8 | #8BC6FF | Keyboard focus |
|
||
| `color.overlay.scrim` | #000000 / 0.52 | #000000 / 0.64 | Модальная подложка |
|
||
|
||
Brand accent используется как цвет бренда; это не разрешение печатать мелкий белый текст на любой голубой поверхности. Filled actions используют `action.fill`. Для ссылок на светлом фоне применяется достаточно тёмный `action.fill` либо другой проверенный role token.
|
||
|
||
### 4.2. Сообщения и градиенты
|
||
|
||
| Токен | Значение | Назначение |
|
||
| --- | --- | --- |
|
||
| `gradient.message.outgoing` | #0969C8 → #0874BA | Outgoing text/audio/file bubble |
|
||
| `color.message.onOutgoing` | #FFFFFF | Основной текст outgoing |
|
||
| `color.message.outgoingMetadata` | Основной secondary вне bubble | Дата/status за пределами синей поверхности |
|
||
| `gradient.brand.hero` | #1461C7 → #1F9EF5 → #70D6F5 | Login/lock background, декоративная область |
|
||
| `gradient.media.video` | #0F2B4A → #176EA3 | Video placeholder |
|
||
| `gradient.media.videoNote` | #144D8F → #12A3D4 | VideoNote placeholder |
|
||
| `gradient.call` | #1C2B3D → #000000 | Call scene |
|
||
|
||
Направление градиентов — от верхнего левого угла к нижнему правому. Для текстовых bubbles используется тёмный сине-голубой градиент, чтобы белый body оставался читаемым; более яркий hero gradient остаётся декоративным.
|
||
|
||
Для обычного белого текста расчётный контраст с концами outgoing gradient — около 5.43:1 и 4.98:1. Контраст промежуточных цветов и всех фактических состояний проверяется при реализации. Полупрозрачный secondary text внутри bubble не должен снижать контраст ниже целевого порога.
|
||
|
||
### 4.3. Аватары и имена участников
|
||
|
||
Fallback avatar получает стабильный цвет по нормализованной identity. Цвет не меняется при rerender/перезапуске. Palette должна обеспечивать читаемый белый или тёмный foreground; случайный hue не используется без выбора контрастного текста.
|
||
|
||
Имя автора MUC может иметь устойчивый цвет, но не является единственным признаком identity. Имя остаётся читаемым в обеих темах. Цвет для имени и фон аватара могут отличаться по насыщенности.
|
||
|
||
### 4.4. Запрещённые применения
|
||
|
||
- Новый близкий синий для той же роли без добавления token.
|
||
- Белая мелкая подпись на ярком accent без проверки контраста.
|
||
- Ошибка/online/selected, различимые только цветом.
|
||
- Hardcoded light background с унаследованным dark foreground из другой темы.
|
||
- Прозрачный control без определённого фона/границы/focus state.
|
||
|
||
## 5. Типографика
|
||
|
||
Используется системный sans-serif шрифт платформы. Единая роль важнее одинакового имени font family. Fallback поддерживает кириллицу, emoji и символы JID. Mono нужен для fingerprint, не для всего интерфейса.
|
||
|
||
| Токен | Размер / line height | Weight | Применение |
|
||
| --- | --- | --- | --- |
|
||
| `type.brand` | 42 / 50 | 700 | Название на login |
|
||
| `type.hero` | 30 / 38 | 600 | Имя звонящего |
|
||
| `type.title` | 22 / 28 | 700 | Крупный заголовок формы/dialog |
|
||
| `type.heading` | 17 / 24 | 600 | Имя списка, toolbar title |
|
||
| `type.body` | 16 / 24 | 400 | Сообщение, поле, основной текст |
|
||
| `type.bodyStrong` | 16 / 24 | 600 | Выделенный текст/primary label |
|
||
| `type.subbody` | 14 / 20 | 400 | Preview списка, вторичные поля |
|
||
| `type.caption` | 12 / 16 | 400/500 | Метаданные, пояснения |
|
||
| `type.badge` | 12 / 16 | 700 | Unread/count |
|
||
| `type.mono` | 12 / 18 | 400 | Fingerprint/техническое значение |
|
||
|
||
Размеры масштабируются пользовательскими настройками; фиксированная высота row не должна обрезать увеличенный текст. Desktop compact density может уменьшать вторичный текст до 13/18, но основной message body не становится мелкой служебной подписью.
|
||
|
||
Таймеры/duration используют tabular numerals. Link/упоминания различимы, доступны клавиатуре и не ломают line wrapping.
|
||
|
||
### 5.1. Ограничение строк
|
||
|
||
- Имя и preview списка: 1 строка при обычном масштабе; при большом тексте допустим адаптивный рост.
|
||
- Имя звонящего: до 2 строк.
|
||
- Toast: до 2 строк, дальнейшее сообщение — dialog/inline error.
|
||
- Compact reply: 1 строка и ellipsis.
|
||
- Message body: без произвольного line clamp; длинный контент может иметь доступное «Показать полностью» только при отдельном согласованном лимите.
|
||
- JID/filename/fingerprint: безопасный wrap/ellipsis с возможностью прочитать/скопировать полное значение.
|
||
|
||
## 6. Отступы, размеры и формы
|
||
|
||
### 6.1. Spacing scale
|
||
|
||
| Токен | u |
|
||
| --- | --- |
|
||
| `space.1` | 4 |
|
||
| `space.2` | 8 |
|
||
| `space.3` | 12 |
|
||
| `space.4` | 16 |
|
||
| `space.5` | 20 |
|
||
| `space.6` | 24 |
|
||
| `space.7` | 32 |
|
||
| `space.8` | 40 |
|
||
| `space.9` | 48 |
|
||
|
||
Компонентные исключения допустимы для оптического выравнивания: bubble padding 13×9, input 14/12/9, metadata gap 4. Они именуются внутри компонента, не создают бессистемный глобальный scale.
|
||
|
||
### 6.2. Радиусы
|
||
|
||
| Токен | u | Применение |
|
||
| --- | --- | --- |
|
||
| `radius.small` | 6 | Reply thumbnail |
|
||
| `radius.control` | 12 | Компактная карточка/системный fallback |
|
||
| `radius.field` | 16 | Caption field |
|
||
| `radius.reply` | 17 | Compact reply |
|
||
| `radius.media` | 20 | Photo/video island |
|
||
| `radius.composer` | 21 | Поле composer |
|
||
| `radius.dialog` | 28 | Auth/dialog card |
|
||
| `radius.pill` | Половина высоты | Badge/chip/toast |
|
||
| `radius.circle` | 50% | Avatar/round control |
|
||
|
||
Message bubble имеет отдельную directional geometry: основное скругление 20, нижняя асимметрия/небольшой хвост на стороне отправителя с выступом около 4 u. Он не должен уменьшать внутреннюю область текста. Vector/path реализации обеих направлений являются зеркальными вариантами одного компонента.
|
||
|
||
Фото/видео не получают текстовый хвостик: у них самостоятельная островковая рамка.
|
||
|
||
### 6.3. Контролы
|
||
|
||
| Роль | Visual size | Hit area |
|
||
| --- | --- | --- |
|
||
| Composer send/attach/record | 42×42 | Touch min 48×48; pointer min 44×44 |
|
||
| Audio play | 44×44 | Touch min 48×48 |
|
||
| Standard icon button | 24 icon в 40/44 visual | Touch min 48×48 |
|
||
| Primary form action | Height min 48 | Вся кнопка |
|
||
| Call control | 58×58 circle | Не меньше visual size |
|
||
| Unread badge | min height 22 | Не отдельная обязательная target |
|
||
| Input | min height 44, composer visual min 42 | Не обрезается при scale |
|
||
|
||
Увеличение hit area не должно перекрывать target соседней кнопки. При узкой ширине gap и общий layout перераспределяются.
|
||
|
||
## 7. Поверхности, границы и elevation
|
||
|
||
| Уровень | Поверхность | Применение |
|
||
| --- | --- | --- |
|
||
| Page | page | Фон, timeline |
|
||
| Base | base | Sidebar, forms, основной контент |
|
||
| Raised | raised + border/elevation | Popover, dialog, floating panel |
|
||
| Glass | Полупрозрачная base surface + blur | Search/composer/toolbars |
|
||
| Inverse | Тёмная сцена + контрастный текст | Media viewer/call |
|
||
|
||
Glass light fallback: base opaque. Glass dark fallback: base opaque. При blur фон имеет достаточно непрозрачный слой для текста; blur не единственный источник читаемости. Reduced transparency отключает blur и сохраняет border.
|
||
|
||
Shadows:
|
||
|
||
- `shadow.floating`: black alpha 0.14, blur 24, offsetY 12 в light; более мягкий/ограниченный вариант в dark;
|
||
- `shadow.toast`: black alpha 0.18, blur 12, offsetY 4;
|
||
- `shadow.callPreview`: black alpha 0.38, blur 16, offsetY 8.
|
||
|
||
List row не получает отдельную тяжёлую тень. Border subtle декоративный; значимые focus/control boundaries используют более контрастный role token.
|
||
|
||
## 8. Адаптивная компоновка
|
||
|
||
### 8.1. Breakpoints
|
||
|
||
Это продуктовые диапазоны доступной ширины, а не определение типа устройства.
|
||
|
||
| Диапазон | Ширина | Layout |
|
||
| --- | --- | --- |
|
||
| Compact | <720 u | Одна основная панель, переход список → чат |
|
||
| Medium | 720…1099 u | Sidebar + detail при достаточной полезной ширине |
|
||
| Wide | ≥1100 u | Sidebar + detail, расширенная зона контента |
|
||
|
||
В medium split включается только если после sidebar остаётся минимум 400 u для detail; иначе compact. Приложение работает при viewport 320 u через reflow; специальные фиксированные размеры уменьшаются до доступной ширины.
|
||
|
||
### 8.2. Sidebar и detail
|
||
|
||
Sidebar min 240, preferred 290, max 380. Default desktop window ориентир 1080×720, рекомендуемый minimum 840×560; при фактически меньшем окне допускается compact layout, а не обрезанный недоступный контент.
|
||
|
||
Detail растёт с окном. Максимальная читаемая ширина обычного text bubble — 640 u и не более 78% доступной message row. Очень короткий текст не растягивается до максимума. Timeline может иметь центрированный content column с max 1000 u.
|
||
|
||
### 8.3. Platform shell
|
||
|
||
| Платформа/режим | Navigation |
|
||
| --- | --- |
|
||
| Android compact | Нижняя навигация: Контакты / Звонки / Чаты / Настройки |
|
||
| Mobile Web | Аналогичная compact bottom navigation, с учётом browser safe areas |
|
||
| Desktop/Web wide | Sidebar navigation/list + выбранный detail |
|
||
| Android large screen | Adaptive split/rail по доступной ширине |
|
||
|
||
Внутри compact чата нижняя глобальная навигация скрыта, Back возвращает к списку. Search остаётся сверху соответствующего списка. Keyboard/IME не перекрывает composer; изменение viewport не сбрасывает draft.
|
||
|
||
## 9. Иконки
|
||
|
||
Единый набор outline/filled vector icons выбирается для каждой платформы с согласованной семантикой. Использование конкретного proprietary symbol set не требуется. Толщина, оптический размер и baseline согласованы в каталоге.
|
||
|
||
| Роль | Графическая семантика |
|
||
| --- | --- |
|
||
| Контакты | Человек/контакты |
|
||
| Звонки | Телефон |
|
||
| Чаты | Два сообщения |
|
||
| Настройки | Шестерёнка |
|
||
| Новый чат | Карандаш/новое сообщение |
|
||
| Новая группа | Несколько людей |
|
||
| Поиск/очистить | Лупа/крестик в круге |
|
||
| Вложения | Плюс |
|
||
| Отправить | Стрелка вверх |
|
||
| Ответить/переслать | Стрелка влево/вправо с поворотом |
|
||
| OMEMO/plaintext | Закрытый/открытый замок |
|
||
| Decryption error | Щит с предупреждением |
|
||
| Retry | Круговая стрелка |
|
||
| Сохранить | Стрелка вниз/скачивание |
|
||
| Завершить звонок | Опущенная трубка |
|
||
| Turn camera | Камера с круговыми стрелками |
|
||
|
||
Обычный icon size 20/24 u; metadata 12/14. Иконка без текста имеет accessible name и tooltip на pointer, если смысл не очевиден. Emoji не заменяет функциональную пиктограмму кнопки.
|
||
|
||
## 10. Компонент Avatar
|
||
|
||
**UI-AVATAR.** Circle, photo fill с crop, fallback — initials/group icon и стабильный фон. Не смешивать photo fit с avatar fill: фотографии сообщений не обрезаются как avatar.
|
||
|
||
| Вариант | Размер |
|
||
| --- | --- |
|
||
| List | 50×50 |
|
||
| Profile | 64×64 |
|
||
| Call hero | 132×132 |
|
||
| Compact inline | 32×32 при необходимом контексте |
|
||
|
||
Initials около 35% diameter, semibold; group icon около 31%. Status dot bottom-end, diameter 25% avatar, border 2.5 u цветом окружающей поверхности. Direct dot означает online, group — joined. Accessible label включает имя и статус. Offline не рисуется ложным зелёным dot.
|
||
|
||
Состояния: photo, fallback, loading cached image, online/joined, offline, image error. Broken image не создаёт пустой невидимый avatar.
|
||
|
||
## 11. Компонент ConversationRow
|
||
|
||
Композиция: avatar → текстовая колонка → trailing time/unread. Gap avatar/text 12. Внешние insets horizontal 12, vertical 8; высота определяется содержимым, ориентир 72 u.
|
||
|
||
- Имя — heading.
|
||
- Preview — subbody secondary.
|
||
- Время — caption secondary.
|
||
- Межстрочный gap 4/5.
|
||
- Иконка group/encryption рядом с preview, не вместо имени.
|
||
- Badge count >0, white on action.fill, padding 7×3, min width 22.
|
||
- В основном списке значение выше 99 показывается «99+»; accessible name сообщает фактическое число.
|
||
- Multiaccount подпись — caption secondary, только когда нужно различать контекст.
|
||
|
||
Состояния: default, hover на pointer, pressed, keyboard focus, selected wide-panel, unread, online, muted при наличии функции, loading/empty отдельно от row.
|
||
|
||
Selected row: accent-tinted surface и ненавязчивый маркер/фон; hover слабее selected. Focus ring не исчезает на selected. Baseline focus outline 2 u с offset 2 u; при недостаточном контрасте используется двойная граница/контрастный separating layer.
|
||
|
||
## 12. SearchField
|
||
|
||
Поле располагается над фильтруемым списком. Height min 40 visual/48 touch target, radius pill либо control по platform shell. Horizontal inset 12, vertical 8, icon/text gap 8, outer margin 12.
|
||
|
||
Leading search icon, placeholder «Поиск чатов»/«Поиск контактов»/«Поиск звонков». Clear button появляется при непустом запросе. Native input behaviors сохраняются: selection, keyboard focus, paste и Escape очистки/закрытия по контексту.
|
||
|
||
Состояния: empty, filled, focused, disabled только при реальной невозможности, no results через отдельный empty state. Search field не обещает поиск message body, если реализована фильтрация списка.
|
||
|
||
## 13. MessageRow и Bubble
|
||
|
||
**UI-MESSAGE.** Incoming слева, outgoing справа. Outer horizontal inset 12, row gap 8. Opposite minimum space 54 для текста и 16 для media, с уменьшением в узком viewport. Body wrapping не расширяет страницу горизонтально.
|
||
|
||
Message stack:
|
||
|
||
1. optional group sender name;
|
||
2. optional compact source reply;
|
||
3. body/media;
|
||
4. reaction chips;
|
||
5. metadata.
|
||
|
||
### 13.1. Body
|
||
|
||
Text/file padding horizontal 13, vertical 9. Audio/voice — 13×10. Outgoing gradient + white; incoming neutral surface + primary. Body type.body.
|
||
|
||
Plaintext/decryption warning расположен в metadata и доступен screen reader. Не добавлять замок каждому слову или watermark поверх читаемого текста.
|
||
|
||
Text bubble использует общую directional rounded shape; media островок имеет независимый radius 20. Long URL/word безопасно переносится; copy возвращает исходный текст, а не строку с декоративными разрывами.
|
||
|
||
### 13.2. Metadata
|
||
|
||
Caption, gap 4, horizontal inset 4, вне основного colored body. Время hour:minute, marker «изм.», security warning и outgoing status.
|
||
|
||
| Состояние | Icon/подпись |
|
||
| --- | --- |
|
||
| Sending | Часы / «Отправка…» |
|
||
| Sent | Галочка / «Отправлено» |
|
||
| Delivered | Отличимая delivery-галочка / «Доставлено» |
|
||
| Read | Заполненный/двойной read marker / «Прочитано» |
|
||
| Failed | Warning red / «Не отправлено», доступен retry по ТЗ |
|
||
|
||
На compact можно оставить icon и доступный подробный label, на focus/hover показывать расшифровку. Статусы не различаются только tint.
|
||
|
||
### 13.3. Tombstone
|
||
|
||
Иконка удаления + «Сообщение удалено», subdued text/italic по доступности. Положение исходного сообщения сохраняется, прежние media/reaction/reply content не видны. Tombstone не приглашает к запрещённым edit/forward.
|
||
|
||
### 13.4. Selection и context menu
|
||
|
||
Touch: long press открывает actions; pointer: context click/явное menu; keyboard: доступная menu action. В selection mode видны checkmarks/count и панель массовых действий. Swipe reply отключён.
|
||
|
||
Список actions определяется ТЗ. Недоступное действие скрывается либо disabled с причиной, но не выполняет частично другую операцию под тем же названием.
|
||
|
||
## 14. CompactReply и ThreadFocus
|
||
|
||
Compact source bubble: max width 230, min height 34, radius 17, vertical padding 7, horizontal 10…12. Text subbody medium, one line, optional thumbnail 27×27/radius 6. Тонкий border 1.25 u; фон приглушённый.
|
||
|
||
Отступ/дуга отражают направление исходного сообщения. Curve width 58/height 34, stroke 2.4 round caps, horizontal offset около 18. Нельзя снижать opacity всего текста ниже readable contrast; muted effect достигается background/border, а не нечитаемой надписью.
|
||
|
||
При известном target компонент interactive и имеет label «Открыть ветку ответа». При preview без target он статичен и не показывает ложный hover-link.
|
||
|
||
ThreadFocus: затемнённый/raised отдельный слой, источник, число ответов, хронологическая ветка, selected reply metadata. Есть Close/Escape/Back и корректный focus restore. Bracket декоративный, не отдельный target. Это фокус replies в существующем чате.
|
||
|
||
## 15. ReactionChip и EmojiPicker
|
||
|
||
Chip: emoji + count, gap 3/4, padding 8×4, pill. Strip gap 5/8, wrap в узкой row.
|
||
|
||
| Вариант | Представление |
|
||
| --- | --- |
|
||
| Чужие реакции | Neutral subtle background + readable border |
|
||
| Есть own reaction | Accent-tinted background + accent border + accessible own state |
|
||
| Hover/focus | Нативная/общая interactive обратная связь |
|
||
| Pending mutation | Ненавязчивый progress, без ложного подтверждения failure |
|
||
|
||
Quick reactions: 👍 ❤️ 😂 😮 😢 🔥. Полный picker: header «Выберите эмодзи», Close/«Готово», grid, category navigation. Emoji visual 28, cell 46×46 с touch hit area минимум 48. Grid адаптивный; focused item виден и управляется keyboard.
|
||
|
||
Text search в emoji picker не обязателен. Если добавлен, он следует общему SearchField и не меняет базовую семантику reactions.
|
||
|
||
## 16. Composer
|
||
|
||
Horizontal structure: attach → multiline input → send/record. Gap 8. Outer horizontal 10/12, top 6/8, bottom 8 плюс safe area. Control visuals 42×42, hit targets по разделу размеров.
|
||
|
||
Input: min height 42 visual, radius 21, leading 14/trailing 12/vertical 9. Body font, от 1 до 6 строк до внутренней прокрутки. Placeholder «Сообщение»; edit mode «Изменить сообщение». Emoji control справа input, secondary, icon 20.
|
||
|
||
- Непустой draft: send arrow accent/action.
|
||
- Пустой draft: record action с доступным выбором audio/video.
|
||
- Reply/edit banner: над input body, автор/preview и Cancel.
|
||
- Upload preparation: progress и доступный cancel, текст не теряется.
|
||
- Recording: elapsed, waveform/indicator, cancel, finish/lock.
|
||
- Selection: separate action bar, обычная отправка не конфликтует с массовыми actions.
|
||
|
||
Keyboard contract Web/Desktop: Enter отправляет при настройке «Enter для отправки», Shift+Enter — новая строка; можно переключить поведение. IME composition не отправляет сообщение по промежуточному Enter. Esc закрывает локальный popup/отменяет reply/edit с сохранением исходного draft по сценарию.
|
||
|
||
Touch recording gestures не являются единственным способом: есть доступные «Начать», «Остановить», «Отменить». Back во время capture спрашивает о потере записи либо явно отменяет по утверждённому сценарию.
|
||
|
||
## 17. MediaIsland и viewer
|
||
|
||
### 17.1. Фото/видео
|
||
|
||
Default width 268, max available width минус row insets. Radius 20, border 1, thumbnail inset 6. Photo natural height по ratio, baseline clamp 164…328, с proportional уменьшением на узком экране. Object-fit/scale mode — contain.
|
||
|
||
Filename/size badge снизу: caption, white on достаточно тёмной scrim, padding 8×5, pill; inset 9. Expand icon сверху справа с hit area. Video имеет центральный play и duration.
|
||
|
||
Состояния: placeholder, loading, thumbnail, ready, playing для inline применимого типа, failed/retry. Error не замещается бесконечным spinner.
|
||
|
||
### 17.2. Audio/voice
|
||
|
||
Content default width 252 с clamp доступной ширины. Play circle 44, waveform около 30 high, progress/seek, duration и speed chip. Voice speed 1×/1.5×/2×; audio filename visible.
|
||
|
||
Seek имеет keyboard arrows/accessible slider semantics и touch target выше визуальной линии. Loading, paused, playing, ended и failed различимы.
|
||
|
||
### 17.3. VideoNote
|
||
|
||
Circle 144 idle, 176 playing при достаточном месте; при узком viewport размер уменьшается. Play icon 24, duration pill снизу, border 1. Увеличение не должно резко двигать viewport или перекрывать соседние controls.
|
||
|
||
### 17.4. Fullscreen viewer
|
||
|
||
Root overlay, тёмный фон, zoom/pan для фото, controls для видео. Обязательно доступны Close button, Escape на keyboard и Back на Android; vertical dismiss — дополнительный touch gesture. Close target располагается в safe area и не закрывает важное media.
|
||
|
||
Zoomed photo pan не запускает dismiss. После закрытия focus возвращается к исходному элементу, viewport остаётся на том же сообщении. Download/save — отдельное явное действие.
|
||
|
||
## 18. AttachmentPreview
|
||
|
||
Modal/panel с title «Перед отправкой», grid, подписью и action row. Grid cell min 128/max 190, column gap 18/20, row gap 22/24, container padding 16. В compact grid адаптируется до одной/двух колонок.
|
||
|
||
Caption field radius 16/padding 13×10, 1…4 строки. Нижняя panel raised/glass с opaque fallback, padding 14/16. Summary слева, primary send справа; mobile может переносить action row без обрезки.
|
||
|
||
На sending отмена/закрытие имеют определённую доступность; failed items остаются с clear status и retry. Caption не показывается как доставленная, если её отдельная отправка завершилась ошибкой.
|
||
|
||
## 19. LocationPicker
|
||
|
||
Header «Отправить геопозицию», карта/marker, текущая выбранная точка, «Моя геопозиция» и primary «Отправить». При denied permission — inline explanation и ручной выбор.
|
||
|
||
Map не поглощает все keyboard/Back actions. Выбор координат доступен через альтернативный ввод, если map недоступна. Preview location message — компактная карта/иконка и координаты/label; tap открывает просмотр без автоматического фонового tracking.
|
||
|
||
## 20. CallScene и CallHistoryCard
|
||
|
||
CallScene — inverse fullscreen/large overlay. Remote video занимает доступный фон, контрастная scrim защищает labels. Local video 118×168/radius 22, top-end с safe area; на compact масштабируется и не перекрывает controls.
|
||
|
||
Без video: call gradient, avatar 132, name hero до 2 строк, media type subbody, status/duration tabular. Основные controls снизу, visual circles 58 и подписанные labels.
|
||
|
||
- Incoming: decline red и accept green/dark-success fill с readable white text.
|
||
- Connecting: status и доступный cancel.
|
||
- Connected: mute, audio route при поддержке, camera/switch при наличии, hangup red.
|
||
- Permission/error: понятное пояснение и возврат без скрытой активной записи.
|
||
|
||
Control selected state передаётся icon + label/accessible state, а не только прозрачностью. Caption labels имеют достаточный контраст на scrim.
|
||
|
||
HistoryCard: max width 300, icon circle 42, content gap 12, title bodyStrong/subbodyStrong, subtitle caption, padding 14×10. Outcome icon/text: completed success, missed/failed/declined error, cancelled/answeredElsewhere neutral. Направление определяется также arrow/текстом.
|
||
|
||
## 21. Auth, Settings, Encryption и Lock
|
||
|
||
### 21.1. AuthCard
|
||
|
||
Hero gradient background; centered column max 460, outer horizontal 24/vertical 48, адаптация к keyboard. Logo tile 88/radius 26, brand 42, card radius 28/padding 24…28.
|
||
|
||
Form: JID, password с show/hide, advanced connection options, primary «Войти». Loading «Подключение…» и spinner. Error у поля/общая область без потери ввода. Native host/port и Web WSS endpoint представлены соответствующими полями, а не общим неработающим transport selector.
|
||
|
||
### 21.2. Settings
|
||
|
||
Grouped sections, стандартные switches/list rows/links. Profile avatar 64. Основные sections: аккаунты, профиль, соединение, шифрование, уведомления, блокировка, приватность.
|
||
|
||
Long values читаются/копируются; destructive logout отделён от обычных preferences. Multiaccount selection имеет checkmark и account identity.
|
||
|
||
### 21.3. EncryptionPanel
|
||
|
||
Итоговый режим чата, global/override control, список устройств, protocol badge, fingerprint mono, trust. Verified/trusted/undecided/compromised различаются текстом и символом. «Готово» не означает автоматической verification.
|
||
|
||
Fingerprint selectable/copyable, разбит на читаемые группы; label устройства не заменяет fingerprint. Empty devices объясняет следующий шаг, не показывает фиктивный secure success.
|
||
|
||
### 21.4. LockScreen
|
||
|
||
Hero/нейтральный фон и полностью скрытое sensitive content. Card radius 28, lock icon 44, title 22, field/action max 260 при обычной ширине, spacing около 22/24. Passcode input masked, explicit unlock, error inline и biometric action только при реальной доступности.
|
||
|
||
Lock не имитируется прозрачным blur поверх читаемого чата. Не существует автоматически перенесённой native-biometric кнопки в неподдержанном браузере.
|
||
|
||
## 22. Feedback, dialogs и слои
|
||
|
||
| Компонент | Контракт |
|
||
| --- | --- |
|
||
| Toast | Краткий результат, max 2 строки, закрываемый/доступный screen reader |
|
||
| Inline error | Ошибка поля/операции рядом с источником |
|
||
| Alert/dialog | Важная ошибка/подтверждение с ясными действиями |
|
||
| Loading | Spinner/progress + конкретная операция |
|
||
| Empty state | Название, пояснение, доступное next action |
|
||
| Connection status | Компактный читаемый статус в заголовке/панели |
|
||
|
||
Toast: padding 16×10, gap 8, pill, raised/glass fallback, shadow.toast. Default display 2.5 с; при accessibility/длинном тексте длительность увеличивается или доступно persistent alternative. Toast не единственное место критической ошибки.
|
||
|
||
Layers:
|
||
|
||
| Токен | Значение |
|
||
| --- | --- |
|
||
| `layer.base` | 0 |
|
||
| `layer.sticky` | 10 |
|
||
| `layer.popover` | 40 |
|
||
| `layer.dialog` | 80 |
|
||
| `layer.media` | 100 |
|
||
| `layer.call` | 200 |
|
||
| `layer.toast` | 300 |
|
||
| `layer.lock` | 400 |
|
||
|
||
Это роли stacking, не обязательные literal z-index в каждой технологии. При открытии слоя проверяются активные dialogs: неподходящий старый modal закрывается/уступает по сценарию. App lock имеет приоритет sensitive content. Error dialog при lock не раскрывает текст переписки.
|
||
|
||
Modal traps focus, имеет label и close action. После close focus восстанавливается. Browser/Android Back сначала закрывает верхний modal, затем navigates.
|
||
|
||
## 23. Анимации и жесты
|
||
|
||
| Токен/эффект | Значение |
|
||
| --- | --- |
|
||
| `motion.fast` | 120 ms |
|
||
| `motion.base` | 180…220 ms |
|
||
| `motion.panel` | 240…300 ms |
|
||
| Timeline entrance | До12 rows, stagger 35 ms, rise 16, scale 0.94 |
|
||
| Outgoing append | Spring около 440 ms; start scale 0.2/rise 12 при допустимом layout |
|
||
| Incoming append | Start scale 0.4/rise 10 |
|
||
| VideoNote resize | Spring около 320 ms |
|
||
| Reply swipe return | Spring около 240 ms |
|
||
|
||
Burst >4 appended rows/архив не анимируется как несколько свежих сообщений. Recycled rows не играют entrance повторно при каждом scroll.
|
||
|
||
Reduced motion: убрать scale/slide/stagger, оставить короткое fade или мгновенную смену. Важное состояние не зависит от завершения анимации. Animation cancellation не оставляет invisible interactive control.
|
||
|
||
Touch reply: horizontal activation 24, trigger 64, max offset 76; раннее доминирование горизонтали 1.6, после lock 1.05. Вертикальный scroll приоритетен. Haptic при пороге там, где поддержан и разрешён.
|
||
|
||
Record: hold около 240 ms, cancel left 92, lock up 78; размеры адаптируются к ограниченному пространству, доступные кнопки остаются.
|
||
|
||
Viewer swipe: vertical dominance 1.15, distance около 14% height clamp 88…140; fast predicted endpoint может завершать жест. На pointer Close/Escape остаются основным способом.
|
||
|
||
## 24. Accessibility и ввод
|
||
|
||
**UI-A11Y-001.** Целевой контраст обычного текста минимум 4.5:1, крупного 3:1; значимых controls/focus минимум 3:1 относительно соседних поверхностей. Проверяются фактические composite colors, не только исходные HEX.
|
||
|
||
**UI-A11Y-002.** Все действия доступны screen reader и имеют names/states. Icon-only button не называется именем файла icon.
|
||
|
||
**UI-A11Y-003.** Web/Desktop полностью поддерживают Tab/Shift+Tab, Enter/Space для действий, Escape для закрытия и предсказуемый focus order. Hover-only actions доступны также по focus/tap.
|
||
|
||
**UI-A11Y-004.** При text scale/zoom до 200% интерфейс reflow без потери основных действий. Fingerprint/URI можно прочитать полностью; text body не скрывается за fixed height.
|
||
|
||
**UI-A11Y-005.** Touch targets минимум 48×48 u для действий; визуально меньшая icon сохраняет просторный hit region. На pointer target не меньше 44×44 для основных icon actions.
|
||
|
||
**UI-A11Y-006.** Errors связаны с input, loading не бесконечно повторяется screen reader, live messages объявляются умеренно и не перехватывают focus.
|
||
|
||
**UI-A11Y-007.** Reduced motion/transparency и high contrast имеют определённые варианты. Цвет не единственный способ различения unread/failed/verified.
|
||
|
||
**UI-A11Y-008.** Record/call/media controls отражают фактическую активность camera/microphone. Для destructive есть явное подтверждение области действия.
|
||
|
||
## 25. Каталог состояний и приёмка UI
|
||
|
||
Поставляемая библиотека компонентов имеет отдельный доступный каталог/preview, не требующий реального XMPP login.
|
||
|
||
Обязательные fixtures:
|
||
|
||
- light/dark и reduced motion/transparency;
|
||
- compact 320/390, medium 840, wide 1280/1440;
|
||
- обычный и 200% текст;
|
||
- короткие/длинные имена/JID/URL/filename;
|
||
- чат без сообщений, search без результатов, offline и syncing;
|
||
- все delivery/security states;
|
||
- incoming/outgoing/group/reply/tombstone/reactions;
|
||
- media loading/ready/error и partial batch;
|
||
- recording start/locked/finalizing/error;
|
||
- incoming/connecting/active/failed call;
|
||
- keyboard focus, selection и menus;
|
||
- lock, denied permissions и уведомления без preview.
|
||
|
||
### 25.1. Чек-лист
|
||
|
||
- [ ] Все цвета/шрифты/spacing берутся из токенов по роли.
|
||
- [ ] Navigation соответствует ширине и платформе.
|
||
- [ ] Любая важная action доступна без жеста/hover.
|
||
- [ ] Focus не теряется в modal/viewer и возвращается после close.
|
||
- [ ] Long content и text scale не ломают layout.
|
||
- [ ] Incoming/outgoing/delivery/security определяются без одной лишь окраски.
|
||
- [ ] Media не обрезается неожиданно и не растягивается.
|
||
- [ ] Keyboard/IME не отправляет незавершённый ввод.
|
||
- [ ] Loading/ошибка/cancel завершаются корректным состоянием.
|
||
- [ ] Contrast проверен для фактических цветов и прозрачностей.
|
||
- [ ] Motion/transparency preferences работают.
|
||
- [ ] UI соответствует functional predicates `tz_clear.md`.
|
||
|
||
## 26. Развитие стандарта
|
||
|
||
Новый компонент сначала определяется как набор ролей/состояний, затем добавляется в tokens/catalog и после этого используется в экранах. Новый вариант существующего компонента получает имя и назначение; нельзя создавать похожую локальную копию с немного другим цветом/radius без причины.
|
||
|
||
Изменение токена проверяется на всех затронутых компонентах, light/dark и платформах. Несовместимые изменения версии UI Kit документируются и мигрируются согласованно.
|
||
|
||
Стандарт сохраняет общий характер Luma, при этом concrete native widget и способ реализации blur/vector/animation выбираются платформой. Ни одна из библиотек Web/Desktop/Android не считается реализованной только по существованию этого документа.
|