Skip to content
shadcn.io

shadcn.io is not affiliated with official shadcn/ui

Shaders

Pro

Mirage Shader for React

A desert at dusk drawn on the GPU: hot air bends a low sun and far mesas into slow wobbles, and a mirage lake mirrors the sky below the horizon. For React with Base UI, when a travel, hospitality or outdoor hero wants warm, quiet heat.

Framework

Mirage Shader paints a flat desert horizon at dusk, from a violet sky overhead to peach at the horizon, with a low sun and a few far mesas. Just above the ground the air shimmers: rising, noisy hot air bends every lookup up and down, so the horizon, the mesas and the sun's lower edge wobble and stretch. Below the horizon an inferior mirage turns the ground into a trembling "lake" that mirrors the sky, the mesas and the sun, and its far edge wanders as the hot layer thickens and thins. The scene is drawn analytically and the haze is fractal noise drifting upward; a WGSL fragment shader on WebGPU draws every pixel each frame.

Use it behind a travel, hotel, festival or outdoor-gear hero, or anywhere a warm, calm landscape fits. For mist in hills instead, use Valley Mist Shader; for warm light through cloud, Cloud Rays Shader; for sand ridges in plain CSS, the Dunes background. It runs only while it's on screen, holds a still frame with reduced motion, and shows a CSS gradient of the same sky and sun in browsers without WebGPU.

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 { MirageShader } from "@/components/ui/mirage-shader";
<MirageShader className="flex min-h-[32rem] flex-col items-center pt-24">
  <h1 className="text-5xl font-semibold text-white">Ship faster</h1>
</MirageShader>

Mirage Shader is a container: your content sits on top of the scene, in normal flow. It fills its own box, so give it a height or content. For a backdrop behind the whole page, render it empty with className="fixed inset-0 -z-10". The sky is dark at the top and light at the horizon, so place text by height: white text in the top third stays above 4:1 (6:1 at the median), fine for a headline, and dark text on the ground below stays above 5:1. Around the sun and the horizon neither reads well. The palette is the same in both color schemes.

Demos

Midday road

The colors make the place. skyColor="#9fc6e8" and zenithColor="#3d6db0" give a clear blue sky, groundColor="#4a4a4f" turns the desert into asphalt, silhouetteColor="#6b6f78" grays the far hills and sunColor="#ffffff" whitens the sun: the mirage now reads as the wet-looking patch over a hot road.

Preview width

In a card

horizon={0.55} raises the horizon a little, so a tall card shows more ground and its mirage. The haze scales with the box's height, so the wobbles keep their size.

Preview width

Performance

About 0.7 ms of GPU time a frame at 1280×720 on an M2 Max laptop; slower GPUs take several times longer. The scene is drawn twice, once for the mirror, plus a few noise lookups for the haze. The edges are soft, so quality={0.5}, a quarter of the pixels, looks almost the same on a large screen. The animation stops when the shader scrolls out of view, and the browser pauses it in a hidden tab. Every shader on a page shares one GPU device and one frame loop, and vgpu (about 50 KB gzipped) loads only when the first one mounts.

Browser support

The shader needs WebGPU: Chrome and Edge, Safari 26 and later, and Firefox on Windows and macOS. Elsewhere, and until the first frame, the box shows a CSS gradient of the same sky, sun and ground, which follows your color props, so the layout never jumps. data-state on the root reads unsupported there, if you want to style around it. See Shader Canvas.

Accessibility

The scene is decoration: it's hidden from assistive tech and lets every click through to your content. With reduced motion it draws one still frame, the sun on the horizon beside the mesas with their mirage below, and stops. Nothing flashes: the shimmer is small and slow, and only the narrow band round the horizon moves. White text reads in the top third of the frame; dark text on the ground.

FAQ

Can I change the colors?

Yes: zenithColor is the sky overhead, skyColor the sky at the horizon and the glow round the sun, sunColor the disc, groundColor the desert floor and silhouetteColor the mesas. They take any CSS color, var(--primary) included, and follow a theme switch. The mirage mirrors whatever sky you choose.

How do I move the horizon?

horizon sets where it sits, from 0.3 to 0.8 of the height from the top. The sun stays on it, so a lower horizon gives more sky for your copy, and a higher one more ground and mirage.

How do I make the mirage bigger or turn it off?

mirage sets how deep the mirrored lake reaches below the horizon, from 0 (none) to 1. intensity sets how strongly the hot air shimmers, from 0 (still air) to 2; it shakes the mirage's ripples too.

How do I make it faster or slower?

speed scales time: 0.5 makes the air rise half as fast. paused holds the frame, and time draws one fixed moment, in seconds, which is handy for a poster.

Will it slow my page down?

It costs about 0.7 ms of GPU time a frame at 1280×720 on a laptop, and the CPU only sets a few numbers each frame. It stops when it's off screen. On a slow GPU or a large screen, quality={0.5} renders a quarter of the pixels, and the soft scene looks almost the same.

What happens in browsers without WebGPU?

The box keeps its CSS gradient, the same sky, sun and ground in your colors, without the mesas or the mirage, so the page still looks finished. Nothing errors: the root's data-state reads unsupported.

API Reference

MirageShader

data-slot="mirage-shader"

PropType
fallback?string

What shows until the first frame and in browsers without WebGPU: a CSS background value (gradients, sizes, a colour) that looks like the shader. It can use the colour props' --shader-<field> variables.

groundColor?string

The desert floor; it darkens toward the bottom. Any CSS colour. @default "#c98a6b".

Default
"#c98a6b"
horizon?number

Where the horizon sits, from 0.3 to 0.8 of the height, from the top. @default 0.62.

Default
0.62
intensity?number

How strongly the hot air shimmers and bends the horizon, from 0 (still air) to 2. @default 1.

Default
1
interactive?boolean

Whether u.pointer follows the pointer. @default false.

mirage?number

How deep the mirage "lake" below the horizon reaches, from 0 (none) to 1. @default 0.5.

Default
0.5
paused?boolean

Holds the current frame. @default false.

quality?number

Render scale, from 0.25 to 1, of the screen's pixels (capped at 2×). @default 1.

silhouetteColor?string

The far mesas on the horizon. Any CSS colour. @default "#7a4b4f".

Default
"#7a4b4f"
skyColor?string

The sky at the horizon, and the glow round the sun. Any CSS colour. @default "#f6b38f".

Default
"#f6b38f"
speed?number

Animation speed: 1 is normal, 0 holds the frame. @default 1.

sunColor?string

The sun's disc. Any CSS colour. @default "#fff0d1".

Default
"#fff0d1"
time?number

A fixed moment, in seconds: draws it and doesn't animate.

zenithColor?string

The sky overhead. Any CSS colour. @default "#5b4a7a".

Default
"#5b4a7a"
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

Forwarded to the part.

All 101 shaders