Tailwind v4MIT licensed

Animated Tailwind Gradient Border Blockquote

A quote card whose border is a rotating conic gradient, drawn with the two-background padding-box / border-box trick and animated by registering the angle with @property. The Tailwind port keeps the keyframes as an --animate-* theme token and adds a blurred glow twin of the border behind the card that brightens on hover.

Live Demo
Plain CSSTailwind v4
Try it

Untitled UI Pro: 5,000+ React + Tailwind CSS v4 components · from $349 one-time

Untitled UI React · Affiliate link

Get Untitled UI Pro

The code

<section class="tbq-06 w-full min-h-svh grid place-items-center bg-tbq06-bg bg-[radial-gradient(70%_55%_at_50%_120%,var(--color-tbq06-glow),transparent)] px-5 py-14 font-tbq06-sans text-tbq06-ink" aria-label="Animated gradient border blockquote demo">
  <div class="isolate grid w-full justify-items-center gap-8">
    <figure class="relative w-full max-w-[520px] animate-tbq06-spin rounded-[20px] border-[1.5px] border-transparent p-[clamp(24px,4vw,36px)] shadow-[0_24px_60px_oklch(0_0_0/0.45)] [background:linear-gradient(var(--color-tbq06-card),var(--color-tbq06-card))_padding-box,conic-gradient(from_var(--tbq06-angle),var(--color-tbq06-c1),var(--color-tbq06-c2),var(--color-tbq06-c3),var(--color-tbq06-c1))_border-box] before:absolute before:-inset-[1.5px] before:-z-10 before:rounded-[inherit] before:opacity-45 before:blur-2xl before:transition-opacity before:duration-500 before:[background:conic-gradient(from_var(--tbq06-angle),var(--color-tbq06-c1),var(--color-tbq06-c2),var(--color-tbq06-c3),var(--color-tbq06-c1))] hover:before:opacity-80 motion-reduce:animate-none">
      <blockquote>
        <p class="text-[clamp(17px,2.2vw,21px)] leading-[1.6] font-medium tracking-[-0.01em] text-pretty">“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="mt-5 flex items-center gap-2.5 text-[13.5px] text-tbq06-mut">
        <span aria-hidden="true" class="size-2.5 flex-none rounded-full bg-[conic-gradient(var(--color-tbq06-c1),var(--color-tbq06-c2),var(--color-tbq06-c3),var(--color-tbq06-c1))]"></span>
        <span><strong class="font-semibold text-tbq06-ink">Marcus Webb</strong> · Design Engineer, Meridian</span>
      </figcaption>
    </figure>
    <p aria-hidden="true" class="rounded-full border border-white/12 px-3.5 py-2 font-tbq06-mono text-[11.5px] text-tbq06-mut">@property &lt;angle&gt; · padding-box / border-box · animate-tbq06-spin</p>
  </div>
</section>
@import "tailwindcss";

@theme {
  --color-tbq06-bg: #090b10;
  --color-tbq06-bg: oklch(0.15 0.012 270);
  --color-tbq06-glow: #291c3c80;
  --color-tbq06-glow: oklch(0.26 0.06 300 / 0.5);
  --color-tbq06-card: #13161d;
  --color-tbq06-card: oklch(0.2 0.015 270);
  --color-tbq06-ink: #f0f2f7;
  --color-tbq06-ink: oklch(0.96 0.008 270);
  --color-tbq06-mut: #acb1be;
  --color-tbq06-mut: oklch(0.76 0.02 270);
  --color-tbq06-c1: #fd736d;
  --color-tbq06-c1: oklch(0.72 0.17 25);
  --color-tbq06-c2: #d78de8;
  --color-tbq06-c2: oklch(0.75 0.15 320);
  --color-tbq06-c3: #53a3f2;
  --color-tbq06-c3: oklch(0.7 0.14 250);
  --font-tbq06-sans: 'Geist', system-ui, sans-serif;
  --font-tbq06-mono: 'JetBrains Mono', ui-monospace, monospace;
  --animate-tbq06-spin: tbq06-spin 7s linear infinite;

  @keyframes tbq06-spin {
    to {
      --tbq06-angle: 400deg;
    }
  }
}
/* Root rule: the full-bleed contract, stated in CSS as well as in the markup's utilities, so the
   stage is sized before the browser build compiles. */

.tbq-06 {
  width: 100%;
  min-height: 100svh;
}
/* Registered so the angle interpolates; inherits:true lets the before: glow read the same value. */

@property --tbq06-angle {
  syntax: '<angle>';
  initial-value: 40deg;
  inherits: true;
}

