back to portfolio

border-shape playground

CSS border-shape reshapes a box's actual geometry — backgrounds, borders, focus outlines, and shadows all follow the new edge. clip-path just hides what falls outside.

Inspired by Una Kravets's writeup.

experimental! Heads up. border-shape only works in Chrome Canary 146+ with experimental web platform features turned on (chrome://flags/#enable-experimental-web-platform-features). Everywhere else this page falls back to clip-path, so you'll see the silhouettes but not the box-shadow trick that makes border-shape special.
comparison shapes syntax una.im article spec

clip-path vs border-shape

Same polygon, same hard shadow. Watch what happens to the shadow on the angled edge.

clip-path

Clips everything — including the shadow that should fall outside the cut edge.

cuts the shadow
.slanted-clip {
  clip-path: polygon(0 0, 100% 0, 85% 100%, 0 100%);
}

border-shape

Reshapes the actual box, so the shadow follows the angled edge.

shadow follows
.slanted-shape {
  border-shape: polygon(0 0, 100% 0, 85% 100%, 0 100%);
}

shapes

Each demo uses border-shape with a clip-path fallback so it renders something everywhere. In Chrome Canary with the flag on, the shadow rides the contour on every one.

chevron tab

One-sided arrow for a "next step" tab.

next step
border-shape: shape(
  from top left,
  hline to calc(100% - var(--arrow)),
  line to right center,
  line to calc(100% - var(--arrow)) bottom,
  hline to left,
  close
);

breadcrumb tag

Two-sided arrow that reads as a breadcrumb segment.

border-shape: shape(
  from left center,
  line to var(--arrow) top,
  hline to calc(100% - var(--arrow)),
  line to right center,
  line to calc(100% - var(--arrow)) bottom,
  hline to var(--arrow),
  close
);

notched corners

Sci-fi panel vibe. Eight-point polygon for the cut corners.

data terminal
border-shape: polygon(
  var(--cut) 0, calc(100% - var(--cut)) 0,
  100% var(--cut), 100% calc(100% - var(--cut)),
  calc(100% - var(--cut)) 100%, var(--cut) 100%,
  0 calc(100% - var(--cut)), 0 var(--cut)
);

speech bubble

Tail on the lower left. The shadow extends down past the tail.

talk soon
border-shape: shape(
  from top left,
  hline to right,
  vline to calc(100% - var(--tail)),
  line to 40% calc(100% - var(--tail)),
  line to 28% bottom,
  line to 26% calc(100% - var(--tail)),
  hline to left,
  close
);

hexagon

Six-point polygon with aspect-ratio for clean proportions.

honeycomb
border-shape: polygon(
  25% 0, 75% 0,
  100% 50%,
  75% 100%, 25% 100%,
  0 50%
);

star

Ten-point polygon. Hard to draw cleanly with anything but math.

border-shape: polygon(
  50% 0%, 61% 35%, 98% 35%,
  68% 57%, 79% 91%, 50% 70%,
  21% 91%, 32% 57%, 2% 35%,
  39% 35%
);

stop sign

Eight-sided regular polygon. The fill still occupies the new geometry.

stop
border-shape: polygon(
  30% 0, 70% 0,
  100% 30%, 100% 70%,
  70% 100%, 30% 100%,
  0 70%, 0 30%
);

ribbon banner

Pointed ends like a flag or award ribbon.

winner
border-shape: shape(
  from top left,
  hline to right,
  line to calc(100% - var(--notch)) center,
  line to right bottom,
  hline to left,
  line to var(--notch) center,
  close
);

dog-ear / folded corner

Mimics a folded paper corner. The triangle is a pseudo-element on top.

memo
border-shape: shape(
  from top left,
  hline to calc(100% - var(--fold)),
  line to right var(--fold),
  vline to bottom,
  hline to left,
  close
);

wavy bottom edge

Curves with the curve to … with command. Good for section breaks.

section
border-shape: shape(
  from top left,
  hline to right,
  vline to 75%,
  curve to 70% 75% with 92% 95% / 78% 95%,
  curve to 40% 75% with 62% 55% / 48% 55%,
  curve to 10% 75% with 32% 95% / 18% 95%,
  curve to 0 95% with 5% 55%,
  close
);

syntax cheat sheet

The border-shape property accepts the same kinds of values as clip-path:

valuewhat it does
circle(r at x y)Reshape into a circle.
ellipse(rx ry at x y)Reshape into an ellipse.
inset(top right bottom left round r)Inset rectangle, optionally rounded.
polygon(x y, x y, …)Straight-edged polygon from a list of points.
path("M … Z")SVG-style path data string.
shape(from x y, line to x y, …)CSS-native path syntax with named keywords like top, right, center.

The shape() function is the easier one to write by hand. Inside it:

from <point>Starting position.
line to <point>Straight line.
hline to <x>Horizontal line — only x changes.
vline to <y>Vertical line — only y changes.
curve to <point> with <cp1> / <cp2>Bezier curve. One control point = quadratic, two = cubic.
arc to <point> of <radius>Circular arc.
closeClose the path back to the start.

Coordinates accept percentages, lengths, calc(), and the keywords top right bottom left center. So line to right center is a real, readable point.

why this matters

For years the standard trick for non-rectangular UI — angled tabs, ribbons, speech bubbles — has been clip-path. It works for the silhouette but it's a mask: anything that should bleed past the new edge gets cut. Drop shadows look wrong. Outlines on focus stop at the shape's bounding box, not its edge. Border images don't follow the curve.

border-shape fixes all of that by treating the new geometry as the actual box. The browser knows where the edge of the element really is, so everything that decorates an edge — shadows, outlines, borders — follows it.

This is still early. Today it's a Chrome Canary preview behind a flag, and the spec ships as part of CSS Borders and Box Decorations Module Level 4. But it's a meaningful change. Padding-must-be-wide-enough warnings and a few quirky bugs aside, this is the missing piece for shaped UI on the web.