Animated Border Mesh
Five blurred radial gradients spin continuously behind a CSS wrapper to reveal a glowing multicolour rim — 100% CSS, zero JavaScript.
You'll get access to the interactive demo with a free account.
This effect is part of Effect.Labs — 811 vanilla effects, some free, some premium. Backgrounds has 50 effects, including 6 free. Explore the category →
3 usage examples



How it works
The effect relies on a two-layer CSS stack inside an overflow:hidden wrapper. .border-mesh-wrapper sets padding:3px and border-radius:18px: those 3 px of padding form the visible "border", revealed because the gradient layer bleeds under the solid background of the inner content.
The .border-mesh-gradient layer is positioned with inset:-50% — it covers four times the wrapper's area — and stacks five radial-gradient calls (pink, cyan, yellow, green, purple) at offset positions and opacities. A filter:blur(15px) blends the hues into a continuous mesh gradient, then the @keyframes borderMeshSpin animation rotates the whole layer over 8 seconds in a linear infinite loop — rotating a large square surface inside a rectangle produces the apparent colour sweep along all four edges.
.border-mesh-inner stacks on top with z-index:1 and an explicit background:#0a0a0f that masks the entire gradient except the padding rim — no pseudo-elements, no SVG. The inner <span> uses background-clip:text with -webkit-text-fill-color:transparent on a linear gradient at 200% width: its background-position shifts via @keyframes borderTextShift over 4 seconds, sweeping pink/cyan/gold hues across the text itself.
Accessibility
- prefers-reduced-motion: absent from the code —
borderMeshSpinandborderTextShiftkeep running even when the OS signals a reduced-motion preference. Add@media (prefers-reduced-motion: reduce) { .border-mesh-gradient, .border-mesh-inner span { animation: none !important; } }if the element is displayed for extended periods. - Text rendered via
background-clip:textcycles dynamically (pink → cyan → gold) over a dark background: contrast varies with the animation phase and may fall below WCAG AA 4.5:1. Use plain white text (color:#fff; -webkit-text-fill-color:unset) for critical copy. - The base structure carries no
role,aria-label, or descriptive attribute. If the element conveys meaningful visual information, addrole="img"andaria-labelto the wrapper. - Keyboard navigation: the effect is 100% CSS with no JS event handlers. Interactive elements placed inside
.border-mesh-inner(buttons, links) remain accessible and maintain normal tab order. - The
.border-mesh-innerbackground is explicitly#0a0a0f— notransparentvalue, preventing readability collisions with a light parent background.
Browser compatibility
Requires the inset CSS shorthand (Chrome 87+, Firefox 87+, Safari 14.1+), filter:blur(), and background-clip:text with the -webkit- prefix already present in the code. Zero JavaScript dependency.
If <code>inset</code> or <code>filter:blur()</code> are unsupported, the wrapper displays its content without the animated border — no blocking errors, the inner content stays readable over its solid background.
The code
HTML structure to paste into your page (CSS + JS available with a premium account):
<div class="border-mesh-wrapper">
<div class="border-mesh-gradient"></div>
<div class="border-mesh-inner">
<!-- Your content here -->
<span>Animated gradient text (optional)</span>
</div>
</div>
Full HTML + CSS + JS, copy-paste ready — with hundreds of premium effects.
Customize
Options passed to the API or data-* attributes:
| Option / property | Default | Effect |
|---|---|---|
| `padding` on `.border-mesh-wrapper` | 3px | Visible border thickness. 2 px = thin elegant rim, 5–6 px = bold prominent frame. |
| `border-radius` on `.border-mesh-wrapper` | 18px | Outer corner radius. Keep `.border-mesh-inner` at outer radius minus padding to align corners (default: 18 − 3 = 15 px). |
| Colours in the `radial-gradient` calls inside `.border-mesh-gradient` | Pink, cyan, yellow, green, purple | Replace the five rgba(…) values for a cool palette (blue/cyan/green), warm palette (orange/red/gold), or brand-aligned colours. |
| `filter: blur(…)` on `.border-mesh-gradient` | 15px | Gradient blend amount. 8 px = sharp, distinct colour zones; 22 px = soft diffuse halo. |
| `animation-duration` in `borderMeshSpin` | 8s | Border rotation speed. 4 s = clearly visible movement; 20 s = near-imperceptible background sweep. |
| `width` / `height` on `.border-mesh-wrapper` | 300px / 190px | Component dimensions. The effect adapts freely: from 160×48 px for a button to 600×400 px for a large card. |
| Gradient on `.border-mesh-inner span` | #ff0080, #00c8ff, #fc0 | Clip-text gradient colours. Remove the rule entirely and add color:#fff; -webkit-text-fill-color:initial for plain white text. |
| `animation-duration` in `borderTextShift` | 4s | Text colour sweep speed. 2 s = fast colour cycling, 8 s = slow barely-perceptible shift. |
FAQ
borderMeshSpin and borderTextShift) are declared in native @keyframes, with no JS file to load and no npm dependency. Copy the HTML and CSS: the effect starts immediately in all modern browsers.width and height on .border-mesh-wrapper. The only related adjustment is the border-radius of .border-mesh-inner, which should equal the outer radius minus the padding value to align corners (default: 18 − 3 = 15 px). The rest of the effect adapts automatically thanks to inset:-50% on the gradient layer..border-mesh-inner keeps an opaque background to mask the gradient under the content. If you don't need the animated gradient text, remove the .border-mesh-inner span { background: … } rule and the borderTextShift animation entirely.