Free · Fast · Privacy-first

CSS Fade In Animation

A fade-in animation is one of the most used effects in web UI, appearing in modals, toasts, hero sections, dashboard cards, and almost every entrance animation in modern interface design.

Control opacity start value, duration, and easing

🔒

Preview the exact fade-in timing before copying

Generates both @keyframes and animation property

Supports delayed fade-in for staggered sequences

Cost
Free forever
Sign-up
Not required
Processing
In your browser
Privacy
Files stay local
FreeNo signupWhite-label

Add this Animation Builder to your website

Drop the Animation Builder into any page — blog post, product docs, intranet, school portal — with a single line of HTML. Your visitors get the full tool, processed entirely in their browser. No backend, no uploads, no signup.

  • Files stay 100% in the visitor's browser
  • Responsive — adapts to any container width
  • Free forever, no API key needed

Embed code

<iframe
  src="https://www.fixtools.io/css-tool/animation-builder?embed=1"
  width="100%"
  height="780"
  frameborder="0"
  style="border:0;border-radius:16px;max-width:900px;"
  title="Animation Builder by FixTools"
  loading="lazy"
  allow="clipboard-write"
></iframe>

Attribution-friendly: a small "Powered by FixTools" link appears in the embed footer.

How a Fade-In Animation Works at the CSS Level and Why Easing Matters

A fade-in animation works by transitioning the opacity CSS property from 0 (fully transparent) to 1 (fully opaque) over a defined duration. At the CSS level, this requires a @keyframes rule that declares opacity: 0 at 0% and opacity: 1 at 100%, and an animation property on the target element that references the keyframe name and sets the duration, easing, delay, and fill-mode. Because opacity is one of only two properties (the other being transform) that the browser handles entirely on the GPU compositor thread, the browser runs the fade-in animation without triggering layout recalculation or paint operations on each frame. This makes opacity-based fade-ins one of the most performant animation types available, reliable at 60 frames per second even on lower-end mobile hardware where heavier animations would visibly stutter. The same property choice also means a fade-in animation does not block interaction on the page: a user can click a button while a sibling element is fading in, and the click is registered immediately without waiting for the animation to finish.

Choosing the right easing function for a fade-in changes how the animation feels at a perceptual level even when the duration is identical. An ease-out easing, which starts fast and slows toward the end, feels natural for appearing elements because it mimics how objects come into focus: quick initial appearance followed by a gentle settling at the final state. A linear easing fades in at a constant rate, which can feel mechanical and is rarely the right choice for UI elements except in rare cases like a precise progress bar fill. An ease-in easing, which starts slow and accelerates toward the end, makes elements feel like they are reluctantly appearing and can create a subtle sense of anxiety in the user that becomes obvious only when the animation is compared side by side with an ease-out version. For most fade-in use cases, ease-out with a duration between 200 and 400 milliseconds produces a result that feels immediate without being jarring.

Pairing a fade-in with a small transform creates the subtle entrance effect that UI frameworks use for modals, toasts, and notifications. Adding translateY(-10px) at the 0% keyframe and translateY(0) at the 100% keyframe alongside the opacity change produces an upward float that signals the element has come from above the viewport edge or a parent container. A translateY(10px) to translateY(0) produces a rise from below, common for bottom sheets, action menus, and toast notifications on mobile interfaces. A translateX(-12px) to translateX(0) produces a slide from the left, suitable for inline alerts and form validation messages. The transform adds no additional performance cost because transform, like opacity, runs on the compositor thread, and the combined effect requires only a few extra characters in the 0% stop of the @keyframes rule, making it a high-leverage upgrade to the basic fade pattern.

Fill-mode is the most commonly forgotten setting in fade-in animations, and forgetting it produces the classic pre-animation flash bug that ships to production more often than any other CSS animation defect. Without animation-fill-mode: both, an element with a positive animation-delay shows in its base opacity (typically 1) during the delay period, then jumps to opacity 0 the moment the animation starts, then fades back to 1 over the duration. The result is a visible flash of the element followed by a fade-in, which looks like an unintended double exposure rather than a polished entrance. Setting animation-fill-mode: both makes the element hold the 0 percent keyframe state throughout any delay and the 100 percent state after the animation completes, eliminating the flash without requiring any JavaScript or additional CSS classes.

