Menu
CoddyTech

CSS Animation and Transition Interview Questions

Transitions, keyframes, transforms, what is cheap to animate, and reduced motion.

10 questions
  1. What is the difference between a CSS transition and a CSS animation?

    A transition animates a property from its old value to its new one when it changes (on :hover, a class toggle); an animation runs @keyframes on its own, with any number of steps, can loop, and needs no state change.

    HTML
    <style>
      .btn { background: #2563eb; color: white; border: 0; padding: 10px 16px; border-radius: 6px;
             transition: background-color 0.3s ease, transform 0.3s ease; }
      .btn:hover { background: #7c3aed; transform: translateY(-3px); }
    </style>
    <button class="btn">Hover me</button>

    More in transition.

  2. How do you create a CSS animation with @keyframes?

    Define the steps with @keyframes name { ... }, then attach them with animation: at least a name and a duration.

    HTML
    <style>
      @keyframes spin { to { transform: rotate(360deg); } }
      @keyframes pulse {
        0%, 100% { transform: scale(1); }
        50% { transform: scale(1.15); }
      }
      .spinner { width: 32px; height: 32px; border: 4px solid #dbeafe; border-top-color: #2563eb;
                 border-radius: 50%; animation: spin 0.8s linear infinite; }
      .dot { width: 20px; height: 20px; margin-top: 16px; border-radius: 50%; background: crimson;
             animation: pulse 1.2s ease-in-out infinite; }
    </style>
    <div class="spinner"></div>
    <div class="dot"></div>

    Useful longhands: -iteration-count, -direction (alternate plays back and forth), -fill-mode (forwards keeps the last frame) and -play-state. More in animation.

  3. What does the transform property do, and does it affect layout?

    transform moves, rotates, scales or skews an element and does not affect layout: it keeps its original space and its neighbors do not move.

    HTML
    <style>
      .row { display: flex; gap: 10px; }
      .row div { width: 70px; height: 70px; background: #2563eb; color: white; display: grid; place-items: center; }
      .t1 { transform: translate(10px, 10px); }
      .t2 { transform: rotate(20deg); }
      .t3 { transform: scale(1.3); }
    </style>
    <div class="row"><div>none</div><div class="t1">move</div><div class="t2">rotate</div><div class="t3">scale</div></div>

    Order matters: each function works in the coordinates left by the one before, so rotate(45deg) translateX(50px) differs from the reverse. transform-origin sets the pivot.

  4. Why can you not transition display: none, and what do you do instead?

    display is discrete: there is no halfway between none and block, so the change is instant. Animate opacity with visibility instead; visibility: hidden also keeps the element out of the tab order.

    HTML
    <style>
      .panel { opacity: 0; visibility: hidden; transform: translateY(-6px);
               transition: opacity 0.25s, transform 0.25s, visibility 0.25s;
               background: #dbeafe; padding: 10px; margin-top: 8px; }
      #toggle:checked ~ .panel { opacity: 1; visibility: visible; transform: none; }
    </style>
    <input type="checkbox" id="toggle"> <label for="toggle">Show panel</label>
    <div class="panel">Fades and slides, and is not focusable while hidden.</div>

    Chrome and Safari 18 can animate display with transition-behavior: allow-discrete and @starting-style; Firefox cannot yet.

  5. Which CSS properties are cheap to animate, and why?

    transform and opacity. In the style, layout, paint, composite pipeline they touch only composite, which can run off the main thread, so they stay smooth. width, top, left and margin force layout every frame; background-color and box-shadow force paint. Move with translate(), grow with scale(), and use will-change: transform sparingly: every layer costs memory.

  6. How do you respect users who turn off animations?

    Use the prefers-reduced-motion media query, which matches when the user turned on "reduce motion" in their operating system.

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

    Cleaner: write static styles first and add motion inside @media (prefers-reduced-motion: no-preference). A short fade is fine; parallax and large slides should go.

  7. What do ease, linear, ease-in-out and steps() mean in CSS animations?

    They are timing functions: how the value moves from start to end over the duration. linear is constant speed, ease (the default) speeds up quickly and slows down at the end, ease-in starts slow, ease-out ends slow, ease-in-out does both. cubic-bezier() draws your own curve, and steps(n) jumps in n equal steps with no frames in between, for sprites and typewriter effects.

    HTML
    <style>
      .track { width: 300px; background: #e5e7eb; margin-bottom: 4px; }
      .ball { width: 24px; height: 24px; border-radius: 50%; background: #2563eb;
              animation: go 2s infinite alternate; }
      @keyframes go { to { transform: translateX(276px); } }
    </style>
    <div class="track"><div class="ball" style="animation-timing-function: linear"></div></div>
    <div class="track"><div class="ball" style="animation-timing-function: ease-in-out"></div></div>
    <div class="track"><div class="ball" style="animation-timing-function: steps(5)"></div></div>
  8. How do you replay a CSS animation from JavaScript?

    Removing and re-adding the class in the same frame does nothing, because the browser never sees it gone. Remove it, force a style recalculation by reading a layout property such as offsetWidth, then add it back, and remove it on animationend.

    HTML
    <style>
      @keyframes shake { 25% { transform: translateX(-8px); } 75% { transform: translateX(8px); } }
      .shake { animation: shake 0.3s 2; }
      input { padding: 6px; }
    </style>
    <input value="wrong password"> <button>Submit</button>
    <script>
      const input = document.querySelector('input');
      document.querySelector('button').addEventListener('click', () => {
        input.classList.remove('shake');
        void input.offsetWidth; // forces a reflow, so the removal counts
        input.classList.add('shake');
      });
      input.addEventListener('animationend', () => input.classList.remove('shake'));
    </script>

    The Web Animations API (element.animate()) replays an animation without the trick.

  9. Why does one transform cancel another, and how do translate, rotate and scale help?

    transform is one property, so two rules or animations that set it overwrite each other: a hover transform: scale(1.3) wipes out a transform: rotate(45deg). The separate translate, rotate and scale properties (in every major browser since 2022) combine instead, applied in that order before transform, and each can be animated on its own.

    HTML
    <style>
      .box { display: inline-block; width: 60px; height: 60px; margin: 30px; background: #2563eb;
             rotate: 45deg; transition: scale 0.2s; }
      .box:hover { scale: 1.3; }
      .old { rotate: none; transform: rotate(45deg); transition: transform 0.2s; }
      .old:hover { scale: none; transform: scale(1.3); }
    </style>
    <div class="box"></div><div class="box old"></div>
    <p>Hover both: the first keeps its tilt, the second loses it.</p>
  10. How do you animate an element's height to auto?

    You cannot transition to height: auto directly, because the browser cannot interpolate to a keyword. The clean trick is a grid row that goes from 0fr to 1fr around a child with overflow: hidden. Older code animates max-height to a large number, which gets the timing wrong. Chrome 129+ also accepts interpolate-size: allow-keywords, which lets height: auto animate.

    HTML
    <style>
      .panel { display: grid; grid-template-rows: 0fr; transition: grid-template-rows 0.3s ease; }
      .panel > div { overflow: hidden; }
      #open:checked ~ .panel { grid-template-rows: 1fr; }
      .panel p { margin: 0; padding: 10px; background: #dbeafe; }
    </style>
    <input type="checkbox" id="open"> <label for="open">Show details</label>
    <div class="panel"><div><p>Any amount of text: the row opens to its real height and the transition follows.</p></div></div>
Coddy programming languages illustration

Learn to code with Coddy

Get started