Cards✨ Premium

Animated Border

Continuously sweeping gradient border on a card — two stacked CSS pseudo-elements and a background-position animation, zero JavaScript.

CSSGradientLoop

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. Cards has 41 effects, including 6 free. Explore the category →

3 usage examples

Pricing grid — Recommended plan highlighted — Animated Border example 1

① Pricing grid — Recommended plan highlighted

WhenWhen a comparative pricing table shows multiple subscription tiers and one plan needs to stand out visually without an extra badge.
WhyThe animated gradient border creates a passive visual signal that draws the eye to the recommended card — more subtle than a badge, more eye-catching than a plain static outline.
SettingsDuration 3s for more vibrancy, brand colors (#6366f1, #8b5cf6, #d946ef, #6366f1), inset -2px.
Dashboard — Premium integration or feature card — Animated Border example 2

② Dashboard — Premium integration or feature card

WhenIn a SaaS dashboard or integrations page, to signal that a block is reserved for premium accounts or deserves particular attention.
WhyThe subtle animation catches the eye without blocking interaction with the rest of the interface — more elegant than a badge or a solid color highlight.
SettingsDuration 6s for an ambient effect, cool palette (#06b6d4, #3b82f6, #6366f1, #06b6d4), inset -3px.
Blog / Editorial — Featured article card — Animated Border example 3

③ Blog / Editorial — Featured article card

WhenOn an editorial home page or content feed, to visually distinguish the most recent or most important article card.
WhyThe animated border differentiates the featured article from standard cards without any extra UI element — it blends naturally into a dark design.
SettingsDuration 5s, purple-pink gradient (#7c3aed, #db2777, #e11d48, #7c3aed), --radius-xl: 12px.

How it works

The technique uses a pseudo-element sandwich. The ::before is positioned at inset: -2px — it extends 2 px beyond every side of the card — and receives the gradient on a canvas three times wider than the element (background-size: 300% 300%). The ::after, positioned at inset: 2px, fills the interior with the card's background color (var(--bg-elevated)). Both pseudo-elements are at z-index: -1; since ::after is later in the DOM, it paints on top of ::before and masks the gradient inside. The visible ring between the outer edge of ::before and the outer edge of ::after forms the gradient border.

The @keyframes borderGradient animation slides background-position from 0 50% to 300% 50% over 4 s, linear, infinite — a horizontal sweep of the full canvas width. The gradient linear-gradient(135deg, #6366f1, #d946ef, #06b6d4, #6366f1) repeats the same indigo as first and last stop: the end-to-start transition is imperceptible, making the loop seamlessly continuous.

Corner radii are offset symmetrically: ::before gets border-radius: calc(var(--radius-xl) + 2px) and ::after gets border-radius: calc(var(--radius-xl) - 2px). This ±2 px symmetry ensures inner and outer border curves stay perfectly concentric regardless of the value of --radius-xl.

Accessibility

  • prefers-reduced-motion absent: no media query stops the animation. For functional UIs, add manually @media (prefers-reduced-motion: reduce) { .card-border-gradient::before { animation: none; } } — the border stays visible, static.
  • CSS pseudo-elements (::before, ::after) are invisible to assistive technologies — no ARIA attributes are needed on these decorative layers.
  • The host element keeps its native semantics: the effect is purely visual and modifies neither role nor tab order.
  • Text contrast depends entirely on the content placed inside the card; the animated border is decorative and does not affect the WCAG ratio.
  • No keyboard or pointer interaction in the effect itself — the host element remains focusable according to its native HTML type.

Browser compatibility

Requires CSS animations and the inset shorthand property — supported in all modern browsers since 2021.

Chrome 87+✓ Full
Firefox 87+✓ Full
Safari 14.1+✓ Full
Edge 87+✓ Full
Mobile iOS 14.5+✓ Full
Android Chrome 87+✓ Full

Without CSS animations (legacy browsers), the border stays visible but static — the first keyframe (background-position: 0 50%) is displayed. Without inset (IE), pseudo-elements are not positioned and the card shows no border.

The code

HTML structure to paste into your page (CSS + JS available with a premium account):

index.html — structure
<div class="card-border-gradient">
  <!-- Your card content -->
</div>
🔒 Unlock the full code — from €2.99 the first month

Full HTML + CSS + JS, copy-paste ready — with hundreds of premium effects.

Customize

Options passed to the API or data-* attributes:

Option / propertyDefaultEffect
--bg-elevated #1a1a24 Card and inner mask background color (::after) — change both together to keep the border visible.
--radius-xl 1rem Corner radius. ::before and ::after are recalculated automatically via calc(± 2px).
Gradient colors (::before) #6366f1, #d946ef, #06b6d4 Stops inside linear-gradient(135deg, …). Repeat the first color as the last stop for a seamless loop.
Border width 2px Change inset: -2px (::before) and inset: 2px (::after) with the same absolute value. 1px = very thin, 4px = prominent.
Animation duration 4s Value in the ::before animation property. 2s = snappy, 8s = ambient and subtle.
background-size 300% 300% Gradient canvas size. At 200% the sweep is shorter and more contrasted; at 400% the scroll is slower. Also adjust the final value in @keyframes.
Gradient angle 135deg Direction inside linear-gradient. 90deg = pure horizontal sweep, 45deg = rising diagonal.

FAQ

No, the effect is 100% CSS — no script, no npm dependency. Simply add the class card-border-gradient to your card and the animation starts automatically. Compatible with any framework (React, Vue, Angular, Svelte) without adaptation.
Modify the stops inside linear-gradient(135deg, …) in the ::before. For a visually continuous loop, repeat the first color as the last stop — for example linear-gradient(135deg, #f59e0b, #ef4444, #8b5cf6, #f59e0b) for a warm palette.
The base effect does not include a prefers-reduced-motion media query. To respect the system preference, add to your CSS: @media (prefers-reduced-motion: reduce) { .card-border-gradient::before { animation: none; } }. The gradient border stays visible, but static.