AI CSS Animations

Guides · · Maxwell Prehoda

12 Modal Animation Examples That Open Fast and Close Even Faster

A good modal animation does three jobs: it shows where the dialog came from, it dims the page so focus moves to the dialog, and it gets out of the way quickly when the user is done. In practice that means a backdrop fade of about 200 ms, a dialog that scales up from roughly 0.9 to 1 or rises a short distance, and an exit that is shorter and simpler than the entrance. React Aria's own example uses exactly that shape: a 200 ms fade in, a 300 ms zoom from 0.8, and a 150 ms fade out.

Below are 12 examples to copy: the native dialog element with the new CSS entry and exit features, the headless libraries most teams use, a drawer library with an iOS-style curve, two classic Codrops-era references, and two of ours. Each live demo is a pure-CSS snippet from our directory that you can copy, not the third party's code.

1. Native dialog with @starting-style: Best for framework-free modals with real entry and exit

Platform: HTML + CSS, dialog is Baseline since 2022

Zoom
Live from our directory: Zoom In. Copy the HTML and CSS →

The dialog element now does most of what modal libraries used to: `showModal()` makes the rest of the page inert, Escape closes it, and the `::backdrop` pseudo-element styles the dimmed layer behind it. What was missing until recently was a clean exit animation, because the dialog jumps from visible to `display: none` and leaves the top layer instantly.

Chrome's entry and exit features fix that. `@starting-style` defines the state the dialog animates from each time it opens. Adding `display` and `overlay` to the transition with `allow-discrete` keeps the dialog rendered and in the top layer until the closing transition finishes. MDN's example transitions opacity and a scaleY from 0, and Chrome's version slides the dialog up from the bottom of the viewport. If you use keyframe animations instead of transitions, you skip `@starting-style` and put `display` in the keyframes.

Our Zoom In keyframes below are the classic dialog entrance: they grow from 30 percent while fading in over the first half, then ease out to full size.

Watch out for: Since Chrome 130, @starting-style rules must come after the normal declarations they override, or the entry animation silently does nothing.

Chrome: four new CSS features for entry and exit ↗

2. React Aria Modal: Best for asymmetric enter and exit timing in React

Platform: React, Adobe, Apache 2.0

Zoom
Live from our directory: Zoom Out. Copy the HTML and CSS →

React Aria's modal is the clearest published example of asymmetric timing. In its plain CSS example, the overlay runs a `modal-fade` keyframe for 200 ms when `[data-entering]` is set and the same keyframe reversed for 150 ms with `ease-in` when `[data-exiting]` is set. The modal itself runs `modal-zoom`, from `scale(0.8)` to 1, over 300 ms on a custom cubic-bezier.

The asymmetry is the lesson. People open a dialog to do something and close it because they are done, so the close should be quicker and should accelerate away (ease-in) rather than decelerate in. The Tailwind version of the same example reads `isEntering` and `isExiting` instead of the data attributes, and `onEnter` and `onExit` can return a promise that resolves when the animation finishes.

Our Zoom Out keyframes below are the matching exit: an ease-in shrink that holds full opacity for the first 40 percent and then fades as it gets small.

Watch out for: isDismissable defaults to false, so clicking outside does nothing unless you turn it on. Escape still closes it unless isKeyboardDismissDisabled is set.

3. Radix UI Dialog: Best for styling modal states with plain CSS selectors

Platform: React, headless, MIT license

Pop
Live from our directory: Scale Up Center. Copy the HTML and CSS →

Radix exposes `data-state` with the values `open` and `closed` on the trigger, the overlay and the content, so you can attach one keyframe to each state and let Radix wait for the exit animation before it unmounts. Focus is trapped inside the dialog, and Escape closes it and returns focus to the trigger.

When you want a JavaScript animation library to own the exit instead, the Portal, Overlay and Content parts accept `forceMount`, which keeps them in the tree so the library can animate them out. That split (CSS keyframes for the simple case, forceMount for springs) is a pattern you will see across headless libraries.

Our Scale Up Center below is a good fit for the open state: it pops from half size to full on an ease-out back curve that overshoots by a few percent.

Watch out for: Overshoot is fine for a small confirm dialog and wrong for a large form. On big surfaces, drop the overshoot and use a plain ease-out so the content does not wobble while people start reading.

