25 CSS Blockquotes22 / 25

CSS onlyMIT licensed

Markdown Compatible HTML CSS Blockquote

The constraint every blog platform imposes: Markdown emits bare <blockquote> tags — no classes, no wrappers, no hooks — so all styling must hang off element selectors alone. This demo styles that raw output beautifully, handles the case tutorials always skip (nested > > quotes, styled by depth with descendant selectors and a deepening color scale), auto-detects GitHub-style [!NOTE] alert syntax with :has() where available, and shows trailing em-dash attribution styled via p:last-child — all with selectors you can paste into any Markdown pipeline's stylesheet.

Published Updated

Live Demo
Try it

The code

<section class="bq-22" aria-label="Markdown compatible blockquote styling demo">
  <div class="bq-22__stage">
    <article class="bq-22__md">
      <p class="bq-22__src" aria-hidden="true">README.md · rendered from bare Markdown — no classes on any quote</p>
      <h1>Contributing guide</h1>
      <p>Before opening a pull request, read the review philosophy that anchors this repo:</p>
      <blockquote>
        <p>Reviews exist to move knowledge, not to gate merges. Approve when you would be comfortable maintaining the change yourself.</p>
        <blockquote>
          <p>And when you wouldn't be? Say precisely why. <em>"I don't like it"</em> is a feeling, not a review.</p>
          <blockquote>
            <p>Feelings are data too — but label them as such.</p>
          </blockquote>
        </blockquote>
        <p>— The maintainers, <cite>REVIEWING.md</cite></p>
      </blockquote>
      <blockquote class="bq-22__alert">
        <p><strong>Note</strong></p>
        <p>First-time contributors: CI runs the full matrix on your fork — expect ~9 minutes before the green check.</p>
      </blockquote>
    </article>
    <p class="bq-22__chip" aria-hidden="true">bare-element selectors · depth via token re-pointing · p:last-child attribution</p>
  </div>
</section>
.bq-22,
.bq-22 *,
.bq-22 *::before,
.bq-22 *::after {
  box-sizing: border-box;
  margin: 0;
  padding: 0;
}

.bq-22 {
  width: 100%;
  min-height: 100vh;
  min-height: 100svh;
  display: block;
  --bg: oklch(0.98 0.004 250);
  --ink: oklch(0.25 0.015 255);
  --quiet: oklch(0.42 0.015 255);
  --mut: oklch(0.55 0.015 255);
  --q1: oklch(0.6 0.13 255);
  --q2: oklch(0.62 0.13 305);
  --q3: oklch(0.63 0.13 355);
  --note: oklch(0.62 0.14 250);
  font-family: 'Segoe UI',system-ui,sans-serif;
  color: var(--ink);
}

.bq-22__stage {
  width: 100%;
  height: 100vh;
  height: 100svh;
  display: grid;
  place-items: center;
  align-content: center;
  gap: 24px;
  padding: clamp(20px,5vw,56px);
  background: var(--bg);
  overflow-y: auto;
}

.bq-22__md {
  max-width: 60ch;
  font-size: 15.5px;
  line-height: 1.7;
}

.bq-22__src {
  font-family: ui-monospace,Menlo,Consolas,monospace;
  font-size: 11px;
  color: var(--mut);
  margin-bottom: 14px;
}

.bq-22__md h1 {
  font-size: clamp(22px,3vw,28px);
  letter-spacing: -.02em;
  margin-bottom: 12px;
  padding-bottom: 8px;
  border-bottom: 1px solid oklch(0 0 0/.09);
}

.bq-22__md>p {
  color: var(--quiet);
  text-wrap: pretty;
}

.bq-22__md blockquote {
  border-inline-start: 3px solid var(--q1);
  background: color-mix(in oklch,var(--q1) 7%,transparent);
  border-radius: 0 8px 8px 0;
  padding: .85em 1.1em;
  margin-block: 1.2em;
}

.bq-22__md blockquote p {
  font-size: 15px;
  color: var(--quiet);
  text-wrap: pretty;
}

.bq-22__md blockquote p+p,
.bq-22__md blockquote blockquote {
  margin-top: .7em;
}

.bq-22__md blockquote blockquote {
  --q1: var(--q2);
  margin-block: .7em .2em;
}

.bq-22__md blockquote blockquote blockquote {
  --q1: var(--q3);
}

.bq-22__md blockquote>p:last-child:not(:only-child) {
  font-size: 13px;
  color: var(--mut);
  text-align: right;
  margin-top: .8em;
}

