← all topics

Opacity & Visibility

CSS

Three ways to hide an element — only one of them actually animates. Watch the 'content after' line to see which properties collapse the layout and which preserve it.

opacity

fades smoothly — element still takes up space and still receives pointer events

↑ content before
opacity
↓ content after
opacity: 1;
transition: opacity 0.5s ease;

visibility

snaps instantly — element still takes up space but receives no pointer events

↑ content before
visibility
↓ content after
visibility: visible;
/* no interpolation — snaps */

display

snaps and collapses — element removed from layout entirely, 'content after' shifts up

↑ content before
display
↓ content after
display: block;
/* can't transition — instant removal */

concepts

only opacity interpolates

opacity is a numeric value the browser can tween between 0 and 1. visibility and display are keyword properties with no intermediate state — they can only snap.

layout impact

opacity: 0 and visibility: hidden both keep the element in the layout flow — its space is preserved and siblings don't shift. display: none removes it entirely, collapsing its space.

pointer events

An opacity: 0 element is invisible but still interactive — it receives clicks and stays in the tab order. Add pointer-events: none to opt out. visibility: hidden removes interactivity automatically.

the practical pattern

For animated show/hide: transition opacity for the visual fade, and pair with visibility or pointer-events: none so hidden elements don't trap keyboard focus or intercept clicks.

core pattern

/* Only opacity actually animates */
.fade { opacity: 1; transition: opacity 0.4s ease; }
.fade.hidden { opacity: 0; }

/* visibility snaps — but delay keeps layout until fade is done */
.fade-hide {
  opacity: 0;
  visibility: hidden;
  transition: opacity 0.4s ease,
              visibility 0s 0.4s; /* delay matches opacity duration */
}

/* opacity: 0 elements still receive clicks — opt out explicitly */
.hidden-accessible {
  opacity: 0;
  pointer-events: none;
}