HTML
<section class="ba-grd" aria-label="Gradient animated border alert demo">
<span class="label">// method 1 — @property conic-gradient spin</span>
<div class="gcard-wrap">
<div class="gcard">
<div class="card-eyebrow"><span class="card-badge">✦ Premium</span><span class="card-live" aria-hidden="true"></span></div>
<div class="card-title">Your plan has been upgraded.</div>
<div class="card-body">Welcome to Pro. Unlimited API requests, priority support, and access to experimental features are now active on your account.</div>
<div class="card-actions">
<button class="btn-primary" type="button">Explore features</button>
<button class="btn-ghost" type="button">View invoice →</button>
</div>
</div>
</div>
<span class="label">// method 2 — background-position shift</span>
<div class="gcard-wrap gcard-wrap--linear">
<div class="gcard">
<div class="card-eyebrow"><span class="card-badge card-badge--blue">⬡ AI-Powered</span></div>
<div class="card-title">Smart suggestions are now active.</div>
<div class="card-body">The AI model has analysed 2,400 patterns in your codebase and is ready to suggest completions, catch bugs, and explain complex logic inline.</div>
<div class="card-actions">
<button class="btn-primary" type="button">Open editor</button>
<button class="btn-ghost" type="button">How it works</button>
</div>
</div>
</div>
<span class="label">// method 3 — SVG stroke-dashoffset</span>
<div class="gcard-wrap gcard-wrap--dash" data-ba-grd-dash>
<svg class="dash-svg" preserveAspectRatio="none" xmlns="http://www.w3.org/2000/svg" aria-hidden="true">
<defs>
<linearGradient id="ba-grd-dashGrad" x1="0%" y1="0%" x2="100%" y2="100%">
<stop offset="0%" stop-color="#7c3aed"/><stop offset="33%" stop-color="#0ea5e9"/><stop offset="66%" stop-color="#10b981"/><stop offset="100%" stop-color="#f59e0b"/>
</linearGradient>
</defs>
</svg>
<div class="gcard">
<div class="card-eyebrow"><span class="card-badge card-badge--green">◈ New Release</span></div>
<div class="card-title">v6.0 is available — major update.</div>
<div class="card-body">This release brings a redesigned query builder, a 40% reduction in bundle size, and full TypeScript 5.4 support. Review the migration guide before upgrading.</div>
<div class="card-actions">
<button class="btn-primary" type="button">Upgrade now</button>
<button class="btn-ghost" type="button">Migration guide →</button>
</div>
</div>
</div>
</section> CSS
/* ─── 12 Gradient Animated Border Alert — holographic ─────────── */
@import url('https://fonts.googleapis.com/css2?family=Space+Grotesk:wght@300;400;500;600;700&family=Space+Mono:wght@400;700&display=swap');
@property --ba-grd-angle {
syntax: '<angle>';
initial-value: 0deg;
inherits: false;
}
.ba-grd {
--ba-grd-bg: #06060e;
--ba-grd-card: #0d0d1a;
--ba-grd-text: #e8e8ff;
--ba-grd-muted: rgba(232,232,255,0.45);
--ba-grd-g1: #7c3aed; --ba-grd-g2: #db2777; --ba-grd-g3: #0ea5e9; --ba-grd-g4: #10b981; --ba-grd-g5: #f59e0b;
--ba-grd-border-size: 2px;
--ba-grd-radius: 14px;
position: relative;
width: 100%;
min-height: 920px;
background: var(--ba-grd-bg);
background-image: radial-gradient(ellipse 60% 40% at 50% 0%, rgba(124,58,237,0.12) 0%, transparent 60%);
display: flex; flex-direction: column; align-items: center; justify-content: center;
gap: 24px;
padding: 60px 20px;
font-family: 'Space Grotesk', sans-serif;
color: var(--ba-grd-text);
box-sizing: border-box;
}
.ba-grd *, .ba-grd *::before, .ba-grd *::after { box-sizing: border-box; margin: 0; padding: 0; }
.ba-grd .label { font-family: 'Space Mono', monospace; font-size: 9px; letter-spacing: 0.25em; text-transform: uppercase; color: rgba(255,255,255,0.14); align-self: flex-start; max-width: 580px; width: 100%; }
.ba-grd .gcard-wrap { position: relative; border-radius: var(--ba-grd-radius); padding: var(--ba-grd-border-size); background: conic-gradient(from var(--ba-grd-angle, 0deg), var(--ba-grd-g1), var(--ba-grd-g2), var(--ba-grd-g3), var(--ba-grd-g4), var(--ba-grd-g5), var(--ba-grd-g1)); animation: ba-grd-spin 3s linear infinite; max-width: 580px; width: 100%; }
@keyframes ba-grd-spin { to { --ba-grd-angle: 360deg; } }
.ba-grd .gcard-wrap::before { content: ''; position: absolute; inset: -4px; border-radius: calc(var(--ba-grd-radius) + 4px); background: inherit; filter: blur(16px); opacity: 0.45; z-index: -1; }
.ba-grd .gcard { background: var(--ba-grd-card); border-radius: calc(var(--ba-grd-radius) - var(--ba-grd-border-size)); padding: 24px 26px; position: relative; overflow: hidden; }
.ba-grd .gcard::before { content: ''; position: absolute; inset: 0; background: linear-gradient(135deg, rgba(255,255,255,0.04) 0%, transparent 40%, rgba(255,255,255,0.02) 100%); pointer-events: none; border-radius: inherit; }
.ba-grd .gcard-wrap--linear { background-size: 300% 300%; background-image: linear-gradient(130deg, var(--ba-grd-g3), var(--ba-grd-g1), var(--ba-grd-g2), var(--ba-grd-g3), var(--ba-grd-g4), var(--ba-grd-g1)); animation: ba-grd-shift 4s linear infinite; }
@keyframes ba-grd-shift { 0% { background-position: 0% 50%; } 50% { background-position: 100% 50%; } 100% { background-position: 0% 50%; } }
.ba-grd .gcard-wrap--dash { background: none; animation: none; padding: 0; position: relative; }
.ba-grd .gcard-wrap--dash::before { display: none; }
.ba-grd .gcard-wrap--dash .gcard { border-radius: var(--ba-grd-radius); position: relative; z-index: 1; border: 2px solid transparent; background-clip: padding-box; background: var(--ba-grd-card); }
.ba-grd .dash-svg { position: absolute; inset: 0; width: 100%; height: 100%; pointer-events: none; z-index: 0; border-radius: var(--ba-grd-radius); overflow: visible; }
.ba-grd .dash-rect { fill: none; stroke: url(#ba-grd-dashGrad); stroke-width: 2; stroke-dasharray: 12 6; animation: ba-grd-dashMove 1.5s linear infinite; }
@keyframes ba-grd-dashMove { to { stroke-dashoffset: -36; } }
.ba-grd .card-eyebrow { display: flex; align-items: center; gap: 8px; margin-bottom: 12px; }
.ba-grd .card-badge { font-family: 'Space Mono', monospace; font-size: 9px; font-weight: 700; letter-spacing: 0.2em; text-transform: uppercase; padding: 3px 8px; border-radius: 4px; background: rgba(124,58,237,0.15); border: 1px solid rgba(124,58,237,0.3); color: #a78bfa; }
.ba-grd .card-badge--blue { background: rgba(14,165,233,0.12); border-color: rgba(14,165,233,0.3); color: #38bdf8; }
.ba-grd .card-badge--green { background: rgba(16,185,129,0.12); border-color: rgba(16,185,129,0.3); color: #34d399; }
.ba-grd .card-live { width: 7px; height: 7px; border-radius: 50%; background: #10b981; animation: ba-grd-liveDot 1.5s ease-in-out infinite; flex-shrink: 0; margin-left: auto; }
@keyframes ba-grd-liveDot { 0%,100% { opacity: 1; } 50% { opacity: 0.3; } }
.ba-grd .card-title { font-size: 18px; font-weight: 700; color: var(--ba-grd-text); line-height: 1.3; margin-bottom: 8px; letter-spacing: -0.01em; }
.ba-grd .card-body { font-size: 13.5px; font-weight: 400; color: var(--ba-grd-muted); line-height: 1.7; margin-bottom: 18px; }
.ba-grd .card-actions { display: flex; gap: 10px; align-items: center; flex-wrap: wrap; }
.ba-grd .btn-primary { font-family: 'Space Grotesk', sans-serif; font-size: 12px; font-weight: 600; letter-spacing: 0.06em; padding: 9px 18px; background: linear-gradient(135deg, var(--ba-grd-g1), var(--ba-grd-g2)); color: white; border: none; border-radius: 8px; cursor: pointer; transition: all 0.2s; }
.ba-grd .btn-primary:hover { opacity: 0.88; transform: translateY(-1px); box-shadow: 0 4px 20px rgba(124,58,237,0.4); }
.ba-grd .btn-ghost { font-family: 'Space Mono', monospace; font-size: 11px; color: var(--ba-grd-muted); background: transparent; border: none; cursor: pointer; text-decoration: underline; text-underline-offset: 3px; transition: color 0.18s; }
.ba-grd .btn-ghost:hover { color: var(--ba-grd-text); }
@media (prefers-reduced-motion: reduce) { .ba-grd .gcard-wrap, .ba-grd .card-live, .ba-grd .dash-rect { animation: none !important; } } JS
(() => {
const root = document.querySelector('.ba-grd');
if (!root) return;
const wrap = root.querySelector('[data-ba-grd-dash]');
if (!wrap) return;
const svg = wrap.querySelector('svg.dash-svg');
if (!svg || !('ResizeObserver' in window)) return;
const ro = new ResizeObserver((entries) => {
for (const e of entries) {
const { width, height } = e.contentRect;
if (!width || !height) continue;
const r = 14, s = 2;
svg.setAttribute('viewBox', '0 0 ' + width + ' ' + height);
svg.setAttribute('width', width);
svg.setAttribute('height', height);
const existing = svg.querySelector('rect.dash-rect');
if (existing) existing.remove();
const rect = document.createElementNS('http://www.w3.org/2000/svg', 'rect');
rect.setAttribute('x', s / 2);
rect.setAttribute('y', s / 2);
rect.setAttribute('width', width - s);
rect.setAttribute('height', height - s);
rect.setAttribute('rx', r);
rect.setAttribute('ry', r);
rect.setAttribute('class', 'dash-rect');
svg.appendChild(rect);
}
});
ro.observe(wrap);
})();