Skip to content

6 • CSS animace a transformace

Transformace, přechody, animace klíčových snímků (keyframes)

Formát: 30 min praktická úloha, 15 min obhajoba + teorie. Teorie pokrývá tři pilíře (transform, transition, animation), performance, accessibility a praktiku.


Část 1: Teorie

Tři pojmy, tři vlastnosti

PojemCSS vlastnostK čemu slouží
TransformacetransformStatická změna podoby prvku (posun, rotace, zvětšení, zkosení)
PřechodytransitionPlynulá změna mezi dvěma stavy (typicky :hover)
Animace@keyframes + animationPředdefinovaná posloupnost změn, může běžet sama dokola

Vztah mezi nimi: Transformace se transformuje (jak teď vypadá). Transition a animation animují přechod (jak se mění v čase). Často se kombinují: transition plynule posune transform, keyframes mění transform po snímcích.

Krátký kontext

CSS animations a transitions byly přidány do specifikace CSS3 v roce 2009-2012. Před nimi se UI animace dělaly v JavaScriptu (jQuery animations) nebo Flash. Dnes je CSS animation hlavní way to go, jQuery animace jsou legacy.

Moderní rozšíření:

  • View Transitions API (2023+): smooth přechody mezi stránkami / stavy SPA
  • Scroll-driven animations (2024): animace navázané na scroll pozici, bez JS
  • @starting-style (2024): animace při prvním zobrazení elementu

Pro maturitu stačí klasické tři vlastnosti, ale stojí za to vědět, že CSS animace se dál vyvíjejí.


Část teorie A: Transformace (transform)

transform mění tvar nebo polohu prvku, aniž by ovlivnil layout. Ostatní prvky se neposunou, prvek si "ukočičí svoji vlastní reality bez okolního světa".

Funkce transform

FunkcePříkladCo dělá
translate(x, y)translate(20px, 50px)Posun po obou osách
translateX(x) / translateY(y)translateY(-10px)Posun po jedné ose
rotate(angle)rotate(45deg)Otočení (kladné = po směru hodinových ručiček)
scale(x, y)scale(1.2) nebo scale(2, 0.5)Zvětšení / zmenšení
scaleX(x) / scaleY(y)scaleY(0.5)Zvětšení po jedné ose
skew(x, y)skew(20deg, 0)Zkosení (lichoběžník, kosočtverec)
matrix(...)matrix(1, 0, 0, 1, 0, 0)Vše najednou (ručně se nepoužívá)

Kombinace více transformací

css
.box {
    transform: translate(20px, 30px) rotate(45deg) scale(1.5);
}

Pozor na pořadí! Transformace se aplikují zleva doprava.

  • rotate(45deg) translate(0, 100px) → otočí o 45°, pak posune o 100 px ve směru nové orientace
  • translate(0, 100px) rotate(45deg) → posune o 100 px svisle, pak otočí na místě

transform-origin: bod, kolem kterého se transformuje

css
.box {
    transform: rotate(45deg);
    transform-origin: top left;     /* default je center center (50% 50%) */
}
HodnotaCo znamená
center nebo 50% 50%Default, otáčí se kolem středu
top left nebo 0 0Otáčí se kolem levého horního rohu
bottom right nebo 100% 100%Pravý dolní roh
0 50%Levý okraj uprostřed

3D transformace

css
.cube {
    transform: rotateY(45deg);
    transform-style: preserve-3d;   /* aby se 3D efekt projevil na potomky */
}
.parent {
    perspective: 600px;                  /* hloubka 3D scény */
}
3D funkceCo dělá
translateZ(d)Posun směrem k uživateli nebo od něj
rotateX(a)Otočení kolem vodorovné osy (jako salto)
rotateY(a)Otočení kolem svislé osy (jako otevírání dveří)
rotateZ(a)Otočení v rovině (= rotate())
perspective(n)Hloubka pohledu

Proč je transform rychlý

