Rethinking What `border-image` Can Do

The CSS border-image property has been around long enough that even Internet Explorer supports it, yet it often feels more like a theoretical curiosity than a practical tool. That’s understandable — the concepts of “slicing” and “outsets” are opaque at first glance. But once the mechanics click, the property reveals itself as a surprisingly versatile way to build everything from gradient overlays to full-width breakout backgrounds.

Before getting to the fun stuff, there are a few behaviors worth understanding because they explain most of the confusion people hit when first experimenting with the property.

Watch the Cascade Order

According to the CSS Backgrounds and Border Module Level 3 specification, border-image is meant to replace the regular border. In practice, that only holds if you’re careful about declaration order. If you set border after border-image, the regular border wins:

/* All I see is a red border */
.element {
  border-image: linear-gradient(blue, red) 1;
  border: 5px solid red;
}

The issue is that border is a shorthand that resets border-image when it appears later in the cascade.

/* 👍 */
.element {
  border: 5px solid red;
  border-image: linear-gradient(blue, red) 1;
}

It’s a subtle gotcha that can make you think the property is broken. A good habit is to avoid using border alongside border-image entirely, unless you’re deliberately managing the reset.

Painting Order Matters

Another thing to remember: border-image paints above the element’s background and box-shadow, but sits below the content. This ordering is what enables some of the overlay tricks we’ll look at shortly.

