Przejdź do głównej zawartości

Ruch i reduced motion

Techniczny: DraftAkceptacja: Draft

Ruch potwierdza, że coś się wydarzyło, i pokazuje, co się zmieniło. System daje na to sześć prymitywów: trzy czasy i trzy krzywe. Nie mają odpowiednika w warstwie semantycznej, bo nie zależą od marki ani trybu, i jako jedyne z warstwy core wolno ich użyć wprost.

TokenZmienna CSSWartośćPodglądPrzeznaczenie
core.motion.duration.fast--motion-duration-fast120msZmiana wyglądu kontrolki pod kursorem, fokusem i po kliknięciu.
core.motion.duration.base--motion-duration-base200msPojawienie się lub zniknięcie elementu w tym samym widoku.
core.motion.duration.slow--motion-duration-slow320msWejście dużej powierzchni. Używać oszczędnie.
TokenZmienna CSSWartośćPodglądPrzeznaczenie
core.motion.easing.accelerate--motion-easing-acceleratecubic-bezier(0.4, 0, 1, 1)Rusza miękko i przyspiesza do końca. Wyjście elementu.
core.motion.easing.decelerate--motion-easing-deceleratecubic-bezier(0, 0, 0.2, 1)Rusza z pełną prędkością i miękko dochodzi. Wejście elementu.
core.motion.easing.standard--motion-easing-standardcubic-bezier(0.4, 0, 0.2, 1)Rusza i hamuje. Zmiana w obrębie ekranu, w obie strony.

Opis krzywej mówi, jak ruch wygląda, nie który efekt jej używa. Przypisanie czasów i krzywych do konkretnych zdarzeń interfejsu jest otwarte, patrz brief niżej.

core.motion.* to jedyny namespace warstwy core, który trafia do zmiennych CSS. Pozostałe, core.space, core.color, brand i mode, są wewnętrznym okablowaniem pipeline’u tokenów i nie istnieją jako zmienne CSS (pełne rampy kolorów niesie tylko palette.json, artefakt narzędzi obszaru). W kodzie sięgasz po zmienną --motion-* z kolumny w tabelach wyżej:

.offer-panel {
transition:
opacity var(--motion-duration-base) var(--motion-easing-decelerate),
transform var(--motion-duration-base) var(--motion-easing-decelerate);
}

Domyślne przejście Tailwinda stoi na naszych tokenach: theme.css przepina --default-transition-duration na --motion-duration-fast, a krzywą na --motion-easing-standard. Klasa transition-colors niesie więc czas z tokenu, nie 150 ms z domyślnego motywu. Klas z nazwą czasu albo krzywej (duration-fast, ease-standard) nie ma — zmienną tokenu podajesz jako wartość dowolną, duration-(--motion-duration-fast) ease-(--motion-easing-standard), tak jak Button. Ruch kilku właściwości naraz piszesz w CSS komponentu.

Czy zmienne dojechały do przeglądarki, sprawdza audyt: --motion-duration-fast jest w nim wartownikiem obok --focus-ring. Pusta wartość kończy przebieg na czerwono, zanim ktokolwiek zobaczy interfejs bez ruchu i bez pierścienia fokusu.

  • Potwierdzenie działania. Kontrolka reaguje na kursor, fokus i kliknięcie. To najczęstszy ruch w systemie i najkrótszy.
  • Zmiana stanu. Element pojawia się, znika albo zmienia miejsce w tym samym widoku. Ruch pokazuje, co dokładnie się zmieniło, gdy zmiana jest łatwa do przeoczenia.
  • Wejście wyniku. Oferta dosortowana po czasie wchodzi na listę widocznie, bo klient zdążył już przeczytać poprzedni zestaw (mikrocopy).
  • Ruch nie opóźnia informacji. Cena, status i wynik pojawiają się od razu. Nie każemy czekać na animację, żeby je zobaczyć.
  • Bez pozornego postępu. Pasek, który udaje odliczanie do wyniku, którego nie znamy, jest zakazany w tekście i tak samo w ruchu. Oczekiwanie pokazuje szkielet treści.
  • Bez dekoracji. Wjazdy sekcji przy przewijaniu, parallax i pulsowanie nie niosą zmiany stanu.
  • Ruch nie zastępuje komunikatu. To, co mówi animacja, musi mówić też tekst i stan elementu.

Reguła: ruch, który przesuwa albo skaluje element, respektuje prefers-reduced-motion: reduce. Zostaje zmiana stanu, znika droga, którą element pokonuje. Zmiana samego koloru może zostać.

W kodzie tej reguły dziś nie ma. Jedyny ruch w @polisoteka/design-ui to transition-colors na komponencie Button, czyli zmiana koloru bez ruchu w przestrzeni, i żaden komponent nie ma zapytania prefers-reduced-motion. Audyt Playwright uruchamia przeglądarkę z reducedMotion: "reduce" i wyłączonymi animacjami, ale robi to dla powtarzalności zrzutów: to warunek pomiaru, nie dowód, że reguła jest zaimplementowana.

  • Nie dokładamy tokenu ruchu pod pojedynczy efekt. Trzy czasy i trzy krzywe wystarczają; brak pasującej wartości jest pytaniem o sens efektu, nie o token.
  • Czas trwania nie informuje o postępie. Sprawa ma stan, a stan ma tekst.
  • Ruch nie przenosi fokusu ani nie zmienia kolejności czytania.

@todo(motion): wykonać poniższy brief, przedstawić rezultat i dowody odbioru. Usunięcie znacznika nie jest wykonaniem zadania.

  • Zakres: przypisać czasy i krzywe do zdarzeń interfejsu na dwóch realnych ekranach, start B2B i decyzja zdrowia: stan kontrolki, wejście panelu i podpowiedzi, dosortowana oferta, zamknięcie warstwy nad stroną. Opisać wzorzec oczekiwania razem ze stanami z mikrocopy, czyli próg, po którym szkielet treści ustępuje tekstowi. Opisać obsługę prefers-reduced-motion wtedy, gdy trafi do paczki; do tego czasu strona mówi, że jej nie ma.
  • Konwencja: najpierw reguła lub gotowe zastosowanie, potem przykład i kontrprzykład, następnie granice oraz sposób sprawdzenia. Nie zastępuj materiału opisem planu jego przygotowania.
  • Źródła: tokens/src/core.tokens.json (sekcja motion), tokens/README.md (namespace’y trafiające do dist/), validators/audit/computed-tokens.spec.ts, validators/playwright.config.ts, mikrocopy.
  • Granica swobody: możesz redagować i pokazywać istniejące ustalenia. Nowy token ruchu, choreografię przejść między widokami i zmianę zachowania komponentu przedstaw do decyzji; nie uzupełniaj z pamięci.

Przebieg pnpm design:audit sprawdza, że zmienne --motion-* mają wartość w przeglądarce w każdej z dziesięciu kombinacji marki i trybu.

Dla każdego efektu sprawdź trzy rzeczy: czy da się wykonać zadanie przy wyłączonym ruchu, czy ruch nie opóźnia informacji, którą klient już mógłby przeczytać, i czy stan po animacji jest opisany tekstem. Wartość czasu wzięta spoza tokenu zostaje w kodzie bez śladu w bramce, więc wyłapuje ją review.

Tokeny i zależności, powierzchnie, obrysy i głębia, Button, mikrocopy, standard dostępności.