Skip to content
goodtabs UI
BrowseBrowse components

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

PropTypeDefaultDescription
risenumber48Pixels 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.