How to use this tool

💡

Set the starting opacity (usually 0), end opacity (usually 1), duration in milliseconds, and easing. Add a delay for staggered sequences. Copy the complete fade-in CSS when the preview looks right.

How It Works

Step-by-step guide to css fade in animation:

  1. 1

    Set opacity and duration

    Set the starting opacity to 0 and the end opacity to 1 for a standard fade-in, or to 0.4 and 1 for a softer reveal that never fully hides the element. Choose a duration based on the role of the element: 200ms for quick UI feedback on tooltips and inline alerts, 400ms for a standard entrance on cards and modals, and 600ms for a slower hero-level reveal that should feel deliberate and premium rather than instant.

  2. 2

    Choose an easing function

    Select ease-out as the default choice for an entrance fade, which feels natural because it mimics how objects come into focus in the physical world. Adjust the cubic-bezier curve for a custom timing if no keyword option produces the right feel, dragging the two control points to shape the acceleration profile. Avoid linear for fade-ins unless you have a specific reason, because linear timing makes the fade feel mechanical and uniform rather than organic.

  3. 3

    Add a transform for depth (optional)

    Enable the optional translateY setting to add a vertical float to the fade, creating the polished entrance effect used by most modern UI frameworks. A value of -10px to 0 creates an upward entrance that suggests the element has arrived from above the viewport edge, while 10px to 0 creates a rise from below suitable for toasts and bottom sheets. The transform adds no measurable performance cost because both opacity and transform run on the compositor thread.

  4. 4

    Copy the generated CSS

    Click Copy Code to copy the @keyframes fade-in block plus the matching animation property declaration to your clipboard, then paste both into your stylesheet at the appropriate selector. Apply the animation class to the target element and verify in the browser that the fade behaves identically to the preview. Add the prefers-reduced-motion override block if it was not already included in the generated output, since fade animations should respect user motion preferences.

Real-world examples

Common situations where this approach makes a real difference:

Modal dialog entrance

A product developer applies a 300ms ease-out fade-in combined with a translateY(-12px) to translateY(0) float to a modal dialog component used throughout the application. The upward entrance signals that the modal is overlaying the current page content rather than replacing it, matching the spatial hierarchy expected for layered UI. The animation plays the moment the modal's open class is added by the trigger handler, and animation-fill-mode: both prevents any flash of the unstyled modal before the transition begins. The same animation class is reused across alert modals, confirm modals, and full-screen dialogs to maintain consistent motion across the product surface.

Dashboard card stagger

A SaaS dashboard UI has six metric cards that should appear sequentially on page load to create the perception of fast rendering even when all six cards finish loading at roughly the same time. A developer generates a single fade-in-up @keyframes block and applies it to each card with animation-delay values from 0ms to 250ms in 50ms increments using nth-child selectors on the card container. The staggered sequence guides the user's eye from top-left to bottom-right across the grid, finishing within roughly 700 milliseconds of the initial render. The technique is purely CSS, requires no JavaScript orchestration, and works regardless of whether the data is server-rendered or fetched on the client.

Toast notification appearance

A notification component uses a 200ms ease-out fade-in with a translateY(8px) to translateY(0) rise-from-below effect that matches the mobile native pattern of toasts appearing at the bottom of the screen. The short duration makes the toast feel responsive to the user action that triggered it, signaling immediate acknowledgment of a save, submit, or upload. The animation runs once, the toast stays visible for three seconds while the user reads it, then a separate fade-out animation plays before the element is removed from the DOM by the dismissal handler. The exit animation uses the reverse translate and a slightly shorter 150ms duration to keep the dismissal feeling responsive rather than dragging.

Hero section content reveal

A marketing page developer creates a sequence of three fade-ins for the hero section: the headline at 0ms delay, the subheadline at 200ms, and the CTA button at 400ms. Each uses a 500ms ease-out duration for a premium, deliberate feel appropriate for the above-the-fold brand moment that sets the tone for the entire visit. The staggered timing guides the visitor's eye from the headline down to the call to action in a natural reading order, increasing CTA click-through on the page measurably compared to a version where all three elements appeared at once. The fill-mode: both setting ensures none of the elements flash visible before their scheduled animation starts.

