CSS @starting-style solves a long-standing limitation: you can now animate an element’s initial appearance — when it first enters the DOM or transitions from display: none — using pure CSS transitions without JavaScript.
Problem: A CSS element — a dropdown, a modal, a tooltip — needs to animate on first render (entry animation) when it transitions from display: none to visible, but standard CSS transitions do not fire on the initial paint.
Solution: Use @starting-style to declare the CSS values the element should transition from on its first render. Define the target styles on the element and the initial styles inside @starting-style { } — the browser treats the first paint as a transition from the starting-style values.
The examples below animate a WordPress notice block fading in on page load, animate a modal dialog appearing from display: none, and use @starting-style with the new overlay transition property for top-layer elements.
/* Animate a .notice element when it's first inserted into the DOM */
.wp-block-notice {
background: #fff3cd;
border-left: 4px solid #ffc107;
padding: 1rem;
opacity: 1;
transform: translateY(0);
/* Declare the transition */
transition: opacity 0.4s ease, transform 0.4s ease;
}
/* @starting-style defines the FROM state for the FIRST transition */
@starting-style {
.wp-block-notice {
opacity: 0;
transform: translateY(-12px);
}
}
/* Animate out when a .is-dismissing class is added via JS */
.wp-block-notice.is-dismissing {
opacity: 0;
transform: translateY(-12px);
}
Animate a dialog element (top-layer) appearing and disappearing:
/* dialog uses the top layer — needs 'overlay' in transition-property
to hold it in the top layer until the exit animation finishes */
dialog {
opacity: 1;
scale: 1;
/* Include 'overlay' and 'display' to animate top-layer elements */
transition: opacity 0.3s, scale 0.3s, overlay 0.3s allow-discrete, display 0.3s allow-discrete;
}
/* Entry animation — FROM state */
@starting-style {
dialog[open] {
opacity: 0;
scale: 0.9;
}
}
/* Exit animation — when dialog is no longer [open] */
dialog:not([open]) {
opacity: 0;
scale: 0.9;
}
/* JS to open/close — no animation code needed in JavaScript */
/* document.querySelector('dialog').showModal(); → enters */
/* document.querySelector('dialog').close(); → exits */
NOTE: @starting-style is supported in Chrome 117+, Safari 17.5+, and Firefox 129+. For unsupported browsers the element simply appears without animation — a perfect progressive enhancement. Combine it with the :popover-open pseudo-class and the Popover API for animating WordPress admin popovers.