Back to CSS Banners & Alerts CSS Gradient Animated Border Alert CSS + JS
Share
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);
})();