15 CSS Glowing Border Buttons03 / 15

Pure CSSMIT licensed

Pure CSS Animated Border Trace Button

A single comet of light sweeps the button's perimeter on hover — no SVG, no JavaScript. A rotating conic-gradient wedge is clipped to a 2px border ring with the mask-composite trick, so the beam hugs any border-radius perfectly. The go-to 'expensive detail' for portfolio and dev-tool sites.

Published

Live Demo
Try it

The code

<section class="gbb-03" aria-label="Animated border trace button demo">
  <a class="gbb-03__btn" href="#">View Case Study<span class="gbb-03__arrow" aria-hidden="true">&rarr;</span></a>
</section>
.gbb-03,
.gbb-03 *,
.gbb-03 *::before,
.gbb-03 *::after {
  box-sizing: border-box;
  margin: 0;
  padding: 0;
}

@property --gbb-a3 {
  syntax: '<angle>';
  inherits: false;
  initial-value: 0deg;
}

.gbb-03 {
  --beam: oklch(0.85 0.12 200);
  font-family: 'Segoe UI',system-ui,sans-serif;
  width: 100%;
  min-height: 100vh;
  display: grid;
  place-items: center;
  padding: 48px 24px;
  background: linear-gradient(180deg,oklch(0.16 0.015 260),oklch(0.12 0.01 260));
}

.gbb-03 .gbb-03__btn {
  position: relative;
  isolation: isolate;
  display: inline-flex;
  align-items: center;
  gap: 10px;
  padding: 16px 30px;
  border-radius: 14px;
  border: 1px solid rgba(255,255,255,.14);
  background: oklch(0.18 0.015 260);
  font: 600 15px/1 'Segoe UI',system-ui,sans-serif;
  color: #f2f5f8;
  text-decoration: none;
  transition: border-color .3s ease;
}

