25 CSS Blockquotes06 / 25

CSS onlyMIT licensed

Animated CSS Gradient Border Blockquote

A quote card wrapped in a slowly rotating conic-gradient border — the effect every 'top CSS tricks' video promises, done the 2026 way: a registered @property custom property (--bq06-angle) makes the gradient's angle itself animatable, so the colors orbit the card with zero JavaScript and zero pseudo-element hacks. The border is painted by a two-layer background (padding-box fill over border-box gradient), which — unlike outline or box-shadow tricks — respects border-radius perfectly and costs one element.

Published

Live Demo
Try it

The code

<section class="bq-06" aria-label="Animated gradient border blockquote demo">
  <div class="bq-06__stage">
    <figure class="bq-06__card">
      <blockquote>
        <p>“We shipped the redesign in six weeks. The border animation in this quote card took two of them — and it was worth every hour.”</p>
      </blockquote>
      <figcaption class="bq-06__by">
        <span class="bq-06__dot" aria-hidden="true"></span>
        <span><strong>Marcus Webb</strong> · Design Engineer, Meridian</span>
      </figcaption>
    </figure>
    <p class="bq-06__chip" aria-hidden="true">@property &lt;angle&gt; · conic-gradient border-box · 0 JS</p>
  </div>
</section>
@property --bq06-angle {
  syntax: '<angle>';
  initial-value: 40deg;
  inherits: false;
}

.bq-06,
.bq-06 *,
.bq-06 *::before,
.bq-06 *::after {
  box-sizing: border-box;
  margin: 0;
  padding: 0;
}

.bq-06 {
  width: 100%;
  min-height: 100vh;
  min-height: 100svh;
  display: block;
  --bg: oklch(0.15 0.012 270);
  --card: oklch(0.2 0.015 270);
  --ink: oklch(0.96 0.008 270);
  --mut: oklch(0.66 0.02 270);
  font-family: 'Segoe UI',system-ui,sans-serif;
  color: var(--ink);
}

.bq-06__stage {
  width: 100%;
  height: 100vh;
  height: 100svh;
  display: grid;
  place-items: center;
  align-content: center;
  gap: 26px;
  padding: clamp(20px,5vw,56px);
  background: radial-gradient(70% 55% at 50% 120%,oklch(0.24 0.05 300/.5),transparent),var(--bg);
  overflow-y: auto;
}

.bq-06__card {
  width: min(100%,520px);
  padding: clamp(24px,4vw,36px);
  border: 1.5px solid transparent;
  border-radius: 20px;
  background: linear-gradient(var(--card),var(--card)) padding-box,conic-gradient(from var(--bq06-angle),oklch(0.72 0.17 25),oklch(0.75 0.15 320),oklch(0.7 0.14 250),oklch(0.72 0.17 25)) border-box;
  animation: bq06-spin 7s linear infinite;
  box-shadow: 0 24px 60px oklch(0 0 0/.45);
}

@keyframes bq06-spin {
  to {
    --bq06-angle: 400deg;
  }
}

.bq-06__card blockquote p {
  font-size: clamp(17px,2.2vw,21px);
  line-height: 1.6;
  font-weight: 550;
  letter-spacing: -.01em;
  text-wrap: pretty;
}

.bq-06__by {
  display: flex;
  align-items: center;
  gap: 10px;
  margin-top: 20px;
  font-size: 13.5px;
  color: var(--mut);
}

.bq-06__by strong {
  color: var(--ink);
  font-weight: 650;
}

.bq-06__dot {
  width: 9px;
  height: 9px;
  border-radius: 99px;
  background: conic-gradient(oklch(0.72 0.17 25),oklch(0.75 0.15 320),oklch(0.7 0.14 250),oklch(0.72 0.17 25));
  flex: none;
}

.bq-06__chip {
  font-family: ui-monospace,Menlo,Consolas,monospace;
  font-size: 11.5px;
  color: var(--mut);
  padding: 7px 13px;
  border: 1px solid oklch(1 0 0/.12);
  border-radius: 99px;
}

@media (prefers-reduced-motion: reduce) {
  .bq-06 * {
    transition-duration: .01ms !important;
  }

  .bq-06__card {
    animation: none;
  }
}
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 Blockquote 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: Animated CSS Gradient Border Blockquote
Source: https://codefronts.com/snippets/css-blockquotes/animated-css-gradient-border-blockquote/