Radix Primitives on GitHub ↗

4. Headless UI Dialog: Best for Tailwind projects that animate with data attributes

Platform: React or Vue, Tailwind Labs, MIT license

Up
Live from our directory: Fade In Up. Copy the HTML and CSS →

Headless UI turns transitions into Tailwind classes. Add the `transition` prop to the dialog and it renders `data-closed`, `data-enter` and `data-leave` attributes while it animates, so `transition duration-300 ease-out data-closed:opacity-0` is a complete fade. Put the prop on `DialogBackdrop` and `DialogPanel` separately when you want the backdrop and the panel on different timings.

The docs also show the Framer Motion route: render the dialog with the `static` prop inside `AnimatePresence`, make the panel a `motion.div`, and animate opacity and a scale from 0.95. The `open` prop still controls scroll locking and focus trapping, which is the detail most hand-rolled modals get wrong.

Our Fade In Up below is the quiet alternative to a scale: the panel rises about two em while it fades in, on an ease-out quint so it lands softly.

Watch out for: The dialog root takes focus by default when it opens. Use data-autofocus on the first field if the dialog is a form, or keyboard users land on nothing useful.

5. Motion layoutId shared element modal: Best for cards that expand into a detail view

Platform: React + Motion (formerly Framer Motion)

Fluid Spring Interface. See the prompt →

The most satisfying modal animation is not a fade at all: the thing you clicked grows into the dialog. Motion does this with `layoutId`. Give the trigger and the dialog the same ID, wrap the dialog in `AnimatePresence`, and Motion animates the new element out of the old one, then back again on close. Its docs use exactly this case, a button with `layoutId="modal"` and a `motion.dialog`.

A detail worth copying: the transition used is the one on the element being animated to. So the dialog can open on a fixed duration while the button it collapses back into uses a spring. Without React, the View Transitions API gives you the same morph by giving both elements the same `view-transition-name`.

Our Fluid Spring Interface below shows the feel in a video: one pill morphs into a notification, a live card and a player on springs that carry momentum.

Watch out for: Shared element morphs stretch text mid-flight. Fade the dialog's content in after the container has landed, or the copy smears across the transition.

6. Vaul drawer by Emil Kowalski: Best for mobile bottom sheets with drag to dismiss

Platform: React on Radix Dialog, MIT license

Grow
Live from our directory: Scale Up Bottom. Copy the HTML and CSS →

On phones the modal is usually a bottom sheet, and Vaul is the best-known open source one for React. It is built on Radix Dialog and adds dragging, snap points (fractions of the screen or pixel values), a `closeThreshold` that defaults to 0.25 of the drawer's height, and `shouldScaleBackground`, which shrinks the page behind the sheet the way iOS does.

Its source is worth reading for the numbers alone: the drawer animates over 0.5 seconds on `cubic-bezier(0.32, 0.72, 0, 1)`, a curve that starts fast and settles long, and a flick faster than a velocity threshold of 0.4 closes it regardless of distance.

Our Scale Up Bottom below is the CSS cousin of that rise: it grows upward from its bottom edge, overshoots and springs back, with the origin at 50% 100% so the base stays planted.

Watch out for: The README now says the repo is unmaintained. It still works, but pin the version and plan for a fork or replacement before building a design system on it.

7. Micromodal.js: Best for accessible modals on server-rendered sites

Platform: Vanilla JavaScript, about 1.9 kB gzipped

Back
Live from our directory: Back In Up. Copy the HTML and CSS →

Micromodal is a tiny dialog script that does the accessibility work and leaves every pixel of styling to you. It traps focus, closes on Escape, an overlay click or a close button, toggles `aria-hidden`, focuses the first focusable element and returns focus to where it was when the modal closes.

For animation it toggles an `is-open` class, and two options make CSS animations safe: `awaitOpenAnimation` waits for the opening animation before moving focus, and `awaitCloseAnimation` waits for the closing one before hiding the modal. Without them, focus jumps into an element that is still invisible, or the exit gets cut off.

Our Back In Up below is a livelier entrance to pair with it: the panel travels up at 70 percent size and partly transparent, then grows to full size on an ease-out back curve.

