24 CSS Tags & Chips12 / 24
Glowing Aurora Gradient Border Tag
The border every AI product shipped in 2025, taken apart properly: a multi-stop conic gradient rotating through a registered @property angle, carved into a ring with mask-composite, and duplicated on a second pseudo-element that is blurred 14px behind the tag — that blurred twin IS the aurora. Idle tags drift slowly; hover snaps the rotation faster and doubles the glow's opacity, like breathing on embers.
Published Updated
The code
<section class="ctc-12" aria-label="Aurora gradient border tag demo">
<div class="ctc-12__stage">
<div class="ctc-12__wrap">
<header class="ctc-12__head">
<p class="ctc-12__kicker">Aurora borders · one gradient, ring + glow</p>
<h1>The border and its light<br>are the same paint</h1>
</header>
<div class="ctc-12__panel">
<p class="ctc-12__lab">01 · Idle drift — hover to make it breathe</p>
<div class="ctc-12__row">
<a class="ctc-12__tag" href="#ctc12">✦ New: AI review</a>
<a class="ctc-12__tag" href="#ctc12">GPT-5 ready</a>
<a class="ctc-12__tag" href="#ctc12">Early access</a>
</div>
</div>
<div class="ctc-12__panel">
<p class="ctc-12__lab">02 · Variants — same engine, three dials</p>
<div class="ctc-12__row">
<span class="ctc-12__tag ctc-12__tag--static">static halo</span>
<span class="ctc-12__tag ctc-12__tag--text"><b>gradient text twin</b></span>
<span class="ctc-12__tag ctc-12__tag--wide">corona · inset -3px</span>
</div>
</div>
<div class="ctc-12__panel">
<p class="ctc-12__lab">03 · In context — one aurora tag per card, no more</p>
<div class="ctc-12__cards">
<article><span class="ctc-12__tag ctc-12__tag--sm">✦ Pro</span><h3>Semantic search</h3><p>Embeddings across your whole workspace, reranked per query.</p></article>
<article><span class="ctc-12__tag ctc-12__tag--sm ctc-12__tag--static">Beta</span><h3>Auto-summaries</h3><p>Every thread condensed to three bullets, on arrival.</p></article>
</div>
</div>
<p class="ctc-12__note">Ring = <code>mask-composite:exclude</code>. Aurora = the same conic on <code>::after</code>, <code>blur(14px)</code>, behind. Hover animates <em>opacity</em>, never the blur radius.</p>
</div>
</div>
</section><section class="ctc-12" aria-label="Aurora gradient border tag demo">
<div class="ctc-12__stage">
<div class="ctc-12__wrap">
<header class="ctc-12__head">
<p class="ctc-12__kicker">Aurora borders · one gradient, ring + glow</p>
<h1>The border and its light<br>are the same paint</h1>
</header>
<div class="ctc-12__panel">
<p class="ctc-12__lab">01 · Idle drift — hover to make it breathe</p>
<div class="ctc-12__row">
<a class="ctc-12__tag" href="#ctc12">✦ New: AI review</a>
<a class="ctc-12__tag" href="#ctc12">GPT-5 ready</a>
<a class="ctc-12__tag" href="#ctc12">Early access</a>
</div>
</div>
<div class="ctc-12__panel">
<p class="ctc-12__lab">02 · Variants — same engine, three dials</p>
<div class="ctc-12__row">
<span class="ctc-12__tag ctc-12__tag--static">static halo</span>
<span class="ctc-12__tag ctc-12__tag--text"><b>gradient text twin</b></span>
<span class="ctc-12__tag ctc-12__tag--wide">corona · inset -3px</span>
</div>
</div>
<div class="ctc-12__panel">
<p class="ctc-12__lab">03 · In context — one aurora tag per card, no more</p>
<div class="ctc-12__cards">
<article><span class="ctc-12__tag ctc-12__tag--sm">✦ Pro</span><h3>Semantic search</h3><p>Embeddings across your whole workspace, reranked per query.</p></article>
<article><span class="ctc-12__tag ctc-12__tag--sm ctc-12__tag--static">Beta</span><h3>Auto-summaries</h3><p>Every thread condensed to three bullets, on arrival.</p></article>
</div>
</div>
<p class="ctc-12__note">Ring = <code>mask-composite:exclude</code>. Aurora = the same conic on <code>::after</code>, <code>blur(14px)</code>, behind. Hover animates <em>opacity</em>, never the blur radius.</p>
</div>
</div>
</section>@property --ctc12a {
syntax: "<angle>";
inherits: false;
initial-value: 0deg;
}
.ctc-12,
.ctc-12 *,
.ctc-12 *::before,
.ctc-12 *::after {
box-sizing: border-box;
margin: 0;
padding: 0;
}
.ctc-12 {
--bg: oklch(0.17 0.02 290);
--ink: oklch(0.95 0.008 290);
--mut: oklch(0.65 0.02 290);
--line: oklch(0.3 0.03 290);
font-family: 'Segoe UI',system-ui,sans-serif;
color: var(--ink);
container-type: inline-size;
}
.ctc-12__stage {
width: 100%;
height: 100vh;
height: 100svh;
overflow-y: auto;
display: grid;
place-items: center;
padding: clamp(16px,4cqi,48px);
background: radial-gradient(70cqi 55cqi at 15% -10%,oklch(0.3 0.09 320/.45),transparent 60%),radial-gradient(60cqi 50cqi at 105% 112%,oklch(0.28 0.08 200/.5),transparent 60%),var(--bg);
container: ctc12/inline-size;
}
.ctc-12__wrap {
width: min(680px,100%);
display: flex;
flex-direction: column;
gap: 14px;
}
.ctc-12__kicker {
font-size: 11.5px;
font-weight: 800;
letter-spacing: .16em;
text-transform: uppercase;
color: oklch(0.75 0.15 320);
}
.ctc-12__head h1 {
font-size: clamp(24px,3.8cqi,38px);
font-weight: 800;
letter-spacing: -.03em;
line-height: 1.12;
margin: 8px 0 8px;
}
.ctc-12__panel {
background: oklch(1 0 0/.045);
border: 1px solid var(--line);
border-radius: 18px;
padding: 16px 18px;
}
.ctc-12__lab {
font-size: 11.5px;
font-weight: 700;
letter-spacing: .05em;
text-transform: uppercase;
color: var(--mut);
margin-bottom: 14px;
}
.ctc-12__row {
display: flex;
flex-wrap: wrap;
gap: 16px;
align-items: center;
}
.ctc-12__tag {
--spin: 6s;
--glow: .35;
position: relative;
display: inline-flex;
align-items: center;
min-height: 44px;
padding: 0 20px;
border-radius: 99px;
font-size: 13.5px;
font-weight: 700;
text-decoration: none;
color: var(--ink);
background: oklch(0.21 0.025 290);
isolation: isolate;
transition: translate .25s;
}
.ctc-12__tag::before,
.ctc-12__tag::after {
content: "";
position: absolute;
inset: 0;
border-radius: inherit;
background: conic-gradient(from var(--ctc12a),oklch(0.75 0.18 320),oklch(0.72 0.19 200),oklch(0.78 0.17 140),oklch(0.75 0.18 320));
animation: ctc12-spin var(--spin) linear infinite;
}
.ctc-12__tag::before {
padding: 1.5px;
-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;
}
.ctc-12__tag::after {
z-index: -1;
filter: blur(14px);
opacity: var(--glow);
transition: opacity .4s;
}
@keyframes ctc12-spin {
to {
--ctc12a: 360deg;
}
}
a.ctc-12__tag:hover,
a.ctc-12__tag:focus-visible {
--spin: 2.5s;
translate: 0 -2px;
}
a.ctc-12__tag:hover::after,
a.ctc-12__tag:focus-visible::after {
opacity: .7;
}
a.ctc-12__tag:focus-visible {
outline: 2px solid oklch(0.75 0.18 320);
outline-offset: 3px;
}
.ctc-12__tag--static::before,
.ctc-12__tag--static::after {
animation: none;
}
.ctc-12__tag--text b {
background: conic-gradient(from 40deg,oklch(0.78 0.18 320),oklch(0.75 0.19 200),oklch(0.8 0.17 140),oklch(0.78 0.18 320));
-webkit-background-clip: text;
background-clip: text;
color: transparent;
font-weight: 800;
}
.ctc-12__tag--wide::after {
inset: -3px;
}
.ctc-12__tag--sm {
min-height: 34px;
padding: 0 14px;
font-size: 12px;
}
.ctc-12__cards {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 12px;
}
.ctc-12__cards article {
display: flex;
flex-direction: column;
align-items: flex-start;
gap: 10px;
padding: 16px;
border-radius: 14px;
border: 1px solid var(--line);
background: oklch(0.2 0.022 290);
}
.ctc-12__cards h3 {
font-size: 15px;
font-weight: 750;
letter-spacing: -.01em;
}
.ctc-12__cards p {
font-size: 12.5px;
line-height: 1.55;
color: var(--mut);
}
.ctc-12__note {
font-size: 12.5px;
line-height: 1.6;
color: var(--mut);
border-left: 3px solid oklch(0.75 0.18 320);
padding-left: 12px;
}
.ctc-12__note code {
font-family: ui-monospace,Menlo,monospace;
font-size: .9em;
background: oklch(1 0 0/.09);
padding: 1px 5px;
border-radius: 4px;
color: var(--ink);
}
@supports (not (transition: --ctc12a 1s)) and (selector(:has(a))) {
.ctc-12__tag::after {
animation: ctc12-pulse 3.2s ease-in-out infinite;
}
@keyframes ctc12-pulse {
50% {
opacity: calc(var(--glow) + .2);
}
}
}
@container ctc12 (width < 520px) {
.ctc-12__cards {
grid-template-columns: 1fr;
}
}
@media (prefers-reduced-motion: reduce) {
.ctc-12__tag::before,
.ctc-12__tag::after {
animation: none !important;
}
.ctc-12 * {
transition-duration: .01ms !important;
}
}@property --ctc12a {
syntax: "<angle>";
inherits: false;
initial-value: 0deg;
}
.ctc-12,
.ctc-12 *,
.ctc-12 *::before,
.ctc-12 *::after {
box-sizing: border-box;
margin: 0;
padding: 0;
}
.ctc-12 {
--bg: oklch(0.17 0.02 290);
--ink: oklch(0.95 0.008 290);
--mut: oklch(0.65 0.02 290);
--line: oklch(0.3 0.03 290);
font-family: 'Segoe UI',system-ui,sans-serif;
color: var(--ink);
container-type: inline-size;
}
.ctc-12__stage {
width: 100%;
height: 100vh;
height: 100svh;
overflow-y: auto;
display: grid;
place-items: center;
padding: clamp(16px,4cqi,48px);
background: radial-gradient(70cqi 55cqi at 15% -10%,oklch(0.3 0.09 320/.45),transparent 60%),radial-gradient(60cqi 50cqi at 105% 112%,oklch(0.28 0.08 200/.5),transparent 60%),var(--bg);
container: ctc12/inline-size;
}
.ctc-12__wrap {
width: min(680px,100%);
display: flex;
flex-direction: column;
gap: 14px;
}
.ctc-12__kicker {
font-size: 11.5px;
font-weight: 800;
letter-spacing: .16em;
text-transform: uppercase;
color: oklch(0.75 0.15 320);
}
.ctc-12__head h1 {
font-size: clamp(24px,3.8cqi,38px);
font-weight: 800;
letter-spacing: -.03em;
line-height: 1.12;
margin: 8px 0 8px;
}
.ctc-12__panel {
background: oklch(1 0 0/.045);
border: 1px solid var(--line);
border-radius: 18px;
padding: 16px 18px;
}
.ctc-12__lab {
font-size: 11.5px;
font-weight: 700;
letter-spacing: .05em;
text-transform: uppercase;
color: var(--mut);
margin-bottom: 14px;
}
.ctc-12__row {
display: flex;
flex-wrap: wrap;
gap: 16px;
align-items: center;
}
.ctc-12__tag {
--spin: 6s;
--glow: .35;
position: relative;
display: inline-flex;
align-items: center;
min-height: 44px;
padding: 0 20px;
border-radius: 99px;
font-size: 13.5px;
font-weight: 700;
text-decoration: none;
color: var(--ink);
background: oklch(0.21 0.025 290);
isolation: isolate;
transition: translate .25s;
}
.ctc-12__tag::before,
.ctc-12__tag::after {
content: "";
position: absolute;
inset: 0;
border-radius: inherit;
background: conic-gradient(from var(--ctc12a),oklch(0.75 0.18 320),oklch(0.72 0.19 200),oklch(0.78 0.17 140),oklch(0.75 0.18 320));
animation: ctc12-spin var(--spin) linear infinite;
}
.ctc-12__tag::before {
padding: 1.5px;
-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;
}
.ctc-12__tag::after {
z-index: -1;
filter: blur(14px);
opacity: var(--glow);
transition: opacity .4s;
}
@keyframes ctc12-spin {
to {
--ctc12a: 360deg;
}
}
a.ctc-12__tag:hover,
a.ctc-12__tag:focus-visible {
--spin: 2.5s;
translate: 0 -2px;
}
a.ctc-12__tag:hover::after,
a.ctc-12__tag:focus-visible::after {
opacity: .7;
}
a.ctc-12__tag:focus-visible {
outline: 2px solid oklch(0.75 0.18 320);
outline-offset: 3px;
}
.ctc-12__tag--static::before,
.ctc-12__tag--static::after {
animation: none;
}
.ctc-12__tag--text b {
background: conic-gradient(from 40deg,oklch(0.78 0.18 320),oklch(0.75 0.19 200),oklch(0.8 0.17 140),oklch(0.78 0.18 320));
-webkit-background-clip: text;
background-clip: text;
color: transparent;
font-weight: 800;
}
.ctc-12__tag--wide::after {
inset: -3px;
}
.ctc-12__tag--sm {
min-height: 34px;
padding: 0 14px;
font-size: 12px;
}
.ctc-12__cards {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 12px;
}
.ctc-12__cards article {
display: flex;
flex-direction: column;
align-items: flex-start;
gap: 10px;
padding: 16px;
border-radius: 14px;
border: 1px solid var(--line);
background: oklch(0.2 0.022 290);
}
.ctc-12__cards h3 {
font-size: 15px;
font-weight: 750;
letter-spacing: -.01em;
}
.ctc-12__cards p {
font-size: 12.5px;
line-height: 1.55;
color: var(--mut);
}
.ctc-12__note {
font-size: 12.5px;
line-height: 1.6;
color: var(--mut);
border-left: 3px solid oklch(0.75 0.18 320);
padding-left: 12px;
}
.ctc-12__note code {
font-family: ui-monospace,Menlo,monospace;
font-size: .9em;
background: oklch(1 0 0/.09);
padding: 1px 5px;
border-radius: 4px;
color: var(--ink);
}
@supports (not (transition: --ctc12a 1s)) and (selector(:has(a))) {
.ctc-12__tag::after {
animation: ctc12-pulse 3.2s ease-in-out infinite;
}
@keyframes ctc12-pulse {
50% {
opacity: calc(var(--glow) + .2);
}
}
}
@container ctc12 (width < 520px) {
.ctc-12__cards {
grid-template-columns: 1fr;
}
}
@media (prefers-reduced-motion: reduce) {
.ctc-12__tag::before,
.ctc-12__tag::after {
animation: none !important;
}
.ctc-12 * {
transition-duration: .01ms !important;
}
}Here's a working CSS Tags & Chip 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: Glowing Aurora Gradient Border Tag
Source: https://codefronts.com/snippets/css-tags-chips/aurora-outline/
The border every AI product shipped in 2025, taken apart properly: a multi-stop conic gradient rotating through a registered @property angle, carved into a ring with mask-composite, and duplicated on a second pseudo-element that is blurred 14px behind the tag — that blurred twin IS the aurora. Idle tags drift slowly; hover snaps the rotation faster and doubles the glow's opacity, like breathing on embers.
## HTML
```html
<section class="ctc-12" aria-label="Aurora gradient border tag demo">
<div class="ctc-12__stage">
<div class="ctc-12__wrap">
<header class="ctc-12__head">
<p class="ctc-12__kicker">Aurora borders · one gradient, ring + glow</p>
<h1>The border and its light<br>are the same paint</h1>
</header>
<div class="ctc-12__panel">
<p class="ctc-12__lab">01 · Idle drift — hover to make it breathe</p>
<div class="ctc-12__row">
<a class="ctc-12__tag" href="#ctc12">✦ New: AI review</a>
<a class="ctc-12__tag" href="#ctc12">GPT-5 ready</a>
<a class="ctc-12__tag" href="#ctc12">Early access</a>
</div>
</div>
<div class="ctc-12__panel">
<p class="ctc-12__lab">02 · Variants — same engine, three dials</p>
<div class="ctc-12__row">
<span class="ctc-12__tag ctc-12__tag--static">static halo</span>
<span class="ctc-12__tag ctc-12__tag--text"><b>gradient text twin</b></span>
<span class="ctc-12__tag ctc-12__tag--wide">corona · inset -3px</span>
</div>
</div>
<div class="ctc-12__panel">
<p class="ctc-12__lab">03 · In context — one aurora tag per card, no more</p>
<div class="ctc-12__cards">
<article><span class="ctc-12__tag ctc-12__tag--sm">✦ Pro</span><h3>Semantic search</h3><p>Embeddings across your whole workspace, reranked per query.</p></article>
<article><span class="ctc-12__tag ctc-12__tag--sm ctc-12__tag--static">Beta</span><h3>Auto-summaries</h3><p>Every thread condensed to three bullets, on arrival.</p></article>
</div>
</div>
<p class="ctc-12__note">Ring = <code>mask-composite:exclude</code>. Aurora = the same conic on <code>::after</code>, <code>blur(14px)</code>, behind. Hover animates <em>opacity</em>, never the blur radius.</p>
</div>
</div>
</section>
```
## CSS
```css
@property --ctc12a {
syntax: "<angle>";
inherits: false;
initial-value: 0deg;
}
.ctc-12,
.ctc-12 *,
.ctc-12 *::before,
.ctc-12 *::after {
box-sizing: border-box;
margin: 0;
padding: 0;
}
.ctc-12 {
--bg: oklch(0.17 0.02 290);
--ink: oklch(0.95 0.008 290);
--mut: oklch(0.65 0.02 290);
--line: oklch(0.3 0.03 290);
font-family: 'Segoe UI',system-ui,sans-serif;
color: var(--ink);
container-type: inline-size;
}
.ctc-12__stage {
width: 100%;
height: 100vh;
height: 100svh;
overflow-y: auto;
display: grid;
place-items: center;
padding: clamp(16px,4cqi,48px);
background: radial-gradient(70cqi 55cqi at 15% -10%,oklch(0.3 0.09 320/.45),transparent 60%),radial-gradient(60cqi 50cqi at 105% 112%,oklch(0.28 0.08 200/.5),transparent 60%),var(--bg);
container: ctc12/inline-size;
}
.ctc-12__wrap {
width: min(680px,100%);
display: flex;
flex-direction: column;
gap: 14px;
}
.ctc-12__kicker {
font-size: 11.5px;
font-weight: 800;
letter-spacing: .16em;
text-transform: uppercase;
color: oklch(0.75 0.15 320);
}
.ctc-12__head h1 {
font-size: clamp(24px,3.8cqi,38px);
font-weight: 800;
letter-spacing: -.03em;
line-height: 1.12;
margin: 8px 0 8px;
}
.ctc-12__panel {
background: oklch(1 0 0/.045);
border: 1px solid var(--line);
border-radius: 18px;
padding: 16px 18px;
}
.ctc-12__lab {
font-size: 11.5px;
font-weight: 700;
letter-spacing: .05em;
text-transform: uppercase;
color: var(--mut);
margin-bottom: 14px;
}
.ctc-12__row {
display: flex;
flex-wrap: wrap;
gap: 16px;
align-items: center;
}
.ctc-12__tag {
--spin: 6s;
--glow: .35;
position: relative;
display: inline-flex;
align-items: center;
min-height: 44px;
padding: 0 20px;
border-radius: 99px;
font-size: 13.5px;
font-weight: 700;
text-decoration: none;
color: var(--ink);
background: oklch(0.21 0.025 290);
isolation: isolate;
transition: translate .25s;
}
.ctc-12__tag::before,
.ctc-12__tag::after {
content: "";
position: absolute;
inset: 0;
border-radius: inherit;
background: conic-gradient(from var(--ctc12a),oklch(0.75 0.18 320),oklch(0.72 0.19 200),oklch(0.78 0.17 140),oklch(0.75 0.18 320));
animation: ctc12-spin var(--spin) linear infinite;
}
.ctc-12__tag::before {
padding: 1.5px;
-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;
}
.ctc-12__tag::after {
z-index: -1;
filter: blur(14px);
opacity: var(--glow);
transition: opacity .4s;
}
@keyframes ctc12-spin {
to {
--ctc12a: 360deg;
}
}
a.ctc-12__tag:hover,
a.ctc-12__tag:focus-visible {
--spin: 2.5s;
translate: 0 -2px;
}
a.ctc-12__tag:hover::after,
a.ctc-12__tag:focus-visible::after {
opacity: .7;
}
a.ctc-12__tag:focus-visible {
outline: 2px solid oklch(0.75 0.18 320);
outline-offset: 3px;
}
.ctc-12__tag--static::before,
.ctc-12__tag--static::after {
animation: none;
}
.ctc-12__tag--text b {
background: conic-gradient(from 40deg,oklch(0.78 0.18 320),oklch(0.75 0.19 200),oklch(0.8 0.17 140),oklch(0.78 0.18 320));
-webkit-background-clip: text;
background-clip: text;
color: transparent;
font-weight: 800;
}
.ctc-12__tag--wide::after {
inset: -3px;
}
.ctc-12__tag--sm {
min-height: 34px;
padding: 0 14px;
font-size: 12px;
}
.ctc-12__cards {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 12px;
}
.ctc-12__cards article {
display: flex;
flex-direction: column;
align-items: flex-start;
gap: 10px;
padding: 16px;
border-radius: 14px;
border: 1px solid var(--line);
background: oklch(0.2 0.022 290);
}
.ctc-12__cards h3 {
font-size: 15px;
font-weight: 750;
letter-spacing: -.01em;
}
.ctc-12__cards p {
font-size: 12.5px;
line-height: 1.55;
color: var(--mut);
}
.ctc-12__note {
font-size: 12.5px;
line-height: 1.6;
color: var(--mut);
border-left: 3px solid oklch(0.75 0.18 320);
padding-left: 12px;
}
.ctc-12__note code {
font-family: ui-monospace,Menlo,monospace;
font-size: .9em;
background: oklch(1 0 0/.09);
padding: 1px 5px;
border-radius: 4px;
color: var(--ink);
}
@supports (not (transition: --ctc12a 1s)) and (selector(:has(a))) {
.ctc-12__tag::after {
animation: ctc12-pulse 3.2s ease-in-out infinite;
}
@keyframes ctc12-pulse {
50% {
opacity: calc(var(--glow) + .2);
}
}
}
@container ctc12 (width < 520px) {
.ctc-12__cards {
grid-template-columns: 1fr;
}
}
@media (prefers-reduced-motion: reduce) {
.ctc-12__tag::before,
.ctc-12__tag::after {
animation: none !important;
}
.ctc-12 * {
transition-duration: .01ms !important;
}
}
```Here's a working CSS Tags & Chip 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: Glowing Aurora Gradient Border Tag
Source: https://codefronts.com/snippets/css-tags-chips/aurora-outline/
The border every AI product shipped in 2025, taken apart properly: a multi-stop conic gradient rotating through a registered @property angle, carved into a ring with mask-composite, and duplicated on a second pseudo-element that is blurred 14px behind the tag — that blurred twin IS the aurora. Idle tags drift slowly; hover snaps the rotation faster and doubles the glow's opacity, like breathing on embers.
## HTML
```html
<section class="ctc-12" aria-label="Aurora gradient border tag demo">
<div class="ctc-12__stage">
<div class="ctc-12__wrap">
<header class="ctc-12__head">
<p class="ctc-12__kicker">Aurora borders · one gradient, ring + glow</p>
<h1>The border and its light<br>are the same paint</h1>
</header>
<div class="ctc-12__panel">
<p class="ctc-12__lab">01 · Idle drift — hover to make it breathe</p>
<div class="ctc-12__row">
<a class="ctc-12__tag" href="#ctc12">✦ New: AI review</a>
<a class="ctc-12__tag" href="#ctc12">GPT-5 ready</a>
<a class="ctc-12__tag" href="#ctc12">Early access</a>
</div>
</div>
<div class="ctc-12__panel">
<p class="ctc-12__lab">02 · Variants — same engine, three dials</p>
<div class="ctc-12__row">
<span class="ctc-12__tag ctc-12__tag--static">static halo</span>
<span class="ctc-12__tag ctc-12__tag--text"><b>gradient text twin</b></span>
<span class="ctc-12__tag ctc-12__tag--wide">corona · inset -3px</span>
</div>
</div>
<div class="ctc-12__panel">
<p class="ctc-12__lab">03 · In context — one aurora tag per card, no more</p>
<div class="ctc-12__cards">
<article><span class="ctc-12__tag ctc-12__tag--sm">✦ Pro</span><h3>Semantic search</h3><p>Embeddings across your whole workspace, reranked per query.</p></article>
<article><span class="ctc-12__tag ctc-12__tag--sm ctc-12__tag--static">Beta</span><h3>Auto-summaries</h3><p>Every thread condensed to three bullets, on arrival.</p></article>
</div>
</div>
<p class="ctc-12__note">Ring = <code>mask-composite:exclude</code>. Aurora = the same conic on <code>::after</code>, <code>blur(14px)</code>, behind. Hover animates <em>opacity</em>, never the blur radius.</p>
</div>
</div>
</section>
```
## CSS
```css
@property --ctc12a {
syntax: "<angle>";
inherits: false;
initial-value: 0deg;
}
.ctc-12,
.ctc-12 *,
.ctc-12 *::before,
.ctc-12 *::after {
box-sizing: border-box;
margin: 0;
padding: 0;
}
.ctc-12 {
--bg: oklch(0.17 0.02 290);
--ink: oklch(0.95 0.008 290);
--mut: oklch(0.65 0.02 290);
--line: oklch(0.3 0.03 290);
font-family: 'Segoe UI',system-ui,sans-serif;
color: var(--ink);
container-type: inline-size;
}
.ctc-12__stage {
width: 100%;
height: 100vh;
height: 100svh;
overflow-y: auto;
display: grid;
place-items: center;
padding: clamp(16px,4cqi,48px);
background: radial-gradient(70cqi 55cqi at 15% -10%,oklch(0.3 0.09 320/.45),transparent 60%),radial-gradient(60cqi 50cqi at 105% 112%,oklch(0.28 0.08 200/.5),transparent 60%),var(--bg);
container: ctc12/inline-size;
}
.ctc-12__wrap {
width: min(680px,100%);
display: flex;
flex-direction: column;
gap: 14px;
}
.ctc-12__kicker {
font-size: 11.5px;
font-weight: 800;
letter-spacing: .16em;
text-transform: uppercase;
color: oklch(0.75 0.15 320);
}
.ctc-12__head h1 {
font-size: clamp(24px,3.8cqi,38px);
font-weight: 800;
letter-spacing: -.03em;
line-height: 1.12;
margin: 8px 0 8px;
}
.ctc-12__panel {
background: oklch(1 0 0/.045);
border: 1px solid var(--line);
border-radius: 18px;
padding: 16px 18px;
}
.ctc-12__lab {
font-size: 11.5px;
font-weight: 700;
letter-spacing: .05em;
text-transform: uppercase;
color: var(--mut);
margin-bottom: 14px;
}
.ctc-12__row {
display: flex;
flex-wrap: wrap;
gap: 16px;
align-items: center;
}
.ctc-12__tag {
--spin: 6s;
--glow: .35;
position: relative;
display: inline-flex;
align-items: center;
min-height: 44px;
padding: 0 20px;
border-radius: 99px;
font-size: 13.5px;
font-weight: 700;
text-decoration: none;
color: var(--ink);
background: oklch(0.21 0.025 290);
isolation: isolate;
transition: translate .25s;
}
.ctc-12__tag::before,
.ctc-12__tag::after {
content: "";
position: absolute;
inset: 0;
border-radius: inherit;
background: conic-gradient(from var(--ctc12a),oklch(0.75 0.18 320),oklch(0.72 0.19 200),oklch(0.78 0.17 140),oklch(0.75 0.18 320));
animation: ctc12-spin var(--spin) linear infinite;
}
.ctc-12__tag::before {
padding: 1.5px;
-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;
}
.ctc-12__tag::after {
z-index: -1;
filter: blur(14px);
opacity: var(--glow);
transition: opacity .4s;
}
@keyframes ctc12-spin {
to {
--ctc12a: 360deg;
}
}
a.ctc-12__tag:hover,
a.ctc-12__tag:focus-visible {
--spin: 2.5s;
translate: 0 -2px;
}
a.ctc-12__tag:hover::after,
a.ctc-12__tag:focus-visible::after {
opacity: .7;
}
a.ctc-12__tag:focus-visible {
outline: 2px solid oklch(0.75 0.18 320);
outline-offset: 3px;
}
.ctc-12__tag--static::before,
.ctc-12__tag--static::after {
animation: none;
}
.ctc-12__tag--text b {
background: conic-gradient(from 40deg,oklch(0.78 0.18 320),oklch(0.75 0.19 200),oklch(0.8 0.17 140),oklch(0.78 0.18 320));
-webkit-background-clip: text;
background-clip: text;
color: transparent;
font-weight: 800;
}
.ctc-12__tag--wide::after {
inset: -3px;
}
.ctc-12__tag--sm {
min-height: 34px;
padding: 0 14px;
font-size: 12px;
}
.ctc-12__cards {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 12px;
}
.ctc-12__cards article {
display: flex;
flex-direction: column;
align-items: flex-start;
gap: 10px;
padding: 16px;
border-radius: 14px;
border: 1px solid var(--line);
background: oklch(0.2 0.022 290);
}
.ctc-12__cards h3 {
font-size: 15px;
font-weight: 750;
letter-spacing: -.01em;
}
.ctc-12__cards p {
font-size: 12.5px;
line-height: 1.55;
color: var(--mut);
}
.ctc-12__note {
font-size: 12.5px;
line-height: 1.6;
color: var(--mut);
border-left: 3px solid oklch(0.75 0.18 320);
padding-left: 12px;
}
.ctc-12__note code {
font-family: ui-monospace,Menlo,monospace;
font-size: .9em;
background: oklch(1 0 0/.09);
padding: 1px 5px;
border-radius: 4px;
color: var(--ink);
}
@supports (not (transition: --ctc12a 1s)) and (selector(:has(a))) {
.ctc-12__tag::after {
animation: ctc12-pulse 3.2s ease-in-out infinite;
}
@keyframes ctc12-pulse {
50% {
opacity: calc(var(--glow) + .2);
}
}
}
@container ctc12 (width < 520px) {
.ctc-12__cards {
grid-template-columns: 1fr;
}
}
@media (prefers-reduced-motion: reduce) {
.ctc-12__tag::before,
.ctc-12__tag::after {
animation: none !important;
}
.ctc-12 * {
transition-duration: .01ms !important;
}
}
```How this works
Two pseudo-elements share one gradient. ::before is the crisp ring (the mask-composite hollowing trick); ::after is the same gradient un-masked, blurred, and pushed behind — glow and border can never drift apart because they are literally the same paint:
.tag::before, .tag::after{ content:""; position:absolute; inset:0;
border-radius:inherit;
background:conic-gradient(from var(--ctc12a),
oklch(0.75 0.18 320), oklch(0.72 0.19 200),
oklch(0.78 0.17 140), oklch(0.75 0.18 320)); }
.tag::before{ padding:1.5px;
mask:linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0);
mask-composite:exclude; }
.tag::after{ z-index:-1; filter:blur(14px); opacity:.35;
animation:ctc12-spin 6s linear infinite; }The rotation is a keyframe on --ctc12a, registered via @property … syntax:'<angle>' so it interpolates. The gradient's first and last stops are the same color — without that, the seam where 360° meets 0° flashes every revolution.
Hover does two things: shortens the spin duration (6s → 2.5s) and lifts the ::after opacity to .7 — motion plus light, the combination that reads as 'this thing woke up'. Because the blurred layer is transform-composited and the blur radius never animates, the whole effect stays off the layout thread. The text-gradient variant clips the same conic onto the label with background-clip:text, and the static variant simply removes the animation for a permanent aurora halo.
Make it yours
- Palette: the four conic stops. Magenta→cyan→green is the 'AI aurora'; try 260/300/340 hues for a synthwave badge. Keep stop 1 = stop 4.
- Glow size: the ::after blur radius (14px) and its inset (0). Negative inset (-3px) grows the halo beyond the ring for a stronger corona.
- Idle speed: 6s. Anything under 3s at rest is distracting next to body text; hover can afford 2–2.5s.
- Ring weight: the ::before padding. 1px is jewelry, 2px is product-card, 3px starts reading as a loading spinner.
Gotchas — read before shipping
- The blur must live on the pseudo-element, NOT the tag — filter on the tag itself blurs your text and creates a stacking context that traps z-index:-1 children.
- Repeat the first conic stop as the last or you get a hard seam strobing once per rotation — the most common aurora-border bug on production sites.
- Animating filter:blur() itself is a per-frame repaint; animate opacity on an already-blurred layer instead, as here.
- Firefox pre-128 has no @property: the ring shows but nothing rotates. The demo's @supports fallback swaps in a subtle opacity pulse so idle tags still feel alive.
Browser support
| Chrome | Safari | Firefox | Edge |
|---|---|---|---|
| 111+ | 16.4+ | 128+ | 111+ |
@property hits Firefox in 128 (July 2024); the fallback pulse covers earlier. mask-composite universal since 2023 (with -webkit- for older Safari). Filters and conic gradients are long-settled.