Many common UI patterns are built from transitions and animations. The simplest hover effect uses a transition on a property that changes on :hover, for example a button that smoothly changes background-color on hover. A spinner — a small rotating element used to indicate loading — is typically made with animation: spin 1s linear infinite together with border-radius: 50% and @keyframes spin { to { transform: rotate(360deg); } }. A pulsing button uses animation: pulse 1.5s ease-in-out infinite with keyframes that scale between 1 and 1.05. A "pop" effect combines a quick scale-up with a fade-in, often transform: scale(0.8) → scale(1) together with opacity: 0 → 1.
A "ripple" effect, where a circular highlight expands and fades from a click point, is typically achieved with an absolutely positioned element, a scale animation, and an opacity transition. A staggered list is created by applying slightly different animation-delay values to each item so they animate sequentially rather than all at once, producing a cascade. SVG paths can be "drawn" by animating stroke-dasharray and stroke-dashoffset together, which moves the dash pattern along the path and creates a writing or erasing effect.
Accessibility is essential. The prefers-reduced-motion media query detects the user's OS-level setting to reduce motion, and designers use it to disable or simplify animations. Heavy animations can be wrapped in @media (prefers-reduced-motion: no-preference) { ... }, or alternatively the reduce branch can override durations and remove transforms. Continuous motion can trigger discomfort or vestibular issues for some users, so respecting this preference is an important accessibility practice. There is also a meaningful difference between opacity: 0 and visibility: hidden: opacity: 0 makes the element fully transparent but still clickable and in the layout, while visibility: hidden makes it invisible and unclickable yet still occupies layout space. To keep a transition target reachable for screen readers, pair opacity/transform with visibility: hidden/visible or with a delayed display: none so assistive technology treats the state correctly.
Several modern features extend what CSS animations can do. The @property rule declares a custom property with an explicit type, initial value, and inheritance, enabling the property itself to be animated; its typical syntax is @property --my-var { syntax: '
For more complex layout-driven motion, the FLIP technique (First, Last, Invert, Play) measures element positions with JavaScript before and after a layout change, then uses CSS transitions to animate the visual move smoothly. JavaScript can also control CSS animations directly: pausing is done by setting element.style.animationPlayState = 'paused', while restarting requires removing the animation property (or setting animation: none), forcing a reflow by reading something like element.offsetWidth, and then re-adding the original animation. CSS transitions are declarative, run on the compositor, and do not require JavaScript frames, whereas JS animations driven by requestAnimationFrame require code execution per frame. CSS transitions and animations are also generally preferred over SMIL, the SVG-native animation system using