48 KiB
Luma UI Kit — единый стандарт Web, Desktop и Android
Версия: 1.0. Дата: 14 октября 2026 года. Статус: целевой стандарт интерфейса.
Связанный документ: техническое задание.
Содержание
- 1. Назначение
- 2. Дизайн-принципы
- 3. Единицы и токены
- 4. Палитра
- 5. Типографика
- 6. Отступы, размеры и формы
- 7. Поверхности, границы и elevation
- 8. Адаптивная компоновка
- 9. Иконки
- 10. Компонент Avatar
- 11. Компонент ConversationRow
- 12. SearchField
- 13. MessageRow и Bubble
- 14. CompactReply и ThreadFocus
- 15. ReactionChip и EmojiPicker
- 16. Composer
- 17. MediaIsland и viewer
- 18. AttachmentPreview
- 19. LocationPicker
- 20. CallScene и CallHistoryCard
- 21. Auth, Settings, Encryption и Lock
- 22. Feedback, dialogs и слои
- 23. Анимации и жесты
- 24. Accessibility и ввод
- 25. Каталог состояний и приёмка UI
- 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:
- optional group sender name;
- optional compact source reply;
- body/media;
- reaction chips;
- 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 не считается реализованной только по существованию этого документа.