Programování

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.

12. 10. 2026 · 4 min čtení

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.