Something new is coming.Join the waitlist

Mesh Gradient Card

PreviousNext

React card with a soft, drifting mesh of blurred radial gradients. Pure CSS plus Framer Motion — no shaders, no WebGL. shadcn/ui compatible and respects prefers-reduced-motion.

Default mesh

Five tent-ui-tinted radial gradients drift behind the content. Animation pauses for users with prefers-reduced-motion.

Custom palette

Pass any 3–5 CSS colors and tune speed / blur to taste — no shader, no WebGL.

Installation

npx shadcn@latest add https://tentui.com/r/mesh-gradient-card.json

Usage

MeshGradientCard is a styled <div> — drop any content inside. The mesh is rendered behind the children, so they remain crisp and selectable.

import { MeshGradientCard } from "@/components/mesh-gradient-card";
 
export function FeatureTile() {
  return (
    <MeshGradientCard className="min-h-56">
      <div className="p-6">
        <h3 className="text-base font-semibold">Ambient</h3>
        <p className="mt-1 text-sm text-muted-foreground">
          A soft, drifting mesh that won't fight your typography.
        </p>
      </div>
    </MeshGradientCard>
  );
}

Patterns

Custom palette

Pass 3–5 CSS colors. OKLCH is recommended for predictable lightness across the wheel, but any CSS color works — including translucent values.

<MeshGradientCard
  colors={[
    "oklch(0.78 0.16 200)",
    "oklch(0.72 0.18 160)",
    "oklch(0.82 0.14 230)",
    "oklch(0.7 0.2 280)"
  ]}
>
  {children}
</MeshGradientCard>

Calmer drift

Set speed below 1 for a slower, more ambient feel — useful as a hero background or behind long-form copy.

<MeshGradientCard speed={0.4} blur={120}>{children}</MeshGradientCard>

Crisper mesh

Drop blur to keep more shape definition (useful at small sizes), or raise grain opacity by disabling and re-applying your own overlay.

<MeshGradientCard blur={40} grain={false}>{children}</MeshGradientCard>

Props

PropTypeDefaultDescription
colorsstring[]tent-ui palette3–5 CSS colors that compose the mesh. Translucent values give softer blends.
speednumber1Animation-speed multiplier. Higher is faster; 0 is effectively static.
blurnumber80Blur radius applied to the gradient layer, in pixels.
grainbooleantrueOverlay a subtle SVG noise grain on top of the mesh.
classNamestringForwarded to the outer <div>.

All other <div> props (onClick, id, style, …) pass through.

How it works

The card stacks three layers inside an overflow-hidden wrapper:

  1. Mesh layer. A pointer-events-none <div> with a large CSS blur() filter. Inside, one absolutely-positioned <span> per color renders a radial-gradient blob. Each blob is wrapped in a Framer Motion motion.span and animates x, y, and scale along a per-blob path, so the gradient layer drifts without React re-rendering.
  2. Grain layer. An inline SVG feTurbulence noise pattern blended with mix-blend-overlay for a tactile, off-screen-print feel. Toggle with the grain prop.
  3. Content layer. Your children render in a relative div on top of both layers. The outer wrapper uses bg-card so the card has a sensible theme-aware fallback when motion is disabled or the gradients haven't painted yet.

useReducedMotion from motion/react short-circuits the animation when prefers-reduced-motion: reduce is set — the blobs render in their starting frame and no requestAnimationFrame loop runs. All movement happens via GPU-accelerated transforms (x, y, scale), so a card grid of these stays smooth even on low-power devices.