Setup — do this once per project

Skip this if your project already runs Tailwind v4.

1Load Tailwind
<script src="https://cdn.jsdelivr.net/npm/@tailwindcss/browser@4"></script>

Compiles in the browser — perfect for a CodePen or a quick test, not for production.

2Add the stylesheetRequiredthe @theme block is what makes the custom utilities exist
@import "tailwindcss";

@theme {
  --color-tbq06-bg: #090b10;
  --color-tbq06-bg: oklch(0.15 0.012 270);
  --color-tbq06-glow: #291c3c80;
  --color-tbq06-glow: oklch(0.26 0.06 300 / 0.5);
  --color-tbq06-card: #13161d;
  --color-tbq06-card: oklch(0.2 0.015 270);
  --color-tbq06-ink: #f0f2f7;
  --color-tbq06-ink: oklch(0.96 0.008 270);
  --color-tbq06-mut: #acb1be;
  --color-tbq06-mut: oklch(0.76 0.02 270);
  --color-tbq06-c1: #fd736d;
  --color-tbq06-c1: oklch(0.72 0.17 25);
  --color-tbq06-c2: #d78de8;
  --color-tbq06-c2: oklch(0.75 0.15 320);
  --color-tbq06-c3: #53a3f2;
  --color-tbq06-c3: oklch(0.7 0.14 250);
  --font-tbq06-sans: 'Geist', system-ui, sans-serif;
  --font-tbq06-mono: 'JetBrains Mono', ui-monospace, monospace;
  --animate-tbq06-spin: tbq06-spin 7s linear infinite;

  @keyframes tbq06-spin {
    to {
      --tbq06-angle: 400deg;
    }
  }
}
/* Root rule: the full-bleed contract, stated in CSS as well as in the markup's utilities, so the
   stage is sized before the browser build compiles. */

.tbq-06 {
  width: 100%;
  min-height: 100svh;
}
/* Registered so the angle interpolates; inherits:true lets the before: glow read the same value. */

@property --tbq06-angle {
  syntax: '<angle>';
  initial-value: 40deg;
  inherits: true;
}
3Fonts used by this demooptional — skip if you have your own type stack
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=Geist:wght@400..800&family=JetBrains+Mono:wght@400;500;600;700&display=swap">

What changed in the port

Which parts became utilities, which stayed CSS, and why.

  • The conic-gradient background moved into one arbitrary [background:…] property; underscores carry the spaces, commas pass through untouched.
  • @keyframes bq06-spin now lives inside @theme next to its --animate-tbq06-spin token — Tailwind only emits keyframes that a used animation references.
  • @property stayed in CSS: an at-rule has no utility spelling. It was changed from inherits:false to true so the new glow layer can follow the angle.
  • UI upgrade from the CSS original: a blurred copy of the gradient sits behind the card (before:blur-2xl before:opacity-45) and brightens on hover, so the border reads as light, not just a coloured line. Caption copy raised from 0.66 to 0.76.
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 demo 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 Tailwind Gradient Border Blockquote
Source: https://codefronts.com/snippets/tailwind-css-blockquotes/animated-tailwind-gradient-border-blockquote/

