25 CSS Blockquotes02 / 25

CSS onlyMIT licensed

Tailwind CSS Blockquote Component

The Tailwind UI testimonial pattern — dark slate card, ring-1 border, avatar with an indigo ring, quote in font-semibold — rebuilt twice: once as the exact utility-class markup Tailwind users search for, and once as the plain-CSS translation that runs with zero build step (that's what renders here). Every utility is annotated with its CSS equivalent, so the demo doubles as a Tailwind-to-vanilla dictionary: ring-1 ring-white/10 is a box-shadow, not a border; divide-x is a sibling selector; the avatar ring is two stacked shadows.

Published

Live Demo
Try it

The code

<section class="bq-02" aria-label="Tailwind style blockquote component demo">
  <div class="bq-02__stage">
    <figure class="bq-02__card">
      <svg class="bq-02__logo" viewBox="0 0 32 32" width="30" height="30" aria-hidden="true"><path d="M16 4l12 7v10l-12 7L4 21V11z" fill="none" stroke="currentColor" stroke-width="2.4"/><circle cx="16" cy="16" r="4.2" fill="currentColor"/></svg>
      <blockquote class="bq-02__text-lg">
        <p>“Utility classes made our design system reviewable. Every visual decision is right there in the diff — no hunting through stylesheets to find who set that margin.”</p>
      </blockquote>
      <figcaption class="bq-02__row">
        <img class="bq-02__avatar" src="https://images.unsplash.com/photo-1494790108377-be9c29b29330?q=80&w=160&auto=format&fit=crop" alt="" width="40" height="40" loading="lazy">
        <div class="bq-02__who">
          <div class="bq-02__name">Judith Black</div>
          <div class="bq-02__role">CEO of Workcation</div>
        </div>
        <span class="bq-02__x" aria-hidden="true"></span>
        <div class="bq-02__meta">ring-1 · ring-white/10</div>
      </figcaption>
    </figure>
    <p class="bq-02__chip" aria-hidden="true">Tailwind pattern → plain CSS · ring = box-shadow · container query re-stack</p>
  </div>
</section>
.bq-02,
.bq-02 *,
.bq-02 *::before,
.bq-02 *::after {
  box-sizing: border-box;
  margin: 0;
  padding: 0;
}

.bq-02 {
  width: 100%;
  min-height: 100vh;
  min-height: 100svh;
  display: block;
  --bg: oklch(0.14 0.015 265);
  --card: oklch(0.21 0.02 265);
  --ink: oklch(0.97 0.005 250);
  --mut: oklch(0.67 0.02 260);
  --acc: oklch(0.6 0.19 275);
  font-family: 'Segoe UI',system-ui,sans-serif;
  color: var(--ink);
}

.bq-02__stage {
  width: 100%;
  height: 100vh;
  height: 100svh;
  display: grid;
  place-items: center;
  align-content: center;
  gap: 24px;
  padding: clamp(20px,5vw,56px);
  background: radial-gradient(90% 70% at 50% 110%,oklch(0.22 0.05 275/.6),transparent),var(--bg);
  overflow-y: auto;
}

.bq-02__card {
  width: min(100%,36rem);
  padding: 2rem;
  border-radius: 1rem;
  background: var(--card);
  box-shadow: 0 0 0 1px oklch(1 0 0/.1),0 22px 50px oklch(0 0 0/.42);
  container-type: inline-size;
}

.bq-02__logo {
  color: var(--acc);
  margin-bottom: 1.25rem;
  display: block;
}

.bq-02__text-lg p {
  font-size: 1.125rem;
  line-height: 1.65;
  font-weight: 600;
  letter-spacing: -.008em;
  text-wrap: pretty;
}

.bq-02__row {
  display: flex;
  align-items: center;
  gap: 1rem;
  margin-top: 1.5rem;
}

.bq-02__avatar {
  width: 2.5rem;
  height: 2.5rem;
  border-radius: 99px;
  object-fit: cover;
  flex: none;
  background: linear-gradient(135deg,oklch(0.5 0.1 275),oklch(0.35 0.06 300));
  box-shadow: 0 0 0 2px var(--acc),0 0 0 4px color-mix(in oklch,var(--acc) 30%,transparent);
}

.bq-02__name {
  font-size: .9rem;
  font-weight: 650;
}

.bq-02__role {
  font-size: .82rem;
  color: var(--mut);
  margin-top: 1px;
}

.bq-02__x {
  width: 1px;
  height: 1.6rem;
  background: oklch(1 0 0/.12);
  margin-left: auto;
}

.bq-02__meta {
  font-family: ui-monospace,Menlo,Consolas,monospace;
  font-size: .7rem;
  color: var(--mut);
}

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

@container (width < 420px) {
  .bq-02__row {
    flex-wrap: wrap;
  }

  .bq-02__x,
    .bq-02__meta {
    display: none;
  }
}

@media (prefers-reduced-motion: reduce) {
  .bq-02 * {
    transition-duration: .01ms !important;
  }
}
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: Tailwind CSS Blockquote Component
Source: https://codefronts.com/snippets/css-blockquotes/tailwind-css-blockquote-component/

The Tailwind UI testimonial pattern — dark slate card, ring-1 border, avatar with an indigo ring, quote in font-semibold — rebuilt twice: once as the exact utility-class markup Tailwind users search for, and once as the plain-CSS translation that runs with zero build step (that's what renders here). Every utility is annotated with its CSS equivalent, so the demo doubles as a Tailwind-to-vanilla dictionary: ring-1 ring-white/10 is a box-shadow, not a border; divide-x is a sibling selector; the avatar ring is two stacked shadows.
## HTML
```html
<section class="bq-02" aria-label="Tailwind style blockquote component demo">
  <div class="bq-02__stage">
    <figure class="bq-02__card">
      <svg class="bq-02__logo" viewBox="0 0 32 32" width="30" height="30" aria-hidden="true"><path d="M16 4l12 7v10l-12 7L4 21V11z" fill="none" stroke="currentColor" stroke-width="2.4"/><circle cx="16" cy="16" r="4.2" fill="currentColor"/></svg>
      <blockquote class="bq-02__text-lg">
        <p>“Utility classes made our design system reviewable. Every visual decision is right there in the diff — no hunting through stylesheets to find who set that margin.”</p>
      </blockquote>
      <figcaption class="bq-02__row">
        <img class="bq-02__avatar" src="https://images.unsplash.com/photo-1494790108377-be9c29b29330?q=80&w=160&auto=format&fit=crop" alt="" width="40" height="40" loading="lazy">
        <div class="bq-02__who">
          <div class="bq-02__name">Judith Black</div>
          <div class="bq-02__role">CEO of Workcation</div>
        </div>
        <span class="bq-02__x" aria-hidden="true"></span>
        <div class="bq-02__meta">ring-1 · ring-white/10</div>
      </figcaption>
    </figure>
    <p class="bq-02__chip" aria-hidden="true">Tailwind pattern → plain CSS · ring = box-shadow · container query re-stack</p>
  </div>
</section>
```
## CSS
```css
.bq-02,
.bq-02 *,
.bq-02 *::before,
.bq-02 *::after {
  box-sizing: border-box;
  margin: 0;
  padding: 0;
}

.bq-02 {
  width: 100%;
  min-height: 100vh;
  min-height: 100svh;
  display: block;
  --bg: oklch(0.14 0.015 265);
  --card: oklch(0.21 0.02 265);
  --ink: oklch(0.97 0.005 250);
  --mut: oklch(0.67 0.02 260);
  --acc: oklch(0.6 0.19 275);
  font-family: 'Segoe UI',system-ui,sans-serif;
  color: var(--ink);
}

.bq-02__stage {
  width: 100%;
  height: 100vh;
  height: 100svh;
  display: grid;
  place-items: center;
  align-content: center;
  gap: 24px;
  padding: clamp(20px,5vw,56px);
  background: radial-gradient(90% 70% at 50% 110%,oklch(0.22 0.05 275/.6),transparent),var(--bg);
  overflow-y: auto;
}

.bq-02__card {
  width: min(100%,36rem);
  padding: 2rem;
  border-radius: 1rem;
  background: var(--card);
  box-shadow: 0 0 0 1px oklch(1 0 0/.1),0 22px 50px oklch(0 0 0/.42);
  container-type: inline-size;
}

.bq-02__logo {
  color: var(--acc);
  margin-bottom: 1.25rem;
  display: block;
}

.bq-02__text-lg p {
  font-size: 1.125rem;
  line-height: 1.65;
  font-weight: 600;
  letter-spacing: -.008em;
  text-wrap: pretty;
}

.bq-02__row {
  display: flex;
  align-items: center;
  gap: 1rem;
  margin-top: 1.5rem;
}

.bq-02__avatar {
  width: 2.5rem;
  height: 2.5rem;
  border-radius: 99px;
  object-fit: cover;
  flex: none;
  background: linear-gradient(135deg,oklch(0.5 0.1 275),oklch(0.35 0.06 300));
  box-shadow: 0 0 0 2px var(--acc),0 0 0 4px color-mix(in oklch,var(--acc) 30%,transparent);
}

.bq-02__name {
  font-size: .9rem;
  font-weight: 650;
}

.bq-02__role {
  font-size: .82rem;
  color: var(--mut);
  margin-top: 1px;
}

.bq-02__x {
  width: 1px;
  height: 1.6rem;
  background: oklch(1 0 0/.12);
  margin-left: auto;
}

.bq-02__meta {
  font-family: ui-monospace,Menlo,Consolas,monospace;
  font-size: .7rem;
  color: var(--mut);
}

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

@container (width < 420px) {
  .bq-02__row {
    flex-wrap: wrap;
  }

  .bq-02__x,
    .bq-02__meta {
    display: none;
  }
}

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

How this works

If you have Tailwind, this is the canonical component — memorize the shape, not the classes:

<figure class="rounded-2xl bg-slate-900 p-8 ring-1 ring-white/10">
  <blockquote class="text-lg font-semibold text-white">
    <p>“Tailwind made our design system reviewable…”</p>
  </blockquote>
  <figcaption class="mt-6 flex items-center gap-x-4">
    <img class="h-10 w-10 rounded-full ring-2 ring-indigo-500" src="…" alt="">
    <div><div class="font-semibold text-white">Judith Black</div>
      <div class="text-slate-400">CEO of Workcation</div></div>
  </figcaption>
</figure>

The vanilla translation is where the learning is. ring-1 ring-white/10 is not a border — it compiles to box-shadow:0 0 0 1px rgb(255 255 255/.1), which is why rings never change layout size. rounded-2xl is border-radius:1rem; p-8 is padding:2rem; the avatar's ring-2 ring-indigo-500 stacks a second shadow outside the first. The demo's stylesheet writes each of these out under .bq-02 classes named after the utilities they mirror, so you can diff the two mentally line by line.

One upgrade over the stock Tailwind example: the card is a container query subject, so at under 420px of available space (not viewport) the figcaption re-stacks vertically — the component adapts wherever you paste it, which utilities alone can't express without arbitrary variants.

Make it yours

  • Light mode: swap --card to white, --ink to slate-900 and the ring to ring-slate-900/10 — the shadow trick works identically on light backgrounds.
  • Brand accent: the indigo ring and the logo mark both read from --acc; set it to your brand hue in oklch and the component re-skins in one line.
  • Company logo row: Tailwind UI's variant adds a logo above the quote — drop an <img class='h-8'> (height:2rem) as the figure's first child.
  • Grid of three: the card is width-capped at 36rem; remove the max-width and place three in a grid-template-columns:repeat(3,1fr) for the classic testimonial wall.

Gotchas — read before shipping

  • ring-* utilities are box-shadows: adding a real border on top double-strokes the card. Pick one system — this demo uses shadows exactly like Tailwind does.
  • text-lg font-semibold on the quote but plain weight on the attribution is the pattern's whole hierarchy — flattening the weights is what makes DIY versions look off.
  • Tailwind's slate-900 is a blue-tinted near-black; if your vanilla translation uses pure #111 the card looks dead. The demo uses oklch(0.21 0.02 265) to match the tint.
  • The <img> needs empty alt (alt='') — the name is already adjacent text; announcing 'photo of Judith Black, Judith Black' is repetition, not accessibility.

Browser support

ChromeSafariFirefoxEdge
114+17.5+121+114+

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

Everything here is 2021-era CSS (box-shadow rings, border-radius, flex). The floor comes from oklch() tokens and the container query re-stack, which degrades to the horizontal layout.

Techniques used in this demo

Search CodeFronts

Loading…