Which border properties can CSS animate?
border-color and border-width interpolate between two values, and so do outline and box-shadow. border-style does not blend at all. A gradient animates only when a registered custom property drives it.
Properties that interpolate
- border-color blends between two colors, including a fade from transparent.
- border-width blends lengths, though every frame forces a layout pass.
- outline-color, outline-width and outline-offset blend like their border counterparts and sit outside the box.
- box-shadow blends offset, blur, spread and color.
MDN’s reference on animatable properties states that every CSS property animates unless its definition says otherwise.
Properties that flip instead of blend
border-style never blends. Going from solid to dashed swaps in one step, because no halfway value exists.
border-image-source behaves the same way. One gradient replaces another with no in-between frames, and MDN lists its animation type as discrete. Unregistered custom properties also jump from the start value to the end value.
Registered custom properties behave differently. MDN says they animate by the computed value type set in their syntax definition, which is why an angle in a plain –angle variable jumps while a registered one spins.
Outline and box-shadow as edge substitutes
outline-offset has worked in every major browser since April 2017, according to MDN.
That history makes outline the safest stand-in when a border has to grow without pushing neighbors aside. box-shadow does the same job with a spread value and no blur.
For triggers, :hover covers pointer users and :focus-visible covers keyboard users (MDN rates it Baseline Widely available). An animation that runs on page load needs no trigger at all.
How to animate a border on hover without layout shift
When only the color changes, a transition on border-color is enough. If the border has to grow, a border-width transition pushes neighboring elements around on every frame, so use outline or box-shadow instead. A scaled pseudo-element works too.
Transition on border-color
Put the transition on the base state, not on :hover. A transition declared only inside :hover animates the change in and snaps back on the way out.
A declaration like transition: border-color 0.3s ease covers most cards and buttons. Tune the duration and easing curve in the browser DevTools before you commit them.
Mirror every hover rule on :focus-visible. A hover-only border leaves keyboard users without the cue.
Buttons and cards take the same pattern, and so do links. A roundup of CSS hover effects shows a variation for each.
Layout-safe alternatives to border-width
Changing border-width triggers layout, and nearby elements shift unless the element has an explicit size, as CSS-Tricks documented in its Animating Border test.
- Outline grows outside the box and leaves layout alone. The original test recorded a rendering artifact in Safari, so check current builds.
- A box-shadow with a 0 0 0 10px spread mimics a thicker border. It costs paint time, not layout time.
- A scaled pseudo-element animates transform instead of size. Per CSS-Tricks, the side edges look thicker than top and bottom until the animation finishes.
The pseudo-element wins on cost, since web.dev’s rendering guide names transform and opacity as the only two properties that change without layout or paint. The catch is the uneven edges and the fixed starting width.
CSS-Tricks also found that a content-sized button cannot grow its border from exactly 5px to 10px with the pseudo-element method, because the button’s dimensions vary with its text. For those elements, outline is the better default.
How to build looping border animations with @keyframes
Looping borders get their steps from @keyframes, and animation-duration, animation-timing-function and an infinite animation-iteration-count attach them to the element. A loop repaints the border on every frame, so keep the animated area small.
A deeper guide on writing CSS keyframes covers percentage selectors and fill modes, plus staggered animation-delay values.
Dashed border that moves
Marching dashes come from background layers, not from border-style, since a dashed border cannot shift its dash pattern. Stack four repeating-linear-gradient layers along the four edges, then animate background-position in @keyframes.
The gradient stops set the dash length and the gap. Shift by exactly one dash period per loop and the cycle restarts without a visible jump.
Glowing border with box-shadow
Alternate between two box-shadow values with animation-direction: alternate to pulse a glow. Use the same number of shadow layers in both keyframes so the interpolation pairs them one to one.
Colt McAnlis’s paint-time tests on web.dev found that cost depends on the specific value combination of box-shadow and border-radius, not on the property alone. Those tests ran on a Chrome 27 beta, so read them as direction, not as a benchmark.
Paint flashing in the Chrome DevTools Rendering tab shows what a given glow repaints on a real page. Glow size and blur radius are the first values to test on a slow phone, and a visual box-shadow tool makes stacking the layers quicker through a box-shadow generator.
Running light along the edge
A DEV Community walkthrough by Simon Bittok rebuilds the moving green lines first spotted on deno.com feature cards.
The ::before pseudo-element carries a conic-gradient, and its padding becomes the visible border. ::after adds a softer glow around it.
overflow: hidden on the card clips any glow that extends past the edge, and border-radius: inherit keeps the light inside rounded corners. Watch the z-index stacking too, because it can drop the pseudo-element behind the card background.
How to create an animated gradient border with @property
The angle gets registered with @property, a conic-gradient reads it on a border-box background layer, and @keyframes animate the property from 0deg to 360deg. The browser interpolates the angle, so the gradient rotates around the edge.
As of October 2026, web-features data lists @property as Baseline Newly Available since 2024-07-09, the day Firefox 128 shipped. Chrome 85 and Safari 16.4 support it earlier.
- Register the angle by declaring @property –angle with syntax ““, inherits: false and initial-value: 0deg.
- Set a transparent border. Pick the border width, add border-radius, and leave the color transparent.
- Stack two backgrounds. Paint the solid fill with the padding-box value and a conic-gradient(from var(–angle), …) with the border-box value.
- Animate the property. Write @keyframes that end at –angle: 360deg, then apply them with a linear timing function and an infinite iteration count.
Background layers with conic-gradient
Jason Lengstorf’s walkthrough explains why conic works here: the gradient sits at the center of the box, so the parts visible in the border appear to follow the edge.
A linear gradient smears at the corners instead. Stops are quicker to pick in a gradient generator for CSS.
Without @property support, the angle cannot interpolate and the border stays a static gradient. That static state is the fallback, so design it to look finished on its own.
border-image and its radius limit
The border-image route works with border-image: conic-gradient(from var(–angle), …) 1. It stays rectangular though, because MDN states that border-radius has no effect on a border image. Add the fill slice keyword and it also paints the middle of the box, over the background.
Background layers do respect border-radius and keep the card fill opaque, at the cost of the two-layer padding-box and border-box setup.
For a transparent center, cut a gradient pseudo-element with mask and mask-composite. Temani Afif’s glowing border demo on css-tip.com uses a subtract mask driven by a registered angle, and MDN lists mask-composite as Baseline since December 2023 (Widely available since June 2026).
Each frame of the spin repaints the gradient layer, because only transform and opacity change without paint. Keep the number of spinning borders per page low.
Drawing a border line by line with SVG and clip-path
Animate stroke-dashoffset on an SVG rect so the border draws itself, or animate a clip-path polygon to reveal a CSS border in stages. SVG gives a true tracing line. clip-path keeps everything in plain CSS.
SVG stroke-dashoffset
MDN lists stroke-dashoffset as Baseline Widely available, working across browsers since April 2017, so the technique carries no support risk.
Set stroke-dasharray and stroke-dashoffset to the path length, then animate the offset to 0. The dash starts as a gap and the line appears to draw itself, as Jake Archibald’s line-drawing walkthrough shows. The length itself can be read from the DOM.
MDN notes that the offset resolves against pathLength when that attribute is set, which lets one set of dash values fit any rect size. Timing and easing for these strokes are covered in a guide on how to animate SVG with CSS.
The SVG overlay needs pointer-events: none, otherwise it blocks clicks on the element underneath. It also has to match the box size, which gets tricky when content changes the box.
clip-path reveal
A full border sits under a clip-path built with calc values that trim it down. On hover, a transition widens the clip until the whole border shows. CSS-Tricks credits Steve Gardner with the first implementation, in its Animating Border test.
Polygon points are easier to place visually with a clip-path tool.
Four-segment pseudo-element draw
::before draws the top and right edges, and ::after takes the bottom and left. Widths and heights transition to 100% on hover, and staggered transition-delay values make the line travel around the box.
Equal durations per segment keep the line moving at a constant speed. Unequal ones make the corners feel like stops.
Which CSS border animation method should you pick?
A color change only needs a border-color or outline transition. A moving gradient calls for @property with conic-gradient, and a line that draws itself is a job for SVG or clip-path. Whichever effect you want, pick the cheapest method that produces it.
| Method | Cost per frame | Fits rounded corners | Main catch |
|---|
| border-color transition | Paint | Yes | Changes color only |
| box-shadow spread | Paint | Yes | Cost depends on blur and radius values |
| Scaled pseudo-element | Composite | Yes, with a matching radius | Uneven edge thickness mid-scale |
| @property conic-gradient | Paint | Yes | Static gradient without @property |
| SVG stroke-dashoffset | Paint | Set rx on the rect | Overlay must match the box size |
How to check what an animation costs
According to Chrome for Developers, the Lighthouse audit Avoid non-composited animations lists every DOM node whose animation could not be composited, with the failure reason. Chrome writes those reasons into the DevTools trace, and Lighthouse reads that trace. Non-composited animations that move elements also add to Cumulative Layout Shift, while composited ones are excluded from it.
Chrome defines a non-composited animation as any animation that triggers style, layout or paint. That flags border-color and box-shadow animations too, because both trigger paint.
A flagged hover transition on one button is normal. A flagged infinite loop on a grid of cards is the finding to act on.
A default that holds up for most components
For most components, a border-color or outline transition on hover and focus is plenty. Stop there unless the design needs more.
Hover and focus borders are small micro-interactions, and they read best when subtle. Save the rotating gradient for one or two elements per page.
Buttons and links get a border-color or outline transition. On pricing or feature cards, put the gradient border on the highlighted card only. A logo or signature mark can take an SVG stroke draw, once, on load.
Browser support for animated borders and fallbacks
Transitions and @keyframes on border-color, outline and box-shadow carry no support risk in current browsers. The gradient border driven by @property is the part to check, and its fallback is a static gradient border.
Every figure below is dated October 2026.
- prefers-reduced-motion has been available across browsers since January 2020 (MDN), with Widely Available status dated 2022-07-15 (web-features).
- conic-gradient() has worked across browsers since November 2020 (MDN), the month Firefox 83 added it (Mozilla Hacks), and has been Baseline Widely available since May 2023 (web-features).
- border-image-source has been available across browsers since July 2015 (MDN).
- @property should reach Widely Available status on 2027-01-09 (web-features).
The two prefers-reduced-motion dates do not conflict. Baseline marks Widely Available 30 months after a feature lands in every major engine, which is why July 2022 trails January 2020.
Fallback for browsers without @property
Declare the gradient border in its static state first, then layer the animation on top. A browser without @property still paints a finished border.
The unregistered angle animates discretely, and 0deg and 360deg draw the same gradient, so the border looks static instead of broken.
Older tutorials say otherwise about Firefox. Cruip’s Tailwind walkthrough builds its card from a three-stop linear gradient and a five-stop conic gradient, then states that Firefox lacks @property.
That note is outdated. Treat any tutorial listing Firefox as unsupported as written before the feature shipped.
Testing across engines
Check the animated state, where the spin runs and the loop restarts without a jump. Then check the static state, where the border still has to read correctly with animation set to none.
Run both states in Chromium, Firefox and Safari with reduced motion on and off. The wider device matrix is covered in a guide to cross-browser compatibility.
Accessibility rules for animated borders
Looping border animation needs a prefers-reduced-motion check. Auto-running loops should stop within five seconds or come with a pause control, and the border itself needs 3:1 contrast against its neighbors. Hover transitions fall under a different criterion than loops.
Using prefers-reduced-motion
One way is to define the animation, then set animation: none inside @media (prefers-reduced-motion: reduce). The other starts from static styles and adds the motion only inside @media (prefers-reduced-motion: no-preference).
W3C’s technique C39 documents both. The second fails safe, because a browser that cannot evaluate the query never sees the motion rules.
Keep the state cue. A hover border that vanishes under reduced motion removes information, so swap the animation for an instant color change instead of deleting the effect.
WCAG criteria that apply to border motion
| Criterion | Level | Border effect it covers |
|---|
| 2.2.2 Pause, Stop, Hide | A | Loops that start on their own, run past five seconds and sit beside other content |
| 2.3.3 Animation from Interactions | AAA | Hover, focus and click effects that move |
| 1.4.11 Non-text Contrast | AA | Borders and focus indicators that identify a component or its state |
W3C’s guidance for 2.2.2 treats content that stops within five seconds as compliant. A gradient border set to one finite turn, such as animation: spin 4s linear 1, finishes under that limit and needs no pause control.
W3C itself does not recommend requiring Level AAA conformance as a general policy for entire sites. The prefers-reduced-motion technique still costs only a few lines of CSS.
Contrast of the border itself
WCAG 1.4.11 sets 3:1 as the minimum contrast against adjacent colors for visual information that identifies a component or its states (W3C, Understanding SC 1.4.11).
A rotating gradient passes through every stop, so test the weakest stop against the adjacent background, not the average. A ratio check goes faster with a guide to color contrast open beside the design.
W3C also warns that thin lines can render fainter than their CSS color because of anti-aliasing, so a nominal pass can fail in practice. When the ratio sits near the limit, use a thicker border or a darker stop.
Test each gradient stop, and test hover and focus states separately. Then run the final result through a web accessibility checklist before release.
Why a CSS border animation does not work
When a border animation does nothing, the cause is usually a bad @property rule, a border with nothing to paint, a pseudo-element hidden behind an ancestor background, or a reduced-motion setting on the developer’s own machine. Each one has its own symptom and fix.
Declaration and registration errors
- An invalid @property rule gets ignored whole. MDN states that syntax and inherits are required, and initial-value is required unless the syntax is the universal one. If either is missing or invalid, the angle never interpolates.
- animation-duration defaults to auto, which MDN equates to 0s for time-based animations, so an animation shorthand without a time never visibly runs.
- A border with no style paints nothing, so a color or width transition has no visible result.
- Your own system setting can switch reduce motion on, which triggers the reduced-motion block and silently disables the animation under test. MDN lists the settings that turn it on in Windows, macOS, iOS, Android and Linux desktops.
Stacking and visibility problems
A pseudo-element with a negative z-index joins the nearest stacking context. With none on an ancestor, it falls behind every ancestor background and disappears.
Give a wrapper isolation: isolate. According to makandra’s z-index write-up, that creates a stacking context with no other side effects, so the pseudo-element sits above the wrapper and below the card content.
Leave the card without a z-index of its own. The pseudo-element gets z-index: -1 with a negative inset, so it shows as a rim around the card.
When to drop the animation
Some effects are not worth fixing.
A grid of looping gradient cards repaints on the main thread every frame, which is the work Lighthouse’s audit counts as non-composited. One static gradient costs nothing per frame. Honestly, a looping gradient on every card is more than most pages need.
If the border carries information (selected, error, focus), animation is decoration on top of the signal. Keep the static state as the source of truth and animate only the arrival.
CSS Border Animation FAQ
Can border animation work without JavaScript?
Without any script, yes. Transitions and @keyframes handle hover borders and looping glows, and @property covers rotating gradients.
Script appears only when measuring an SVG path length, and setting pathLength on the shape removes that need.
How do you pause a border animation on hover?
Set animation-play-state: paused inside the :hover rule. The border freezes at its current angle or position and resumes from there when the pointer leaves.
Flip the logic for effects that should only move on interaction. Paused by default, running on hover.
Is border-radius animatable?
It interpolates like any length or percentage, so a square corner can round on hover.
A border-image gradient will not follow the change, because it stays rectangular. Gradient borders built on background layers do follow it.
Does a border animation follow scroll position?
Through CSS scroll-driven animations, it can. The animation-timeline property ties keyframe progress to scroll offset or to an element’s visibility instead of to time.
Chrome 115 and Safari 26 ship them, and Firefox 159 turns them on by default in October 2026. Support is still newer than for transitions, so keep a static border as the fallback. More scroll-linked patterns are collected in a guide to CSS scroll effects.
How do you animate a border with Tailwind CSS?
Utility classes handle the simple cases, meaning a color transition plus a hover variant of the border color class. Rotating gradient borders also need an @property rule in plain CSS and a custom keyframe animation registered in the theme configuration.
A Tailwind CSS guide covers the setup before you add either.
When should the Web Animations API replace CSS keyframes?
Switch when the border needs runtime control, meaning starting, reversing, seeking or chaining animations from script, or reading progress while it plays. MDN lists the Animation interface as Baseline Widely available since March 2020.
For fixed hover and loop effects, plain CSS stays simpler.
Which CSS Border Animation to Ship First
A border-color or outline transition belongs in production first, because it needs no registration and degrades to an instant color change under reduced motion.
After that, the order costs least at each stage and leaves a releasable result after every one. Start with the hover and focus transition, with :focus-visible parity, and test the reduced-motion rule with the system setting on. Add one @property gradient border last, static fallback first.
The trade-off is that this order gives up continuous motion on most components and spends paint time only where the gradient runs.
Verified October 2026, the sequence changes once @property reaches Widely Available status, because the fallback then guards only older browsers.
The same transition and keyframe tools apply to other properties, covered in a broader guide to CSS animation.