When to use this guide

Use this when you need a fade-in effect for a modal, toast, card, hero section, or any element that should appear gradually rather than snapping into view.

Pro tips

Get better results with these expert suggestions:

1

Use will-change: opacity sparingly for complex layouts

Adding will-change: opacity to an element before a fade-in animation tells the browser to promote the element to its own GPU compositor layer in advance, which can prevent a layer promotion hitch at the start of the animation on complex pages with many DOM nodes. Remove will-change after the animation completes to free the GPU memory, as leaving it on static elements wastes resources and can actually degrade performance on pages where many elements have it set unnecessarily. Use will-change as a targeted optimization for animations you have measured to be janky, not as a default.

2

Set initial opacity in your CSS, not just in @keyframes

For elements that fade in on page load, set opacity: 0 as a base style on the element selector in addition to declaring it at the @keyframes 0 percent stop. This prevents a brief flash of the fully visible element before the browser applies the animation, which can happen on slow connections or when the stylesheet loads after the initial HTML render. The base style is overridden by animation-fill-mode: forwards or both once the animation starts and completes, so the redundant opacity declaration does not interfere with the final state.

3

Match fade-in duration to content weight

Heavier, denser content like tables, long text blocks, and image grids warrants slightly longer fade-in durations than lightweight elements like tooltips, badges, and chips. A 400ms fade-in on a table feels appropriately measured and gives the eye time to register the new content. A 400ms fade-in on a tooltip feels sluggish because the small element does not require the same perception time. Match the perceived weight of the content to the duration, treating element size and visual density as the primary inputs to the timing decision rather than picking a single duration for everything.

4

Test fade-in animations with the page in a slow-CPU throttle

Open Chrome DevTools Performance panel, set CPU throttle to 4x or 6x slowdown, and reload the page to simulate the experience on lower-end devices. On throttled hardware, delayed fade-ins sometimes appear to start late because the initial render takes longer than on the development machine, pushing the animation start relative to when the user expects to see content. Reduce delays if this occurs, or use the IntersectionObserver pattern to trigger animations on scroll rather than on a fixed delay, which makes the timing relative to when the element is actually visible rather than to page load.

FAQ

Frequently asked questions

