14 / 15
All 15 in 15 CSS Glowing Border Buttons
- 01Rotating Rainbow Gradient Glowing Border Button
- 02Neon Pulse Glow Button on Hover
- 03Pure CSS Animated Border Trace Button
- 04Cyberpunk Sci-Fi Glowing Corner Border Button
- 05Dark Mode Glowing Input & Submit Focus States
- 06Tailwind CSS Glowing Gradient Border Button (Recipe)
- 07Glassmorphism Button with Subtle Glow Border
- 08Minimalist Outer Glow Box-Shadow Button
- 09Multi-Layer Breathing Glow Pulse Button
- 10Inner Glow Border Toggle Button (Pressed State)
- 11Subtle Soft Glow Dark Theme Button
- 12SVG Animated Path Glowing Stroke Button
- 13Retro 80s Synthwave Dual Neon Glowing Button
- 14Smooth Gradient Hue-Shift Border Button
- 15Accessible Glowing Focus State Button (WCAG-Ready)
Smooth Gradient Hue-Shift Border Button
The 'Most Popular' plan highlighter: the button's border and halo drift continuously through the color wheel — violet to cyan to rose and back — by animating a single registered hue number through oklch(). Because only hue moves (lightness and chroma pinned), every frame is equally vivid and the label never loses contrast.
Build interactive UI like this from scratch: 20 HTML, CSS & JavaScript projects
ZTM · JavaScript Web Projects · 30‑day money-back guarantee · Affiliate link
The code
<section class="gbb-14" aria-label="Hue shifting gradient border button demo">
<div class="gbb-14__plan">
<span class="gbb-14__badge"><span class="gbb-14__dot" aria-hidden="true"></span>Most Popular</span>
<button class="gbb-14__btn" type="button">Choose Pro — $19/mo</button>
</div>
</section><section class="gbb-14" aria-label="Hue shifting gradient border button demo">
<div class="gbb-14__plan">
<span class="gbb-14__badge"><span class="gbb-14__dot" aria-hidden="true"></span>Most Popular</span>
<button class="gbb-14__btn" type="button">Choose Pro — $19/mo</button>
</div>
</section>.gbb-14,
.gbb-14 *,
.gbb-14 *::before,
.gbb-14 *::after {
box-sizing: border-box;
margin: 0;
padding: 0;
}
@property --gbb-h {
syntax: '<number>';
inherits: true;
initial-value: 280;
}
.gbb-14 {
font-family: 'Segoe UI',system-ui,sans-serif;
width: 100%;
min-height: 100vh;
display: grid;
place-items: center;
padding: 48px 24px;
background: oklch(0.14 0.015 280);
animation: gbb14-drift 6s linear infinite;
}
.gbb-14 .gbb-14__plan {
display: grid;
justify-items: center;
gap: 14px;
}
.gbb-14 .gbb-14__badge {
display: inline-flex;
align-items: center;
gap: 7px;
font: 700 11px/1 'Segoe UI',system-ui,sans-serif;
letter-spacing: .09em;
text-transform: uppercase;
color: rgba(255,255,255,.85);
border: 1px solid oklch(0.7 0.19 calc(var(--gbb-h) * 1deg) / .4);
border-radius: 999px;
padding: 6px 13px;
}
.gbb-14 .gbb-14__dot {
width: 7px;
height: 7px;
border-radius: 50%;
background: oklch(0.75 0.19 calc(var(--gbb-h) * 1deg));
box-shadow: 0 0 8px oklch(0.75 0.19 calc(var(--gbb-h) * 1deg));
}
.gbb-14 .gbb-14__btn {
cursor: pointer;
border: 2px solid transparent;
border-radius: 14px;
padding: 16px 32px;
font: 700 15px/1 'Segoe UI',system-ui,sans-serif;
color: #fff;
background: linear-gradient(oklch(0.18 0.02 280),oklch(0.18 0.02 280)) padding-box,linear-gradient(120deg,oklch(0.7 0.19 calc(var(--gbb-h) * 1deg)),oklch(0.7 0.19 calc((var(--gbb-h) + 60) * 1deg))) border-box;
box-shadow: 0 0 22px oklch(0.7 0.19 calc(var(--gbb-h) * 1deg) / .3);
transition: box-shadow .3s ease,transform .25s ease;
}
.gbb-14 .gbb-14__btn:hover,
.gbb-14 .gbb-14__btn:focus-visible {
transform: translateY(-2px);
box-shadow: 0 0 38px oklch(0.7 0.19 calc(var(--gbb-h) * 1deg) / .5),0 0 0 4px oklch(0.7 0.19 calc(var(--gbb-h) * 1deg) / .18);
}
.gbb-14:has(.gbb-14__btn:hover),
.gbb-14:has(.gbb-14__btn:focus-visible) {
animation-duration: 2.5s;
}
.gbb-14 .gbb-14__btn:focus-visible {
outline: 2px solid #fff;
outline-offset: 4px;
}
.gbb-14 .gbb-14__btn:active {
transform: translateY(0) scale(.98);
}
@keyframes gbb14-drift {
from {
--gbb-h: 0;
}
to {
--gbb-h: 360;
}
}
@media (prefers-reduced-motion: reduce) {
.gbb-14 {
animation: none;
}
}.gbb-14,
.gbb-14 *,
.gbb-14 *::before,
.gbb-14 *::after {
box-sizing: border-box;
margin: 0;
padding: 0;
}
@property --gbb-h {
syntax: '<number>';
inherits: true;
initial-value: 280;
}
.gbb-14 {
font-family: 'Segoe UI',system-ui,sans-serif;
width: 100%;
min-height: 100vh;
display: grid;
place-items: center;
padding: 48px 24px;
background: oklch(0.14 0.015 280);
animation: gbb14-drift 6s linear infinite;
}
.gbb-14 .gbb-14__plan {
display: grid;
justify-items: center;
gap: 14px;
}
.gbb-14 .gbb-14__badge {
display: inline-flex;
align-items: center;
gap: 7px;
font: 700 11px/1 'Segoe UI',system-ui,sans-serif;
letter-spacing: .09em;
text-transform: uppercase;
color: rgba(255,255,255,.85);
border: 1px solid oklch(0.7 0.19 calc(var(--gbb-h) * 1deg) / .4);
border-radius: 999px;
padding: 6px 13px;
}
.gbb-14 .gbb-14__dot {
width: 7px;
height: 7px;
border-radius: 50%;
background: oklch(0.75 0.19 calc(var(--gbb-h) * 1deg));
box-shadow: 0 0 8px oklch(0.75 0.19 calc(var(--gbb-h) * 1deg));
}
.gbb-14 .gbb-14__btn {
cursor: pointer;
border: 2px solid transparent;
border-radius: 14px;
padding: 16px 32px;
font: 700 15px/1 'Segoe UI',system-ui,sans-serif;
color: #fff;
background: linear-gradient(oklch(0.18 0.02 280),oklch(0.18 0.02 280)) padding-box,linear-gradient(120deg,oklch(0.7 0.19 calc(var(--gbb-h) * 1deg)),oklch(0.7 0.19 calc((var(--gbb-h) + 60) * 1deg))) border-box;
box-shadow: 0 0 22px oklch(0.7 0.19 calc(var(--gbb-h) * 1deg) / .3);
transition: box-shadow .3s ease,transform .25s ease;
}
.gbb-14 .gbb-14__btn:hover,
.gbb-14 .gbb-14__btn:focus-visible {
transform: translateY(-2px);
box-shadow: 0 0 38px oklch(0.7 0.19 calc(var(--gbb-h) * 1deg) / .5),0 0 0 4px oklch(0.7 0.19 calc(var(--gbb-h) * 1deg) / .18);
}
.gbb-14:has(.gbb-14__btn:hover),
.gbb-14:has(.gbb-14__btn:focus-visible) {
animation-duration: 2.5s;
}
.gbb-14 .gbb-14__btn:focus-visible {
outline: 2px solid #fff;
outline-offset: 4px;
}
.gbb-14 .gbb-14__btn:active {
transform: translateY(0) scale(.98);
}
@keyframes gbb14-drift {
from {
--gbb-h: 0;
}
to {
--gbb-h: 360;
}
}
@media (prefers-reduced-motion: reduce) {
.gbb-14 {
animation: none;
}
}/* No JavaScript — one registered number (--gbb-h) animates 0→360; every oklch() color on the button derives its hue from it via calc(). */
/* No JavaScript — one registered number (--gbb-h) animates 0→360; every oklch() color on the button derives its hue from it via calc(). */Here's a working CSS Glowing Border Button from CodeFronts. Use it as-is or adapt to your framework. All classes are scoped under a unique prefix so the code won't collide with your existing styles. MIT licensed.
Demo: Smooth Gradient Hue-Shift Border Button
Source: https://codefronts.com/components/css-glowing-border-buttons/smooth-gradient-hue-shift-border-button/
The 'Most Popular' plan highlighter: the button's border and halo drift continuously through the color wheel — violet to cyan to rose and back — by animating a single registered hue number through oklch(). Because only hue moves (lightness and chroma pinned), every frame is equally vivid and the label never loses contrast.
## HTML
```html
<section class="gbb-14" aria-label="Hue shifting gradient border button demo">
<div class="gbb-14__plan">
<span class="gbb-14__badge"><span class="gbb-14__dot" aria-hidden="true"></span>Most Popular</span>
<button class="gbb-14__btn" type="button">Choose Pro — $19/mo</button>
</div>
</section>
```
## CSS
```css
.gbb-14,
.gbb-14 *,
.gbb-14 *::before,
.gbb-14 *::after {
box-sizing: border-box;
margin: 0;
padding: 0;
}
@property --gbb-h {
syntax: '<number>';
inherits: true;
initial-value: 280;
}
.gbb-14 {
font-family: 'Segoe UI',system-ui,sans-serif;
width: 100%;
min-height: 100vh;
display: grid;
place-items: center;
padding: 48px 24px;
background: oklch(0.14 0.015 280);
animation: gbb14-drift 6s linear infinite;
}
.gbb-14 .gbb-14__plan {
display: grid;
justify-items: center;
gap: 14px;
}
.gbb-14 .gbb-14__badge {
display: inline-flex;
align-items: center;
gap: 7px;
font: 700 11px/1 'Segoe UI',system-ui,sans-serif;
letter-spacing: .09em;
text-transform: uppercase;
color: rgba(255,255,255,.85);
border: 1px solid oklch(0.7 0.19 calc(var(--gbb-h) * 1deg) / .4);
border-radius: 999px;
padding: 6px 13px;
}
.gbb-14 .gbb-14__dot {
width: 7px;
height: 7px;
border-radius: 50%;
background: oklch(0.75 0.19 calc(var(--gbb-h) * 1deg));
box-shadow: 0 0 8px oklch(0.75 0.19 calc(var(--gbb-h) * 1deg));
}
.gbb-14 .gbb-14__btn {
cursor: pointer;
border: 2px solid transparent;
border-radius: 14px;
padding: 16px 32px;
font: 700 15px/1 'Segoe UI',system-ui,sans-serif;
color: #fff;
background: linear-gradient(oklch(0.18 0.02 280),oklch(0.18 0.02 280)) padding-box,linear-gradient(120deg,oklch(0.7 0.19 calc(var(--gbb-h) * 1deg)),oklch(0.7 0.19 calc((var(--gbb-h) + 60) * 1deg))) border-box;
box-shadow: 0 0 22px oklch(0.7 0.19 calc(var(--gbb-h) * 1deg) / .3);
transition: box-shadow .3s ease,transform .25s ease;
}
.gbb-14 .gbb-14__btn:hover,
.gbb-14 .gbb-14__btn:focus-visible {
transform: translateY(-2px);
box-shadow: 0 0 38px oklch(0.7 0.19 calc(var(--gbb-h) * 1deg) / .5),0 0 0 4px oklch(0.7 0.19 calc(var(--gbb-h) * 1deg) / .18);
}
.gbb-14:has(.gbb-14__btn:hover),
.gbb-14:has(.gbb-14__btn:focus-visible) {
animation-duration: 2.5s;
}
.gbb-14 .gbb-14__btn:focus-visible {
outline: 2px solid #fff;
outline-offset: 4px;
}
.gbb-14 .gbb-14__btn:active {
transform: translateY(0) scale(.98);
}
@keyframes gbb14-drift {
from {
--gbb-h: 0;
}
to {
--gbb-h: 360;
}
}
@media (prefers-reduced-motion: reduce) {
.gbb-14 {
animation: none;
}
}
```
## JavaScript
```js
/* No JavaScript — one registered number (--gbb-h) animates 0→360; every oklch() color on the button derives its hue from it via calc(). */
```Here's a working CSS Glowing Border Button from CodeFronts. Use it as-is or adapt to your framework. All classes are scoped under a unique prefix so the code won't collide with your existing styles. MIT licensed.
Demo: Smooth Gradient Hue-Shift Border Button
Source: https://codefronts.com/components/css-glowing-border-buttons/smooth-gradient-hue-shift-border-button/
The 'Most Popular' plan highlighter: the button's border and halo drift continuously through the color wheel — violet to cyan to rose and back — by animating a single registered hue number through oklch(). Because only hue moves (lightness and chroma pinned), every frame is equally vivid and the label never loses contrast.
## HTML
```html
<section class="gbb-14" aria-label="Hue shifting gradient border button demo">
<div class="gbb-14__plan">
<span class="gbb-14__badge"><span class="gbb-14__dot" aria-hidden="true"></span>Most Popular</span>
<button class="gbb-14__btn" type="button">Choose Pro — $19/mo</button>
</div>
</section>
```
## CSS
```css
.gbb-14,
.gbb-14 *,
.gbb-14 *::before,
.gbb-14 *::after {
box-sizing: border-box;
margin: 0;
padding: 0;
}
@property --gbb-h {
syntax: '<number>';
inherits: true;
initial-value: 280;
}
.gbb-14 {
font-family: 'Segoe UI',system-ui,sans-serif;
width: 100%;
min-height: 100vh;
display: grid;
place-items: center;
padding: 48px 24px;
background: oklch(0.14 0.015 280);
animation: gbb14-drift 6s linear infinite;
}
.gbb-14 .gbb-14__plan {
display: grid;
justify-items: center;
gap: 14px;
}
.gbb-14 .gbb-14__badge {
display: inline-flex;
align-items: center;
gap: 7px;
font: 700 11px/1 'Segoe UI',system-ui,sans-serif;
letter-spacing: .09em;
text-transform: uppercase;
color: rgba(255,255,255,.85);
border: 1px solid oklch(0.7 0.19 calc(var(--gbb-h) * 1deg) / .4);
border-radius: 999px;
padding: 6px 13px;
}
.gbb-14 .gbb-14__dot {
width: 7px;
height: 7px;
border-radius: 50%;
background: oklch(0.75 0.19 calc(var(--gbb-h) * 1deg));
box-shadow: 0 0 8px oklch(0.75 0.19 calc(var(--gbb-h) * 1deg));
}
.gbb-14 .gbb-14__btn {
cursor: pointer;
border: 2px solid transparent;
border-radius: 14px;
padding: 16px 32px;
font: 700 15px/1 'Segoe UI',system-ui,sans-serif;
color: #fff;
background: linear-gradient(oklch(0.18 0.02 280),oklch(0.18 0.02 280)) padding-box,linear-gradient(120deg,oklch(0.7 0.19 calc(var(--gbb-h) * 1deg)),oklch(0.7 0.19 calc((var(--gbb-h) + 60) * 1deg))) border-box;
box-shadow: 0 0 22px oklch(0.7 0.19 calc(var(--gbb-h) * 1deg) / .3);
transition: box-shadow .3s ease,transform .25s ease;
}
.gbb-14 .gbb-14__btn:hover,
.gbb-14 .gbb-14__btn:focus-visible {
transform: translateY(-2px);
box-shadow: 0 0 38px oklch(0.7 0.19 calc(var(--gbb-h) * 1deg) / .5),0 0 0 4px oklch(0.7 0.19 calc(var(--gbb-h) * 1deg) / .18);
}
.gbb-14:has(.gbb-14__btn:hover),
.gbb-14:has(.gbb-14__btn:focus-visible) {
animation-duration: 2.5s;
}
.gbb-14 .gbb-14__btn:focus-visible {
outline: 2px solid #fff;
outline-offset: 4px;
}
.gbb-14 .gbb-14__btn:active {
transform: translateY(0) scale(.98);
}
@keyframes gbb14-drift {
from {
--gbb-h: 0;
}
to {
--gbb-h: 360;
}
}
@media (prefers-reduced-motion: reduce) {
.gbb-14 {
animation: none;
}
}
```
## JavaScript
```js
/* No JavaScript — one registered number (--gbb-h) animates 0→360; every oklch() color on the button derives its hue from it via calc(). */
```How this works
Instead of animating a giant multi-stop gradient background-position (the old way — bandy and memory-hungry), this registers @property --gbb-h { syntax:'<number>' } and animates just that number 0→360. Every color on the button derives from it: border gradient stops at oklch(0.7 0.19 calc(var(--gbb-h) * 1deg)) and a second stop offset +60°, the halo shadow, even the badge dot. One animation, total palette coherence.
The oklch choice is the modern half of the lesson: in HSL, rotating hue at fixed s/l swings wildly through perceived brightness (yellow blinds, blue goes dark). OKLCH is perceptually uniform — pin L=0.7, C=0.19 and every hue lands at the same visual weight, so the border never has a 'weak frame'. The border itself is the padding-box/border-box gradient pair (same trick as demo 07), and hover simply widens the halo and speeds the drift from 6s to 2.5s — urgency through tempo, not size.
Make it yours
- Constrain the drift to a brand arc by animating 250→330 instead of 0→360 (alternate direction) for an on-palette shimmer.
- The +60° second stop sets gradient 'spread' — +180° gives a two-tone opposing border.
- Tempo: 6s rest / 2.5s hover shown; enterprise pricing pages read best at 10s+.
- Reuse --gbb-h on the parent card's border for a matched set highlighting the whole tier.
Gotchas — read before shipping
- syntax:'<number>' + calc(var() * 1deg) — animating a raw '<angle>' also works, but numbers let you reuse the value in non-angle contexts.
- Without @property registration, the hue snaps once per loop instead of drifting — the same failure mode as demo 01.
- Keep lightness ≥0.65 for dark themes; below that some hues fail 3:1 non-text contrast against near-black.
Browser support
| Chrome | Safari | Firefox | Edge |
|---|---|---|---|
| 111+ | 16.4+ | 128+ | 111+ |
Floor set by :has(), oklch(), @property as this demo is written. The core technique itself goes back further — Chrome 85+.
@property + oklch() gate. Fallback: a static two-color gradient border — set the custom property's initial-value and skip the animation.