.bq-22__md blockquote cite {
  font-style: italic;
}

.bq-22__alert {
  --q1: var(--note);
}

.bq-22__alert>p:first-child {
  display: flex;
  align-items: center;
  gap: 7px;
  font-weight: 700;
  color: var(--note);
  font-size: 13.5px;
}

.bq-22__alert>p:first-child::before {
  content: '';
  width: 15px;
  height: 15px;
  border-radius: 99px;
  border: 1.6px solid currentColor;
  background: radial-gradient(circle at 50% 72%,currentColor 1.2px,transparent 1.6px),radial-gradient(circle at 50% 34%,currentColor 3px,transparent 3.4px);
  flex: none;
  opacity: .9;
}

.bq-22__alert>p:last-child:not(:only-child) {
  font-size: 15px;
  color: var(--quiet);
  text-align: left;
  margin-top: .4em;
}

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

@media (prefers-reduced-motion: reduce) {
  .bq-22 * {
    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: Markdown Compatible HTML CSS Blockquote
Source: https://codefronts.com/snippets/css-blockquotes/markdown-quote/

The constraint every blog platform imposes: Markdown emits bare <blockquote> tags — no classes, no wrappers, no hooks — so all styling must hang off element selectors alone. This demo styles that raw output beautifully, handles the case tutorials always skip (nested > > quotes, styled by depth with descendant selectors and a deepening color scale), auto-detects GitHub-style [!NOTE] alert syntax with :has() where available, and shows trailing em-dash attribution styled via p:last-child — all with selectors you can paste into any Markdown pipeline's stylesheet.
## HTML
```html
<section class="bq-22" aria-label="Markdown compatible blockquote styling demo">
  <div class="bq-22__stage">
    <article class="bq-22__md">
      <p class="bq-22__src" aria-hidden="true">README.md · rendered from bare Markdown — no classes on any quote</p>
      <h1>Contributing guide</h1>
      <p>Before opening a pull request, read the review philosophy that anchors this repo:</p>
      <blockquote>
        <p>Reviews exist to move knowledge, not to gate merges. Approve when you would be comfortable maintaining the change yourself.</p>
        <blockquote>
          <p>And when you wouldn't be? Say precisely why. <em>"I don't like it"</em> is a feeling, not a review.</p>
          <blockquote>
            <p>Feelings are data too — but label them as such.</p>
          </blockquote>
        </blockquote>
        <p>— The maintainers, <cite>REVIEWING.md</cite></p>
      </blockquote>
      <blockquote class="bq-22__alert">
        <p><strong>Note</strong></p>
        <p>First-time contributors: CI runs the full matrix on your fork — expect ~9 minutes before the green check.</p>
      </blockquote>
    </article>
    <p class="bq-22__chip" aria-hidden="true">bare-element selectors · depth via token re-pointing · p:last-child attribution</p>
  </div>
</section>
```
## CSS
```css
.bq-22,
.bq-22 *,
.bq-22 *::before,
.bq-22 *::after {
  box-sizing: border-box;
  margin: 0;
  padding: 0;
}

.bq-22 {
  width: 100%;
  min-height: 100vh;
  min-height: 100svh;
  display: block;
  --bg: oklch(0.98 0.004 250);
  --ink: oklch(0.25 0.015 255);
  --quiet: oklch(0.42 0.015 255);
  --mut: oklch(0.55 0.015 255);
  --q1: oklch(0.6 0.13 255);
  --q2: oklch(0.62 0.13 305);
  --q3: oklch(0.63 0.13 355);
  --note: oklch(0.62 0.14 250);
  font-family: 'Segoe UI',system-ui,sans-serif;
  color: var(--ink);
}

.bq-22__stage {
  width: 100%;
  height: 100vh;
  height: 100svh;
  display: grid;
  place-items: center;
  align-content: center;
  gap: 24px;
  padding: clamp(20px,5vw,56px);
  background: var(--bg);
  overflow-y: auto;
}

.bq-22__md {
  max-width: 60ch;
  font-size: 15.5px;
  line-height: 1.7;
}

.bq-22__src {
  font-family: ui-monospace,Menlo,Consolas,monospace;
  font-size: 11px;
  color: var(--mut);
  margin-bottom: 14px;
}

.bq-22__md h1 {
  font-size: clamp(22px,3vw,28px);
  letter-spacing: -.02em;
  margin-bottom: 12px;
  padding-bottom: 8px;
  border-bottom: 1px solid oklch(0 0 0/.09);
}

.bq-22__md>p {
  color: var(--quiet);
  text-wrap: pretty;
}

.bq-22__md blockquote {
  border-inline-start: 3px solid var(--q1);
  background: color-mix(in oklch,var(--q1) 7%,transparent);
  border-radius: 0 8px 8px 0;
  padding: .85em 1.1em;
  margin-block: 1.2em;
}

.bq-22__md blockquote p {
  font-size: 15px;
  color: var(--quiet);
  text-wrap: pretty;
}

.bq-22__md blockquote p+p,
.bq-22__md blockquote blockquote {
  margin-top: .7em;
}

.bq-22__md blockquote blockquote {
  --q1: var(--q2);
  margin-block: .7em .2em;
}

.bq-22__md blockquote blockquote blockquote {
  --q1: var(--q3);
}

.bq-22__md blockquote>p:last-child:not(:only-child) {
  font-size: 13px;
  color: var(--mut);
  text-align: right;
  margin-top: .8em;
}

.bq-22__md blockquote cite {
  font-style: italic;
}

.bq-22__alert {
  --q1: var(--note);
}

.bq-22__alert>p:first-child {
  display: flex;
  align-items: center;
  gap: 7px;
  font-weight: 700;
  color: var(--note);
  font-size: 13.5px;
}

.bq-22__alert>p:first-child::before {
  content: '';
  width: 15px;
  height: 15px;
  border-radius: 99px;
  border: 1.6px solid currentColor;
  background: radial-gradient(circle at 50% 72%,currentColor 1.2px,transparent 1.6px),radial-gradient(circle at 50% 34%,currentColor 3px,transparent 3.4px);
  flex: none;
  opacity: .9;
}

.bq-22__alert>p:last-child:not(:only-child) {
  font-size: 15px;
  color: var(--quiet);
  text-align: left;
  margin-top: .4em;
}

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

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

How this works

No classes allowed — depth is the only signal, and descendant selectors read it:

.md blockquote{
  border-inline-start:3px solid var(--q1);
  background:color-mix(in oklch, var(--q1) 7%, transparent);
  padding:.85em 1.1em; margin-block:1.2em;
}
.md blockquote blockquote{         /* > >  */
  --q1:var(--q2);                   /* re-point the token */
  margin:.8em 0 .2em;
}
.md blockquote blockquote blockquote{ --q1:var(--q3); }

The elegant move is re-pointing the custom property instead of restating rules: every visual (border, wash, marker) derives from --q1, so each nesting level only overrides the token and inherits the rest. Email-thread-style depth coloring for free, to any depth you define tokens for.

The attribution convention: Markdown authors end quotes with a — Name paragraph; blockquote > p:last-child:not(:only-child) targets exactly that line (only when the quote has more than one paragraph), letting you set it smaller, right-aligned and de-italicized — attribution styling with zero markup cooperation. And where :has() exists, blockquote:has(> p:first-child > strong:first-child)-class heuristics can dress GitHub alert syntax; the demo shows the [!NOTE] pattern already compiled the way GitHub renders it, styled by the same stylesheet as everything else.

Make it yours

  • Depth palette: --q1/--q2/--q3 are three steps of one hue here; use three different hues for email-client-style thread colors.
  • Platform paste: on WordPress target .entry-content blockquote, on Ghost .gh-content blockquote — same rules, scoped to the content wrapper your platform gives you.
  • Compact mode: for comment sections, halve the paddings and drop the wash — depth borders alone carry the threading.
  • Attribution flourish: give the p:last-child an ::before of '\2014\00a0' so authors don't even need to type the dash consistently — the CSS normalizes it.

Gotchas — read before shipping

  • Markdown renderers wrap quote text in <p> — style blockquote p, not blockquote's own text node, or margins double up.
  • The p:last-child attribution heuristic fires on any final paragraph — the :not(:only-child) guard keeps single-paragraph quotes from being demoted to attribution styling.
  • Sanitizers on most platforms strip classes and style attributes from user Markdown — that's WHY this must work from bare element selectors; don't design around hooks you'll never receive.
  • Nested quotes inherit font-size: set a fixed rem size on blockquote p, or three levels of 1.05em compounding gives you accidentally-growing text.

Browser support

ChromeSafariFirefoxEdge
114+17.5+121+114+

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

Descendant selectors and custom-property re-pointing are universal; color-mix sets the practical floor. The :has() alert detection is progressive (Chrome 105 / Safari 15.4 / Firefox 121) and optional.

Search CodeFronts

Loading…