Transformace běží na GPU, nezpůsobuje layout reflow ani repaint (jako třeba width nebo top). Proto:

Pravidlo: Pro plynulou animaci animuj transform a opacity. NIKDY width, height, top, left (jsou pomalé, "blikají").


Část teorie B: Přechody (transition)

transition říká: "Když se ta vlastnost změní, neudělej to skokem, ale plynule za X sekund."

css
.button {
    background: blue;
    transition: background 0.3s ease;
}
.button:hover {
    background: red;        /* plynule za 0.3 s */
}

Vlastnosti transition

css
.box {
    transition-property: transform, opacity;      /* které vlastnosti */
    transition-duration: 0.5s;                     /* jak dlouho */
    transition-timing-function: ease-in-out;      /* křivka rychlosti */
    transition-delay: 0.1s;                        /* čekání před začátkem */
}

/* Shorthand: property duration timing-function delay */
.box {
    transition: transform 0.5s ease-in-out 0.1s;
}

/* Více vlastností najednou */
.box {
    transition: transform 0.3s ease, opacity 0.5s linear;
}

/* Vše */
.box {
    transition: all 0.3s ease;     /* pohodlné, ale animuje úplně všechno (pomalé) */
}

Easing (timing functions): křivka rychlosti

HodnotaPopisKdy použít
linearKonstantní rychlost po celou dobuSpinner, pokračující pohyb
easeDefault, mírně ease-in-outKdyž nejsi jistý → ease
ease-inPomalý začátek, rychlý konecNěco se objevuje, akceleruje
ease-outRychlý začátek, pomalý konecNěco dojíždí, vychází
ease-in-outPomalé krajní body, rychlejší středPlynulé tam-zpět
cubic-bezier(x1, y1, x2, y2)Vlastní křivkaSpecifický feel (např. iOS bounce)
steps(n)Skoková animace v n krocíchSprite animace, typewriter

Co lze a nelze animovat

Lze: transform, opacity, background-color, color, border-radius, width, height, box-shadow, filter, font-size, letter-spacing a další.

Nelze (skok): display, position (hodnota), font-family, většina diskrétních (visibility má svůj timing trik).

Trik s display: none: nelze plynule animovat. Místo toho použij kombinaci opacity + visibility + pointer-events:

css
.modal { opacity: 0; visibility: hidden; pointer-events: none;
          transition: opacity 0.3s, visibility 0s 0.3s; }
.modal.open { opacity: 1; visibility: visible; pointer-events: auto;
               transition: opacity 0.3s, visibility 0s; }

Klíčový pattern: hover lift

css
.card {
    transform: translateY(0);
    transition: transform 0.2s ease, box-shadow 0.2s ease;
}
.card:hover {
    transform: translateY(-4px);
    box-shadow: 0 8px 16px rgba(0, 0, 0, 0.1);
}

Důležité: transition musí být v základním stavu, ne v :hover. Jinak by animace fungovala jen při najetí, ne při odjetí.


Část teorie C: Animace klíčových snímků (@keyframes)

@keyframes definuje posloupnost stavů v procentech času. animation pak tuto posloupnost přehrává.

Definice klíčových snímků

css
/* Forma 1: from/to (jednoduché) */
@keyframes fadeIn {
    from { opacity: 0; }
    to   { opacity: 1; }
}

/* Forma 2: procenta (víc kroků) */
@keyframes bounce {
    0%   { transform: translateY(0); }
    50%  { transform: translateY(-30px); }
    100% { transform: translateY(0); }
}

/* Forma 3: víc procent najednou */
@keyframes pulse {
    0%, 100% { transform: scale(1); }
    50%      { transform: scale(1.1); }
}

Aplikace animace na prvek

css
.box {
    animation-name: bounce;
    animation-duration: 1s;
    animation-timing-function: ease-in-out;
    animation-delay: 0s;
    animation-iteration-count: infinite;     /* nebo číslo: 1, 3, 10... */
    animation-direction: alternate;          /* normal, reverse, alternate */
    animation-fill-mode: both;               /* co před a po animaci */
    animation-play-state: running;           /* nebo paused */
}

