CSS animace: transition, keyframes a plynulé efekty bez JS
Jak psát plynulé CSS animace: transition vs. keyframes, easing, výkon s transform a opacity, animace při scrollu, View Transitions a prefers-reduced-motion.
Dobře zvolená animace pomáhá uživateli pochopit, co se stalo: menu se vysunulo, položka se přidala, stránka se přepnula. Špatná animace zdržuje a seká. Většinu efektů zvládnete čistým CSS – plynuleji a s menším kódem než v JavaScriptu.
Transition: přechod mezi dvěma stavy
.button {
background: #2563eb;
transform: translateY(0);
transition: background-color 150ms ease-out, transform 150ms ease-out;
}
.button:hover {
background: #1d4ed8;
transform: translateY(-2px);
}
Transition se spustí, kdykoli se změní hodnota vlastnosti – přes :hover, :focus, přidání třídy z JavaScriptu atd. Vyjmenujte konkrétní vlastnosti; transition: all animuje i věci, které nechcete, a ztěžuje ladění.
Keyframes: vícekrokové a opakované animace
@keyframes pulse {
0%, 100% { opacity: 1; }
50% { opacity: 0.4; }
}
.skeleton {
animation: pulse 1.5s ease-in-out infinite;
}
@keyframes slide-in {
from { opacity: 0; transform: translateY(12px); }
to { opacity: 1; transform: translateY(0); }
}
.toast {
animation: slide-in 250ms ease-out both;
}
Užitečné vlastnosti zkratky animation:
| Vlastnost | Příklad | Význam |
|---|---|---|
animation-delay |
100ms |
zpoždění startu |
animation-iteration-count |
infinite, 3 |
počet opakování |
animation-direction |
alternate |
tam a zpět |
animation-fill-mode |
both |
drží styl z from před startem a z to po skončení |
animation-play-state |
paused |
pozastavení |
Kaskádovité zobrazení položek
.list li {
animation: slide-in 300ms ease-out both;
animation-delay: calc(var(--i) * 60ms);
}
<ul class="list">
<li style="--i: 0">První</li>
<li style="--i: 1">Druhá</li>
<li style="--i: 2">Třetí</li>
</ul>
CSS proměnná v inline stylu nastaví každé položce jiné zpoždění.
Easing: rozdíl mezi „levným” a „vyladěným”
ease-out– rychlý start, pomalý konec. Ideální pro prvky, které přicházejí (menu, modální okna).ease-in– pro prvky, které odcházejí.ease-in-out– pro pohyb z místa na místo.linear– jen pro nekonečné rotace a progress bary.
Vlastní křivka přes cubic-bezier():
transition: transform 300ms cubic-bezier(0.22, 1, 0.36, 1);
Tahle křivka (podobná „ease-out-quint”) působí svižně a přirozeně. Délky: mikrointerakce 100–200 ms, menší přechody 200–300 ms, velké přesuny do 500 ms. Delší animace začnou zdržovat.
Výkon: animujte transform a opacity
Prohlížeč vykresluje stránku v krocích: layout (rozměry a pozice) → paint (vykreslení pixelů) → composite (skládání vrstev). Změna width, height, top, left nebo margin spouští layout celé části stránky v každém snímku – a to seká.
transform a opacity obvykle zvládne kompozitor bez přepočtu layoutu, často na GPU:
/* ❌ */
.panel { transition: left 300ms; left: -300px; }
.panel.open { left: 0; }
/* ✅ */
.panel { transition: transform 300ms ease-out; transform: translateX(-100%); }
.panel.open { transform: translateX(0); }
will-change: transform dávejte jen na prvky, které se opravdu brzy budou animovat – každá vrstva stojí paměť.
Kontrola: DevTools → Rendering (v menu More tools) → zapněte Paint flashing. Zeleně blikající oblasti se překreslují; při dobré animaci by neměly blikat.
Animace výšky na „auto”
Dlouho nebylo možné plynule animovat z height: 0 na height: auto. Dnes existují dvě cesty:
/* 1) grid trik – funguje všude */
.accordion-body {
display: grid;
grid-template-rows: 0fr;
transition: grid-template-rows 250ms ease-out;
}
.accordion.open .accordion-body { grid-template-rows: 1fr; }
.accordion-body > div { overflow: hidden; }
/* 2) interpolate-size – zatím jen prohlížeče postavené na Chromiu */
:root { interpolate-size: allow-keywords; }
details::details-content { transition: height 250ms, content-visibility 250ms allow-discrete; height: 0; overflow: clip; }
details[open]::details-content { height: auto; }
Animace vstupu a odchodu z display: none
Moderní CSS umí animovat i zobrazení prvku, který byl skrytý:
.popover {
transition: opacity 200ms, display 200ms allow-discrete;
opacity: 0;
}
.popover:popover-open {
opacity: 1;
@starting-style { opacity: 0; }
}
@starting-style určí výchozí stav při prvním vykreslení a allow-discrete dovolí přechod u display. Podpora je ve všech hlavních prohlížečích v aktuálních verzích; ve starších se prvek prostě zobrazí bez animace.
Animace řízené scrollem
@keyframes grow { from { transform: scaleX(0); } to { transform: scaleX(1); } }
.progress {
position: fixed; top: 0; left: 0; right: 0; height: 4px;
background: #2563eb;
transform-origin: left;
animation: grow linear;
animation-timeline: scroll();
}
Ukazatel průběhu čtení bez řádku JavaScriptu. animation-timeline je podporované v Chromiu a Safari 26; tam, kde podpora chybí, ověřte chování přes @supports (animation-timeline: scroll()).
View Transitions
Plynulý přechod mezi stavy stránky:
document.startViewTransition(() => updateDom());
Pro vícestránkové weby stačí na obou stránkách CSS @view-transition { navigation: auto; }. Prohlížeč udělá prolínačku mezi starou a novou stránkou; nepodporující prohlížeče stránku normálně přepnou.
Ohleduplnost: prefers-reduced-motion
@media (prefers-reduced-motion: reduce) {
*, *::before, *::after {
animation-duration: 0.01ms !important;
animation-iteration-count: 1 !important;
transition-duration: 0.01ms !important;
}
}
Lidé s vestibulárními poruchami mohou mít z pohybu nevolnost. Toto pravidlo animace prakticky vypne, ale události animationend/transitionend se pořád spustí, takže JavaScript na ně navázaný funguje dál. Jemné prolínání (opacity) je obvykle v pořádku i v omezeném režimu – vypněte hlavně posuny, zoomy a paralaxu.