Simple Analytics
css js Intermediate

CSS @starting-style

CSS @starting-style
@starting-style Entry Animations
Interactive editor — edit code and see live results Open in Editor

How It Works

The demo shows two patterns: a <dialog> with entry/exit animations, and toggle cards that animate from display: none to visible.

Dialog — entry, exit, and backdrop:

.dialog {
  opacity: 1;
  transform: scale(1) translateY(0);
  transition: opacity 0.3s ease, transform 0.3s ease,
    display 0.3s allow-discrete, overlay 0.3s allow-discrete;
}

.dialog:not([open]) {
  opacity: 0;
  transform: scale(0.9) translateY(10px);
}

@starting-style {
  .dialog[open] {
    opacity: 0;
    transform: scale(0.9) translateY(10px);
  }
}

Toggle cards (display: none → block):

.reveal-card {
  display: none;
  opacity: 0;
  transform: translateY(10px);
  transition: opacity 0.3s ease, transform 0.3s ease,
    display 0.3s allow-discrete;
}

.reveal-card.visible {
  display: block;
  opacity: 1;
  transform: translateY(0);
}

@starting-style {
  .reveal-card.visible {
    opacity: 0;
    transform: translateY(10px);
  }
}

JavaScript — just wiring up buttons:

openBtn.addEventListener('click', () => dialog.showModal());
closeBtn.addEventListener('click', () => dialog.close());
dialog.addEventListener('click', (e) => {
  if (e.target === dialog) dialog.close();
});

document.querySelectorAll('.reveal-btn').forEach((btn) => {
  btn.addEventListener('click', () => {
    btn.nextElementSibling.classList.toggle('visible');
  });
});

Summary

  • @starting-style defines the initial styles when an element first renders or transitions from display: none. The browser uses these as the “from” state.
  • display 0.3s allow-discrete keeps the element visible during exit transitions instead of jumping straight to display: none.
  • .dialog:not([open]) defines the exit state — the dialog fades and scales out before hiding. The backdrop gets the same treatment.
  • The same pattern works for toggle cards: display: nonedisplay: block with @starting-style for entry animation.
  • Replaces the setTimeout(0) or double requestAnimationFrame hack — set display, wait a frame, add animation class.
  • Works with dialogs, popovers, dropdown menus, toast notifications — any element toggling between hidden and visible.
  • Supported in Chrome 117+ (Sep 2023), Safari 17.5+ (May 2024), Firefox 129+ (Aug 2024), and Edge 117+. Fully cross-browser.

Try this in our interactive code editor

Practice hands-on with our built-in code sandbox.

Open Code Editor
Adrian Bigaj
Adrian Bigaj

Creator of BigDevSoon

Full-stack developer and educator passionate about helping developers build real-world skills through hands-on projects. Creator of BigDevSoon, a vibe coding platform with 21 projects, 100 coding challenges, 40+ practice problems, and Merlin AI.