Движение
Движение в ките — кривые и длительности Material Design 3, а поведение — как у MUI. Почти всё делает CSS. Скрипт кита добавляет четыре вещи, без которых страница всё равно работает целиком.
Кривые#
Кривая задаёт характер движения. Стандартная — для мелких переходов внутри экрана, выразительная — для того, что входит на экран или покидает его.
--md-sys-motion-easing-standard--md-sys-motion-easing-standard-decelerate--md-sys-motion-easing-standard-accelerate--md-sys-motion-easing-emphasized--md-sys-motion-easing-emphasized-decelerate--md-sys-motion-easing-emphasized-accelerate--md-sys-motion-easing-legacy--md-sys-motion-easing-linear
Наведите указатель на строку или встаньте на неё клавишей Tab — точка проедет дорожку с этой кривой за одинаковое время.
| Токен | Значение | Где в ките |
|---|---|---|
standard |
cubic-bezier(.2, 0, 0, 1) |
Слой состояния, подпись поля, цвет шапки, рябь |
standard-decelerate |
cubic-bezier(0, 0, 0, 1) |
— |
standard-accelerate |
cubic-bezier(.3, 0, 1, 1) |
— |
emphasized |
cubic-bezier(.2, 0, 0, 1) |
Раскрывашка, индикатор вкладок, поворот шеврона |
emphasized-decelerate |
cubic-bezier(.05, .7, .1, 1) |
Появление: меню, шторка, лист, диалог, тост, ползунок тумблера |
emphasized-accelerate |
cubic-bezier(.3, 0, .8, .15) |
— |
legacy |
cubic-bezier(.4, 0, .2, 1) |
— |
linear |
cubic-bezier(0, 0, 1, 1) |
— |
Токен кривой — --md-sys-motion-easing-<кривая>. Выразительная кривая Material составная, одной cubic-bezier её не записать, поэтому emphasized в CSS совпадает со стандартной — так же её записывает и сама Material.
Длительности#
Шкала длительностей Material 3 — четыре ступени по четыре шага.
| Ступень | 1 | 2 | 3 | 4 |
|---|---|---|---|---|
short |
50 | 100 | 150 | 200 |
medium |
250 | 300 | 350 | 400 |
long |
450 | 500 | 550 | 600 |
extra-long |
700 | 800 | 900 | 1000 |
Токен длительности — --md-sys-motion-duration-<ступень><шаг>, значения в миллисекундах. Чем больше путь и площадь элемента, тем длиннее его движение: слой наведения — 150, шторка — 300.
Прежние имена остались псевдонимами:
| Прежнее имя | Значение | Для чего |
|---|---|---|
--motion-fast |
short3, 150 |
Наведение, фокус, переключатель |
--motion-medium |
medium1, 250 |
Раскрытие секции, поворот шеврона |
--motion-slow |
medium2, 300 |
Смена раздела настроек |
--motion-ease |
standard |
Кривая по умолчанию |
--motion-pulse |
1600 | Оборот индикатора занятой кнопки |
Что делает CSS#
Всё движение, которому хватает состояния разметки, лист кита делает сам — без единой строки скрипта.
| Что | Как |
|---|---|
| Слой состояния | Фон за short3, кнопка на наведении приподнимается тенью за short4 |
| Подпись поля | Уменьшается до 4/5 (12 пикселей) и поднимается к верхнему краю за short4; путь короче прежнего — поле 44 пикселя, а не 56; у поля со СкрытаяМетка метки на экране нет — нечему и плыть, пример виден всегда |
| Рамка поля в фокусе | Из outline (в покое, не меньше 3:1 к заливке поля и карточке) становится 2 пикселя цветом primary за short3; содержимое поля не сдвигается — второй пиксель рисует кольцо внутрь |
| Тумблер | Ползунок едет за medium1; поповер справки «?» рядом с ним проявляется, как у поля |
| Выпадающее меню | Растёт из угла у кнопки, как Grow у MUI: проявление за short4, размер за medium1; кольцо-контур растёт вместе с ним |
| Шторка единого меню | Выезжает из-за левого края за medium2; заголовки групп и рубрики в ней — одним стилем, title-small обычным регистром |
| Лист меню на телефоне | Выезжает снизу за medium2 |
| Окно на телефоне | Базовое окно проявляется по центру, как на широком экране; листом во весь экран встаёт только окно ВоВесьЭкран и окно с шагами — без своего движения, тем же проявлением |
| Диалог | Проявляется и чуть вырастает, скрим набирает плотность вместе с ним — medium2; растёт из центра экрана, в каком бы контейнере его ни напечатали |
| Раскрывашка | Высота содержимого едет от нуля до своей за medium2, как аккордеон MUI |
| Тост | Въезжает снизу на 8 пикселей и проявляется за medium2 |
| Шапка | Меняет цвет и даёт тень за medium1, когда страница прокручена; линия outline-variant под ней стоит всегда и не анимируется |
| Сегменты | Заливка выбранного за short3, галка въезжает за short4; набор полей отмеченного сегмента показывает :has(:checked); на телефоне группа — во всю ширину равными колонками, подпись переносится, а не обрезается; источник значения (Источник) стоит справа на строке подписи группы и не движется |
| Подсказка | Поповер проявляется за short4; открывает и закрывает его движок (popover) |
| Сетка | Не движется: число колонок меняют точки перелома листа, ряд перестраивается сразу, без анимации и без скрипта; у сеток в 3 и 4 колонки число — потолок; у сетки ПолныеРяды на смене точки перелома хвост неполного ряда сразу пропадает или возвращается — лист, без скрипта |
| Шапка карточки объекта | Не движется: имя сжимается последним и не уже девяти знаков, в узкой карточке плашки сразу встают строкой под имя — по ширине самой карточки (@container), а не окна |
| Блок команды | Не движется: длинная строка переносится у края блока, кнопка «Копировать» держит ширину; на телефоне она сразу встаёт под код, если рядом не помещается |
| Окно без скрипта | Окно, напечатанное открытым (?окно=…, отбитая форма), без скрипта не модальное, но стоит, как модальное: закреплено по центру экрана выше шапки и секций, подложка гасит страницу (тень со спредом во весь экран) и ловит щелчок мимо окна, страница под ним прокручивается; появление то же — проявление с небольшим ростом, и доходит до конца без :modal |
| Секция со списком карточек | Не движется: на телефоне (до 600 px) карточка или секция с сеткой карточек объектов становится плоской — без кольца, заливки и боковых полей, — а карточки объектов в ней остаются карточками во всю ширину экрана без полей страницы (на 390 — 358 px), без черты между ними; без :has() — прежний вид; сетка простых карточек (Кит.Карточка в ячейках сетки — обзор групп) — так же (дев-сборка 8) |
| Панель сохранения | Тень уровня 2 появляется за medium1, только когда панель прилипла к низу экрана и под ней прокручивается форма (признак data-stuck ставит скрипт); в конце короткой формы панель без тени; без скрипта признака нет, и панель всегда обведена кольцом outline-variant — прилипшая отделена от полей под ней краем (дев-сборка 10); слой панели выше плавающих подписей полей; несохранённые правки меняют её заливку на secondary-container |
| Заголовок-ссылка карточки | Не движется: у карточки и секции с Адресом и Иконкой значок и имя внутри ссылки стоят с зазором 8 px, как у заголовка без ссылки |
| Шеврон панели | Раскрывашка-панель несёт шеврон справа: вниз у свёрнутой, вверх у открытой — поворот за short4; у записи реестра его нет |
| Сегменты в окне | Не движутся: в окне на любой ширине — как на телефоне (во всю ширину, колонка не уже слова, подпись переносится между словами); пять сегментов в окне, а на телефоне четыре, встают столбиком — сегмент на строку |
| Длинное слово в окне | Не движется: адрес, ключ или хеш без пробелов в заголовке, лиде и теле окна и в подписи эха подтверждения переносится по знакам — окно не шире экрана, слово не обрезано его краем; подпись эха стоит над полем обычной строкой |
| Подсветка кода README | Не движется: в светлой теме содержимое блока кода, раскрашенного shiki в цвета github-light, притемнено фильтром до 0.7 — самый бледный токен на белом 6.3:1 вместо 3.5:1, оттенки те же; тёмная тема не меняется |
| Подвал страницы | Не движется: тело страницы — столбик во всю высоту окна, основное забирает остаток, и на короткой странице подвал стоит у низа окна, а не посреди него; длинная страница прокручивается, как прежде |
| Вложенная поверхность | Не движется: карточка, карточка объекта, плитка и свёртка внутри карточки или секции — с одним кольцом, без своей тени (в светлой теме тени больше не лежат одна в другой); на наведении — тень уровня 1, на ступень ниже, чем у карточки на странице |
| Карточка в карточке | Не движется: на телефоне (до 600 px) вложенная карточка, секция и таблица сразу становятся частью родителя — без тени, кольца и боковых полей, с чертой между соседями; поля страницы и карточки — 16 px |
| Ссылка в тексте | Не движется: подчёркнута всегда, а не только под указателем; ссылка навигации, кнопки, ячейки таблицы и имя карточки подчёркиваются только под указателем, как прежде |
| Строка поиска со значком | Не движется: значок «поиск» стоит внутри рамки поля справа на любой ширине, на телефоне ничего не переносится; на нажатии — рябь и слой состояния кнопки-значка. Поиск дерева галок (дев-сборка 8) — той же строкой |
| Кнопка без отправки | Не движется: кнопка БезОтправки в форме — type="button", на нажатии та же рябь и слой состояния, что у любой кнопки, а форма не уходит |
| Плашка с крестиком формой | Не движется: плашка со словом Снять печатается внутри своей тихой формы (tag-form) — строчным блоком, и в ряду плашек форма встаёт, как сама плашка; крестик — кнопка отправки, ответ ложится на страницу тихой отправкой |
| Тёмная тема помягче | Не движется: поверхности тёмной темы на ~4 тона светлее базовой схемы Material — фон L* 10 (#1d1a21 при базовом акценте) вместо 6, контейнеры 6/13/16/20/24, кольцо карточек поднято вслед за фоном; текст на любой поверхности — не меньше 4.5:1, карточка на 6 тонов светлее фона; светлая тема прежняя |
| Значок бренда | Не движется: логотип бренда (бренд:<ключ>, Simple Icons) — цветом бренда; тёмный логотип (GitHub, Keycloak, Mattermost) в тёмной теме — цветом текста: и инлайн-значок (лист), и файл для <img> (его стиль по color-scheme страницы) |
| Справка у флажка и тумблера | Не движется: значок «?» флажка и тумблера стоит в строке подписи и остаётся позиционированным, поэтому его зона касания 48 px — вокруг значка, а не во всю строку: щелчок по флажку, дорожке и подписи переключает, по «?» — открывает справку (было с дев-сборки 5: зона накрывала всю строку, и тумблер со справкой не переключался) |
| Рубрика шторки | Не движется: рубрика внутри группы разделов в шторке — ступенью ниже имени группы: label-medium приглушённо, левым краем — с текстом пунктов; имя группы — прежний title-small (дев-сборка 9) |
| Пустое состояние с двумя путями | Не движется: второй путь из пустоты (Действие дважды или Действия(…)) — рядом с первым одной группой по центру, второй без своего вида — вторичной кнопкой; на телефоне (до 600 px) — столбиком во всю ширину; у корня пустота держит отбивку снизу --space-4, как карточка, — карточка под ней не прилипает (дев-сборка 9) |
| Переключатель темы пилюлей | Бегунок пилюли темы едет за short4 (200 мс) стандартной кривой, когда страница меняет тему: положение и значок (солнце в светлой, луна в тёмной) лист берёт у html[data-theme], поэтому со скриптом бегунок едет вместе с перекраской, а aria-checked скрипт правит сразу; без скрипта — POST и страница возвращается с бегунком на новом месте; при «уменьшить движение» — без перехода (дев-сборка 9) |
| Пресеты темы | Не движутся: пресет (classic — кит 0.8, material — по умолчанию, oscript — сайт oscript.io) меняет затравку, нейтраль и тоны поверхностей обеих тем; рамка образца со словом Пресет(Ключ, Поле) перекрашивается на месте при выборе пресета (зеркало признаком), её переключатель «Светлая / Тёмная» показывает пресет во второй теме (дев-сборка 9) |
| Выбор значка | Не движется: сетка значков (глифы и бренды) — радиокнопки, выбранная ячейка — заливка secondary-container и кольцо primary, наведение — слой состояния за short3; со скриптом строка поиска отбирает ячейки на месте (пустой раздел прячется, счёт найденного — в живую область), Enter выбирает первый найденный; зеркало поля пишет в образец сам значок (дев-сборка 9) |
| Страница входа | Не движется: узкая страница — колонка 360 px по центру (на телефоне во всю ширину с полями), шапка колонки — знак 48 px, заголовок headline-small и подпись по центру; каркас вокруг неё печатает только знак и пилюлю темы; разделитель «или» — линии outline-variant по бокам текста; рамочная строка — текст по центру в рамке; ссылка «Забыли пароль?» — над полем справа (дев-сборка 9) |
| Шапка узкой страницы | Не движется: у страницы с узкой колонкой (вход, ворота) шапка на телефоне — одной строкой, знак слева, пилюля темы справа (прежняя шапка без единого меню на узком встаёт колонкой); длинное имя знака сжимается многоточием (дев-сборка 10) |
| Сноска | Не движется: примечание вида «сноска» — body-small цветом пояснения, без рамки и своих маргинов, ссылка того же кегля; в узкой колонке по центру (дев-сборка 10) |
| Служебный значок ключа | Не движется: key каталога провайдеров — глиф Material, а не бренд: в перечне Иконка.ИменаБрендов() и в разделе «Бренды» выбора значка его нет; файл key.svg и имя бренд:key печатаются, как прежде (дев-сборка 10) |
| Служебные глифы | Не движутся: в выборе значка стрелок, меню, крестика, солнца и луны нет — раздел «служебные» печатается, только если назван; слово Исключить убирает значки, которых домен не принимает; сохранённое значение остаётся ячейкой (дев-сборка 10) |
| Окно сброса слота картинки | Слово АдресЗакрытия у слота — как у окна и подтверждения: крестик и «Отмена» окна «Вернуть стандартную?» — ссылки на страницу без окна, без скрипта переход, со скриптом закрытие на месте той же анимацией (дев-сборка 10) |
| Лид ворот | Не движется: подпись шапки узкой страницы — строка или узел; абзац длиннее строки переносится по словам по центру, строки выровнены по длине, не шире колонки; длинное слово (адрес, логин) переносится внутри себя (дев-сборка 10) |
| Отказ у сегментов | Не движется: слово Ошибка у сегментов — строка field__error под пилюлей, рамка и черты пилюли цветом ошибки, группа aria-invalid; со скриптом после тихой отправки фокус встаёт на отмеченный сегмент отбитой группы (дев-сборка 10) |
| Значок у сегмента | Не движется: значок сегмента (слово Иконка, глиф или бренд) стоит перед подписью 18 px (у малых — 16), не сжимается; галка выбранного въезжает перед ним; на телефоне подпись переносится, значок остаётся (дев-сборка 10) |
| Пустая ячейка со своим значком | Не движется: Пусто(Подпись, Иконка) у выбора значка — приглушённый значок вместо перечёркнутого круга (выбранный — в полную силу) и видимая подпись справа от ячейки; поиск её не прячет (дев-сборка 10) |
| Проза | Не движется: Кит.Проза(Html) — чужой документ (README), санитизированный китом при печати, в оформлении вида «проза» (блоки кода, таблицы, картинки) (дев-сборка 10) |
| Три режима ветки | Не движутся: режим ветки дерева галок — массовая отметка; «все» и «никакие» отмечают или снимают детей и запирают их на месте, «выбранные» отпирают — галки меняются сразу, без перехода; сервер печатает детей «все» и «никакие» запертыми их состоянием (дев-сборка 10) |
| Страница ветки | Не движется: страница ветки с режимом (Ветка(Ключ, Страница, Всего, Режим)) печатает строку ветки с сегментами режима, запертых детей «все»/«никакие», ряд отмеченного и правок — без скрипта она выглядит и правится как ветка дерева; скрипт берёт из неё только детей (дев-сборка 10) |
| Сегменты режима строкой | Не движутся: сегменты режима у строки дерева галок — малой пилюлей своей ширины одной строкой на любой ширине, и в окне (правила окна и телефона — столбиком и во всю ширину — для выбора в форме, не для строки дерева); подписи не переносятся, строка ветки переносит пилюлю под подпись целиком (дев-сборка 10) |
| Скрипт-модуль | Не движется: Кит.Скрипт().Модуль(Текст) — встроенный <script type="module"> в теле страницы (подсветка кода README); модули отложены движком сами, порядок — порядок узлов (дев-сборка 10) |
| Закрытие окна переходом | Окно со словом Переходом() не закрывается на месте: крестик, «Закрыть», «Отмена» — переход на адрес закрытия, Esc со скриптом — тоже (data-close-to); анимации закрытия нет — страница сменяется (дев-сборка 10) |
| Предел ряда отмеченного | Не движется: ряд отмеченного вне показанного со словом ПределОтмеченных печатает первые плашки и хвост «ещё N» — текстом или ссылкой на продолжение (дев-сборка 10) |
| Ворота вошедшего | Узкая страница вошедшему печатает его учётку справа от пилюли темы; меню открывается тем же движением, что единое меню (на широком — выпадающим, на телефоне — шторкой), и в нём нет дорог (дев-сборка 10) |
| Цели касания | Не движутся: на касании (pointer: coarse) область нажатия кнопки-значка, крестика плашки, кружка акцента и «?» — 48 × 48 при прежнем видимом размере; слой состояния и рябь — в видимых границах |
| Шапка врезки | Маркер раскрытия впереди — шеврон, как у дерева галок и свёрнутой секции (дев-сборка 8; был треугольник), поворачивается вниз у открытой врезки и подробности (короткая стандартная кривая); у простой раскрывашки — тот же шеврон вместо системного треугольника браузера; дети шапки — значок, подпись, плашки — стоят в строку с зазором 8 px и переносятся вместе |
| Панель сегмента | Не движется: панель вне набора (ПанельСегмента) появляется сразу, как только в форме отмечен её сегмент, — лист по месту группы и номеру сегмента, без скрипта; без :has() видны все панели |
| Подпись сегмента на телефоне | Не движется: подпись переносится только между словами; колонка сегмента не уже самого длинного слова своей подписи, остаток ширины — поровну, и однословная подпись делает свою колонку шире соседей, а не рвётся посреди слова |
| Колонка чисел | Не движется: число и заголовок над ним — по правому краю, цифры одной ширины; в таблице карточками на телефоне число сразу встаёт слева под подписью — лист, без скрипта |
| Прочерк пустой ячейки | Не движется: печатает сервер — прочерк таблицы, у колонки свой поверх него (Колонка(…).Пустое(…)), у колонки действий его можно снять |
Меню, шторка и диалог открываются движком браузера по атрибутам popover и <dialog>. Их выход тоже анимирован: display и верхний слой снимаются дискретно, после движения. Движок, который так не умеет, открывает и закрывает их мгновенно — поведение то же, только без анимации.
Раскрывашка анимируется там, где движок знает interpolate-size и ::details-content. Остальные открывают её сразу, как раньше.
Подпись поля плавает, рамка в фокусе становится primary
HTML = Набор.Поле().Отрисовать(Новый Структура("Имя, Метка, Подсказка",
"название", "Название проекта", "Видно всем участникам."));Ползунок тумблера
HTML = Набор.Тумблер().Отрисовать(
Новый Структура("Имя, Метка, Включено, ПодписьВкл, ПодписьВыкл",
"видимость", "Видимость проекта", Ложь, "публичный", "приватный"));Раскрывашка — высота едет до своей
Подробности сборки
Собрано за 42 секунды, предупреждений нет.
HTML = Кит.Раскрывашка(Кит.Примечание("Собрано за 42 секунды, предупреждений нет."))
.Шапка("Подробности сборки").Рендер();Меню растёт из угла у кнопки
Пункты = Новый Массив();
Пункты.Добавить(Новый Структура("Подпись, Адрес, Иконка",
"Настройки", "/настройки", "шестерёнка"));
Пункты.Добавить(Новый Структура("Подпись, Адрес, Иконка",
"История", "/история", "история"));
HTML = Набор.Меню().Отрисовать(Новый Структура("Подпись, Пункты",
"Действия проекта", Пункты));Тост въезжает снизу
HTML = Набор.Тост().Отрисовать(Новый Структура("Текст, Вид", "Проект сохранён.", "ok"));Что делает скрипт#
Скрипт кита oscript-ui.js делает то, чего одним CSS не сделать. Каркас страницы подключает его сам, перед скриптом инсталляции. Глобальных имён скрипт не заводит и разметку не печатает — только атрибуты, рябь и текст отказа под полем (тот же field__error, что печатает сервер); новую плашку списка он клонирует из шаблона, который напечатал сервер.
- Рябь. Круг расходится от точки нажатия и гаснет, как в MUI. С клавиатуры, на Enter и пробеле, рябь идёт из середины. Даже от короткого касания она успевает показаться — живёт не меньше 225 миллисекунд. Рябь есть у кнопки, чипа, вкладки, пункта дерева, страницы пагинации, хода в шапке, плашки-ссылки, плитки-ссылки и кнопок копирования.
- Пустота поля. Атрибут
data-emptyу пустого текстового поля опускает подпись на место значения. Скрипт следит за вводом, сбросом формы и автозаполнением браузера. У выбора, даты, цвета и файла подпись всегда наверху: они всегда что-то показывают. - Шапка при прокрутке. Атрибут
data-scrolledу шапки, когда страница сдвинута вниз. По нему шапка берёт цвет контейнера и тень второго уровня. - Индикатор вкладок. При переходе по вкладке того же ряда скрипт даёт активной вкладке имя перехода (
data-vt), и движок везёт индикатор со старой страницы на новую (View Transitions между документами). Сама страница меняется сразу — двигается только индикатор, как у Tabs в MUI. - Закрываемый тост. Крестик
data-closeгасит тост и убирает его со страницы. - Тихая отправка. Форма с
БезПерезагрузки()уходит в фоне, и ответ сервера ложится на страницу на место, без перезагрузки. Подробно — в разделе Формы деревом. - Тема сразу. Переключатель темы (форма с
data-theme-toggle) перекрашивает страницу, не дожидаясь сервера: обе палитры уже лежат в голове страницы, и смена темы — один атрибут. Где движок умеет View Transitions, старая тема растворяется в новой. Сервер узнаёт о смене тем же запросом, что и без скрипта, только в фоне, и его ответ страницу не трогает: куку ставит ответ, а из него скрипт берёт только сам переключатель — значок и подсказку. Куда бы сервер ни перенаправил ответ, хоть на главную, человек остаётся на своей странице, с прокруткой и введённым; сбой сети тоже ничего не меняет. - Отправка по изменению и несохранённое. Тумблер с
СохранятьСразуотправляет форму сам; запасную кнопку отправки его формы, где других полей нет, лист при работающем скрипте прячет, а у формы без своей кнопки её печатает сам кит (toggle__save, словарьtoggle.save) и при скрипте тоже прячет (@media (scripting: enabled)) — в любом месте страницы, а не только в записи реестра. Панель сохраненияdata-savebarподсвечивается на первом же вводе в её форму, и подсветку снимает ответ сервера после сохранения. Форму, в которой правили, пока шёл запрос, скрипт помечаетdata-edited, и ответ её введённое не сбрасывает. Панель сохранения, прилипшую к низу экрана, скрипт отмечает признакомdata-stuck(наблюдатель пересечений; лист держит панель на пиксель ниже края) — по нему лист даёт ей тень. - Окна. Команды кнопки
show-modalиclose(commandиcommandfor) исполняет движок; движку, который их ещё не знает, их доучивает скрипт. Ссылка-кнопка сdata-opens(кнопкаОткрывает(Окно), когда у окна или у кнопки естьАдрес) не уводит со страницы: скрипт открывает окно с этим идентификатором на месте, без запроса; без скрипта она ведёт на страницу, где сервер печатает окно открытым. Окна и подтверждения из записей реестра сервер печатает после списка, а не в теле записи: скрипт открывает их на виду, даже когда запись свёрнута. Окно, которое сервер напечатал открытым, скрипт поднимает модальным. Форма в окне после удачной тихой отправки закрывает окно и ставит фокус на элемент сdata-focus(словоФокус()), после отказа (4xx) остаётся в окне с отказами у полей; ответ 2xx, который сам печатает другое окно открытым, закрывает окно формы и поднимает окно ответа модальным, а открытое окно, которого в ответе нет, закрывает — раздел «В окне» страницы Формы деревом. Открывая окно, скрипт (а с командойshow-modal— движок по его признакуautofocus) ставит фокус на первое поле тела — поле с отказом раньше, — а окну без полей на заголовок (tabindex="-1", без кольца), а не на крестик в шапке (дев-сборка 8). Ссылка сdata-closes(крестик и «Отмена» окна со словомАдресЗакрытия) закрывает окно на месте, без перехода и перезагрузки; без скрипта — ведёт на страницу без окна. - Окно по адресу. Кнопка с
data-window-link(Кит.ОкноПоАдресу) не уводит на страницу с окном: скрипт берёт её запросом в фоне, кладёт окно в одну на страницу оболочкуdata-window-shellи открывает. Не вышло — показывает тостdata-window-error, напечатанный сервером рядом с оболочкой. Подробно — Окно. - Подтверждение. Форма с
data-confirm(СПодтверждением) не уходит сразу: скрипт открывает окно подтверждения, а уходит форма его кнопкой. Значение признака — идентификатор окна, и обслуживает его скрипт кита: свойwindow.confirmнаdata-confirmприложение не вешает. Эхо-поле держит эту кнопку недоступной (disabled), пока набранное не совпало с образцом. Форма из набора записи реестра печатается дважды — пунктом меню ⚙ и кнопкой внизу, — ноidнесёт только пункт меню: копия внизу безidтоже открывает подтверждение своимdata-confirm, а кнопка окна шлёт форму меню. - Шаги. У шагов видна одна панель, над ней полоса номеров, под ней «Назад / Дальше». «Дальше» и Enter в поле пускают на следующий шаг, только когда поля текущего годны: первому негодному браузер показывает свой отказ (
reportValidity). Признаки:data-steps(корень, номер шага, с которого начать),data-steps-bar,data-steps-go,data-steps-panel,data-steps-back,data-steps-next,data-steps-submitпечатает сервер;data-steps-liveу корня иdata-steps-doneу пройденного шага ставит скрипт. Без скрипта шаги — панели подряд, и форма отправляется целиком. Единственная видимая панель шагов со скриптом — без рамки и своей заливки: содержимое шага стоит прямо под полосой номеров, и в окне нет поверхности в поверхности (дев-сборка 8). - Сегменты и подсказка (0.12). Своего раздела в скрипте у них нет. Сегменты со словом
СохранятьСразупечатают у каждой радиокнопкиdata-autosubmit— тот же признак, что у тумблера, его исполняет пункт 8. Признакdata-segment(значение сегмента) у панели набора полей печатает сервер для своего CSS и скрипта страницы; наборы переключает лист. Панель вне набора (ПанельСегмента) и её группа несутdata-segments-slot(место группы в форме), панель — ещёdata-segments-number; её тоже переключает лист. Подсказка открывается движком поpopovertarget. - Поле-список. У списка скрипт показывает плашки, напечатанные сервером, отдаёт имя поля скрытому полю
data-chips-valueи пишет в него строку через запятую при каждой правке. Enter, запятая и уход из строки делают из набранного плашку — её скрипт клонирует из шаблонаdata-chips-chip, напечатанного сервером; крестикdata-chips-removeи Backspace в пустой строке плашку снимают. Признаки:data-chips,data-chips-value,data-chips-chip,data-chips-removeпечатает сервер;data-chips-liveу корня ставит скрипт. Без скрипта это обычное текстовое поле со строкой. - Поле цвета. У поля цвета скрипт показывает ряд образцов
data-swatches-rowи держит его в согласии с полем кода: кружок пишет свой код, набранный код отмечает свой кружок, палитраdata-swatches-own— «свой»; имя выбранного пишет вdata-swatches-name. Корень —data-swatches. Палитра кита (ОбразцыТемы) — те же кружки, своего поведения у неё нет. Без скрипта ряд спрятан, и код набирают руками. - Проверка до отправки. Поле с текстами отказов (
data-error-required,-type,-number,-pattern,-minlength,-maxlength,-min,-max— их печатает поле из словДлина,Шаблон,ПроверкаДоОтправки) не показывает пузырь браузера: на попытке отправить и на уходе из поля скрипт пишет текст отказа (уход в кнопку отправки своей формы отказ не печатает — его напечатает проверка отправки на том же щелчке, и строка отказа не сдвигает кнопку из-под отпускаемой мыши) в егоfield__error, ставитaria-invalidи ссылку изaria-describedby, фокус — на первое негодное поле; исправленное поле снимает отказ на вводе. Поле сdata-check(ПроверкаНаСервере) спрашивает адрес, свободно ли значение:200— свободно,409с текстом — занято. - Тост из ответа. Новые тосты из ответа тихой отправки скрипт поднимает вниз экрана и показывает по одному, каждый 5 секунд (
data-toast-live:wait,show,gone); пока на тосте мышь или фокус, время стоит. Тост с рольюalertостаётся на своём месте — подробно на странице Тост. - Слот картинки. На файловом поле слота картинки признак
data-autosubmit: выбор файла отправляет форму слота сам, тихой отправкой (multipart), а выбранная картинка сразу встаёт в предпросмотр из памяти браузера. Отказ (4xx) ложится на страницу, как любой ответ тихой отправки: под слотом встаёт напечатанный сервером отказ (Ошибка), в рамке — прежняя картинка. Меню ⚙ «Вернуть стандартную» отправляет форму сброса сdata-confirm— через подтверждение. Закрытое окно сброса возвращает фокус на кнопку ⚙. - Зеркало и образец. Узел с
data-mirror="имя"(словоЗеркало) показывает текущее значение поля с этим именем при каждом вводе; поле цвета пишет свойство CSS--mirror-<имя>; узел сdata-mirrors="имя …"(словоЗеркалоПризнаком) получает значение поля в признакdata-mirror-<имя>, вид по нему задаёт лист приложения; свойство--mirror-accentв образце лист сразу превращает в акцент узла — основная кнопка, плашка и ссылка внутри перекрашиваются без своего движения. Кнопкиdata-preview-themeобразца меняютdata-themeего рамки (aria-pressed), а палитру для рамки скрипт собирает из блока токенов страницы и подключает листом без разметки (adoptedStyleSheets). - Таблица карточками и свойства (0.13). Своего поведения у них нет. Запись-карточку раскладывает лист: заглавная ячейка
cell-title— первой строкой, ячейка менюcell-menu— в правом верхнем углу. Секрет свойства открывает лист по галке «Показать». Кнопку копирования свойства сdata-copy— тот же признак, что у блока команды и поля из конфига, — оживляет скрипт инсталляции. Скрипт кита даёт этой кнопке и кнопке «Показать» только рябь. - Страницы и отбор без перезагрузки. Ссылка с
data-quiet— шаг пагинации и ссылка сортировки колонки со словомБезПерезагрузки— не уводит со страницы: скрипт берёт её адрес запросом в фоне и кладёт ответ на место тем же морфингом, что ответ тихой формы, прокрутка и фокус остаются, адрес уходит в историю вкладки (pushState). Пока переход в пути, навигация или таблица помеченыaria-busy. Тихая GET-форма — строка поиска и форма отбора — ещё и уходит сама по изменению поля: через 0,4 с после последней буквы, выбор — почти сразу; тот же отбор дважды не уходит, и форма после ответа не сбрасывается. «Назад» и «Вперёд» по записям истории, которые пометил скрипт, тоже берутся тихо и возвращают прокрутку и значения полей отбора. Щелчок с Ctrl/Cmd, Shift или средней кнопкой — по-прежнему браузеру. Без скрипта это обычные ссылки и обычная GET-форма. - Дерево галок (0.13). Щелчок по галке ветки (
data-checktree-box) дерева галок раздаёт её состояние детям, а ветки выше пересчитывают «частично» по детям (indeterminate; напечатанное серверомdata-checktree-stateскрипт не переписывает). «Показать ещё» (data-checktree-more) и раскрытие пустой ветки берут страницу детей запросом в фоне и дописывают её в списокdata-checktree-pageс тем же ключом. «Найти» в поискеdata-checktree-searchберёт ответdata-checktree-foundс адреса поиска и кладёт его на место веток. У дереваdata-checktree-edits-onlyправки галок и режимов (data-checktree-modes) копятся плашками вdata-checktree-edits— плашку скрипт клонирует из шаблонаdata-checktree-chipс текстамиdata-checktree-text-on,-off,-mode, крестикdata-checktree-undoснимает правку, — и форма отдаёт только их:правки=1,включить,снять,режим. Обёртка —data-checktree-set. Режим ветки скрипт узнаёт по значению радиокнопки (дев-сборка 10 — три режима с массовыми галками): «все» отмечает всех детей и запирает их (disabledс признакомdata-checktree-locked, список —inert), «никакие» снимает и запирает, «выбранные» отпирает и начинает с того, что было видно — после «все» все отмечены, после «никакие» все сняты (массовая отметка — основа: правки детей считаются против неё, с формой уходитоснова=<ключ>:все|никакие); дети, приехавшие страницей позже, встают по режиму, отмеченному сейчас; «кроме» (дев-сборка 8) отпирает детей отмеченными. Скрытое поле показанногоdata-checktree-shown(словоПравкиБезСкрипта) при отправке без правок скрипт пересчитывает по всем галкам обёртки. Крестик плашки отмеченного вне показанного (рядdata-checktree-picked, плашкаdata-checktree-pick) у дерева с правками становится правкой того же узла в общем ряду, и ряд правок держит плашку зачёркнутой, пока правка стоит; без скрипта он — галка формы, и зачёркивает плашку лист. Строку сводки правокdata-checktree-changes(словоСводкаПравок) скрипт пишет по плашкам ряда: «Изменений: 3 (+2 −1)», тексты —data-checktree-text-changesи-split; без правок она спрятана. Без скрипта ветка показывает первые 50, ссылка и кнопка поиска ведут на свои адреса, и форма отдаёт галки. - Подсказка обрезанного имени (0.13). Имя карточки объекта, которое лист обрезал многоточием, на наведении получает
titleс именем целиком; у целого имени подсказки нет, свойtitleот сервера скрипт не трогает. Без скрипта имя так же обрезано, а целиком его читает скринридер и видно по ссылке.
Без скрипта страница работает и выглядит законченной: подпись поля просто стоит наверху, ряби нет, шапка без тени, индикатор появляется на новом месте, а формы и переключатель темы отправляются обычной навигацией с перезагрузкой. Окно открывает движок, а где он команд не знает — сервер печатает его открытым по адресу с ?окно=…; подтверждение спрашивает сервер страницей. Негодное поле не пускает браузер своим пузырём, а то, что знает только сервер, он отбивает отказом под полем; тост стоит на странице, где его напечатал сервер. Слот картинки показывает системное файловое поле и кнопку отправки, образец — в теме страницы, а зеркала — то, что напечатал сервер. Секрет свойства открывается и без скрипта, а значение для копирования выделяют мышью.
Рябь — нажмите на кнопку
HTML = Набор.Кнопка().Отрисовать(Новый Структура("Подпись, Иконка", "Сохранить", "сохранить"))
+ Набор.Кнопка().Отрисовать(Новый Структура("Подпись, Вариант", "Отмена", "вторичная"))
+ Набор.Кнопка().Отрисовать(Новый Структура("Подпись, Вариант, Иконка, ТолькоИконка",
"Настройки", "ссылка", "шестерёнка", Истина));Меньше движения#
Человек может попросить систему убрать движение — настройка prefers-reduced-motion. Кит её слушает:
- лист сводит длительность всех анимаций и переходов почти к нулю, повторы — к одному, плавную прокрутку отключает;
- скрипт перестаёт рождать рябь.
Состояния при этом меняются как прежде — пропадает только путь между ними.
Гайдлайны#
Код
HTML = Набор.Кнопка().Отрисовать(Новый Структура("Подпись, Вариант", "Наведите", "вторичная"));Код
HTML = "<button class=""button button--ghost"" type=""button"" "
+ "style=""transition:background-color 1200ms linear"">Наведите</button>";Код
HTML = Набор.Кнопка().Отрисовать(Новый Структура("Подпись, Занята", "Сохраняется", Истина));Код
HTML = "<button class=""button"" type=""button"" "
+ "style=""animation:ui-toast 600ms ease-in-out infinite alternate"">Сохраняется</button>";Доступность#
- Настройка «меньше движения» гасит анимации кита целиком. Своё движение в листе инсталляции тоже оборачивайте в
@media (prefers-reduced-motion: reduce). - Движение ничего не сообщает в одиночку: занятая кнопка несёт
aria-busy, тост — рольstatusилиalert. - Рябь — декорация: её контейнер скрыт от скринридера (
aria-hidden) и не ловит указатель.