/* Shorthand */
.box {
    animation: bounce 1s ease-in-out 0s infinite alternate both;
    /*         name   dur  timing   delay iter direction fill */
}

Klíčové vlastnosti animation

VlastnostHodnotyCo dělá
animation-nameJméno z @keyframesKterou animaci přehrát
animation-durationČas (1s, 500ms)Doba jednoho cyklu
animation-timing-functionJako u transitionEasing
animation-delayČasČekání před prvním spuštěním
animation-iteration-countČíslo nebo infiniteKolikrát opakovat
animation-directionnormal / reverse / alternate / alternate-reverseSměr přehrávání
animation-fill-modenone / forwards / backwards / bothCo před a po animaci
animation-play-staterunning / pausedPauza animace (typicky přes JS nebo :hover)

animation-direction: chování opakování

HodnotaCo dělá
normal (default)Vždy 0% → 100%, pak skok zpět
reverseVždy 100% → 0%
alternateTam-zpět: 0% → 100% → 0% → 100% (tam i zpět animace)
alternate-reverseZačne reverse

animation-fill-mode: co před a po

HodnotaPřed začátkem (delay)Po skončení
none (default)Default stylyDefault styly (skok zpět)
forwardsDefaultZůstane na 100% snímku
backwardsPředem převezme 0% snímekDefault
bothPředem 0%, na konci 100%

forwards je nejčastější potřeba: chceš, aby animace zůstala kde skončila, nepřeskočila zpět.

Pauza při hover

css
.spinner {
    animation: spin 1s linear infinite;
}
.spinner:hover {
    animation-play-state: paused;
}

Transition vs Animation: kdy co

Aspekttransitionanimation
TriggerZměna vlastnosti (hover, focus, JS class)Spustí se sama (load nebo přidáním class)
Stavů2 (z A do B)Libovolný počet (přes %)
OpakováníNeAno (iteration-count)
KontrolaImplicitní (změna spouští)Explicitní (přesné keyframes)
PoužitíHover efekty, state changesSpinner, loading, atrakce
Mám trigger jako :hover/:focus?
├─ Ano → transition (jednoduchý přechod ze stavu A do B)
└─ Ne, má běžet sama
   ├─ Víc kroků nebo opakování? → animation + @keyframes
   └─ Jednou na začátku → animation s iteration-count: 1 + fill-mode: forwards

Performance a accessibility

Co animovat (rychlé, GPU)

  • transform (translate, rotate, scale)
  • opacity

Tyhle běží na GPU. Animace je plynulá i na slabém HW (60fps).

Co NE-animovat (pomalé, CPU layout reflow)

  • width, height (CSS musí přepočítat layout pozice)
  • top, left, right, bottom
  • margin, padding, border-width
  • font-size (na velkém textu)

Místo top: 100px → 0 použij transform: translateY(100px) → 0. Vypadá stejně, ale je 10× rychlejší.

will-change: hint pro prohlížeč

css
.box {
    will-change: transform;     /* slíbí prohlížeči, že tahle vlastnost se bude měnit */
}

Prohlížeč si může předem připravit GPU vrstvu. Nepoužívat plošně, jen u prvků, kde opravdu budeš animovat.

prefers-reduced-motion: accessibility

Někteří uživatelé mají v OS zapnuto "omezit pohyb" (vestibulární poruchy, migrény, epilepsie). CSS to umí detekovat:

css
@media (prefers-reduced-motion: reduce) {
    *, *::before, *::after {
        animation-duration: 0.01ms !important;
        transition-duration: 0.01ms !important;
        scroll-behavior: auto !important;
    }
}

Nepoužívej v praxi animation: none, ale velmi krátkou dobu (0.01 ms), aby JavaScript spoléhající na animationend event nepřestal fungovat.


