Mesh Card Backdrop
The three-layer shader backdrop shared by the CTA card and the case study closing card: a static gradient base, a live WebGL shader over it, and a legibility scrim. Navy mesh by default; the aqua tone is a port of the composio.dev flowing-silk card background with a cursor-driven liquid trail.
On a card
Let’s start something big
The backdrop fills its host. Content needs relative z-10 to sit above it.
tsx
<div className="card-cta surface-ink relative overflow-hidden rounded-2xl p-7">
<MeshCardBackdrop />
<div className="relative z-10">…</div>
</div>Aqua tone
Not quite what you need?
A different shader: flowing silk folds and grain. Move the cursor across it — the liquid trail is the hover effect. The host carries the paper ladder so the copy is ink and the scrim is white.
tsx
<div className="card-cta-aqua surface-paper surface-white relative overflow-hidden rounded-2xl p-7">
<MeshCardBackdrop tone="aqua" />
<div className="relative z-10">…</div>
</div>Blue tone
Let’s start something big
The same folds in the hero’s navy. The trail is light pooling in the dark — move the cursor across it. The host is card-cta-blue surface-ink, which also selects the directional scrim.
tsx
<div className="card-cta-blue surface-ink relative overflow-hidden rounded-2xl p-7">
<MeshCardBackdrop tone="blue" />
<div className="relative z-10">…</div>
</div>Host requirements
- The host must be
relative overflow-hiddenwith a radius — the shader canvas is a rectangle and squares off corners otherwise. - Content needs
relative z-10to sit above the three layers. - The host carries the surface class that matches the tone —
surface-inkfor ink, so its copy keeps the dark ladder on a paper ground;surface-paper surface-whitefor aqua, so its copy is ink and the scrim resolves to white on the dark page. The scrim reads--backgroundfrom the host. - Pair with
card-cta(ink),card-cta-aqua(aqua) orcard-cta-blue(blue) for the ring and shadow — all are ground-aware, the flowing two add a hover lift, and the blue one selects the lifted scrim. Do not combine withcard-carousel-item: that class paints its own surface over the mesh, and its.surface-papertwin forces an opaque backing that hides the mesh entirely.
Layers
.hero-mesh-base(ink) /.card-mesh-base-aqua(aqua) — static gradient. Carries the card before the shader mounts, when WebGL is unavailable, and on context loss. Neither tries to imitate the shader; it cross-fades in over them.- The live shader, imported with
ssr: falsebecause it reads its colour stops off the DOM at mount (literal hex — WebGL cannot parsecolor-mix()). Ink:HeroMeshGradientwith--hero-mesh-1…4. Aqua:FlowingGradientwith--cta-flow-1…4plus--cta-flow-2/--hero-mesh-3/--cta-flow-3for the trail. Both honour reduced motion by freezing on a formed frame (and aqua disables the trail). FlowingGradientis our own WebGL2 mount, not the paper shaders library: the original is Paper’s private WebGPU node runtime, so its five layers were reverse-engineered and rebuilt as one GLSL pass — FlowingGradient (oklab silk folds, seed 32, distortion 0.2), ChromaFlow (a 128² fluid field splatted by the cursor, advected with momentum) and FilmGrain (0.5). The Gaussian blur pass was dropped, and the original’s faint 30-cell square grid was removed site-wide (Oct 2026). Trail colours are the site’s blues rather than the original’s red/green/yellow, laid on with a normal blend instead of overlay so they show on the pale field. On the blue tone (field="dark") grain drops to 0.15 — it is near-uniform on a dark field and would otherwise read far stronger than on white..card-mesh-scrim— a 135° scrim built from--background, darkest at the two corners the copy and CTA occupy and lightest through the middle where the gradient should still show. Two host-selected variants:.card-cta-aqualightens it (30% → 0 → 20%) because the ink copy clears AA on the raw field;.card-cta-blueis directional (35% → 30% over the copy, 8% past it) so the far end of the compact card glows while white copy stays AA-safe.
Props
| Prop | Type | Default | Description |
|---|---|---|---|
tone | 'ink' | 'aqua' | 'blue' | 'navy' | 'ink' | Which shader. ink is HeroMeshGradient with the navy hero stops (--hero-mesh-1…4) over .hero-mesh-base — this primitive’s default, which CaseStudyCtaCard relies on (CTACard defaults to aqua; its rule is aqua on dark grounds, blue on light). aqua and blue are FlowingGradient — the composio.dev silk gradient with a cursor-driven liquid trail and grain (the original’s square grid is removed) — in the light --cta-flow-1…4 palette over .card-mesh-base-aqua, or the hero navy family (--hero-mesh-1…3) over .hero-mesh-base with the dark-field grain. navy is the same silk in the tint’s own family (--hero-flow-1…4, trail --hero-trail-1…3) over .hero-flow-base, with no fold highlight, a gentler trail and texture 0.2 (the grain is added in linear light, so at full strength a near-black full-width field read as static) — the page heroes’ backdrop via SectionFrame surface="hero". The scrim is built from --background, so it follows the host’s ladder; the host class picks the per-tone variant. |
speed | number | 0.7 | Mesh shader animation speed (ink tone only). Slower than the hero’s 1.2 — a card is a fraction of the hero’s area, so the same speed reads as busy behind the headline at card size. The aqua shader runs at its original pace. |
lazy | boolean | false | Hold the shader back until the card has been on screen. Use it wherever a card renders more than once per page (e.g. a component with separate desktop and mobile trees) so the hidden copy never allocates its own WebGL context. |
Import
tsx
import { MeshCardBackdrop } from '@/components/ui/MeshCardBackdrop'