Angled divider
A slanted section edge with a fixed pixel rise and a live angle readout.
npx shadcn@latest add https://ui.goodtabs.co/r/angled-divider.json
With the @goodtabs namespace in components.json: npx shadcn@latest add @goodtabs/angled-divider
Fixed rise
atan(32 / 0) = 0.00°
The cut is 32px high at any width.
A 32px rise, with the measured angle printed in the section.
Steeper
Steeper cut
atan(80 / 0) = 0.00°
An 80px rise over a narrower box gives a steeper angle.
An 80px rise in a wider box, so the angle is different but the rise is the same.
Usage
import { AngledDivider } from "@/components/ui/angled-divider"
<AngledDivider rise={48}>
<h2>Section title</h2>
</AngledDivider>Props
| Prop | Type | Default | Description |
|---|---|---|---|
| rise | number | 48 | Pixels the bottom edge rises across the full width. |
Source
angled-divider/angled-divider.tsx
"use client";
import * as React from "react";
import { cn } from "cn";
type AngledDividerProps = React.ComponentProps<"div"> & {
/** Pixels the cut rises over the full width. Constant at every breakpoint. */
rise?: number;
};
/**
* A section with a slanted bottom edge. The rise is fixed in pixels, so the angle
* changes with the width: angle = atan(rise / W).
*/
export function AngledDivider({ rise = 48, className, children, style, ...props }: AngledDividerProps) {
const ref = React.useRef<HTMLDivElement>(null);
const [width, setWidth] = React.useState(0);
React.useEffect(() => {
const el = ref.current;
if (!el) return;
const ro = new ResizeObserver(([entry]) => setWidth(entry.contentRect.width));
ro.observe(el);
return () => ro.disconnect();
}, []);
const angle = width ? (Math.atan(rise / width) * 180) / Math.PI : 0;
return (
<div
ref={ref}
className={cn("relative bg-muted", className)}
style={{ clipPath: `polygon(0 0, 100% 0, 100% calc(100% - ${rise}px), 0 100%)`, ...style }}
{...props}
>
<div className="px-4 pt-4" style={{ paddingBottom: rise + 16 }}>
{children}
<p className="mt-2 font-mono text-xs text-muted-foreground tabular-nums">
atan({rise} / {Math.round(width)}) = {angle.toFixed(2)}°
</p>
</div>
</div>
);
}
For AI agents
Hand your agent this page, the markdown at /components/angled-divider/prompt.md, or the Copy AI prompt button above.
When to use
- Section breaks where the cut should look the same on phone and desktop
- Pages that explain how an angle follows from a width
When not to use
- Content that must not be clipped near the edge: the cut hides the corner
- Sections that need a border or shadow on the slanted edge, since clip-path removes them
Agent notes
- It keeps the pixel rise, not the angle, so the angle is measured with a ResizeObserver and shown
- Bottom padding grows by the rise so content stays clear of the cut
- Pass a background through className, it defaults to bg-muted
The maths
- Angle = atan(rise / W), so a 32px rise over 256px is 7.13° and over 1024px is 1.79°.
- Clip-path: polygon(0 0, 100% 0, 100% calc(100% - rise), 0 100%) keeps the rise in pixels at every width.