Moderní features (general knowledge bonus)

Tyhle nejsou klasická SŠ látka, ale stojí za zmínku.

View Transitions API (2023+)

Smooth přechody mezi stránkami / stavy SPA, bez JS animation knihoven.

jsx
// JS trigger
document.startViewTransition(() => {
    updateUI();
});
css
::view-transition-old(root) { animation: fade-out 0.3s; }
::view-transition-new(root) { animation: fade-in 0.3s; }

Scroll-driven animations (2024)

Animace navázané na scroll pozici, bez JS:

css
@keyframes reveal {
    from { opacity: 0; transform: translateY(50px); }
    to   { opacity: 1; transform: translateY(0); }
}

.card {
    animation: reveal linear;
    animation-timeline: view();        /* animace navázaná na viewport scroll */
    animation-range: entry 0% cover 30%;
}

@starting-style (2024)

Pro animace při prvním zobrazení (object insertion):

css
.modal {
    opacity: 1;
    transition: opacity 0.3s;
}
@starting-style {
    .modal { opacity: 0; }
}

Časté patterns

Spinner (nekonečné rotování)

css
@keyframes spin {
    to { transform: rotate(360deg); }
}

.spinner {
    width: 40px;
    height: 40px;
    border: 4px solid #e5e7eb;
    border-top-color: #6366f1;
    border-radius: 50%;
    animation: spin 1s linear infinite;
}

Pulse (notifikační dot)

css
@keyframes pulse {
    0%, 100% { transform: scale(1); opacity: 1; }
    50%      { transform: scale(1.5); opacity: 0.5; }
}

.dot {
    animation: pulse 1.5s ease-in-out infinite;
}

Fade-in při loadu

css
@keyframes fadeInUp {
    from { opacity: 0; transform: translateY(20px); }
    to   { opacity: 1; transform: translateY(0); }
}

.hero {
    animation: fadeInUp 0.6s ease-out both;
}

Hover lift

css
.card {
    transition: transform 0.2s ease, box-shadow 0.2s ease;
}
.card:hover {
    transform: translateY(-4px);
    box-shadow: 0 8px 16px rgba(0, 0, 0, 0.1);
}

Tlačítko press efekt

css
.btn {
    transition: transform 0.1s ease, background 0.2s ease;
}
.btn:hover {
    background: #4f46e5;
    transform: scale(1.05);
}
.btn:active {
    transform: scale(0.95);
}

Skeleton loading shimmer

css
@keyframes shimmer {
    from { background-position: -200% 0; }
    to   { background-position: 200% 0; }
}

