Mesh Gradient Background
Three blurred radial pools over an opaque base - a mesh section that never has to fight its own text for contrast.
6 frameworksBeginner
Blurred colour blobs drifting on twenty-second loops behind ordinary, fully legible content.
<!--
Tailwind has no utility that declares @keyframes, so the drift lives in a
<style> block. Both keyframes animate transform ONLY - translate and scale.
The blur is the expensive part of a blob, and a transform-only animation lets
the browser rasterise it once and move the bitmap on the compositor;
animating left/top or width would re-run the blur every frame.
-->
<style>
@keyframes blob-drift-a {
0%, 100% { transform: translate3d(0, 0, 0) scale(1); }
50% { transform: translate3d(2.5rem, -2rem, 0) scale(1.15); }
}
@keyframes blob-drift-b {
0%, 100% { transform: translate3d(0, 0, 0) scale(1); }
50% { transform: translate3d(-2rem, 2rem, 0) scale(0.9); }
}
</style>
<section class="relative isolate w-full overflow-hidden rounded-2xl bg-white dark:bg-gray-950">
<div class="absolute inset-0 -z-10" aria-hidden="true">
<div class="absolute -left-16 -top-16 h-56 w-56 rounded-full bg-[radial-gradient(circle_at_center,rgba(59,130,246,0.35),transparent_70%)] blur-2xl animate-[blob-drift-a_18s_ease-in-out_infinite] motion-reduce:animate-none sm:h-80 sm:w-80"></div>
<div class="absolute -bottom-20 -right-16 h-56 w-56 rounded-full bg-[radial-gradient(circle_at_center,rgba(217,70,239,0.3),transparent_70%)] blur-2xl animate-[blob-drift-b_24s_ease-in-out_infinite] motion-reduce:animate-none sm:h-80 sm:w-80"></div>
<div class="absolute left-1/3 top-1/2 h-48 w-48 rounded-full bg-[radial-gradient(circle_at_center,rgba(34,211,238,0.25),transparent_70%)] blur-2xl animate-[blob-drift-a_28s_ease-in-out_-9s_infinite] motion-reduce:animate-none sm:h-64 sm:w-64"></div>
</div>
<div class="px-6 py-14 text-center sm:px-8 sm:py-20">
<h2 class="text-2xl font-bold leading-tight tracking-tight text-gray-900 sm:text-4xl dark:text-gray-100">
Calm on the surface
</h2>
<p class="mx-auto mt-4 max-w-xl text-base leading-relaxed text-gray-600 dark:text-gray-400">
Three blobs drifting on twenty-second loops - slow enough to feel like
weather, not like a screensaver.
</p>
</div>
</section>| Prop | Type | Default | Description |
|---|---|---|---|
titlerequired | string | - | Heading text for the card. |
copy | string | - | Body text shown under the title. |
className | string | - | Additional classes merged onto the root element. |
The drift animates `transform` only - translate and scale. That is not a style choice: the blur is the expensive part of a blob, and a transform-only animation lets the browser rasterise each blob once and move the bitmap on the compositor, where animating `left`/`top` or `width` would re-run the blur every frame. Keep any new keyframes inside that budget. Durations are 18-28s with a negative delay desynchronising the third blob; halving them turns weather into a screensaver. `motion-reduce:animate-none` freezes the blobs in place and keeps the colour - the motion is the decoration, not the design. Contrast works like the mesh: low-opacity blobs over an opaque base, so the text never depends on where a blob happens to be.