Watch out for: Micromodal ships no CSS at all, not even the rule that hides closed modals. Until you add display rules for .modal and .modal.is-open, every modal is visible on the page.

GitHub ↗

8. Codrops Nifty Modal Window Effects: Best for studying a full menu of entrance styles

Platform: CSS transitions and animations, free demo

Flip X
Live from our directory: Flip In X. Copy the HTML and CSS →

Manoela Ilic's June 2013 Codrops article is the modal equivalent of a type specimen: one structure and a long list of experimental appearance effects built with CSS transitions and animations, from simple scales to 3D flips and perspective tricks. The overlay sits after the modal in the markup so it can be styled with the adjacent sibling selector, and each trigger points at its modal through a data attribute.

Its age shows in the mechanics (it predates the dialog element being usable), but it is still the fastest way to see a dozen entrance styles side by side and decide which one fits your product. Most of them are too much for a confirm dialog and just right for a launch announcement.

Our Flip In X below is a modern take on the 3D style: it flips in around its horizontal axis with a perspective() inside the transform, overshoots past flat and wobbles to rest.

Watch out for: The original effects animate in 3D and through visibility tricks, and the article itself flags issues on old iOS Safari. Port the motion onto a real dialog element rather than copying the markup.

9. Val Head's Web Animation Review: modals: Best for designers deciding how expressive a modal should be

Platform: Video review, free

Tilt
Live from our directory: Tilt In. Copy the HTML and CSS →

Val Head, author of Designing Interface Animation, reviewed five animated modals in her Web Animation Review series: Pinterest for directional motion that helps people keep their place, SoundCloud for symmetrical motion, a zoomy Shopify sign-up modal with a lesson on asymmetric entrances and exits, Crowdcast for lively event modals, and leapsecond2015.com for modals that move through 3D space.

Her framing is the useful part: modals are a good place to be purposeful and expressive, because they are a clear moment of change. The direction of travel can say where content came from, and the exit can be different from the entrance.

Our Tilt In below is a restrained version of that 3D idea: the panel comes forward out of the page from a pose tilted on two axes and straightens as it lands.

Watch out for: Expressive is not the same as slow. Even a 3D entrance should finish in a few hundred milliseconds, and it should never replay every time the same dialog opens in a session.

10. Bootstrap and the reduced-motion default: Best for sites that need a safe, boring baseline

Platform: Bootstrap 5, MIT license

Slide
Live from our directory: Slide In Down. Copy the HTML and CSS →

Bootstrap's modal is the baseline most of the web still sees. The `.fade` class makes the dialog slide down and fade in from the top: its Sass defaults start it at `translate(0, -50px)` and run `transform .3s ease-out`. Remove `.fade` and it simply appears. The docs state that the animation depends on the `prefers-reduced-motion` media query.

That is the bar to clear before you add anything clever. A modal animation has to make the change of context clearer than a cut would; if it only adds delay, the boring version wins.

Our Slide In Down below is the same family of motion done more carefully: it drops out of its own top edge with a clip-path inset following every keyframe, so nothing shows above the fold line.

Watch out for: Bootstrap supports one modal at a time and does not support nested modals. If a flow needs a second dialog, close the first or put the steps inside one modal.

11. Our Glass Modal Dialog: Best for frosted confirm dialogs over busy app screens

Platform: Motions directory, free, pure CSS Ours

Live from our directory: Glass Modal Dialog. Copy the HTML and CSS →

A confirm dialog in frosted glass that pops over a blurred, dimmed app, holds, then closes, on a loop you can pause by hovering so you can inspect each phase. The overlay fades its opacity, never its blur radius, which is the single most important performance rule for blurred backdrops: animating a blur radius repaints every frame, fading a fixed blur does not.

Pick it when the content behind the dialog matters for context (a canvas, a dashboard, a photo) and you want it visibly there but out of focus. For more frosted components see our glassmorphism examples.

Watch out for: backdrop-filter over large areas is expensive on low-end phones. Test on a real mid-range Android device, and fall back to a solid tinted backdrop if it stutters.

12. Our entrance and exit keyframe pairs: Best for matching an open animation to its close

Platform: Motions directory, free, pure CSS Ours

Puff
Live from our directory: Puff In. Copy the HTML and CSS →