.skeleton {
    background: linear-gradient(90deg, #e5e7eb 25%, #f3f4f6 50%, #e5e7eb 75%);
    background-size: 200% 100%;
    animation: shimmer 1.5s linear infinite;
}

Card flip ve 3D

css
.flip-card {
    perspective: 1000px;
}
.flip-card-inner {
    transition: transform 0.6s;
    transform-style: preserve-3d;
}
.flip-card:hover .flip-card-inner {
    transform: rotateY(180deg);
}
.flip-card-front, .flip-card-back {
    backface-visibility: hidden;
    position: absolute;
    inset: 0;
}
.flip-card-back {
    transform: rotateY(180deg);
}

Cheat sheet

Transform

css
transform: translate(x, y) | translateX | translateY | translateZ;
transform: rotate(deg) | rotateX | rotateY | rotateZ;
transform: scale(n) | scaleX | scaleY;
transform: skew(x, y) | skewX | skewY;
transform-origin: center | top left | 50% 50% | 0 0;

Transition (shorthand)

css
transition: <property> <duration> <timing-function> <delay>;
transition: transform 0.3s ease 0s;
transition: all 0.2s ease-out;

Animation (shorthand)

css
animation: <name> <duration> <timing> <delay> <iteration> <direction> <fill-mode>;
animation: spin 1s linear infinite;
animation: fadeIn 0.5s ease-out forwards;
animation: bounce 1s ease-in-out 0.2s 3 alternate both;

Časté chyby

ChybaDůsledekŘešení
transition v :hover, ne v základním stavuAnimuje jen najetí, ne odjetíDát transition do základního stavu
Animuju width/topSekaná animacePoužít transform
Po skončení animace prvek skočí zpětDefault fill-mode: nonePřidat animation-fill-mode: forwards
display: none se neanimujeDisplay je diskrétní vlastnostPoužít opacity + visibility
Spinner zpomaluje na konci a začíná pomaluDefault easePro nekonečnou rotaci vždy linear
transform se aplikuje špatněPořadí transformací!Změnit pořadí (zprava doleva)
Karta při rotaci "blikne"Backface viditelnýbackface-visibility: hidden
Animace ignorována uživatelem s motion preferenceNení respektován prefers-reduced-motionPřidat media query
will-change aplikováno všudePaměťový bloatJen u prvků, které opravdu animuješ

Část 2: Praktická úloha

Co může praktická úloha obsahovat

Typická úloha kombinuje:

  • Hero / element fade-in při loadu (@keyframes + animation: ... forwards)
  • Hover lift na kartách (transition: transform, :hover { transform: translateY })
  • Hover efekt na tlačítku (transition na color + scale)
  • Press efekt (:active { transform: scale(0.95) })
  • Spinner (@keyframes spin, animation: spin 1s linear infinite)
  • Pulse notifikace (@keyframes pulse se scale a opacity, alternate direction)
  • Bonus: skeleton shimmer, card flip ve 3D, prefers-reduced-motion

Příklad zadání: PixelShop

Vytvoř stránku e-shopu. Pomocí CSS doplnit:

  1. Hero fade-in při loadu (@keyframes fadeInUp)
  2. Spinner načítání (@keyframes spin, infinite linear)
  3. Karty hover lift (transition + transform: translateY)
  4. Tlačítko Koupit hover + press efekt
  5. Notification dot pulse (@keyframes pulse, infinite)
  6. Bonusy: skeleton, 3D flip, prefers-reduced-motion

Řešení: kompletní styly.css

css
/* ===== RESET ===== */
* { margin: 0; padding: 0; box-sizing: border-box; }
body { font-family: system-ui, sans-serif; line-height: 1.6; color: #222; background: #f3f4f6; padding: 2rem; }

/* ===== TODO 1: HERO FADE-IN ===== */
@keyframes fadeInUp {
    from {
        opacity: 0;
        transform: translateY(30px);
    }
    to {
        opacity: 1;
        transform: translateY(0);
    }
}

.hero {
    background: linear-gradient(135deg, #6366f1, #8b5cf6);
    color: white;
    padding: 3rem;
    text-align: center;
    border-radius: 12px;
    margin-bottom: 2rem;
    animation: fadeInUp 0.8s ease-out both;
}
.hero h1 { font-size: 2.5rem; margin-bottom: 0.5rem; }

/* ===== TODO 2: SPINNER ===== */
@keyframes spin {
    to { transform: rotate(360deg); }
}

.loading {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 1rem;
    margin-bottom: 2rem;
    color: #6b7280;
}

.spinner {
    width: 40px;
    height: 40px;
    border: 4px solid #e5e7eb;
    border-top-color: #6366f1;
    border-radius: 50%;
    animation: spin 1s linear infinite;
}

/* ===== KARTY ===== */
.produkty {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
    gap: 1.5rem;
}

/* ===== TODO 3: KARTY HOVER LIFT ===== */
.karta {
    position: relative;
    background: white;
    padding: 1.5rem;
    border-radius: 8px;
    box-shadow: 0 2px 4px rgba(0, 0, 0, 0.05);
    transition: transform 0.2s ease, box-shadow 0.2s ease;
}
.karta:hover {
    transform: translateY(-6px);
    box-shadow: 0 12px 24px rgba(0, 0, 0, 0.1);
}
.karta h3 { margin-bottom: 0.5rem; }
.karta .cena { margin: 1rem 0; font-weight: bold; color: #6366f1; }

/* ===== TODO 4: TLAČÍTKO KOUPIT ===== */
.btn {
    width: 100%;
    padding: 0.75rem;
    background: #6366f1;
    color: white;
    border: none;
    border-radius: 6px;
    font-weight: bold;
    cursor: pointer;
    transition: background 0.2s ease, transform 0.1s ease;
}
.btn:hover {
    background: #4f46e5;
    transform: scale(1.05);
}
.btn:active {
    transform: scale(0.95);
}

/* ===== TODO 5: PULSE NOTIFIKACE ===== */
@keyframes pulse {
    0%, 100% {
        transform: scale(1);
        opacity: 1;
    }
    50% {
        transform: scale(1.5);
        opacity: 0.5;
    }
}

.dot {
    position: absolute;
    top: -6px;
    right: -6px;
    width: 16px;
    height: 16px;
    background: #ef4444;
    border-radius: 50%;
    animation: pulse 1.5s ease-in-out infinite;
}

/* ============ BONUSY ============ */

/* Bonus A: Skeleton loading shimmer */
@keyframes shimmer {
    from { background-position: -200% 0; }
    to   { background-position: 200% 0; }
}

.skeleton {
    height: 20px;
    background: linear-gradient(90deg, #e5e7eb 25%, #f3f4f6 50%, #e5e7eb 75%);
    background-size: 200% 100%;
    animation: shimmer 1.5s linear infinite;
    border-radius: 4px;
}

/* Bonus B: Card flip ve 3D */
.flip-karta {
    perspective: 1000px;
    width: 220px;
    height: 300px;
}

.flip-karta-inner {
    position: relative;
    width: 100%;
    height: 100%;
    transition: transform 0.6s;
    transform-style: preserve-3d;
}

.flip-karta:hover .flip-karta-inner {
    transform: rotateY(180deg);
}

.flip-karta-front, .flip-karta-back {
    position: absolute;
    inset: 0;
    backface-visibility: hidden;
    border-radius: 8px;
    padding: 1.5rem;
    display: grid;
    place-items: center;
}

.flip-karta-front {
    background: white;
}

.flip-karta-back {
    background: #6366f1;
    color: white;
    transform: rotateY(180deg);
}

/* Bonus C: prefers-reduced-motion */
@media (prefers-reduced-motion: reduce) {
    *, *::before, *::after {
        animation-duration: 0.01ms !important;
        transition-duration: 0.01ms !important;
        scroll-behavior: auto !important;
    }
}

Co se v řešení děje

TODO 1 (Hero fade-in): @keyframes fadeInUp definuje dva stavy (skryté + posunuté dolů → viditelné + na místě). animation: fadeInUp 0.8s ease-out both spustí jednou s fill-mode: both, takže element začne neviditelný a zůstane viditelný.

TODO 2 (Spinner): @keyframes spin rotuje od 0 do 360°. Klíčové je linear (žádné zpomalování) a infinite (donekonečna).

TODO 3 (Hover lift): Transition v základním stavu, transform v :hover. Karta se zvedne nahoru a získá výraznější stín, plynule v obou směrech.

TODO 4 (Button): transition v základu na background a transform. Hover má větší scale + tmavší barvu. :active (kliknutí) má scale 0.95 → "zmáčknutí".

TODO 5 (Pulse): @keyframes pulse se třemi snímky (0%, 50%, 100%). Default direction normal, takže scale jede 1 → 1.5 → 1 v každém cyklu.

Bonus A (Shimmer): Background-size 200% a animace background-position se širokém přechodu vytváří "lesk", co projíždí elementem.

Bonus B (Flip): perspective na rodiči, transform-style: preserve-3d na inner kontejneru, rotateY(180deg) na hover. Backface-visibility skrývá rubovou stranu.

Bonus C (Reduced motion): Pro uživatele s preferencí omezeného pohybu se animace prakticky vypnou (0.01 ms).


Část 3: Tipy pro obhajobu

Co u obhajoby říct

"V zadání jsem použil všechny tři pilíře CSS animací. Pro transformace jsem využil translateY a scale, které běží na GPU a jsou plynulé. Transitions jsem použil pro hover efekty: karty a tlačítka mají transition v základním stavu, takže přechod funguje plynule v obou směrech, najetí i odjetí. Animace přes keyframes jsem použil tam, kde má něco běžet samo: hero fade-in při loadu jednou s fill-mode forwards, spinner s infinite linear (linear je důležité, jinak by spinner zpomaloval), pulse notifikace s infinite alternate. V bonusu jsem přidal skeleton shimmer přes posun background-position, 3D flip karty s perspective a backface-visibility, a respekt pro prefers-reduced-motion media query."

Klíčové pojmy pro teorii

PojemRychlá odpověď
transformStatická změna podoby (translate, rotate, scale, skew), neovlivňuje layout
transitionPlynulý přechod mezi dvěma stavy, trigger je změna vlastnosti
animationPředdefinovaná posloupnost přes @keyframes, může běžet sama
@keyframesDefinice klíčových snímků (from/to nebo procenta)
transform-originBod, kolem kterého se transformuje (default center)
GPU akceleracetransform a opacity běží na GPU, jsou rychlé
fill-mode: forwardsAnimace zůstane na 100% snímku po skončení
iteration-count: infiniteNekonečné opakování
direction: alternateTam-zpět: 0→100→0→100
linear timingKonstantní rychlost (pro spinner)
will-changeHint pro prohlížeč, který vlastnosti budou animovány
prefers-reduced-motionMedia query pro accessibility
backface-visibilitySkrytí rubu při 3D rotaci

Časté chytáky

OtázkaOdpověď
Rozdíl transition a animation?Transition reaguje na změnu vlastnosti (hover). Animation má vlastní průběh (keyframes), může běžet sama, opakovat se.
Co animovat, co ne?Animuj transform a opacity (GPU, rychlé). Nikdy ne width, top, left (layout reflow, pomalé).
Proč transition v base, ne v hover?Aby animovalo oběma směry. V :hover by se transition aplikoval jen při najetí.
Co je fill-mode?Co se zobrazí před spuštěním a po skončení. forwards znamená "zůstaň na konci".
Proč spinner linear?Default ease by zpomaloval na konci. Linear je konstantní rychlost, vypadá to jako opravdové točení.
Co je transform-origin?Bod, kolem kterého se transformuje. Default center center.
Lze animovat display?Ne, je to diskrétní vlastnost. Použij opacity + visibility.
Co je prefers-reduced-motion?Media query pro uživatele, kteří mají v OS zapnuto omezit animace (epilepsie, migrény).
Co je will-change?Hint pro prohlížeč, že prvek bude animován. Pomáhá s performance.

Časté chyby v praktické úloze

  • transition v :hover, ne v základním stavu (animuje jen najetí)
  • Animace width nebo top místo transform (pomalé, sekané)
  • Po animaci skok zpět: chybí animation-fill-mode: forwards
  • Spinner s ease místo linear (zpomaluje)
  • Pulse bez alternate (skok zpět na začátek místo plynulého tam-zpět)
  • Hover bez transition (animace najednou skoková)
  • Display none v animaci (nelze plynule)
  • 3D flip bez perspective na rodiči (rotace vypadá ploše)
  • 3D flip bez backface-visibility: hidden (vidíš rub karty)
  • prefers-reduced-motion s animation: none místo krátkého trvání (rozbije JS spoléhající na animationend)
  • Pulse s opacity menší než 0 (neviditelný moment, vypadá to jako blikání)
  • Hover lift s top místo translateY (sekané, pomalé)
  • will-change aplikované plošně (memory bloat)