Cascade Lines
Cascading lines texture: seven uneven bands of color fading inward from any edge of a positioned container, in warm red-to-amber and cool blue finishes. Used by the cascade Card examples.
pnpm dlx shadcn add @01orbital/cascade-linesimport type { ComponentProps } from "react"
import { cn } from "cn"
// Thickness, color and opacity of each band. Each list runs
// from the edge inward, strongest first.
const BANDS = {
warm: [
[4, "oklch(0.42 0.13 27)", 0.7],
[3, "oklch(0.44 0.12 30)", 0.5],
[5, "oklch(0.46 0.11 35)", 0.35],
[2, "oklch(0.48 0.1 40)", 0.25],
[4, "oklch(0.5 0.1 45)", 0.18],
[3, "oklch(0.52 0.11 48)", 0.12],
[5, "oklch(0.54 0.12 50)", 0.07],
],
cool: [
[4, "oklch(0.24 0.09 245)", 0.7],
[3, "oklch(0.26 0.08 248)", 0.5],
[5, "oklch(0.28 0.08 250)", 0.35],
[2, "oklch(0.3 0.08 252)", 0.25],
[4, "oklch(0.32 0.07 255)", 0.18],
[3, "oklch(0.34 0.07 258)", 0.12],
[5, "oklch(0.36 0.07 260)", 0.07],
],
} as const
const EDGES = {
top: "inset-x-0 top-0 flex-col",
bottom: "inset-x-0 bottom-0 flex-col",
left: "inset-y-0 left-0 flex-row",
right: "inset-y-0 right-0 flex-row",
} as const
/**
* Seven uneven bands of color, strongest at an edge and fading inward over
* 26px. Place inside a positioned parent; its background, padding and corner
* clipping belong to that parent. The lines ignore the pointer and are hidden
* from assistive tech. A span root also fits inside a button or link.
*/
function CascadeLines({
variant = "warm",
edge = variant === "warm" ? "top" : "bottom",
className,
...props
}: Omit<ComponentProps<"span">, "children"> & {
/** Red-to-amber `warm`, or blue `cool`. Defaults to `warm`. */
variant?: keyof typeof BANDS
/** The edge the bands fall from. Defaults to top for warm, bottom for cool. */
edge?: keyof typeof EDGES
}) {
const vertical = edge === "left" || edge === "right"
const near = edge === "top" || edge === "left"
const bands = BANDS[variant]
// Bands are listed edge-first, so the far edges lay them out in reverse.
const ordered = near ? bands : bands.toReversed()
return (
<span
aria-hidden="true"
data-slot="cascade-lines"
data-edge={edge}
data-variant={variant}
className={cn(
"pointer-events-none absolute flex",
EDGES[edge],
className
)}
{...props}
>
{ordered.map(([thickness, background, opacity], index) => (
<i
key={index}
className={vertical ? "block h-full shrink-0" : "block w-full"}
style={{
[vertical ? "width" : "height"]: thickness,
background,
opacity,
}}
/>
))}
</span>
)
}
export { CascadeLines }
import { CascadeLines } from "@/components/texture/cascade-lines"
export default function CascadeLinesDemo() {
return (
<div className="grid w-full max-w-3xl gap-6 sm:grid-cols-2">
<div className="relative overflow-hidden rounded bg-[oklch(0.13_0.03_30)] p-8 pt-14 text-white">
<CascadeLines />
<p className="font-medium">Warm / from the top</p>
<p className="mt-2 text-sm/6 text-white/70">
Seven bands of fading color.
</p>
</div>
<div className="relative overflow-hidden rounded bg-[oklch(0.12_0.025_250)] p-8 pb-14 text-white">
<CascadeLines variant="cool" />
<p className="font-medium">Cool / from the bottom</p>
<p className="mt-2 text-sm/6 text-white/70">
A surface of your choosing.
</p>
</div>
</div>
)
}
Usage
import { CascadeLines } from "@/components/texture/cascade-lines"
export default function CascadeLinesDemo() {
return (
<div className="grid w-full max-w-3xl gap-6 sm:grid-cols-2">
<div className="relative overflow-hidden rounded bg-[oklch(0.13_0.03_30)] p-8 pt-14 text-white">
<CascadeLines />
<p className="font-medium">Warm / from the top</p>
<p className="mt-2 text-sm/6 text-white/70">
Seven bands of fading color.
</p>
</div>
<div className="relative overflow-hidden rounded bg-[oklch(0.12_0.025_250)] p-8 pb-14 text-white">
<CascadeLines variant="cool" />
<p className="font-medium">Cool / from the bottom</p>
<p className="mt-2 text-sm/6 text-white/70">
A surface of your choosing.
</p>
</div>
</div>
)
}Examples
New
cascade-lines-announcement
New
cascade-lines-sidebar
New
cascade-lines-section
API reference
Seven uneven bands of color, strongest at an edge and fading inward over 26px. Place inside a positioned parent; its background, padding and corner clipping belong to that parent. The lines ignore the pointer and are hidden from assistive tech. A span root also fits inside a button or link.
Prop
Type
It also accepts the HTML attributes of the element it renders.
Dependencies
npm: cn. The CLI installs them for you.
No other registry pieces.