A fade-in requires two things at the CSS level: a @keyframes rule that sets opacity: 0 at the 0 percent stop and opacity: 1 at the 100 percent stop, and an animation property on the target element that references the keyframe name along with a duration, easing, and fill-mode. A complete declaration looks like animation: fade-in 300ms ease-out both, where 300ms is the duration, ease-out is the easing, and both is the animation-fill-mode value. The both value for fill-mode is the part most developers forget and is what keeps the element invisible before the animation starts when there is a positive delay, then holds the opacity: 1 state after the animation completes so the element does not snap back to its base style.
This happens when animation-fill-mode is not set to forwards or both. Without fill-mode, the element shows its natural opacity (which is usually 1 in the absence of any base style override) during the animation delay period, then jumps to opacity 0 the instant the animation starts, then fades back to 1 over the duration. The result is a visible flash of the element followed by a fade-in, which reads as a bug rather than a designed effect. Setting animation-fill-mode: both prevents this by holding the element at the 0 percent keyframe state (opacity: 0) throughout the delay period, then holding the 100 percent state after the animation completes, so the element appears only at the beginning of its scheduled animation and remains visible afterward.
Duration depends on the element's role and weight in the interface. Quick feedback elements like tooltips, inline alerts, and small badges work best at 150 to 200 milliseconds, where the fade feels immediate and matches the speed of the cursor or tap that triggered it. Standard entrances for cards, modals, dropdowns, and panels feel right at 250 to 400 milliseconds, slow enough to register as motion but fast enough to keep the interface responsive. Slow reveals for landing page hero content or illustrated onboarding screens work at 500 to 700 milliseconds. Going above 700 milliseconds makes users wait noticeably and should be reserved for intentional, theatrical effects. The easing matters as much as the duration: an ease-out at 300ms feels noticeably faster than a linear at 300ms because of the front-loaded acceleration.
Define a single @keyframes fade-in block at the top of your stylesheet and apply it to each list item with a progressively larger animation-delay value, which creates a staggered cascade effect that guides the eye down the list. For four items, use delays of 0s, 0.1s, 0.2s, and 0.3s. In CSS, you can target items using nth-child selectors: li:nth-child(1) { animation-delay: 0s; } li:nth-child(2) { animation-delay: 0.1s; } and so on. In a JavaScript framework like React or Vue, calculate the delay inline based on the item's index in the array: style={{ animationDelay: `${index * 100}ms` }}. Cap the maximum delay at around 500ms for long lists so the last items do not feel delayed.
Use a CSS animation for fade-ins that happen on page load, on element insertion into the DOM, or on a timer, where the trigger is the element's appearance rather than a property change. Use a CSS transition for hover-triggered fades or class-toggle fades that need to reverse automatically when the class is removed, because transitions handle bidirectional motion natively while animations require a separate exit sequence. The practical difference is that an animation plays when the element renders and is controlled by animation properties on the element selector, while a transition plays whenever a CSS property value changes and requires a triggering event like a class change, hover state, or programmatic style update. Both approaches use the same easing functions.
Not directly, because CSS cannot interpolate between display: none and display: block. The display property is discrete rather than continuous, so the browser snaps between the two values without animating any property change that occurs at the same moment. The standard solution is to set the element to display: block (or another non-none display value) at the same time you add the animation class, relying on animation-fill-mode: both and the 0 percent keyframe opacity: 0 to keep the element invisible until the animation starts. Alternatively, use visibility: hidden at 0% and visibility: visible at 100% if you need the element to take up space in the document flow while invisible, which is occasionally useful for layout-stable fade-in patterns where reflow would be disruptive.
Add a @media (prefers-reduced-motion: reduce) block that sets animation: none on any element with the fade-in animation class. Users who have enabled the Reduce motion preference in their OS accessibility settings will not see the animation and the element will appear immediately at full opacity at its scheduled time. This is a requirement under WCAG 2.1 guideline 2.3.3 for non-essential animations, which covers every decorative fade-in in a typical UI. The prefers-reduced-motion media query is supported by all modern browsers and respects the OS-level setting automatically, so no JavaScript detection logic is needed. The override block should be placed in the same stylesheet as the animation declaration to keep the two together for future refactoring.
Yes, but with a performance caveat that matters on mobile devices and lower-end laptops. Animating CSS filter properties like blur, brightness, contrast, and saturate alongside opacity is technically possible by declaring both properties in the @keyframes stops, and the combined effect can produce a cinematic feel for hero entrances and onboarding screens. However, filter animations trigger the paint phase on every frame in most browsers, unlike opacity and transform which run on the GPU compositor thread without touching paint. The combined animation may drop below 60 frames per second on lower-end devices, especially if multiple filtered elements animate simultaneously. If smooth performance is required across all devices, use opacity and transform alone and skip the filter, or apply the filter only as a static style with the fade animating opacity.
Opacity controls visual transparency while leaving the element fully interactive: a button at opacity: 0 still receives click events and still occupies its layout space. Visibility hides the element from both display and interaction, removing it from accessibility tree traversal as well. For a fade-in animation, opacity is almost always the correct choice because the element should not respond to clicks while it is invisible during the delay period before the animation starts. If you have a specific need to block interaction during the invisible phase, set pointer-events: none on the 0 percent keyframe alongside opacity: 0, which prevents accidental clicks on the element while it fades in.
Pure CSS cannot trigger an animation based on scroll position in most browsers (CSS scroll-driven animations are emerging but not yet widely supported). The standard approach uses JavaScript's IntersectionObserver API to detect when an element enters the viewport, then adds a CSS class that applies the fade-in animation. Define the @keyframes and animation properties in a class like .visible, and use IntersectionObserver to add this class to each element as it scrolls into view. Set the element's base style to opacity: 0 so it stays invisible until the observer fires. This pattern is widely used for scroll-triggered reveal effects on long marketing pages and feature lists.

Related guides

More use-case guides for the same tool:

Ready to get started?

Open the full Animation Builder — free, no account needed, works on any device.

Open Animation Builder →

Free · No account needed · Works on any device