Most modal bugs are exit bugs, so our Keyframe Animations directory ships exits next to entrances: Fade Out ends on `visibility: hidden` so the invisible dialog stops taking clicks and leaves the accessibility tree, Back Out Down steps back before it drops away, and Zoom Out pairs with Zoom In.

Each one is a single `@keyframes` block with a `prefers-reduced-motion` guard, so you can drop the pair onto a dialog's open and closing states (or onto Radix's data-state, Headless UI's data-closed or React Aria's data-exiting) without a library.

The demo below is Puff In: it starts oversized, blurred and transparent and collapses into focus on an ease-in curve, landing with a small thump.

Watch out for: Puff In animates filter, which costs more than transform and opacity. Use it on a single small dialog, not on a large panel or a list of items.

How to choose a modal animation

Match it to what the modal interrupts

Nielsen Norman Group's advice is to reserve modals for critical warnings, essential input and step-by-step flows, because they block the page. The animation should be as serious as the interruption: a short scale or rise for a confirm dialog (Scale Up Center, Fade In Up), a morph from the trigger when the dialog is a detail view of something on screen (Motion's layoutId), and a bottom sheet with drag to dismiss on phones (Vaul's model).

Match it to your stack

No framework: the native dialog with @starting-style, display and overlay transitions. React: Radix (data-state), Headless UI (data-closed), React Aria (data-entering and data-exiting) or Motion for springs and shared elements. Server-rendered pages: Micromodal with awaitOpenAnimation and awaitCloseAnimation. Any of them can take a keyframe pair from our Keyframe Animations directory.

Match it to the close

Design the exit first. It should be shorter than the entrance (React Aria's 150 ms against 300 ms is a good ratio), use an ease-in so it accelerates away, and keep the dialog in the top layer until it finishes. Then check the three boring things: focus returns to the trigger, Escape closes it, and prefers-reduced-motion turns the travel into a plain fade.

Fix your modal before your next release

Open your product's most common dialog and time it. If the entrance is longer than about 300 ms, the exit is not shorter than the entrance, or the backdrop pops instead of fading, you have found the fix: a 200 ms backdrop fade, a scale from 0.9 or a short rise for the panel, and a quicker ease-in exit. Add the morph or the bottom sheet only where the context calls for it.

Copy a keyframe pair from our Keyframe Animations directory, or describe your dialog and get production-ready CSS from the creator. If the modal leads into a new screen, our page transition examples cover what happens next.

A prompt to start from
Make a CSS animation for a native dialog element used as a delete confirmation.
On open: the ::backdrop fades to 40% black over 200 ms and the dialog scales from 0.92 to 1 with a 12px rise, 240 ms ease-out, using @starting-style.
On close: 150 ms ease-in fade and scale to 0.96, with display and overlay transitioned using allow-discrete.
Under prefers-reduced-motion, use an opacity fade only.

Make your own with AI →

Common questions

How do you animate a modal closing in CSS?

With the native dialog element, transition opacity and transform, and add display and overlay to the transition list with allow-discrete. That keeps the dialog rendered and in the top layer until the transition ends. With keyframe animations, include display in the keyframes instead. Libraries such as Radix, Headless UI and React Aria expose closing states (data-state, data-leave, data-exiting) for the same purpose.

How long should a modal animation take?

Roughly 200 to 300 ms to open and 150 to 200 ms to close. React Aria's example uses a 200 ms overlay fade, a 300 ms zoom and a 150 ms reversed fade on exit. Vaul's drawer uses 500 ms because a bottom sheet travels much further.

What is @starting-style used for?

It defines the style an element transitions from when it first renders or goes from display: none to visible, which is exactly what a dialog does when it opens. Without it, the browser has no previous style to transition from and the dialog just appears.

Should a modal animate with scale or slide?

Scale suits centered dialogs that appear in place, such as confirmations. Slide suits panels that come from an edge, such as bottom sheets and side drawers, and it tells people where to swipe to dismiss. A morph from the trigger suits a dialog that is a bigger view of something already on screen.

How do I make a modal animation accessible?

Respect prefers-reduced-motion by replacing travel and scale with a fade, move focus into the dialog after it opens and back to the trigger after it closes, and make sure Escape closes it. Micromodal's awaitOpenAnimation option exists because moving focus into a dialog that is still animating in can confuse screen reader users.

Sources