Skip to content
shadcn.io

shadcn.io is not affiliated with official shadcn/ui

Backgrounds

Pro

Wavy Background for React

Softly blurred ribbons of colour that drift across a box at different depths, crossing and parting as they flow over a faint wash. For React with Base UI, when a music, wellness or creative page wants calm ambient motion behind its copy.

Framework

Softly blurred ribbons of colour drift across the box at different depths, crossing and parting as they go: sky, indigo, purple, fuchsia and cyan by default. The near ribbons are bold and fairly crisp, the far ones thin, faint and softer, and they spread over most of the box's height above a faint wash of the same colours, so the box never splits into a band and empty space. Each ribbon is a wave drawn once as SVG and blurred once; its wrapper slides sideways by exactly one wave at its own speed, every other ribbon the other way, and bobs gently up and down. Only transforms move, so the browser never repaints, and no script runs.

Use it behind a music player, a wellness app or a creative portfolio, where the motion should stay ambient. For wide silk ribbons that twist and catch the light, use Ribbons; for quieter filled waves, Light Waves; for soft sheets of light at the same calm pace, Aurora; for thin curves instead of soft bands, Paths.

Preview width

Installation

Part of Pro
Pro unlocks the code and one-line install of every Pro button and text effect, plus the MCP for your AI editor.
Have Pro already? Sign in to open it.Opens here as soon as you're Pro.

Usage

import { Wavy } from "@/components/ui/wavy";
<Wavy className="flex min-h-[520px] flex-col items-center justify-center">
  <h1 className="text-5xl font-semibold">Music for long focus</h1>
</Wavy>

Wavy is a container: whatever you put inside sits on top of the ribbons, in normal flow. The ribbons spread over the middle 70% of the box's height by default (spread), in a portrait card as in a wide banner, so give the box a height. For a backdrop behind the whole page, render it empty with className="fixed inset-0 -z-10". There's no painted panel: your page's background shows through, so the ribbons read as pastels on a light page and glow on a dark one.

Demos

Theme colours

colors takes up to five CSS colours, used in turn; here the theme's var(--chart-1) to var(--chart-5), so the ribbons follow a theme switch. count={7} with spread={0.9} fills the box from top to bottom.

Preview width

Lines

thickness={6} and blur={2} turn the ribbons into fine lines, count={3} keeps three, and speed={0.6} slows them down, here in a small card.

Preview width

Accessibility

The ribbons are decoration: they're hidden from assistive tech and let every click through to your content. With reduced motion they stand still, each at its own offset, so they still cross. Nothing flashes.

Put copy in your foreground colour (text-foreground). At the default intensity it keeps about 5.7:1 on a light page at the worst frame. On a dark page it keeps 4.5:1 over most of the box, dipping to about 3.5:1 for a moment where the brightest ribbons cross behind it, enough for a large headline; the ribbons are dimmer on a dark surface for this reason. intensity="subtle" behind body text keeps it clear, and intensity="strong" brightens them, so keep body text off the crossings there.

FAQ

How do the ribbons move?

Each ribbon slides sideways at its own speed, one full wave every 18 to 33 seconds, every other one the other way, while it drifts up and down. Where they cross keeps changing. There's no noise library and no script: each ribbon is a fixed shape moved by CSS transforms.

How do I spread the ribbons over more of the box?

spread is the share of the box's height they spread over, from 0 (all through the middle) to 1 (top to bottom); 0.7 by default runs them from about 15% to 85%. Raise count with it for a fuller box, as in the theme colours demo.

Can I change the colours?

Yes. colors takes up to five CSS colours, used in turn across the ribbons, such as var(--chart-1) to follow your theme. You can also set --wavy-color-1 to --wavy-color-5 in your CSS. The faint wash behind them uses the same colours.

What do blur and thickness do?

thickness sets the nearest ribbons' width in pixels (50 by default) and blur their softness (10 by default, 0 for crisp lines). Farther ribbons are thinner, down to about half, and softer, for depth. The blur is drawn once, when the ribbon first shows, and never again while it moves.

How many ribbons can I have?

count takes 1 to 8 (5 by default). Each has its own wave, depth, speed and starting point, the same on every render, and they share the spread evenly.

How do I slow it down?

speed={0.5} halves every ribbon's speed and speed={2} doubles it. With reduced motion the ribbons stop where they are.

API Reference

Wavy

<div> data-slot="wavy"

PropType
blur?number

How soft the ribbons' edges are, in pixels: 0 draws crisp lines. Far ribbons are softer than this, near ones a little crisper.

Default
10
colors?string[]

Up to five CSS colours for the ribbons, repeated when there are fewer. By default sky, indigo, purple, fuchsia and cyan; pass theme variables such as var(--chart-1) to follow your theme.

count?number

How many ribbons, from 1 to 8. @default 5.

Default
5
intensity?"subtle" | "medium" | "strong"

How strongly the ribbons show. @default "medium".

Type
"subtle" | "medium" | "strong"
Default
"medium"
speed?number

How fast the ribbons flow: 2 is twice as fast, 0.5 half as fast.

Default
1
spread?number

How much of the box's height the ribbons spread over, from 0 (all through the middle) to 1 (top to bottom): 0.7 runs them from 15% to 85%.

Default
0.7
thickness?number

The nearest ribbons' width, in pixels; farther ones are thinner, down to about half.

Default
50
render?ReactElement | ((props: object, state: object) => ReactElement)

Replace the host element or compose it with another component.

Type
ReactElement | ((props: object, state: object) => ReactElement)
className?string

Merged with the part's classes.

CSS variableType
--wavy-blurstring

How soft the nearest ribbons are (10px).

--wavy-color-1string

The first ribbon colour (sky).

--wavy-color-2string

The second ribbon colour (indigo).

--wavy-color-3string

The third ribbon colour (purple).

--wavy-color-4string

The fourth ribbon colour (fuchsia).

--wavy-color-5string

The fifth ribbon colour (cyan).

--wavy-speedstring

The flow's speed, a multiplier (1).

--wavy-spreadstring

The share of the height the ribbons spread over (0.7).

--wavy-thicknessstring

The nearest ribbons' width (50px).

All 100 backgrounds