See the Pen [Showing border-image above background and shadow!](https://codepen.io/t_afif/pen/xxMaVjG) by Temani Afif.

See the Pen Showing border-image above background and shadow! by Temani Afif.

Decoding the Syntax

Part of the difficulty is that the syntax is dense. The full form includes the source, slice, width, outset, and repeat values. If we strip out the repeat parameter and focus on the core structure, we’re left with three key components:

border-image: *-gradient() <slice>/<width>/<outset>

Here’s the analogy that makes it click if you’re familiar with the box model: <width> behaves like border-width, and <outset> behaves like margin — both accept one to four values. The double slash in the syntax separates the slice values from the width/outset values, a syntax quirk that makes this property immediately recognizable.

The <slice> value, which takes one to four unitless numbers or percentages plus an optional fill keyword, is what divides your source image into nine regions. Each of those regions gets mapped to a corresponding section of the element’s border area. By default, the middle region stays empty — unless you pass fill, in which case the center slice fills it completely.

border-image:
  linear-gradient(...)
  s-top s-right s-bottom s-left / 
  w-top w-right w-bottom w-left /
  o-top o-right o-bottom o-left;

The <outset> property effectively expands the size of the area in which the border image is drawn, which lets you create decorations that spill out beyond the element’s box. Without an outset, the border image is confined to the element’s usual boundary.

Making a Gradient Overlay in One Line

A simple but powerful demonstration of border-image is adding a gradient overlay over an existing background. This is often used to boost text legibility. Unlike the usual pseudo-element approach, border-image gives us a one-liner:

.overlay {
  border-image: fill 0 linear-gradient(#0003,#000); 
}

That’s it — no pseudo-elements or extra markup.

See the Pen [Gradient Overlay with border-image](https://codepen.io/t_afif/pen/vYbdVjb) by Temani Afif.

See the Pen Gradient Overlay with border-image by Temani Afif.

Turning to the anatomy of it, the fill 0 slice value means the entire gradient is loaded into the center slice. Region 1 through 8 collapse to zero width because the <width> defaults to 0, leaving just the center region to paint. The lack of an outset keeps everything inside the element’s box. So you get the gradient rendered above the background, and behind content.

There’s another approach that produces the same end result but uses different slicing. Instead of putting one slice in the center, you split the content into four slices at 50% and match the width to 50%, placing each corner slice into place:

.overlay {
  border-image: linear-gradient(#0003, #000) 50%/50%; 
}

Both of these examples might take some time to wrap your head around. The trick is to first visualize how the source splits into slices, then how those slices fit into the regions defined by width and outset. Once your mental model solidifies, the property becomes much more approachable.

The “Break-Out” Background Pattern

Backgrounds that span the whole viewport width while still being contained within a centered container are an engineering challenge on their own. The typical solution involves CSS Grid or negative margins. But border-image offers an alternative.

Light pink background extending the full-screen width against a paragraph.
Figure 2: The background is able to “break out” of the parent’s element’s constrained width. (Large preview)

Here the key choice is the source. Since border-image can’t take a direct color, we use a gradient that generates a solid. The trick is to pick a conic-gradient() with matching color stops for the smallest possible syntax:

.full-background {
  border-image: conic-gradient(pink 0 0) fill 0//0 100vw;
}

See the Pen [Full screen background color](https://codepen.io/t_afif/pen/oNEaqQX) by Temani Afif.

See the Pen Full screen background color by Temani Afif.

If the gradient overlay example slotted the slice into the center region, this one works by expanding the area. An outset of 0 100vw extends the element’s edges by the full viewport width, meaning the equally-sized slice has to span the entire width of the screen. Large outset values don’t cause a page overflow, so using a bit of extra buffer that covers “all possibilities” is fine.

Value of 100vw, which is larger than the screen width
Figure 3: Using a large value guarantees that the background is always wide enough. (Large preview)

The nice part is that we’re still dealing with gradients here, so you can get a breakout background with multi-color styling or patterns:

See the Pen [Full screen background coloration](https://codepen.io/t_afif/pen/NWoLeWW) by Temani Afif.

See the Pen Full screen background coloration by Temani Afif.

Getting Creative With Clipping

You can push the concept much further. The same principle that lets you overflow the boundaries allows you to define skewed or angled “edge” panels by combining border-image with clip-path.

See the Pen [CSS-only full screen slanted background](https://codepen.io/t_afif/pen/zYmpdeK) by Temani Afif.

See the Pen CSS-only full screen slanted background by Temani Afif.

Here, a hearty <outset> pushes the paint well past the boundaries in all directions so the following clip-path can carve the design out of that expanded canvas:

.slant {
  --a: 3deg; /* control the angle (it should be small) */
  
  border-image: conic-gradient(pink 0 0) fill 0//9999px;
  clip-path: 
    polygon(
      -9999px calc(tan(var(--a)) * 9999px),
      9999px calc(tan(var(--a)) * -9999px),
      calc(100% + 9999px) calc(100% - tan(var(--a)) * 9999px),
      calc(100% - 9999px) calc(100% + tan(var(--a)) * 9999px)
    );
}
Comparing the background before and after clipping.
Figure 4: Clipping the background creates a slanted appearance. (Large preview)

By matching the outset to your shape’s “bleed” requirements, you can turn seamless decorative backgrounds into defining visual elements of page sections.

Once the mental model of slices mapping to regions clicks, border-image sheds its reputation as a quirky and confusing property. With this foundation, you can explore gradients, overlays, and forced overflow layouts.

Heading Borders Revisited

The same technique used for full-width backgrounds can double as a heading decoration. Swap the conic-gradient() for a linear-gradient() and apply it to an <h1>:

.full-background {
  border-image: linear-gradient(0deg, #1095c1 5px, lightblue 0) fill 0//0 100vw;
}

See the Pen [Full screen gradient coloration on title](https://codepen.io/t_afif/pen/JjxawEv) by Temani Afif.

See the Pen Full screen gradient coloration on title by Temani Afif.

With a sharp color stop, that creates a border-like bottom edge running the full width. Make one color transparent and set a large <outset> in one direction, and the border only extends to one edge of the screen:

See the Pen [CSS only extended underline](https://codepen.io/t_afif/pen/yLvwgNy) by Temani Afif.

See the Pen CSS only extended underline by Temani Afif.

Still a single line of CSS:

.full-line {
  border-image: linear-gradient(0deg, #1095c1 5px, #0000 0) fill 0//0 100vw 0 0;
}

There is an alternative syntax for the same effect. Size the top regions to 100% - 8px and the others to 0. Going back to the first diagram, region five now consumes 100% - 8px of the height, leaving an 8px middle region. Since the slice is 0, the middle slice gets painted in that 8px gap:

The height of the region is set to the full height of the element, minus 8px at the bottom edge.
Figure 5: The height of the region is set to the full height of the element, minus 8px at the bottom edge. (Large preview)

A third way uses a bottom slice of 1 — unitless values compute as pixels — producing two slices: the seventh (bottom center) and the ninth (center). The seventh region is given an 8px height. Without the fill keyword, the center region stays empty while only the seventh region is painted, spanning the full width of the border-image area at 8px tall:

Filling the seventh region that is 8px tall with a solid color.
Figure 6. (Large preview)

This works because the slice value only matters as a fill switch when dealing with a solid color. Anything from 0.5 to 76% produces the same result; 1 is simply the shortest way to request a filled region. Think of it as binary logic: the slice is either empty or filled. A fourth variation exists as an exercise in tracing which slices and regions get used:

.full-line {
  border-image: conic-gradient(#1095c1 0 0) 0 1 0 0/calc(100% - 8px) 100% 0 0/0 100vw 0 0;
}

Having multiple valid paths to the same output makes border-image hard to internalize. For this heading border, the second syntax is preferable because it lets you swap the solid color for a genuine gradient by editing a single value:

.full-line {
  border-image: repeating-linear-gradient(...) fill 0 /
    calc(100% - var(--b)) 0 0/0 100vw 0 0 repeat;
}

See the Pen [CSS only extended underline with gradient](https://codepen.io/t_afif/pen/mdXYyRg) by Temani Afif.

See the Pen CSS only extended underline with gradient by Temani Afif.

The repeat keyword in that example matters. Earlier, we saw that a slice size differing from its corresponding region distorts the image. Solid colors mask that issue. Real gradients reveal it. Setting repeat as the last value in the declaration fixes most cases (stretch being the default). If distortion persists, the limit may be in the technique rather than the code — border-image is powerful but has boundaries.

Dividers Through Headings

A related pattern draws a line through the heading rather than below it:

See the Pen [Horizontal lines around your title](https://codepen.io/t_afif/pen/BaYXdmM) by Temani Afif.

See the Pen Horizontal lines around your title by Temani Afif.

The structure relies on CSS variables for easy reconfiguration:

h2 {
  --s: 3px;   /* the thickness */
  --c: red;   /* the color */
  --w: 100px; /* the width */
  --g: 10px;  /* the gap */

  border-image: 
    linear-gradient(
      #0000      calc(50% - var(--s)/2),
      var(--c) 0 calc(50% + var(--s)/2),
      #0000 0) 
    0 1 / 0 var(--w) / 0 calc(var(--w) + var(--g));
}

The slice is 0 1, taking 1px from the left and right edges. That yields slice eight (center-right) and slice six (center-left), each 1px wide, with the remaining space held by the ninth (center) slice — which is irrelevant without fill, as it will not be painted.

The --w variable sizes regions six and eight, and the same value is written into the <outset> to push those regions outside the element box. An extra variable, --g, sits in the outset formula to control the space between the text and the line. The gradient mirrors the bottom-border case, but the color band is centered with the --s variable controlling its thickness:

Showing the 6 and 8 slices and their corresponding regions.
Figure 7. (Large preview)

Another equivalent syntax sets the top and bottom of the slice to 0 and the left and right to 50%. Slices six and eight then share the gradient; every other slice, center included, is empty. The top and bottom region groups each take 50% - var(--s)/2 of the height, leaving --s for the middle band of regions six, eight and nine. As before, filled slices six and eight land in regions of the same dimensions — the line thickness and the --w width:

Showing slices 6 and 8 applied to their corresponding regions.
Figure 8. (Large preview)

Using 50% here shows that practically any slice value can trigger the fill. That attitude changes with real gradients, where slice precision matters and border-image becomes trickier to reason about, even with practice:

See the Pen [Horizontal lines around your title with gradient coloration](https://codepen.io/t_afif/pen/RwvYvGr) by Temani Afif.

See the Pen Horizontal lines around your title with gradient coloration by Temani Afif.

More title treatments follow that mix border-image with other properties:

See the Pen [Fancy title divider with one element](https://codepen.io/t_afif/pen/VwXOmjW) by Temani Afif.

See the Pen Fancy title divider with one element by Temani Afif.

See the Pen [Fancy title divider with one element](https://codepen.io/t_afif/pen/zYWQmyo) by Temani Afif.

See the Pen Fancy title divider with one element by Temani Afif.

Assorted Patterns

The remaining examples stand on their own. Work through the CSS to identify how slices and regions behave in each one, and treat them as starting points.

Image Decorations

border-image helps decorate images where pseudo-elements are off-limits. These variations share the same underlying structure:

See the Pen [Infinite image shadow](https://codepen.io/t_afif/pen/XWoNdGK) by Temani Afif.

See the Pen Infinite image shadow by Temani Afif.

See the Pen [Infinite image shadow II](https://codepen.io/t_afif/pen/mdvaeoq) by Temani Afif.

See the Pen Infinite image shadow II by Temani Afif.

See the Pen [Infinite image stripes shadow](https://codepen.io/t_afif/pen/yLZwLKj) by Temani Afif.

See the Pen Infinite image stripes shadow by Temani Afif.

See the Pen [3D trailing shadow for images](https://codepen.io/t_afif/pen/mdQwgMO) by Temani Afif.

See the Pen 3D trailing shadow for images by Temani Afif.

Custom Range Sliders

Range inputs are notoriously inconsistent across browsers, but the thumb element is shared. This slider styles only the thumb with border-image:

See the Pen [CSS only custom range sliders](https://codepen.io/t_afif/pen/KKGpmGE) by Temani Afif.

See the Pen CSS only custom range sliders by Temani Afif.

Ribbon Shapes

A collection of single-element ribbon shapes relies on border-image for some of its members, called “infinite ribbons”:

See the Pen [Full screen Ribbon title](https://codepen.io/t_afif/pen/rNqJYrZ) by Temani Afif.

See the Pen Full screen Ribbon title by Temani Afif.

See the Pen [Infinite Ribbon Shapes](https://codepen.io/t_afif/pen/NWoRJMy) by Temani Afif.

See the Pen Infinite Ribbon Shapes by Temani Afif.

Heart Shapes and Overlapping Slices

Heart shapes can be produced via border-image among other methods:

.heart {
  width: 200px;
  aspect-ratio: 1;
  border-image: radial-gradient(red 69%,#0000 70%) 84.5%/50%;
  clip-path: polygon(-42% 0,50% 91%, 142% 0);
}

See the Pen [Heart shape using border-image](https://codepen.io/t_afif/pen/MWPOJpP) by Temani Afif.

See the Pen Heart shape using border-image by Temani Afif.

The slice here is 84.5% — a value exceeding 50% that still computes safely because slices are allowed to overlap. Once a slice passes half the source, corner slices (1 through 4) begin sharing content while the others turn empty. A slice of 100% yields four slices, each containing the full source:

See the Pen [Overview of the slice effect](https://codepen.io/t_afif/pen/jOdemWL) by Temani Afif.

See the Pen Overview of the slice effect by Temani Afif.

As the slider moves from 0% to 100%, the behavior is intuitive below 50% and becomes overlapping above it. At the upper end, the full circle appears four times, once per corner slice. This makes for useful custom shapes, even if the mental model takes time to click.

Tooltips and Inner Radius

A basic tooltip shape can come from just two properties:

See the Pen [A simple Tooltip using 2 CSS properties](https://codepen.io/t_afif/pen/ExrEXoO) by Temani Afif.

See the Pen A simple Tooltip using 2 CSS properties by Temani Afif.
.tooltip {
  /* triangle dimension */
  --b: 2em; /* base */
  --h: 1em; /* height*/

  border-image: conic-gradient(#CC333F 0 0) fill 0//var(--h);
  clip-path: 
    polygon(0 100%,0 0,100% 0,100% 100%,
      calc(50% + var(--b)/2) 100%,
      50% calc(100% + var(--h)),
      calc(50% - var(--b)/2) 100%);
}

A notable quirk of border-image is that it ignores border-radius. Unlike box-shadow, outline or standard border, it will paint right over rounded corners. Instead of a drawback, the behavior can decorate images with an inner radius:

See the Pen [Inner radius to image element](https://codepen.io/t_afif/pen/abMvjZj) by Temani Afif.

See the Pen Inner radius to image element by Temani Afif.

The effect comes from a single declaration:

img {
  --c: #A7DBD8;
  --s: 10px; /* the border thickness*/

border-image: conic-gradient(var(--c) 0 0) fill 0 // var(--s);
} 

Leaving the center region empty produces a border-only variant:

See the Pen [Rounded images inside squares](https://codepen.io/t_afif/pen/gOqBWvg) by Temani Afif.

See the Pen Rounded images inside squares by Temani Afif.

Closing Thoughts

border-image is a dense property, but the effort to master it pays off. Multiple syntactic routes often lead to the same visual output, so struggling to keep all the combinations straight is normal — it takes time, repeated reading, and hands-on experimentation to feel comfortable with slicing and region assignment. The property goes far beyond the examples covered here; do experiment with it on your own. Special thanks to @SelenIT2 for pushing exploration of the property and writing an excellent article on it.

Smashing Editorial