A quote card wrapped in a slowly rotating conic-gradient border — the effect every 'top CSS tricks' video promises, done the 2026 way: a registered @property custom property (--bq06-angle) makes the gradient's angle itself animatable, so the colors orbit the card with zero JavaScript and zero pseudo-element hacks. The border is painted by a two-layer background (padding-box fill over border-box gradient), which — unlike outline or box-shadow tricks — respects border-radius perfectly and costs one element.
## HTML
```html
<section class="bq-06" aria-label="Animated gradient border blockquote demo">
  <div class="bq-06__stage">
    <figure class="bq-06__card">
      <blockquote>
        <p>“We shipped the redesign in six weeks. The border animation in this quote card took two of them — and it was worth every hour.”</p>
      </blockquote>
      <figcaption class="bq-06__by">
        <span class="bq-06__dot" aria-hidden="true"></span>
        <span><strong>Marcus Webb</strong> · Design Engineer, Meridian</span>
      </figcaption>
    </figure>
    <p class="bq-06__chip" aria-hidden="true">@property &lt;angle&gt; · conic-gradient border-box · 0 JS</p>
  </div>
</section>
```
## CSS
```css
@property --bq06-angle {
  syntax: '<angle>';
  initial-value: 40deg;
  inherits: false;
}

.bq-06,
.bq-06 *,
.bq-06 *::before,
.bq-06 *::after {
  box-sizing: border-box;
  margin: 0;
  padding: 0;
}

.bq-06 {
  width: 100%;
  min-height: 100vh;
  min-height: 100svh;
  display: block;
  --bg: oklch(0.15 0.012 270);
  --card: oklch(0.2 0.015 270);
  --ink: oklch(0.96 0.008 270);
  --mut: oklch(0.66 0.02 270);
  font-family: 'Segoe UI',system-ui,sans-serif;
  color: var(--ink);
}

.bq-06__stage {
  width: 100%;
  height: 100vh;
  height: 100svh;
  display: grid;
  place-items: center;
  align-content: center;
  gap: 26px;
  padding: clamp(20px,5vw,56px);
  background: radial-gradient(70% 55% at 50% 120%,oklch(0.24 0.05 300/.5),transparent),var(--bg);
  overflow-y: auto;
}

.bq-06__card {
  width: min(100%,520px);
  padding: clamp(24px,4vw,36px);
  border: 1.5px solid transparent;
  border-radius: 20px;
  background: linear-gradient(var(--card),var(--card)) padding-box,conic-gradient(from var(--bq06-angle),oklch(0.72 0.17 25),oklch(0.75 0.15 320),oklch(0.7 0.14 250),oklch(0.72 0.17 25)) border-box;
  animation: bq06-spin 7s linear infinite;
  box-shadow: 0 24px 60px oklch(0 0 0/.45);
}

@keyframes bq06-spin {
  to {
    --bq06-angle: 400deg;
  }
}

.bq-06__card blockquote p {
  font-size: clamp(17px,2.2vw,21px);
  line-height: 1.6;
  font-weight: 550;
  letter-spacing: -.01em;
  text-wrap: pretty;
}

.bq-06__by {
  display: flex;
  align-items: center;
  gap: 10px;
  margin-top: 20px;
  font-size: 13.5px;
  color: var(--mut);
}

.bq-06__by strong {
  color: var(--ink);
  font-weight: 650;
}

.bq-06__dot {
  width: 9px;
  height: 9px;
  border-radius: 99px;
  background: conic-gradient(oklch(0.72 0.17 25),oklch(0.75 0.15 320),oklch(0.7 0.14 250),oklch(0.72 0.17 25));
  flex: none;
}

.bq-06__chip {
  font-family: ui-monospace,Menlo,Consolas,monospace;
  font-size: 11.5px;
  color: var(--mut);
  padding: 7px 13px;
  border: 1px solid oklch(1 0 0/.12);
  border-radius: 99px;
}

@media (prefers-reduced-motion: reduce) {
  .bq-06 * {
    transition-duration: .01ms !important;
  }

  .bq-06__card {
    animation: none;
  }
}
```

How this works

Gradients aren't animatable — but a registered custom property is. That inversion is the whole technique:

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

.card{
  border:1.5px solid transparent; border-radius:20px;
  background:
    linear-gradient(var(--card), var(--card)) padding-box,
    conic-gradient(from var(--bq06-angle),
      oklch(0.72 0.17 25), oklch(0.75 0.15 320),
      oklch(0.7 0.14 250), oklch(0.72 0.17 25)) border-box;
  animation:bq06-spin 7s linear infinite;
}
@keyframes bq06-spin{ to{ --bq06-angle:360deg; } }

Without @property, custom properties are opaque strings — animating one just snaps at 50%. Registering it with syntax:'<angle>' tells the engine it's a real angle, so the keyframe interpolates smoothly and the conic gradient repaints each frame.

The border itself is the classic two-background sandwich: a flat card-colored gradient clipped to padding-box sits over the conic clipped to border-box; the transparent 1.5px border is the window where the bottom layer shows through. Because it's a real border, the radius curves it perfectly — the thing outline and clip-path versions always get wrong. The first and last conic stops repeat the same color so the seam at 0deg is invisible. Reduced-motion users get the gradient frozen at a designed angle — the card still looks finished, it just doesn't move.

Make it yours

  • Speed and mood: 7s linear reads as ambient; 2.5s reads as loading-spinner — keep ambient borders slower than 6s.
  • Thickness: the border width IS the gradient reveal — 1px is jewelry, 3px is festive. Adjust border and border-radius together to keep the curve smooth.
  • Hover-only motion: move the animation to .card:hover, .card:focus-within with animation-play-state — ambient calm, alive on approach.
  • Glow pairing: add a blurred drop-shadow in one of the gradient hues at 25% — see demo 10 for the full glow treatment; here restraint keeps it editorial.

Gotchas — read before shipping

  • Forgetting @property is the classic failure: the animation 'runs' but the angle jumps 0→360 with no in-between. Firefox shipped @property in 128 (mid-2024); older Firefox shows the static border.
  • The two backgrounds must be listed padding-box FIRST, border-box second — reversed, the card color paints over the gradient and the border vanishes.
  • background-clip shorthand resets: if you later override background on the card (e.g. a hover), you must restate both layers, clips included.
  • An infinite animation repaints forever, even offscreen in some engines — for long pages, pause it with animation-play-state when not hovered, or gate it behind :has(:hover) on the section.

Browser support

ChromeSafariFirefoxEdge
114+17.5+128+114+

Floor set by oklch(), @property, text-wrap as this demo is written. The core technique itself goes back further — Chrome 111+.

Floor set by @property (Chrome 85 / Safari 16.4 / Firefox 128). Everywhere else the card renders with a static multicolor border — a designed fallback, not a broken one.

Techniques used in this demo

Search CodeFronts

Loading…