惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

N
Netflix TechBlog - Medium
大猫的无限游戏
大猫的无限游戏
B
Blog
J
Java Code Geeks
T
Tailwind CSS Blog
腾讯CDC
A
About on SuperTechFans
GbyAI
GbyAI
H
Help Net Security
IT之家
IT之家
L
LangChain Blog
Y
Y Combinator Blog
Hugging Face - Blog
Hugging Face - Blog
钛媒体:引领未来商业与生活新知
钛媒体:引领未来商业与生活新知
S
SegmentFault 最新的问题
博客园 - 叶小钗
小众软件
小众软件
I
InfoQ
爱范儿
爱范儿
有赞技术团队
有赞技术团队
博客园 - 司徒正美
博客园 - 【当耐特】
Jina AI
Jina AI
D
Docker

CSS-Tricks

Let’s Use the Emergent CSS random() Function in all the Browsers | CSS-Tricks What’s !important #18: <geolocation>, Syntax ::highlight()ing, named-feature(), and More | CSS-Tricks Creating Web Widgets Using the Document Picture-in-Picture API | CSS-Tricks animation-trigger | CSS-Tricks MicroLighter: Syntax Highlighter | CSS-Tricks WordPress PHP-Only Block Registration | CSS-Tricks Resolved: CSS Class Prefix Selector | CSS-Tricks CSS Navigation Matching, Early Days | CSS-Tricks WordPress.com Student Plan | CSS-Tricks Dark mode toggles: two states are enough | CSS-Tricks What’s !important #17: Custom Highlight API, CSS Navigation Matching, Fixing text-stroke, and More | CSS-Tricks Blocked aria-hidden: The Warning is Right, and Every Fix You've Found is Wrong | CSS-Tricks SmashingConf Freiburg 2026, September 7-10 | CSS-Tricks Using and Styling the Dialog Element | CSS-Tricks 2026 State of CSS, Devs Surveys | CSS-Tricks Gap Decorations Are Now Available, Here’s What’s New | CSS-Tricks What’s !important #16: sibling-index() Animations, Use Cases for the infinity Keyword, Container Stuck Queries, and More | CSS-Tricks writing-mode | CSS-Tricks pointer-events | CSS-Tricks What’s !important #15: Boundary-aware CSS, Time-based CSS, Full-bleed CSS, and More | CSS-Tricks Get Ready For the Powerful CSS border-shape Property! | CSS-Tricks What’s !important #14: Gap Decorations, random(), <select> field sizing, and More | CSS-Tricks The Shifting Line Between CSS States and JavaScript Events | CSS-Tricks translateZ() | CSS-Tricks translateY() | CSS-Tricks translateX() | CSS-Tricks translate() | CSS-Tricks Using Scroll-Driven Animations for Opposing Scroll Directions | CSS-Tricks A First Look at Scroll-Triggered Animations | CSS-Tricks The Siren Song of  ariaNotify() | CSS-Tricks
Animating CSS border-image | CSS-Tricks
Preethi · 2026-08-10 · via CSS-Tricks

The CSS border-image property is one of those features we take for granted. And it’s not new at all! In fact, Andy Clarke has a fairly recent article that “revisits” the property and shows just how creative we can get with it. Go read that article — it’s worth it.

The TL;DR is that we can use an image or gradient as a border instead of standard (ahem, boring) styles, say, solid or dashed border lines. What I want to do is take them a little further to show how we can animate border images for even more interesting user interfaces!

Like this:

Why border images at all?

There’s a pretty big caveat to border images: they don’t curve to border shapes. That’s a big limitation for an animation that travels around an element like we’re doing here. But we can work around that for this example.

But that also begs the question: “Why work around that if there are other approaches?” Just look at Temani Afif’s approach that uses a CSS mask (he has a great write-up on it):

I like border-image because it’s super efficient, especially when it comes to animating border images and gradients. Mainly because it’s easy to replicate the designs across all the borders — it’s automatic — and also because we can “slice” the images using the border-image-slice property. It’s similar to background-size and background-position where a single slice of the image can be used. However the border image slices can run across the entire borders, and animating it creates beautiful effects, as you’ll see.

The HTML

We’re doing nothing crazy here. Just a div that contains a bit of formatted text in it:

<div class=card><strong>Bruce Wayne</strong></div>

What about the image? That’s set up as a CSS background, so let’s jump right in that.

Base styles

We’re giving the .card set dimensions then dropping in a background image on it:

.card {
  width: 150px;
  aspect-ratio: 0.69;
  position: relative;
  background: center/90% no-repeat;
  background-image: url("batman.jpg");
}

Cool, cool. I’m also adding a little styling to the text to make sure it’s not right on top of the background:

I’ll be using a single-color CSS gradient because (1) it’s a type of background image and (2) it’s a little easier to demonstrate the animation.

I’ll also set all the values of border-image separately in their own longhand properties, so it’s clearer for you to know which value affects the gradient in which way.

