25 CSS Blockquotes21 / 25

CSS onlyMIT licensed

IDE Code Comment Style Blockquote CSS

A quote typeset as a JSDoc block comment inside a believable editor pane — line numbers generated by CSS counters (zero markup, renumbers itself when you edit), syntax tinting via a handful of token spans, a title bar with traffic lights, an active-line highlight, and a blinking caret. For developer blogs and changelogs, quoting a maxim as a docblock is native dress — and the counters technique is the actually-reusable part, the same trick that numbers real code blocks without a highlight library.

Published Updated

Live Demo
Try it

The code

<section class="bq-21" aria-label="IDE code comment style blockquote demo">
  <div class="bq-21__stage">
    <figure class="bq-21__editor">
      <div class="bq-21__bar">
        <span class="bq-21__dots" aria-hidden="true"><i></i><i></i><i></i></span>
        <p class="bq-21__file">principles.ts — engineering-handbook</p>
        <span class="bq-21__lang" aria-hidden="true">TypeScript</span>
      </div>
      <blockquote class="bq-21__quote">
        <code>
          <span class="bq-21__line"><span class="bq-21__cmt">/**</span></span>
          <span class="bq-21__line"><span class="bq-21__cmt"> * Every sufficiently senior engineer eventually</span></span>
          <span class="bq-21__line bq-21__line--active"><span class="bq-21__cmt"> * rewrites the same sentence:</span> <span class="bq-21__str">"make it work,"</span></span>
          <span class="bq-21__line"><span class="bq-21__str"> * "make it right, make it fast"</span> <span class="bq-21__cmt">— in that order,</span></span>
          <span class="bq-21__line"><span class="bq-21__cmt"> * and never all in the same pull request.</span></span>
          <span class="bq-21__line"><span class="bq-21__cmt"> *</span></span>
          <span class="bq-21__line"><span class="bq-21__cmt"> * </span><span class="bq-21__tag">@author</span><span class="bq-21__cmt"> Kent Beck (paraphrasing generations)</span></span>
          <span class="bq-21__line"><span class="bq-21__cmt"> * </span><span class="bq-21__tag">@since</span><span class="bq-21__cmt">  forever</span></span>
          <span class="bq-21__line"><span class="bq-21__cmt"> */</span><span class="bq-21__caret" aria-hidden="true"></span></span>
        </code>
      </blockquote>
      <figcaption class="bq-21__status"><span>UTF-8</span><span>Ln 9, Col 4</span><span>Spaces: 2</span></figcaption>
    </figure>
    <p class="bq-21__chip" aria-hidden="true">counter(line) gutter · user-select:none · steps(1) caret</p>
  </div>
</section>
.bq-21,
.bq-21 *,
.bq-21 *::before,
.bq-21 *::after {
  box-sizing: border-box;
  margin: 0;
  padding: 0;
}

.bq-21 {
  width: 100%;
  min-height: 100vh;
  min-height: 100svh;
  display: block;
  --bg: oklch(0.17 0.012 270);
  --pane: oklch(0.21 0.014 270);
  --bar: oklch(0.25 0.016 270);
  --ink: oklch(0.9 0.01 260);
  --lineno: oklch(0.48 0.015 265);
  --cmt: oklch(0.68 0.06 150);
  --tag: oklch(0.72 0.12 300);
  --str: oklch(0.78 0.12 75);
  --sel: oklch(0.3 0.03 265);
  font-family: 'Segoe UI',system-ui,sans-serif;
  color: var(--ink);
}

.bq-21__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(80% 60% at 50% -10%,oklch(0.22 0.03 280/.6),transparent),var(--bg);
  overflow-y: auto;
}

.bq-21__editor {
  width: min(100%,620px);
  border-radius: 14px;
  overflow: hidden;
  background: var(--pane);
  border: 1px solid oklch(1 0 0/.09);
  box-shadow: 0 26px 60px oklch(0 0 0/.5);
}

