Рух
У продукті вже рухалося 46 файлів із 88, і жодне число там ніхто не обирав: 12 різних тривалостей і крива ease на 817 значеннях з 818 - тобто значення, яке дістається, коли криву не називав ніхто. Цей етап руху не додає, він його називає: три роботи, три тривалості, три криві. Усе на сторінці живе і читає токени з tokens.css.
Спершу називається робота, потім береться анімація. Момент, для якого жодна з трьох не називається, в інвентар не потрапляє: рух без роботи коштує увагу на кожному відкритті екрана, а виглядає як старанність.
--dur-slow + --ease-enter + --move-mdШвидкість, що не змінюється, каже «живе» і нічого не обіцяє. Цикл, який прискорюється, читається як «майже готово» - обіцянка, якої продукт дати не може: скільки триває зовнішня перевірка, він не знає. Береться лише там, де процес довший за ~300 мс: миготіння на швидкій відповіді гірше за нерухомість.
У цього циклу ще немає токена, і це відкрите питання кроку 2
Три тривалості описують ПЕРЕХІД, а не цикл: пульс на 150 або 330 мс це миготіння. Сьогодніskeleton.css несе 1.1s, а два обертуни .9s - літералами, що контракт називає дефектом. Рішення про четвертий токен за власником, і поки воно не ухвалене, тут стоїть те саме число, що в системі.--dur-fast + --move-smВідгук, повільніший за ~150 мс, уже не відповідь: він читається як роздум інтерфейсу. Тому --dur-fast стоїть на верхньому краї цього вікна, а не за ним.
Три об'єкти рушають одночасно від одного натиску. Порівняння тут не оформлення, а єдиний спосіб побачити різницю: окремо жодна з трьох не читається ні швидкою, ні повільною.
| Токен | Значення | Робота | Звідки число |
|---|---|---|---|
--dur-fast | 150ms | відповідь | власна більшість продукту, взята, а не замінена: 92 зі 133 оголошень і 26 083 з 42 303 обчислених значень уже стоять тут. .14s і .12s згортаються в неї |
--dur-base | 220ms | перехід у компоненті | теж наявне значення (6 оголошень), але робота змінюється: сьогодні це ціна ПОЯВИ (шухляда, шит, тост), відсьогодні поява піднімається на щабель вище |
--dur-slow | 330ms | поява елемента | єдине число, якого в переписі не було, і це знахідка, а не виняток: продукт живе в смузі 120-280 мс, де немає щабля, що читається як прибуття. Ніщо не сповільнюється - одинадцять організмів зі станом open сьогодні не мають переходу взагалі. 330 це 1.5x від базової, тобто те саме відношення, у якому базова стоїть до швидкої |
Тривалість у всіх трьох однакова, тож видно рівно форму. Референс кроку 1 назвав родину (power_.out) і не дав чотирьох чисел - тому числа підігнані до функції родини приладом tools/ease-fit.mjs. Пригадана з таблиці крива це вигадане число, лише на одну таблицю далі.
--dur-slow| Токен | Значення | Де вживається | Похибка підгонки |
|---|---|---|---|
--ease-standard | cubic-bezier(.48, .04, .52, .96) | стан змінюється, і жоден кінець руху не важливіший за інший | power1.inOut, 0.36% |
--ease-enter | cubic-bezier(.33, 1, .67, 1) | поява: енергія витрачена на старті, тож око приходить, коли елемент уже осідає і вже читається | power2.out, 0.19% |
--ease-exit | cubic-bezier(.33, 0, .67, 0) | зникнення: рушає повільно, щоб було видно ЩО йде, далі швидко, бо дивитись до кінця нема навіщо | дзеркало, 0.19% |
Пружин і відскоків немає, і це рішення з джерелом
back.out(1.7) (перельот) і elastic зняті за назвою: voice.md, Принцип 5, забороняє святкування, а таблиця тону ставить фазу 8 - очікування після оплати, дно емоційної кривої - на цільову емоцію нуль. Перельот це маленьке святкування, доставлене рівно туди, де людина його найменше хоче.--move-sm 2px - підйом під курсором; у системі вже 10 оголошень translateY(-2px) і два -1px, що згортаються сюди. --move-md 10px - шлях елемента, що прибуває: тост уже стартує з translateY(10px). Рух, довший за півтори висоти елемента, перестає читатись як зв'язок і починає читатись як подорож.
Старт за екраном це НЕ ця дистанція
translateY(100%) і translateX(-100%) у шухляди та шита це відношення до власного розміру елемента. Записане довжиною, воно зламається тієї миті, коли елемент змінить розмір.Головна знахідка кроку 4, і вона більша за будь-яке число з переписом. Кожна поверхня цього продукту з'являється перемиканням display. display - дискретна властивість: у неї немає середини, тож інтерполювати нема чого, і перехід не бачить її взагалі. Компонент міг чесно читати --dur-slow, пройти перекличку кроку 3 і все одно виникати в одному кадрі.
| Число | Що воно означає |
|---|---|
| 20 | поверхонь у системі, які з'являються перемиканням display. Рахує tools/motion.mjs --surfaces, з двох боків: клас може ДАВАТИ видимість (дев'ятнадцять) або ЗАБИРАТИ її (банер cookie - і саме його прилад не бачив, поки читав один бік) |
| +1 | панель спліт-в'ю тренера, яку перемикає javascript через атрибут hidden. Прилад читає css і цю поверхню не бачить; вона порахована рукою, і це сказано, щоб «20» ніколи не читалось як «усі» |
| 0 | @starting-style і transition-behavior: allow-discrete у системі до цього кроку |
| 20 з 20 | несуть пару після кроку 4. Перевірку перевірено з іншого боку: пару знято з галочки меню, лічильник почервонів на ній одній - перевірка, яка ніколи не падала, не показана робочою |
--move-mdОбидва елементи стоять на display: none у спокої, як у продукті. Приберіть @starting-style - і вони почнуть виникати, не змінивши більше нічого.
Тиха деградація, і межа виміру названа
Браузер, який не знає цього правила, викидає його і показує поверхню миттєво - тобто рівно так, як показував учора. Заміряно цієї сесії: Chrome 151 розбирає@starting-style, allow-discrete, @view-transition і view-transition-name. Safari і Firefox у цій сесії не міряні, і це записано, а не припущено.Спершу структурна відповідь, бо вона змінює саме питання. У цьому продукті стан екрана - це ОКРЕМИЙ ДОКУМЕНТ: goal-empty.html, goal-error.html, goal-loading.html. У сірому корпусі таких 16 порожніх, 13 помилок і 16 завантажень; у кольоровому 14, 11 і 13. Жоден javascript не перемикає стан усередині сторінки. Тобто «перехід між станами екрана» звичайним переходом не робиться взагалі - це навігація, і вона лежить у секції нижче. Усередині документа станів рівно два: панель спліт-в'ю тренера і повідомлення валідації під полем.
Стан (voice.md) | Носій | Тривалість | Крива | Амплітуда | Напрямок | Звірка |
|---|---|---|---|---|---|---|
| Порожній чому порожньо + дія-вихід | .empty / .emptybox, і #clDetailEmpty у спліті | --dur-base | --ease-enter | – | без зсуву | сходиться. Зсув знизу читається як «щось вантажиться», а нічого не вантажиться - тому проявлення без руху |
| Помилка що сталося + що робити | .pm-e під полем; .wf-toast.error | --dur-fast · --dur-base | --ease-standard | --move-sm | вгору, 2px | було розійшлось, виправлено. Тост помилки приходив тим самим розмахом, що й «Адресу збережено» |
| Завантаження мовчить або каже коротко | .skpulse; .auth-spin / .co-spin | --dur-cycle | --ease-cycle | – | рівний повтор | сходиться. «Рівний повтор» і фізика обертання вимагають того самого - linear |
| Успіх факт + наступний крок, без святкування | .wf-toast (ok) | --dur-slow вхід · --dur-base вихід | --ease-enter / --ease-exit | --move-md | знизу вгору | сходиться, без пружини й відскоку. Вихід укоротився з іншої причини - див. нижче |
| OOS / наявність | availability.css, restock-note.css | – | – | – | – | руху немає навмисно. Це позначки, а не події: вони не приходять і не йдуть |
| Небезпечна дія | .ceov + діалог підтвердження | --dur-slow | --ease-enter / --ease-exit | --move-md | підйом у центр | сходиться, і однаковість тут навмисна. Принцип 4 - спокійно й упевнено: діалог видалення приходить так само, як будь-який інший, бо драма в русі це тиск |
Одна анімація виявилась обрізаною числом із файлу, якого етап не чіпає
wireframes/_nav.js:1260 знімає вузол тоста через 250мс після класу .out. Крок 3 поставив вихід на --dur-slow, тобто 330 - тост зникав за 80мс до кінця власного згасання, і жоден перепис css цього побачити не міг. Сірий корпус заморожений, тож рухається число всередині системи: 220 вміщається в 250. Це єдине місце етапу, де число ЗЗОВНІ системи вирішило число всередині неї.І одна розбіжність, яка виявилась не в анімації, а в тексті
microcopy.md тримає рядок «Вітаємо у Stack! Ви увійшли 🎉». У продукті стоїть wfToast('ok', 'Ви увійшли') - без вітання і без емодзі. Редакція продукту правильна за тоном (Принцип 4: без святкування), редакція інвентаря - ні, і жоден продуктовий рядок не існує у двох редакціях. Знайдено звіркою тону, полагоджено в microcopy.md і пересобрано в voice.html тим самим кроком.Патернів у системі рівно один - action-row.css, ряд із двох дій, який стоїть на 70 сірих екранах під 53 різними іменами контейнера. Він не отримує власного переходу композиції, і це відповідь, а не пропуск.
Чому. Перехід патерна відповідає на «що з'являється, звідки і в якому порядку». Ряд дій ніколи не з'являється сам: він приходить разом із блоком, який його тримає - підвалом діалогу, ногою картки, порожнім станом, - і в того блока вже є своя робота ЗВ'ЯЗОК. Дати ряду власний вихід означало б анімувати момент, для якого не називається жодна з трьох робіт. Рух, який на ньому видно, приходить із button.css: стан в одному файлі, рух в іншому - той самий клас, що icon.css на кроці 3.
Зате спліт-в'ю - найчистіший ЗВ'ЯЗОК у продукті
Деталь клієнта приходить З БОКУ СПИСКУ, бо саме там щойно були око й палець тренера:--move-md ліворуч, --dur-base, і лише всередині двох воріт спліта. І без проявлення - крок 10.5 переводить фокус на ім'я клієнта тієї ж миті, тож панель непрозора кожен кадр руху: скрінрідер не має права читати те, чого око ще не бачить. Рух не приходить пізніше за фокус.Кожен екран продукту - окремий html-документ. «Деталь виїжджає зі списку» звичайним transition не робиться взагалі: сторінка вивантажується разом зі своїми стилями. Три чесні виходи, і мовчки не береться жоден.
| Гілка | Що це | Рішення |
|---|---|---|
| A | не анімуємо міждокументні переходи; робота ЗВ'ЯЗОК лишається за тим, що відбувається всередині документа | рекомендація кроку 4, не взята |
| B | один запис @view-transition { navigation: auto } у base.css | УХВАЛЕНО власником |
| C | окреме рішення під конкретний флоу | не взята |
Рекомендацією була A, і причина була не обережність, а власне правило пака: гілка B береться, «лише якщо в інвентарі є конкретна пара екранів з роботою ЗВ'ЯЗОК», а інвентар моментів такої пари не називає - він називає поверхні всередині документа і чотири кроки сесії. Власник обрав B; отже цей момент прийшов із РІШЕННЯ, а не з корпусу, і платить за це двома речами: власним рядком в інвентарі та критикою на кроці 6, якої пак вимагає для вигляду, якого раніше не було.
Перейти на «Адаптив» Перейти на «Геометрію»
Сторінки стенда вантажать ту саму system/index.css, тож запис із base.css діє й тут. У браузері без підтримки обидва посилання просто перейдуть, як переходили.
Крок 6: гілка B купувала МЕХАНІЗМ, а значення успадковувала
Один запис купує кросфейд і більше нічого - криву й тривалість браузер підставляє свої. Зняте зсередини живої навігації в Chrome 151, ДО правки:-ua-view-transition-fade-out, -ua-view-transition-fade-in, -ua-view-transition-group-anim-root і -ua-mix-blend-mode-plus-lighter, усі 250мс, крива ease. 250 - це не 150, не 220 і не 330, а ease - рівно те значення, заради видалення якого етап існує: перепис рахує його на 817 із 818 функцій і називає «значенням, яке оголошення отримує, коли криву не назвали». Воно повернулося в повний зріст на моменті, який власник обрав особисто.У кривої два написання, і перше зняття взяло не те
CSS-анімація несеlinear на ЕФЕКТІ (це дефолт) і справжню animation-timing-function на КОЖНОМУ кадрі. Перший запис у лог критики сказав «linear» - правдиве зняття не тієї половини. Рендериться кадрова, тобто ease. Виправлено в логу, а не тихо, і motion.mjs --view тепер друкує обидва написання поруч.| Псевдоелемент | було | стало | чому саме цей токен |
|---|---|---|---|
::view-transition-old(root) | 250мс ease | --dur-slow · --ease-exit | те, що ПІШЛО. Та сама читка напрямку, що в двадцяти поверхнях кроку 4 |
::view-transition-new(root) | 250мс ease | --dur-slow · --ease-enter | те, що ПРИЙШЛО. Цілий документ - це ПОЯВА, а поява бере верхню сходинку тих самих сходів |
::view-transition-group(root) | 250мс ease | --dur-slow · --ease-standard | морфить КОРОБКУ знімка, а не прозорість; на двох однакових документах морфити нічого, тож крива симетрична |
330 замість 250 - це на 80мс повільніше за дефолт браузера, і це сказано вголос: альтернатива - другі сходи заради одного моменту.
Перепис цього знайти НЕ МІГ, і це дорожча половина знахідки
Кожне інше питанняmotion.mjs ставиться ЕЛЕМЕНТУ. Ці чотири анімації живуть на дереві псевдоелементів, яке браузер будує на початок навігації і руйнує на її кінець: у документі в спокої його немає, тож querySelectorAll('*') не дістає до нього принципово, а не випадково. Зелений лічильник не міг почервоніти - він міг лише мовчати. Питає режим motion.mjs --view: він ставить слухача pagereveal ДО першого рядка скриптів вхідного документа, чекає ready переходу і читає document.getAnimations().І це фальсифіковано, перш ніж у це повірили
Із закоментованим перевизначенням той самий прогін друкує п'ять рядків ПОЗА РЕЄСТРОМ на 250мсease. Перевірка кривої навмисне суворіша за «чи є таке написання в таблиці», бо linear у таблиці Є - це --ease-cycle, - і сама лише звірка з реєстром пропустила б дефолт. Кожен псевдоелемент звіряється з кривою, якої вимагає його РОЛЬ. Плюс підміна токена: --dur-slow: 7.77s -> усі п'ять поїхали на 7770мс, тобто читають токен, а не літерал, що збігся.Що перехід НЕСЕ: нічого, крім самої сторінки
view-transition-name має 0 оголошень у дереві, тож перетікає один знімок сторінки в інший. Спадкоємність елемента (фото картки стає фото картки товару) тут НЕ береться, і причина - обмеження, а не смак: ім'я мусить бути унікальним у документі, а лістинг малює 12 карток, і 12 дублікатів змушують Chrome пропустити перехід ЦІЛКОМ, а не наполовину. Унікальні поіменні імена не можуть прийти ані з файлу компонента, ані з файлу екрана, бо етап 11 забороняє там рух. Це замовлення на систему, і воно в backlog.md.Токен-оверрайд сюди не дістає, і це закрито поіменно
Кросфейд браузера - це анімація, а не перехід, тож перевизначення--dur-* його не бачить. У base.css стоїть власний блок prefers-reduced-motion, який гасить ::view-transition-old/new/group поіменно. Навігація не чіпається: наступний екран приходить, зникає лише рух - стан не має права зникати разом із ним.Рух на точці адаптиву - НІ, і виняток теж НІ
Дефолт пака - не анімувати перебудову сітки: точка спрацьовує, коли людина тягне край вікна і на вміст не дивиться. Пак дозволяє виняток для поверхні, якої на вузькій ширині не існувало - у нас це рейка фільтрів.frail, рейка категорій .hrail і права панель спліта. Виняток не взято, і причина сильніша за дефолт: усі три приходять ЛИШЕ через медіазапит, тобто від зміни розміру вікна, а не від дії людини на них. Анімувати нема чого повідомляти.Дешево анімуються рівно дві властивості - transform і opacity: браузер віддає їх композитору і не чіпає ані розкладку, ані малювання. Усе інше або змушує перерахувати РОЗКЛАДКУ щокадру, або перемалювати площину.
| Було | Стало | Чому |
|---|---|---|
.sw i · transition: left, left: 21px / 3px | transform: translateX(var(--sw-travel)) | розкладка щокадру -> композитор. І хід став відношенням: 44 - 20 - 3*2 = 18, тобто рівно 21 - 3 - два числа були одним фактом, записаним двічі. Заміряно: 21 -> 3, хід -18, до пікселя те саме |
.co-sw::after · transition: left | transform: translateX(var(--cosw-travel)) | 38 - 18 - 2*2 = 16, тобто 18 - 2. Заміряно на checkout.html: 2 -> 18, хід +16 |
.ck-tog i · transition: left | transform: translateX(var(--ckt-travel)) | 40 - 18 - 2*2 = 18 проти padding-box треку. Пікселями не доводиться: банер cookie стоїть на одній сторінці всього дерева - wireframes/system.html, у замороженому корпусі, - тож цей файл сьогодні не малює нічого. Доказ буде на етапі 12 |
.wfh-meta · transition: max-height | лишається | і це відповідь із виміром, а не виняток. Шапка sticky, тобто займає місце в потоці, і весь сенс згортання в тому, щоб вона стала нижчою - жоден transform місця не звільняє |
Що коштує єдина розкладкова анімація, у числах самого браузера
listing.html, 2006 елементів, Performance.getMetrics: спокій 400мс - 0 layouts · згортання мета-бара - 25 layouts, 1.1мс layout, 1.3мс style · розгортання - 26 · той самий елемент на самій лише opacity - 1 layout, 0.1мс. І другий вимір, який знімає головний аргумент: мета-бар існує лише від 860px, тож слабкий телефон його не рендерить узагалі.Дороге МАЛЮВАННЯ: 24 оголошення, а не 70. Перепис рахує 70, але 46 із них це color, background і border-color на ховерах - площини вони не навантажують. box-shadow і filter несуть 24 правила.
Списку з двадцяти карток, якого боїться метод, у цьому продукті НЕМАЄ
Перевірено тригер кожного з 24: усі до одного це:hover, :focus-visible або клас вибору .on / [aria-current]. Курсор один, фокус один, вибраний елемент у групі один - одночасно перемальовується один елемент, максимум два на перемиканні вибору. Двадцять карток разом не бувають.Зате є місце, де одночасно рухається пів документа, і воно не в списку карток
Перемикач теми міняє[data-theme] на корені, тобто колір УСЬОГО документа одразу. Заміряно: listing.html - 2006 елементів, з них 463 з переходом кольору, зміна теми коштує 32.1мс перерахунку стилів в один момент; index.html - 459 і 21.7мс. Цього ніхто не замовляв: переходи писалися під ховер, а перетікання всієї сторінки - побічний ефект 463 правил. Той самий аргумент, яким прибрали transition: all.Власник обрав придушити. theme.js вішає .uiv-theming на корінь, міняє атрибут і знімає клас через ДВА кадри анімації: перший колбек виконується ДО того, як зміну намальовано, тож зняти клас там означало б усе одно дати переходам стартувати. Під цим класом одне правило гасить переходи на * через !important - тобто рівно те, проти чого цей етап бореться всюди, і саме в цьому суть: воно мусить перебити кожен компонент на довжину одного кадру. Це ЗАСЛІНКА, а не страхувальна сітка: вона живе по 16мс за раз і нічого не ховає, бо аудит іде на сторінці, де ніхто нічого не перемикає.
| Екран | через uivTheme() | повз заслінку |
|---|---|---|
listing.html | 0 переходів у русі через кадр після перемикання | 390 |
index.html | 0 | 263 |
Заміряно через продуктовий вхід, а не встановленням атрибута руками - перший замір робив саме так і тому не побачив жодної різниці. Права колонка це та сама перевірка, доведена з іншого боку: коли заслінки немає, число не нуль.
will-change: нуль
У всьому дереві його немає жодного разу, і це правильний стан: постійний will-change це витік пам'яті на графіці, а не оптимізація.Прилад теж мав борг, і крок 4 його оплатив
opacity стояла В ОБОХ списках переписом - і як дороге малювання, і як дешева властивість. Поки продукт майже не користувався прозорістю, це нічого не коштувало; тієї миті, коли крок 4 поставив прозорість на двадцять поверхонь, лічильник «анімує дороге» стрибнув з 73 до 107, і кожен новий випадок був найдешевшою річчю в css. Таблиця ціни кадру на кроці 5 будувалась би на роздутому числі. Прозорість композиться, а не малюється.Механізм один: у блоці @media (prefers-reduced-motion: reduce) перевизначаються ті самі токени. Компонент, що читає var(), підкоряється сам - і той, який з'явиться на етапі 12, підкориться теж, нічого про це не знаючи. Саме заради цього тривалості стали токенами, а не звичкою.
Він ставить клас на корінь документа і перевизначає ті самі п'ять токенів - той самий механізм, що й медіазапит. У продукті такого перемикача немає і не буде: перемикач це системне налаштування людини, а для частини людей це не питання смаку, а нудота і мігрень.
Справжня перевірка робиться не цим перемикачем, а емуляцією prefers-reduced-motion: reduce у браузері, обчисленим стилем, на кроці 5. Знак один: більше за 1ms це дефект.
| Рішення | Чому саме так |
|---|---|
| 1ms, а не 0s | на нулі деякі браузери не шлють transitionend, і обробник, що чекає кінця руху, чекає вічно. Скорочення руху зламало б поведінку замість того, щоб зняти оздоблення |
| стан не зникає | елемент, який з'являється, з'являється і при reduce. Зникає ШЛЯХ, а не прибуття - тому дистанції йдуть у нуль, а display і opacity цей блок не чіпає |
чому @media тут працює, а на етапі 10 не працював | там запит ПИТАВ про змінну, а медіаумова обчислюється до каскаду кастомних властивостей - правило не спрацьовувало і помилки не давало. Тут запит нічого не питає: всередині нього змінна ПЕРЕВИЗНАЧАЄТЬСЯ. Питати і присвоювати це різні операції |
| сітки не буде - рішення власника на кроці 5 | аудит зелений БЕЗ неї: 5826 рухомих елементів, 0 лишились рухомими. Аргумент ЗА неї - код, який напишуть субагенти на етапі 12; аргумент ПРОТИ - докази цього ж етапу: сітка, яка вже стояла, коштувала чотирьох етапів цифр, і під нею перший прогін аудиту дав би 0 дефектів замість 96. Етап 12 закривається приладом: motion.mjs --reduce тепер гейт, і літерал від субагента зробить його червоним так само, як зробив тут |
страхувальної сітки на * немає | на кроці 2 це речення було ХИБНИМ, і виправлене на кроці 5. Воно описувало намір, а не систему: base.css ніс @media (prefers-reduced-motion: reduce){ *{ transition: none !important } } ще з етапу 07, а !important на * б'є будь-яке оголошення в tokens.css. Під ним аудит кроку 5 був би зеленим на порожньому місці. І та сама сітка вписала хибне речення в запис кроку 3: нулі, які повертала емуляція, пояснили як «Chrome обнуляє переходи сам». Chrome такого не робить - це був власний рядок цього репозиторію, і tools/cdp.mjs казав про нього в коментарі з етапу 07. Знято на 11.5 ПЕРЕД першим аудитом, і саме це зробило аудит червоним. Прилад не має ховати дефект, який шукає - а твердження, що приладу нічого не заважає, питається в дерева, а не в наміру |
| чого оверрайд не дістає | три записи, і крок 4 закрив два з них. Цикл @keyframes - при reduce його треба ЗАМІНИТИ статикою, а не прискорити (пульс на 1ms це миготіння, тобто гірше): три з трьох закриті у власних файлах. Літерал усередині компонента - у продукті нуль. Перехід між документами - закритий поіменно: кросфейд браузера це анімація, тож base.css має власний блок reduce, який гасить ::view-transition-old/new/group, не чіпаючи саму навігацію. Доказ у браузері - крок 5 |
| токени циклу в цьому блоці НЕМАЄ | і це рішення, а не пропуск. Перехід на 1ms закінчується раніше, ніж його побачать, - це і значить «руху немає». Цикл на 1ms не закінчується взагалі: він повторюється тисячу разів на секунду |
Повний перепис і обидва корпуси - у docs/motion.md. Тут зведення з колонкою стану. Корпусів два, і вони не взаємозамінні: карта флоу ніколи не називає ховер кнопки, тож інвентар, зібраний по екранах, бачить ЗВ'ЯЗОК і СТАТУС повністю і не бачить ВІДПОВІДЬ майже зовсім.
| Момент | Корпус | Робота | Носій | Рівень | Тривалість | Стан є? | Стан |
|---|---|---|---|---|---|---|---|
| Панель клієнта у спліт-в'ю | B1 | зв'язок | coach-clients | організм | повільна | так | крок 4 |
| Оверлей і вуаль під поверхнею | B1 | зв'язок | overlay | організм | повільна | так | крок 4 |
| Шухляда меню · шит фільтрів | B1 | зв'язок | nav-drawer, filter-sheet | організм | повільна | так | крок 4 |
| Діалоги: вхід, місто, відгук, каталог | B1 | зв'язок | auth-dialog, city-dialog, review-modal, cat-overlay | організм | повільна | так | крок 4 |
| Мега-меню розкривається | B1 | зв'язок | mega-menu | організм | повільна | так | крок 4 |
| Тост приходить і йде | B1 | зв'язок | toast | організм | повільна | так | крок 4 |
Дев'ять станів завантаження з flows.md | B1 | статус | skeleton, pagination | атом, молекула | цикл | так | крок 4, і токен циклу відкритий |
| Ховер, натиск, фокус на 42 компонентах | B2 | відповідь | кнопка, поле, чип, посилання, картка | атом → організм | швидка | так | крок 3 |
| Серце на картці, 92 входження | B2 | відповідь | favourite | атом | швидка | лише focus | крок 3 |
| Поточна сторінка в таб-барі | B2 | відповідь | tabbar | організм | швидка | так | крок 3 |
| Іконка, що рухається без жодного стану | B2 | жодної | icon | атом | - | ні | знімається на кроці 3 |
Рівень, названий уголос зараз: організм
34 компоненти, стан мають 24, рухаються 13. Одинадцять зі станом і без руху - це рівно поверхні. Дізнатись про це на кроці 3 було б удвічі дорожче, тому воно сказане тут.ПОТРІБЕН СТАН - сім рядків, і рішення по них не тут
client-dialog, buy-bar, upsell, toolbar, product-grid, system-page і патерн action-row не несуть жодного інтерактивного стану. Стан, якого немає, це замовлення за п'ятіркою етапу 08, а не домальований клас: поки воно не розміщене, ці рядки анімації не отримують.Референс відповідає рівно на одне питання - характер - і лише на момент, уже названий в інвентарі. Тривалість не береться ніколи: чуже число несе чужий контекст. Через півроку питання «звідки взявся цей рух» мусить мати відповідь у браузері, а не в чиїйсь пам'яті - тому таблиця стоїть тут.
Джерело замінено, і заміна записана рядком
motionsites віддавfree_prompts_remaining: 0 - три безкоштовні промпти акаунта витрачені раніше, а найрелевантніші результати виявились платними. Власник замінив джерело на greensock/gsap-skills, прочитане живим 21.08.2026. GSAP у продукт не їде і в залежності не заводиться: береться прийом, а не бібліотека, і напрямок перекладу назвало саме джерело - його CustomEase «can use cubic-bezier values (as used in CSS cubic-bezier())».| Момент | Робота | Джерело | Що взято | Що це дає роботі | Що каже тон |
|---|---|---|---|---|---|
| Панель клієнта у спліт-в'ю | зв'язок | скіл gsap-core | родина кривої power_.out; напрямок від краю рейки, а не від краю вікна; мала амплітуда | напрямок каже, ЧИЙ це запис; крива віддає енергію на старті, тож око приходить у панель, коли вона вже осідає і вже читається | збігається. back.out(1.7) і elastic зняті за назвою: Принцип 5, без святкування |
| Перевірка соцпосилання тренера | статус | gsap-core ("none") + gsap-performance | рівний цикл без прискорення; лише opacity; will-change не лишати постійно | швидкість, що не змінюється, каже «живе» і нічого не обіцяє - а час зовнішньої перевірки продукт не знає | збігається. Фаза 8 = емоція 0, не +2; цикл, що прискорюється, це обіцянка |
| Мега-меню | зв'язок, композиція | gsap-core stagger + gsap-timeline | черговість from: "start" від тригера; amount, а не each; перекриття, щоб читалось як одна поверхня | порядок вказує назад на тригер; фіксований розкид тримає появу однаковою, скільки б категорій меню не несло | нейтрально, тексту при меню немає. Bounce не береться, Принцип 4 |
| - | - | thorne.com, живий замір | нічого не взято, лише число для звірки | лідер бенчмарку тримає 2 тривалості і одну обрану криву на 95 елементах зі 110; ми тримали 12 і жодної обраної | - |