Let’s start with the effect of a simple linear-gradient:

.card {
  /* ... */

  /* Creates a linear color */
  border-image-source: linear-gradient(-45deg, red 0%, transparent 0%);
  
  /* Controls how the gradient is carved */
  border-image-slice: 1;

  /* Sets the physical thickness of the border */
  border-image-width: 5px;
}

You may have noticed in the demo that there is a little gap between the animated border and the image. We can push the border away from the .card using the border-image-outset property:

.card {
  /* ... */

  border-image-source: linear-gradient(-45deg, red 0%, transparent 0%);
  border-image-slice: 1;
  border-image-width: 5px;

  /* Pushes the border away from the div */
  border-image-outset: 5px;
}

We need border-image-source and border-image-width to render the border. Initially, the image source — the gradient — is fully transparent. Because both the red and transparent colors are set to start at 0%, the browser doesn’t have any space to blend the colors, and since transparent comes second in the code, it gets the starting position at 0% and fills the rest of the gradient. We’re using border-image-slice: 1 to create 1px slices of the gradient, which creates a smooth fill of the gradient across the whole border.

Animating the border image

There’s something else to remember about gradients, specifically about animating them: we can’t by default. Transitioning (or interpolating) from one color to another is difficult because we’re essentially transitioning the starting and ending points of a gradient, which are percentages.

That’s where registering our own custom property comes into play because we can indeed animate CSS variables:

@property --p {
  syntax: "<percentage>";
  initial-value: 0%;
  inherits: false;
}

And we can use that in our gradient:

.card {
  /* ... */
  border-image-source: linear-gradient(-45deg, red var(--p), transparent 0%);
}

Now let’s say that we want --p to transition from its default value of 0% to 100% when the .card is hovered:

.card {
  /* ... */
  border-image-source: linear-gradient(-45deg, red var(--p), transparent 0%);
  /* ... */

  &:hover {
    --p: 100%;
  }
}

This tells the browser that red should finish its transition at the 100% mark (the very end). The gradient will grow to be a solid red color, like the border is drawing itself:

Variations

We could have stopped with that last example, but there’s so much else we can do here! We can change direction, make it faster or slower, stop in other positions, or even use different types of gradients. Seriously, it’s a lot of fun.

Let’s try using a conic-gradient instead of a linear one. We can animate the border-image-slice value, and use border-image-repeat: round to tile the slices within the borders. The round tiling tries to fit a whole number of tiles in the border and if it couldn’t, the image slightly stretches or squeezes until the fit occurs.

.card {
  /* Creates a sharp, expanding color wheel using the custom angle variable */
  border-image-source: conic-gradient(from 0deg, red 0deg, transparent 0%);

  /* Repeats the sliced regions smoothly without clipping */
  border-image-repeat: round;

  border-image-width: 5px;
  border-image-slice: 1;
}

We still need to register custom properties to move the gradient around, this time to transition the border-image-slice value (we’ll call it --n) and the conic-gradient’s angle (which we’ll call --a):

Note: You can also animate border-image-slice directly and not through a registered custom property. The result will slightly vary based on how the browser handles those changes.

/* Registers a custom property for the border-image-slice */
@property --n {
  syntax: "<number>";
  initial-value: 1;
  inherits: false;
}

/* Registers a custom property for the gradient angle */
@property --a {
  syntax: "<angle>";
  initial-value: 0deg;
  inherits: false;
}

Let’s drop those into our original CSS:

.card {
  border-image-source: conic-gradient(from var(--a), red var(--a), transparent 0%);
  border-image-width: 5px;
  border-image-slice: var(--n);
  border-image-repeat: round;
}

…and tell the CSS exactly which properties we’re transitioning, which is good for performance:

.card {
  border-image-source: conic-gradient(from var(--a), red var(--a), transparent 0%);
  border-image-width: 5px;
  border-image-slice: var(--n);
  border-image-repeat: round;
  transition-property: --n, --a;
  transition-duration: 0.6s;
}

…and then make the properties transition on hover:

.card {
  border-image-source: conic-gradient(from var(--a), red var(--a), transparent 0%);
  border-image-width: 5px;
  border-image-slice: var(--n);
  border-image-repeat: round;
  transition-property: --n,--a;
  transition-duration: 0.6s;

  &:hover {
    /* Increases slicing depth to repeat/tile the border pattern */
    --n: 20;
    /* Rotates the gradient full circle to draw the border in */
    --a: 360deg;
  }
}

The slice value (--n) is initially 1. It goes up to 20 on hover. That means the slice gets bigger (up to 20px thick). You’ll notice this when you see the breaks in the borders.

More examples!

Like I said earlier, there’s so much we can do with animated border images. We can animate multiple colors, use repeating gradients. Here are the examples I pulled together.

Your homework: pick these apart — and please let me know if you make any other effects! I’d love to see them.