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-hidden with a radius — the shader canvas is a rectangle and squares off corners otherwise.
  • Content needs relative z-10 to sit above the three layers.
  • The host carries the surface class that matches the tone — surface-ink for ink, so its copy keeps the dark ladder on a paper ground; surface-paper surface-white for aqua, so its copy is ink and the scrim resolves to white on the dark page. The scrim reads --background from the host.
  • Pair with card-cta (ink), card-cta-aqua (aqua) or card-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 with card-carousel-item: that class paints its own surface over the mesh, and its .surface-paper twin 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: false because it reads its colour stops off the DOM at mount (literal hex — WebGL cannot parse color-mix()). Ink: HeroMeshGradient with --hero-mesh-1…4. Aqua: FlowingGradient with --cta-flow-1…4 plus --cta-flow-2 / --hero-mesh-3 / --cta-flow-3 for the trail. Both honour reduced motion by freezing on a formed frame (and aqua disables the trail).
  • FlowingGradient is 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-aqua lightens it (30% → 0 → 20%) because the ink copy clears AA on the raw field; .card-cta-blue is 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

PropTypeDefaultDescription
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.
speednumber0.7Mesh 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.
lazybooleanfalseHold 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'