.bq-21__bar {
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 10px 14px;
  background: var(--bar);
  border-bottom: 1px solid oklch(0 0 0/.25);
}

.bq-21__dots {
  display: flex;
  gap: 7px;
}

.bq-21__dots i {
  width: 11px;
  height: 11px;
  border-radius: 99px;
}

.bq-21__dots i:nth-child(1) {
  background: oklch(0.65 0.18 25);
}

.bq-21__dots i:nth-child(2) {
  background: oklch(0.8 0.15 90);
}

.bq-21__dots i:nth-child(3) {
  background: oklch(0.72 0.17 145);
}

.bq-21__file {
  font-size: 12px;
  color: oklch(0.72 0.015 265);
  margin-inline: auto;
}

.bq-21__lang {
  font-family: ui-monospace,Menlo,Consolas,monospace;
  font-size: 10.5px;
  color: var(--lineno);
}

.bq-21__quote {
  padding: 16px 0 14px;
}

.bq-21__quote code {
  display: block;
  font-family: ui-monospace,Menlo,Consolas,'Cascadia Code',monospace;
  font-size: 13.5px;
  line-height: 1.7;
  counter-reset: bq21-line;
}

.bq-21__line {
  display: block;
  padding-right: 16px;
  counter-increment: bq21-line;
}

.bq-21__line::before {
  content: counter(bq21-line);
  display: inline-block;
  width: 3.4ch;
  margin-right: 1.8ch;
  text-align: right;
  color: var(--lineno);
  user-select: none;
  -webkit-user-select: none;
}

.bq-21__line--active {
  background: var(--sel);
}

.bq-21__line--active::before {
  color: var(--ink);
}

.bq-21__cmt {
  color: var(--cmt);
}

.bq-21__tag {
  color: var(--tag);
  font-weight: 600;
}

.bq-21__str {
  color: var(--str);
}

.bq-21__caret {
  display: inline-block;
  width: 2px;
  height: 1.1em;
  background: var(--ink);
  vertical-align: -.18em;
  margin-left: 2px;
  animation: bq21-blink 1.1s steps(1,end) infinite;
}

@keyframes bq21-blink {
  0%,
    49% {
    opacity: 1;
  }

  50%,
    100% {
    opacity: 0;
  }
}