.gbb-03 .gbb-03__btn::before,
.gbb-03 .gbb-03__btn::after {
  content: "";
  position: absolute;
  inset: -1px;
  border-radius: inherit;
  padding: 2px;
  background: conic-gradient(from var(--gbb-a3),transparent 0 82%,color-mix(in oklab,var(--beam) 45%,transparent) 88%,var(--beam) 93%,transparent 97%);
  -webkit-mask: linear-gradient(#000 0 0) content-box,linear-gradient(#000 0 0);
  mask: linear-gradient(#000 0 0) content-box,linear-gradient(#000 0 0);
  -webkit-mask-composite: xor;
  mask-composite: exclude;
  opacity: 0;
  pointer-events: none;
  transition: opacity .3s ease;
}

.gbb-03 .gbb-03__btn::after {
  filter: blur(8px);
  inset: -3px;
  padding: 4px;
}

.gbb-03 .gbb-03__btn:hover,
.gbb-03 .gbb-03__btn:focus-visible {
  border-color: rgba(255,255,255,.22);
}

.gbb-03 .gbb-03__btn:hover::before,
.gbb-03 .gbb-03__btn:hover::after,
.gbb-03 .gbb-03__btn:focus-visible::before,
.gbb-03 .gbb-03__btn:focus-visible::after {
  opacity: 1;
  animation: gbb03-lap 1.4s linear infinite;
}

.gbb-03 .gbb-03__btn:focus-visible {
  outline: 2px solid var(--beam);
  outline-offset: 4px;
}

.gbb-03 .gbb-03__arrow {
  transition: translate .3s ease;
}

.gbb-03 .gbb-03__btn:hover .gbb-03__arrow {
  translate: 4px 0;
}

@keyframes gbb03-lap {
  to {
    --gbb-a3: 360deg;
  }
}

@media (prefers-reduced-motion: reduce) {
  .gbb-03 .gbb-03__btn:hover::before,
    .gbb-03 .gbb-03__btn:hover::after,
    .gbb-03 .gbb-03__btn:focus-visible::before,
    .gbb-03 .gbb-03__btn:focus-visible::after {
    animation: none;
    opacity: .6;
  }
}
/* No JavaScript — a conic-gradient comet wedge is clipped to a 2px ring with mask-composite:exclude and spun via an @property angle. */
Paste this into ChatGPT, Claude, Cursor, or any coding assistant. The block below is pre-framed with everything the AI needs to integrate this demo into your project — markup, styles, scoping notes, and the source URL. Hit Copy and paste straight into your chat.
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: Pure CSS Animated Border Trace Button
Source: https://codefronts.com/components/css-glowing-border-buttons/pure-css-animated-border-trace-button/

A single comet of light sweeps the button's perimeter on hover — no SVG, no JavaScript. A rotating conic-gradient wedge is clipped to a 2px border ring with the mask-composite trick, so the beam hugs any border-radius perfectly. The go-to 'expensive detail' for portfolio and dev-tool sites.
## HTML
```html
<section class="gbb-03" aria-label="Animated border trace button demo">
  <a class="gbb-03__btn" href="#">View Case Study<span class="gbb-03__arrow" aria-hidden="true">&rarr;</span></a>
</section>
```
## CSS
```css
.gbb-03,
.gbb-03 *,
.gbb-03 *::before,
.gbb-03 *::after {
  box-sizing: border-box;
  margin: 0;
  padding: 0;
}

@property --gbb-a3 {
  syntax: '<angle>';
  inherits: false;
  initial-value: 0deg;
}

.gbb-03 {
  --beam: oklch(0.85 0.12 200);
  font-family: 'Segoe UI',system-ui,sans-serif;
  width: 100%;
  min-height: 100vh;
  display: grid;
  place-items: center;
  padding: 48px 24px;
  background: linear-gradient(180deg,oklch(0.16 0.015 260),oklch(0.12 0.01 260));
}

.gbb-03 .gbb-03__btn {
  position: relative;
  isolation: isolate;
  display: inline-flex;
  align-items: center;
  gap: 10px;
  padding: 16px 30px;
  border-radius: 14px;
  border: 1px solid rgba(255,255,255,.14);
  background: oklch(0.18 0.015 260);
  font: 600 15px/1 'Segoe UI',system-ui,sans-serif;
  color: #f2f5f8;
  text-decoration: none;
  transition: border-color .3s ease;
}

.gbb-03 .gbb-03__btn::before,
.gbb-03 .gbb-03__btn::after {
  content: "";
  position: absolute;
  inset: -1px;
  border-radius: inherit;
  padding: 2px;
  background: conic-gradient(from var(--gbb-a3),transparent 0 82%,color-mix(in oklab,var(--beam) 45%,transparent) 88%,var(--beam) 93%,transparent 97%);
  -webkit-mask: linear-gradient(#000 0 0) content-box,linear-gradient(#000 0 0);
  mask: linear-gradient(#000 0 0) content-box,linear-gradient(#000 0 0);
  -webkit-mask-composite: xor;
  mask-composite: exclude;
  opacity: 0;
  pointer-events: none;
  transition: opacity .3s ease;
}

.gbb-03 .gbb-03__btn::after {
  filter: blur(8px);
  inset: -3px;
  padding: 4px;
}

.gbb-03 .gbb-03__btn:hover,
.gbb-03 .gbb-03__btn:focus-visible {
  border-color: rgba(255,255,255,.22);
}

.gbb-03 .gbb-03__btn:hover::before,
.gbb-03 .gbb-03__btn:hover::after,
.gbb-03 .gbb-03__btn:focus-visible::before,
.gbb-03 .gbb-03__btn:focus-visible::after {
  opacity: 1;
  animation: gbb03-lap 1.4s linear infinite;
}

.gbb-03 .gbb-03__btn:focus-visible {
  outline: 2px solid var(--beam);
  outline-offset: 4px;
}

.gbb-03 .gbb-03__arrow {
  transition: translate .3s ease;
}

.gbb-03 .gbb-03__btn:hover .gbb-03__arrow {
  translate: 4px 0;
}

@keyframes gbb03-lap {
  to {
    --gbb-a3: 360deg;
  }
}

@media (prefers-reduced-motion: reduce) {
  .gbb-03 .gbb-03__btn:hover::before,
    .gbb-03 .gbb-03__btn:hover::after,
    .gbb-03 .gbb-03__btn:focus-visible::before,
    .gbb-03 .gbb-03__btn:focus-visible::after {
    animation: none;
    opacity: .6;
  }
}
```

## JavaScript
```js
/* No JavaScript — a conic-gradient comet wedge is clipped to a 2px ring with mask-composite:exclude and spun via an @property angle. */
```

How this works

Two techniques stacked. First, the ring: an overlay ::before covers the button, gets padding:2px, and is masked with two stacked layers — mask: linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0) — combined with mask-composite:exclude. The content-box layer punches out the middle, leaving exactly the 2px band between padding edge and outer edge. This is the modern replacement for nested border wrappers, and it follows border-radius for free.

Second, the comet: the ring's background is conic-gradient(from var(--gbb-a), transparent 0 82%, var(--beam) 92%, transparent 97%) — mostly transparent with one bright 15% wedge whose leading edge is sharp and trailing edge fades, i.e. a tail. Animating the registered --gbb-a spins the wedge around the ring. A second blurred pseudo-element re-uses the same gradient as the beam's outer glow. The animation only runs on :hover/:focus-visible, so the page at rest costs nothing.

Make it yours

  • Trace speed: 1.2s per lap feels snappy; 2.5s feels like a scanner. Set it on the animation shorthand.
  • Two comets: duplicate the wedge 180° apart inside the same conic-gradient stop list.
  • Leave a faint static ring at rest by giving the ring layer a low-opacity base color behind the wedge.
  • Beam color is one variable (--beam) — match it to your accent, or use white for a 'light on metal' look.

Gotchas — read before shipping

  • Write both -webkit-mask and mask; Safari still needs the prefix and silently shows a filled rectangle without it.
  • mask-composite:exclude (standard) pairs with -webkit-mask-composite:xor — the keywords differ between the two syntaxes.
  • The overlay must be pointer-events:none or hovering the border stutters the trace.

Browser support

ChromeSafariFirefoxEdge
120+15.4+128+120+

Gated by mask-composite + @property together. No @property → the comet jumps instead of gliding; no mask → show a plain border fallback.

Techniques used in this demo

Search CodeFronts

Loading…