Border Gradient
A 2 px indigo-to-violet gradient border fades in on hover using two stacked pseudo-elements — no transform, pure CSS.
Updated
This effect is part of Effect.Labs — 840 vanilla effects, some free, some premium. Hover has 48 effects, including 5 free. Explore the category →
3 usage examples



How it works
The .fx-0345-root wrapper centres a 200 px button. The technique uses two pseudo-elements: ::before places a gradient background (135°, indigo → violet) in negative (inset: -2px) behind the element, creating a 2 px overhang on all sides; ::after places a solid card-colour background in positive (inset: 2px), masking the interior. At rest, ::before is at opacity: 0. On hover, it transitions to 1 over 0.3 s. The gradient-border illusion works because only the 2 px gap between the two pseudo-elements is visible.
The element itself carries border: 2px solid transparent to reserve space. z-index: -1 on both pseudo-elements places them behind the content. :focus-visible triggers the same ::before opacity as hover, with an indigo outline added. Pure CSS, no JavaScript. Under reduced motion, the transition is disabled but the border appears instantly.
Accessibility
- Reduced motion: under
prefers-reduced-motion: reduce, theopacitytransition is disabled. The gradient border appears instantly on hover and focus — state feedback is guaranteed. - Keyboard: the element is a natively focusable
<button>.:focus-visibletriggers the border and adds a 2 px outline offset by 4 px, stacking two visual signals for keyboard focus. - Touch: a tap activates the hover state on most mobile browsers. The border stays visible until focus moves.
- Border contrast: on white backgrounds, the gradient border may lack contrast depending on the colours. Increase the thickness (
inset: -3px) or adjust hues to maintain a WCAG ≥ 3:1 ratio against the background.
Browser compatibility
Pure CSS. Properties: ::before/::after, inset shorthand, opacity, transition, negative z-index, :focus-visible. Verified in Chromium and Firefox: border visible, two independent copies, 0 errors. inset shorthand requires Chrome 87+, Firefox 87+, Safari 14.1+.
Without inset shorthand (before 2021), use top: -2px; right: -2px; bottom: -2px; left: -2px. Without :focus-visible, keyboard focus does not trigger the border.
The code
HTML structure to paste into your page (the CSS + JS come with your purchase):
<div class="fx-0345-root">
<button class="gbd-box gbd-border" type="button">
<span>Your text</span>
</button>
</div>
One-time payment, no subscription — the code is yours forever
Customize
Options passed to the API or data-* attributes:
| Option / property | Default | Effect |
|---|---|---|
Border colours (CSS — linear-gradient(135deg, #6366f1, #d946ef)) |
indigo → violet | Replace both colours to match your brand. Add intermediate stops for a rainbow effect. |
Border thickness (CSS — inset: -2px and inset: 2px) |
2 px | Switching to -3px and 3px gives a 3 px border. Both values must match in absolute value. |
Duration (CSS — 0.3s) |
0.3 s | 0.15 s is snappier; 0.5 s is softer. |
Gradient angle (CSS — 135deg) |
135° (diagonal) | 90deg gives a horizontal gradient; to right or to bottom are equally readable. |
Corner radius of pseudo-element (CSS — calc(var(--radius-xl) + 2px)) |
card radius + 2 px | Use calc() to auto-adjust if you change the card's radius. |
FAQ
border-image combined with border-radius: rounded corners and native gradient borders are incompatible. The two stacked pseudo-elements work around this limitation.z-index: -1 on the pseudo-elements places them behind the card background. If your card has an explicit positive z-index, add isolation: isolate to the card to create a new stacking context.opacity transition with a @keyframes that animates background-position on a wider gradient (conic-gradient or large linear gradient). That technique is outside the scope of this effect, which is hover-only.