A quote card whose border is a rotating conic gradient, drawn with the two-background padding-box / border-box trick and animated by registering the angle with @property. The Tailwind port keeps the keyframes as an --animate-* theme token and adds a blurred glow twin of the border behind the card that brightens on hover.
## HTML
```html
<section class="tbq-06 w-full min-h-svh grid place-items-center bg-tbq06-bg bg-[radial-gradient(70%_55%_at_50%_120%,var(--color-tbq06-glow),transparent)] px-5 py-14 font-tbq06-sans text-tbq06-ink" aria-label="Animated gradient border blockquote demo">
  <div class="isolate grid w-full justify-items-center gap-8">
    <figure class="relative w-full max-w-[520px] animate-tbq06-spin rounded-[20px] border-[1.5px] border-transparent p-[clamp(24px,4vw,36px)] shadow-[0_24px_60px_oklch(0_0_0/0.45)] [background:linear-gradient(var(--color-tbq06-card),var(--color-tbq06-card))_padding-box,conic-gradient(from_var(--tbq06-angle),var(--color-tbq06-c1),var(--color-tbq06-c2),var(--color-tbq06-c3),var(--color-tbq06-c1))_border-box] before:absolute before:-inset-[1.5px] before:-z-10 before:rounded-[inherit] before:opacity-45 before:blur-2xl before:transition-opacity before:duration-500 before:[background:conic-gradient(from_var(--tbq06-angle),var(--color-tbq06-c1),var(--color-tbq06-c2),var(--color-tbq06-c3),var(--color-tbq06-c1))] hover:before:opacity-80 motion-reduce:animate-none">
      <blockquote>
        <p class="text-[clamp(17px,2.2vw,21px)] leading-[1.6] font-medium tracking-[-0.01em] text-pretty">“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="mt-5 flex items-center gap-2.5 text-[13.5px] text-tbq06-mut">
        <span aria-hidden="true" class="size-2.5 flex-none rounded-full bg-[conic-gradient(var(--color-tbq06-c1),var(--color-tbq06-c2),var(--color-tbq06-c3),var(--color-tbq06-c1))]"></span>
        <span><strong class="font-semibold text-tbq06-ink">Marcus Webb</strong> · Design Engineer, Meridian</span>
      </figcaption>
    </figure>
    <p aria-hidden="true" class="rounded-full border border-white/12 px-3.5 py-2 font-tbq06-mono text-[11.5px] text-tbq06-mut">@property &lt;angle&gt; · padding-box / border-box · animate-tbq06-spin</p>
  </div>
</section>
```
## CSS
```css
@import "tailwindcss";

@theme {
  --color-tbq06-bg: #090b10;
  --color-tbq06-bg: oklch(0.15 0.012 270);
  --color-tbq06-glow: #291c3c80;
  --color-tbq06-glow: oklch(0.26 0.06 300 / 0.5);
  --color-tbq06-card: #13161d;
  --color-tbq06-card: oklch(0.2 0.015 270);
  --color-tbq06-ink: #f0f2f7;
  --color-tbq06-ink: oklch(0.96 0.008 270);
  --color-tbq06-mut: #acb1be;
  --color-tbq06-mut: oklch(0.76 0.02 270);
  --color-tbq06-c1: #fd736d;
  --color-tbq06-c1: oklch(0.72 0.17 25);
  --color-tbq06-c2: #d78de8;
  --color-tbq06-c2: oklch(0.75 0.15 320);
  --color-tbq06-c3: #53a3f2;
  --color-tbq06-c3: oklch(0.7 0.14 250);
  --font-tbq06-sans: 'Geist', system-ui, sans-serif;
  --font-tbq06-mono: 'JetBrains Mono', ui-monospace, monospace;
  --animate-tbq06-spin: tbq06-spin 7s linear infinite;

  @keyframes tbq06-spin {
    to {
      --tbq06-angle: 400deg;
    }
  }
}
/* Root rule: the full-bleed contract, stated in CSS as well as in the markup's utilities, so the
   stage is sized before the browser build compiles. */

.tbq-06 {
  width: 100%;
  min-height: 100svh;
}
/* Registered so the angle interpolates; inherits:true lets the before: glow read the same value. */

@property --tbq06-angle {
  syntax: '<angle>';
  initial-value: 40deg;
  inherits: true;
}
```

How this works

Two backgrounds, one border. A transparent 1.5px border exposes the border-box layer; the solid padding-box layer covers everything inside it:

class="border-[1.5px] border-transparent animate-tbq06-spin
  [background:linear-gradient(var(--color-tbq06-card),var(--color-tbq06-card))_padding-box,
  conic-gradient(from_var(--tbq06-angle),…)_border-box]"

Why @property stays in CSS. Browsers cannot interpolate an unregistered custom property — it would jump from 40deg to 400deg. Registering it as <angle> makes the keyframe smooth, and there is no utility for an at-rule.

The glow is the same gradient, blurred. Because the property now inherits: true, the before: layer reads the parent's animated angle and its blur rotates in lockstep. hover:before:opacity-80 turns it up.

Make it yours

  • Speed: change 7s in the --animate-tbq06-spin token.
  • Thicker frame: border-2 or border-[3px]; keep the glow's before:-inset-[…] equal to it.
  • Static gradient border: drop animate-tbq06-spin and the @property block; the rest still works.

Gotchas — read before shipping

  • Without isolate on an ancestor, a -z-10 pseudo-element slips behind the section's own background and disappears.
  • Firefox added @property in 128; older engines show a static gradient border, which is a fine fallback.
  • motion-reduce:animate-none is on the card — the glow inherits the stopped angle, so both freeze together.

Browser support

ChromeSafariFirefoxEdge
111+16.4+128+111+

@property is Chrome 85 / Safari 16.4 / Firefox 128 — identical to Tailwind v4's own floor.

Techniques used in this demo

Utilitiesfirst-class utility or variant

Plain CSSno Tailwind surface

Search CodeFronts

Loading…