.bq-21__status {
  display: flex;
  gap: 18px;
  justify-content: flex-end;
  padding: 8px 14px;
  background: var(--bar);
  border-top: 1px solid oklch(0 0 0/.25);
  font-family: ui-monospace,Menlo,Consolas,monospace;
  font-size: 10.5px;
  color: var(--lineno);
}

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

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

  .bq-21__caret {
    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: IDE Code Comment Style Blockquote CSS
Source: https://codefronts.com/snippets/css-blockquotes/code-comment/

A quote typeset as a JSDoc block comment inside a believable editor pane — line numbers generated by CSS counters (zero markup, renumbers itself when you edit), syntax tinting via a handful of token spans, a title bar with traffic lights, an active-line highlight, and a blinking caret. For developer blogs and changelogs, quoting a maxim as a docblock is native dress — and the counters technique is the actually-reusable part, the same trick that numbers real code blocks without a highlight library.
## HTML
```html
<section class="bq-21" aria-label="IDE code comment style blockquote demo">
  <div class="bq-21__stage">
    <figure class="bq-21__editor">
      <div class="bq-21__bar">
        <span class="bq-21__dots" aria-hidden="true"><i></i><i></i><i></i></span>
        <p class="bq-21__file">principles.ts — engineering-handbook</p>
        <span class="bq-21__lang" aria-hidden="true">TypeScript</span>
      </div>
      <blockquote class="bq-21__quote">
        <code>
          <span class="bq-21__line"><span class="bq-21__cmt">/**</span></span>
          <span class="bq-21__line"><span class="bq-21__cmt"> * Every sufficiently senior engineer eventually</span></span>
          <span class="bq-21__line bq-21__line--active"><span class="bq-21__cmt"> * rewrites the same sentence:</span> <span class="bq-21__str">"make it work,"</span></span>
          <span class="bq-21__line"><span class="bq-21__str"> * "make it right, make it fast"</span> <span class="bq-21__cmt">— in that order,</span></span>
          <span class="bq-21__line"><span class="bq-21__cmt"> * and never all in the same pull request.</span></span>
          <span class="bq-21__line"><span class="bq-21__cmt"> *</span></span>
          <span class="bq-21__line"><span class="bq-21__cmt"> * </span><span class="bq-21__tag">@author</span><span class="bq-21__cmt"> Kent Beck (paraphrasing generations)</span></span>
          <span class="bq-21__line"><span class="bq-21__cmt"> * </span><span class="bq-21__tag">@since</span><span class="bq-21__cmt">  forever</span></span>
          <span class="bq-21__line"><span class="bq-21__cmt"> */</span><span class="bq-21__caret" aria-hidden="true"></span></span>
        </code>
      </blockquote>
      <figcaption class="bq-21__status"><span>UTF-8</span><span>Ln 9, Col 4</span><span>Spaces: 2</span></figcaption>
    </figure>
    <p class="bq-21__chip" aria-hidden="true">counter(line) gutter · user-select:none · steps(1) caret</p>
  </div>
</section>
```
## CSS
```css
.bq-21,
.bq-21 *,
.bq-21 *::before,
.bq-21 *::after {
  box-sizing: border-box;
  margin: 0;
  padding: 0;
}

.bq-21 {
  width: 100%;
  min-height: 100vh;
  min-height: 100svh;
  display: block;
  --bg: oklch(0.17 0.012 270);
  --pane: oklch(0.21 0.014 270);
  --bar: oklch(0.25 0.016 270);
  --ink: oklch(0.9 0.01 260);
  --lineno: oklch(0.48 0.015 265);
  --cmt: oklch(0.68 0.06 150);
  --tag: oklch(0.72 0.12 300);
  --str: oklch(0.78 0.12 75);
  --sel: oklch(0.3 0.03 265);
  font-family: 'Segoe UI',system-ui,sans-serif;
  color: var(--ink);
}

.bq-21__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(80% 60% at 50% -10%,oklch(0.22 0.03 280/.6),transparent),var(--bg);
  overflow-y: auto;
}

.bq-21__editor {
  width: min(100%,620px);
  border-radius: 14px;
  overflow: hidden;
  background: var(--pane);
  border: 1px solid oklch(1 0 0/.09);
  box-shadow: 0 26px 60px oklch(0 0 0/.5);
}

.bq-21__bar {
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 10px 14px;
  background: var(--bar);
  border-bottom: 1px solid oklch(0 0 0/.25);
}

.bq-21__dots {
  display: flex;
  gap: 7px;
}

.bq-21__dots i {
  width: 11px;
  height: 11px;
  border-radius: 99px;
}

.bq-21__dots i:nth-child(1) {
  background: oklch(0.65 0.18 25);
}

.bq-21__dots i:nth-child(2) {
  background: oklch(0.8 0.15 90);
}

.bq-21__dots i:nth-child(3) {
  background: oklch(0.72 0.17 145);
}

.bq-21__file {
  font-size: 12px;
  color: oklch(0.72 0.015 265);
  margin-inline: auto;
}

.bq-21__lang {
  font-family: ui-monospace,Menlo,Consolas,monospace;
  font-size: 10.5px;
  color: var(--lineno);
}

.bq-21__quote {
  padding: 16px 0 14px;
}

.bq-21__quote code {
  display: block;
  font-family: ui-monospace,Menlo,Consolas,'Cascadia Code',monospace;
  font-size: 13.5px;
  line-height: 1.7;
  counter-reset: bq21-line;
}

.bq-21__line {
  display: block;
  padding-right: 16px;
  counter-increment: bq21-line;
}

.bq-21__line::before {
  content: counter(bq21-line);
  display: inline-block;
  width: 3.4ch;
  margin-right: 1.8ch;
  text-align: right;
  color: var(--lineno);
  user-select: none;
  -webkit-user-select: none;
}

.bq-21__line--active {
  background: var(--sel);
}

.bq-21__line--active::before {
  color: var(--ink);
}

.bq-21__cmt {
  color: var(--cmt);
}

.bq-21__tag {
  color: var(--tag);
  font-weight: 600;
}

.bq-21__str {
  color: var(--str);
}

.bq-21__caret {
  display: inline-block;
  width: 2px;
  height: 1.1em;
  background: var(--ink);
  vertical-align: -.18em;
  margin-left: 2px;
  animation: bq21-blink 1.1s steps(1,end) infinite;
}

@keyframes bq21-blink {
  0%,
    49% {
    opacity: 1;
  }

  50%,
    100% {
    opacity: 0;
  }
}

.bq-21__status {
  display: flex;
  gap: 18px;
  justify-content: flex-end;
  padding: 8px 14px;
  background: var(--bar);
  border-top: 1px solid oklch(0 0 0/.25);
  font-family: ui-monospace,Menlo,Consolas,monospace;
  font-size: 10.5px;
  color: var(--lineno);
}

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

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

  .bq-21__caret {
    animation: none;
  }
}
```

How this works

Line numbers without markup — each line is a plain element; counters do the numbering:

.editor code{ counter-reset:bq21-line; }
.editor .line{ counter-increment:bq21-line; }
.editor .line::before{
  content:counter(bq21-line);
  display:inline-block; width:2.6ch;
  margin-right:1.6ch; text-align:right;
  color:var(--line-no);
  user-select:none;                /* copy code, not numbers */
}

Insert, delete or reorder lines and the gutter renumbers itself — this is why counters beat hand-written spans and beat CSS-generated background images. user-select:none on the pseudo-element keeps the numbers out of the clipboard, the detail that separates docs sites people love from ones they curse.

The quote itself lives in real docblock grammar — /**, leading *, @author and @since tags — with each token type on a span tinted from the theme's map (comment green-gray, tag purple, string amber). The blinking caret is a 2px ::after animated in steps(1) — a real terminal caret snaps rather than fades — and it holds steady under reduced motion. The pane is still a semantic <figure>/<blockquote>: the costume is monospace, the markup is a citation.

Make it yours

  • Theme: the whole editor reads from six --tok-* custom properties — port your VS Code theme's hexes in and the pane re-skins (a One Dark and a light Solarized set are commented in the CSS).
  • Language dress: swap the docblock for a Python docstring ('''…''') or a Rust //! module comment — change the token spans, keep the counter gutter.
  • Active line: move the .is-active class to any line; the highlight bar + brighter number follow. Wire it to :hover per line for an interactive read-along feel.
  • Real code blocks: lift the counter gutter verbatim onto your pre>code blocks — it composes with any syntax highlighter that emits line elements.

Gotchas — read before shipping

  • Counters reset per counter-reset scope: nest two editors and forgetting a fresh counter-reset on the inner one continues the outer numbering.
  • Generated content is read by some screen readers despite user-select:none — add aria-hidden to the gutter via speak:never fallbacks, or accept numbers being announced; test your audience.
  • Monospace at small sizes needs MORE line-height, not less — 1.7 here; tight code quotes read as a wall.
  • steps(1) caret animation must be end-aligned (steps(1,end)) or the caret spends half its cycle invisible on load.

Browser support

ChromeSafariFirefoxEdge
111+16.4+113+111+

CSS counters are CSS 2.1 — universal. Everything else is cosmetics; the floor is oklch tokens. No JS, no highlight library.

Search CodeFronts

Loading…