Tailwind + JSMIT licensed

Tailwind Vertical Rhythm with a Consistent Baseline

An article column where every line and block boundary lands on a 1.5rem rhythm, set once as --tmu07-rhythm and consumed through leading-(--tmu07-rhythm) and mb-(--tmu07-rhythm). Press the button (or G) to draw the baseline grid over it — a data-grid: variant swaps in a repeating-linear-gradient background, and the image block shows where strict rhythm stops being free.

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="tmu-07 relative w-full min-h-svh bg-tmu07-bg font-tmu07-ui text-tmu07-ink antialiased" aria-labelledby="tmu07-title">
  <div class="absolute top-4 right-4 z-10">
    <input id="tmu07-theme" type="checkbox" class="peer sr-only">
    <label for="tmu07-theme" class="grid size-11 cursor-pointer place-items-center rounded-full border border-tmu07-line bg-tmu07-surface text-tmu07-ink-2 shadow-[0_1px_2px_rgb(0_0_0/0.06)] transition-[color,rotate] duration-300 hover:text-tmu07-ink peer-checked:rotate-180 peer-focus-visible:outline-2 peer-focus-visible:outline-offset-2 peer-focus-visible:outline-tmu07-acc motion-reduce:transition-none">
      <svg class="size-5" viewBox="0 0 24 24" aria-hidden="true" focusable="false"><circle cx="12" cy="12" r="8" fill="none" stroke="currentColor" stroke-width="1.6"/><path d="M12 4a8 8 0 0 0 0 16Z" fill="currentColor"/></svg>
      <span class="sr-only">Switch colour theme</span>
    </label>
  </div>
  <div class="mx-auto grid max-w-[60rem] gap-12 px-6 pt-20 pb-24 md:px-12">
    <header class="grid max-w-[62ch] gap-3">
      <p class="inline-flex items-center gap-2.5 font-tmu07-mono text-xs font-medium uppercase tracking-[0.14em] text-tmu07-ink-2"><span class="tabular-nums text-tmu07-acc">07</span><span aria-hidden="true" class="h-px w-6 bg-tmu07-line"></span>Rhythm</p>
      <h2 id="tmu07-title" class="text-[clamp(1.75rem,1.2rem+2vw,2.75rem)] font-semibold leading-[1.08] tracking-[-0.03em] text-balance">One unit, every line</h2>
      <p class="text-[1.0625rem] leading-relaxed text-tmu07-ink-2 text-pretty">Body lines take one unit, headings take two, gaps take one. Turn the overlay on: every block starts on a rule.</p>
    </header>
    <div class="flex flex-wrap items-center gap-x-4 gap-y-3">
      <button id="tmu07-grid-btn" type="button" aria-pressed="false" class="group inline-flex min-h-11 min-w-11 items-center gap-2 rounded-md border border-tmu07-line bg-tmu07-surface px-4 text-sm font-medium text-tmu07-ink cursor-pointer shadow-[0_1px_2px_rgb(0_0_0/0.05)] transition-[border-color,color] duration-150 hover:border-tmu07-acc aria-pressed:border-tmu07-acc aria-pressed:text-tmu07-acc focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-tmu07-acc motion-reduce:transition-none">
        <svg class="size-[18px] flex-none" viewBox="0 0 24 24" aria-hidden="true" focusable="false"><path d="M3 6h18M3 12h18M3 18h18" fill="none" stroke="currentColor" stroke-width="1.6"/></svg>
        Baseline overlay
        <kbd class="ml-1 rounded border border-tmu07-line px-1.5 font-tmu07-mono text-[0.6875rem] text-tmu07-ink-2 group-aria-pressed:border-tmu07-acc group-aria-pressed:text-tmu07-acc">G</kbd>
      </button>
      <p class="text-[0.8125rem] tabular-nums text-tmu07-ink-2">Unit <b class="font-semibold text-tmu07-ink">1.5rem</b> &middot; 24px at a 16px root</p>
    </div>
    <article id="tmu07-article" class="relative min-w-0 max-w-[66ch] font-tmu07-serif text-[1.0625rem] leading-(--tmu07-rhythm) text-pretty data-grid:bg-[repeating-linear-gradient(to_bottom,var(--color-tmu07-grid)_0,var(--color-tmu07-grid)_1px,transparent_1px,transparent_var(--tmu07-rhythm))]">
      <h3 class="mb-(--tmu07-rhythm) text-2xl font-semibold leading-[calc(var(--tmu07-rhythm)*2)] tracking-[-0.018em] text-balance">Rhythm survives real content, grids do not</h3>
      <p class="mb-(--tmu07-rhythm) text-[1.1875rem] leading-[calc(var(--tmu07-rhythm)*1.5)]">Set the rhythm once and the column composes itself. Body copy takes one unit per line, a heading takes two, and the gap between blocks takes one &mdash; so the eye reads order it cannot name.</p>
      <p class="mb-(--tmu07-rhythm)">Turn the overlay on and the mechanism becomes obvious: every line of prose sits on a rule, and every block boundary lands on one too. Nothing here is hand-nudged. The whole column is derived from a single custom property, which means the rhythm can be retuned for a different reading size in one edit.</p>
      <h4 class="mt-[calc(var(--tmu07-rhythm)*2)] mb-(--tmu07-rhythm) font-tmu07-ui text-[0.8125rem] font-bold uppercase leading-(--tmu07-rhythm) tracking-[0.1em] text-tmu07-ink-2">Where it stops being free</h4>
      <p class="mb-(--tmu07-rhythm)">The image below is 3:2, which is not a multiple of anything. Its caption is smaller than the body, so its own line height is not a whole unit either. A strict per-line grid would need padding calculated from the image height &mdash; a value that changes with the viewport, so the correction cannot be static.</p>
      <figure class="mt-[calc(var(--tmu07-rhythm)*2)] mb-(--tmu07-rhythm)">
        <div role="img" aria-label="Tonal placeholder standing in for an article image, 3 by 2" class="mb-(--tmu07-rhythm) grid aspect-[3/2] w-full place-items-center rounded-lg border border-tmu07-line bg-tmu07-surface bg-[repeating-linear-gradient(135deg,transparent_0_10px,var(--color-tmu07-line)_10px_11px)]">
          <span class="rounded-full bg-tmu07-surface px-3 py-1 font-tmu07-ui text-xs uppercase tracking-[0.1em] text-tmu07-ink-2">3:2 image area</span>
        </div>
        <figcaption class="font-tmu07-ui text-[0.8125rem] leading-(--tmu07-rhythm) text-tmu07-ink-2">A block on the grid, with an interior that is not. This is the compromise that keeps the system usable.</figcaption>
      </figure>
      <p class="mb-(--tmu07-rhythm)">The practical answer is rhythm at the block level. Each block <em>starts</em> on the grid; its interior may not. That is nine-tenths of the perceived order at a tenth of the fragility, and it holds when an editor adds a pull quote, a table, or an embed you did not plan for.</p>
      <blockquote class="my-[calc(var(--tmu07-rhythm)*2)] border-l-2 border-tmu07-acc pl-5">
        <p class="text-[1.3125rem] leading-[calc(var(--tmu07-rhythm)*1.5)] tracking-[-0.014em]">If the correction has to be recalculated every time the content changes, it is not a system.</p>
      </blockquote>
      <p>Where a print designer can force every element onto the baseline because the page is fixed, a web page is not. Accepting that is what separates a rhythm that ships from a grid that gets quietly deleted in the third sprint.</p>
    </article>
  </div>
</section>
@import "tailwindcss";

@theme {
  --color-tmu07-bg: light-dark(#faf6ee, #14120e);
  --color-tmu07-bg: light-dark(oklch(97.5% 0.012 88), oklch(18% 0.009 70));
  --color-tmu07-surface: light-dark(#fffdf8, #1c1914);
  --color-tmu07-surface: light-dark(oklch(99.2% 0.006 88), oklch(22.5% 0.01 72));
  --color-tmu07-ink: light-dark(#1a1712, #f4efe4);
  --color-tmu07-ink: light-dark(oklch(20% 0.01 70), oklch(96% 0.012 88));
  --color-tmu07-ink-2: light-dark(#57503f, #b3a993);
  --color-tmu07-ink-2: light-dark(oklch(45% 0.018 78), oklch(76% 0.02 85));
  --color-tmu07-line: light-dark(#ddd4c1, #3b342a);
  --color-tmu07-line: light-dark(oklch(87% 0.02 85), oklch(33% 0.015 78));
  --color-tmu07-grid: light-dark(rgba(139, 74, 20, 0.22), rgba(232, 168, 106, 0.24));
  --color-tmu07-acc: light-dark(#8b4a14, #e8a86a);
  --color-tmu07-acc: light-dark(oklch(45% 0.12 55), oklch(79% 0.11 60));
  --font-tmu07-serif: ui-serif, Georgia, "Iowan Old Style", "Palatino Linotype", Palatino, serif;
  --font-tmu07-ui: system-ui, -apple-system, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
  --font-tmu07-mono: ui-monospace, SFMono-Regular, Menlo, Consolas, "Liberation Mono", monospace;
}
/* Root rule: the full-bleed contract, restated so the stage is sized before the browser build
   compiles. color-scheme drives every light-dark() token above. */

.tmu-07 {
  width: 100%;
  min-height: 100vh;
  min-height: 100svh;
  color-scheme: light dark;
  --tmu07-rhythm: 1.5rem;
}
/* Theme toggle: flip the scheme and every token follows. Mirrored for a dark OS. */

.tmu-07:has(#tmu07-theme:checked) {
  color-scheme: dark;
}

@media (prefers-color-scheme: dark) {
  .tmu-07:has(#tmu07-theme:checked) {
    color-scheme: light;
  }
}
(() => {
  const root = document.querySelector('.tmu-07');
  if (!root) return;
  const btn = root.querySelector('#tmu07-grid-btn');
  const article = root.querySelector('#tmu07-article');
  if (!btn || !article) return;

  const setGrid = (on) => {
    btn.setAttribute('aria-pressed', String(on));
    article.toggleAttribute('data-grid', on);
  };
  const onClick = () => setGrid(btn.getAttribute('aria-pressed') !== 'true');
  const onKey = (e) => {
    if (e.target.closest('input, textarea, select, [contenteditable]')) return;
    if (e.key.toLowerCase() === 'g' && !e.metaKey && !e.ctrlKey && !e.altKey) onClick();
  };

  btn.addEventListener('click', onClick);
  document.addEventListener('keydown', onKey);

  root.__tmuCleanup = () => {
    btn.removeEventListener('click', onClick);
    document.removeEventListener('keydown', onKey);
    setGrid(false);
  };
})();

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-tmu07-bg: light-dark(#faf6ee, #14120e);
  --color-tmu07-bg: light-dark(oklch(97.5% 0.012 88), oklch(18% 0.009 70));
  --color-tmu07-surface: light-dark(#fffdf8, #1c1914);
  --color-tmu07-surface: light-dark(oklch(99.2% 0.006 88), oklch(22.5% 0.01 72));
  --color-tmu07-ink: light-dark(#1a1712, #f4efe4);
  --color-tmu07-ink: light-dark(oklch(20% 0.01 70), oklch(96% 0.012 88));
  --color-tmu07-ink-2: light-dark(#57503f, #b3a993);
  --color-tmu07-ink-2: light-dark(oklch(45% 0.018 78), oklch(76% 0.02 85));
  --color-tmu07-line: light-dark(#ddd4c1, #3b342a);
  --color-tmu07-line: light-dark(oklch(87% 0.02 85), oklch(33% 0.015 78));
  --color-tmu07-grid: light-dark(rgba(139, 74, 20, 0.22), rgba(232, 168, 106, 0.24));
  --color-tmu07-acc: light-dark(#8b4a14, #e8a86a);
  --color-tmu07-acc: light-dark(oklch(45% 0.12 55), oklch(79% 0.11 60));
  --font-tmu07-serif: ui-serif, Georgia, "Iowan Old Style", "Palatino Linotype", Palatino, serif;
  --font-tmu07-ui: system-ui, -apple-system, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
  --font-tmu07-mono: ui-monospace, SFMono-Regular, Menlo, Consolas, "Liberation Mono", monospace;
}
/* Root rule: the full-bleed contract, restated so the stage is sized before the browser build
   compiles. color-scheme drives every light-dark() token above. */

.tmu-07 {
  width: 100%;
  min-height: 100vh;
  min-height: 100svh;
  color-scheme: light dark;
  --tmu07-rhythm: 1.5rem;
}
/* Theme toggle: flip the scheme and every token follows. Mirrored for a dark OS. */

.tmu-07:has(#tmu07-theme:checked) {
  color-scheme: dark;
}

@media (prefers-color-scheme: dark) {
  .tmu-07:has(#tmu07-theme:checked) {
    color-scheme: light;
  }
}

What changed in the port

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

  • .article.is-grid became data-grid: on the article. The JS now sets an attribute instead of a class, which keeps state out of the class list Tailwind scans.
  • The controller only flips aria-pressed and data-grid; every visual consequence is a variant in the markup.
  • UI upgrade: the overlay colour now also tints the column's rhythm unit read-out, and the toggle shows a G hint so the shortcut is discoverable.
  • A small fix: the G shortcut ignores keystrokes from editable targets, so typing a g into a future search field does not flip the grid.
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: Tailwind Vertical Rhythm with a Consistent Baseline
Source: https://codefronts.com/design-styles/tailwind-css-minimalist-ui/tailwind-vertical-rhythm-baseline-grid/

An article column where every line and block boundary lands on a 1.5rem rhythm, set once as --tmu07-rhythm and consumed through leading-(--tmu07-rhythm) and mb-(--tmu07-rhythm). Press the button (or G) to draw the baseline grid over it &mdash; a data-grid: variant swaps in a repeating-linear-gradient background, and the image block shows where strict rhythm stops being free.
## HTML
```html
<section class="tmu-07 relative w-full min-h-svh bg-tmu07-bg font-tmu07-ui text-tmu07-ink antialiased" aria-labelledby="tmu07-title">
  <div class="absolute top-4 right-4 z-10">
    <input id="tmu07-theme" type="checkbox" class="peer sr-only">
    <label for="tmu07-theme" class="grid size-11 cursor-pointer place-items-center rounded-full border border-tmu07-line bg-tmu07-surface text-tmu07-ink-2 shadow-[0_1px_2px_rgb(0_0_0/0.06)] transition-[color,rotate] duration-300 hover:text-tmu07-ink peer-checked:rotate-180 peer-focus-visible:outline-2 peer-focus-visible:outline-offset-2 peer-focus-visible:outline-tmu07-acc motion-reduce:transition-none">
      <svg class="size-5" viewBox="0 0 24 24" aria-hidden="true" focusable="false"><circle cx="12" cy="12" r="8" fill="none" stroke="currentColor" stroke-width="1.6"/><path d="M12 4a8 8 0 0 0 0 16Z" fill="currentColor"/></svg>
      <span class="sr-only">Switch colour theme</span>
    </label>
  </div>
  <div class="mx-auto grid max-w-[60rem] gap-12 px-6 pt-20 pb-24 md:px-12">
    <header class="grid max-w-[62ch] gap-3">
      <p class="inline-flex items-center gap-2.5 font-tmu07-mono text-xs font-medium uppercase tracking-[0.14em] text-tmu07-ink-2"><span class="tabular-nums text-tmu07-acc">07</span><span aria-hidden="true" class="h-px w-6 bg-tmu07-line"></span>Rhythm</p>
      <h2 id="tmu07-title" class="text-[clamp(1.75rem,1.2rem+2vw,2.75rem)] font-semibold leading-[1.08] tracking-[-0.03em] text-balance">One unit, every line</h2>
      <p class="text-[1.0625rem] leading-relaxed text-tmu07-ink-2 text-pretty">Body lines take one unit, headings take two, gaps take one. Turn the overlay on: every block starts on a rule.</p>
    </header>
    <div class="flex flex-wrap items-center gap-x-4 gap-y-3">
      <button id="tmu07-grid-btn" type="button" aria-pressed="false" class="group inline-flex min-h-11 min-w-11 items-center gap-2 rounded-md border border-tmu07-line bg-tmu07-surface px-4 text-sm font-medium text-tmu07-ink cursor-pointer shadow-[0_1px_2px_rgb(0_0_0/0.05)] transition-[border-color,color] duration-150 hover:border-tmu07-acc aria-pressed:border-tmu07-acc aria-pressed:text-tmu07-acc focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-tmu07-acc motion-reduce:transition-none">
        <svg class="size-[18px] flex-none" viewBox="0 0 24 24" aria-hidden="true" focusable="false"><path d="M3 6h18M3 12h18M3 18h18" fill="none" stroke="currentColor" stroke-width="1.6"/></svg>
        Baseline overlay
        <kbd class="ml-1 rounded border border-tmu07-line px-1.5 font-tmu07-mono text-[0.6875rem] text-tmu07-ink-2 group-aria-pressed:border-tmu07-acc group-aria-pressed:text-tmu07-acc">G</kbd>
      </button>
      <p class="text-[0.8125rem] tabular-nums text-tmu07-ink-2">Unit <b class="font-semibold text-tmu07-ink">1.5rem</b> &middot; 24px at a 16px root</p>
    </div>
    <article id="tmu07-article" class="relative min-w-0 max-w-[66ch] font-tmu07-serif text-[1.0625rem] leading-(--tmu07-rhythm) text-pretty data-grid:bg-[repeating-linear-gradient(to_bottom,var(--color-tmu07-grid)_0,var(--color-tmu07-grid)_1px,transparent_1px,transparent_var(--tmu07-rhythm))]">
      <h3 class="mb-(--tmu07-rhythm) text-2xl font-semibold leading-[calc(var(--tmu07-rhythm)*2)] tracking-[-0.018em] text-balance">Rhythm survives real content, grids do not</h3>
      <p class="mb-(--tmu07-rhythm) text-[1.1875rem] leading-[calc(var(--tmu07-rhythm)*1.5)]">Set the rhythm once and the column composes itself. Body copy takes one unit per line, a heading takes two, and the gap between blocks takes one &mdash; so the eye reads order it cannot name.</p>
      <p class="mb-(--tmu07-rhythm)">Turn the overlay on and the mechanism becomes obvious: every line of prose sits on a rule, and every block boundary lands on one too. Nothing here is hand-nudged. The whole column is derived from a single custom property, which means the rhythm can be retuned for a different reading size in one edit.</p>
      <h4 class="mt-[calc(var(--tmu07-rhythm)*2)] mb-(--tmu07-rhythm) font-tmu07-ui text-[0.8125rem] font-bold uppercase leading-(--tmu07-rhythm) tracking-[0.1em] text-tmu07-ink-2">Where it stops being free</h4>
      <p class="mb-(--tmu07-rhythm)">The image below is 3:2, which is not a multiple of anything. Its caption is smaller than the body, so its own line height is not a whole unit either. A strict per-line grid would need padding calculated from the image height &mdash; a value that changes with the viewport, so the correction cannot be static.</p>
      <figure class="mt-[calc(var(--tmu07-rhythm)*2)] mb-(--tmu07-rhythm)">
        <div role="img" aria-label="Tonal placeholder standing in for an article image, 3 by 2" class="mb-(--tmu07-rhythm) grid aspect-[3/2] w-full place-items-center rounded-lg border border-tmu07-line bg-tmu07-surface bg-[repeating-linear-gradient(135deg,transparent_0_10px,var(--color-tmu07-line)_10px_11px)]">
          <span class="rounded-full bg-tmu07-surface px-3 py-1 font-tmu07-ui text-xs uppercase tracking-[0.1em] text-tmu07-ink-2">3:2 image area</span>
        </div>
        <figcaption class="font-tmu07-ui text-[0.8125rem] leading-(--tmu07-rhythm) text-tmu07-ink-2">A block on the grid, with an interior that is not. This is the compromise that keeps the system usable.</figcaption>
      </figure>
      <p class="mb-(--tmu07-rhythm)">The practical answer is rhythm at the block level. Each block <em>starts</em> on the grid; its interior may not. That is nine-tenths of the perceived order at a tenth of the fragility, and it holds when an editor adds a pull quote, a table, or an embed you did not plan for.</p>
      <blockquote class="my-[calc(var(--tmu07-rhythm)*2)] border-l-2 border-tmu07-acc pl-5">
        <p class="text-[1.3125rem] leading-[calc(var(--tmu07-rhythm)*1.5)] tracking-[-0.014em]">If the correction has to be recalculated every time the content changes, it is not a system.</p>
      </blockquote>
      <p>Where a print designer can force every element onto the baseline because the page is fixed, a web page is not. Accepting that is what separates a rhythm that ships from a grid that gets quietly deleted in the third sprint.</p>
    </article>
  </div>
</section>
```
## CSS
```css
@import "tailwindcss";

@theme {
  --color-tmu07-bg: light-dark(#faf6ee, #14120e);
  --color-tmu07-bg: light-dark(oklch(97.5% 0.012 88), oklch(18% 0.009 70));
  --color-tmu07-surface: light-dark(#fffdf8, #1c1914);
  --color-tmu07-surface: light-dark(oklch(99.2% 0.006 88), oklch(22.5% 0.01 72));
  --color-tmu07-ink: light-dark(#1a1712, #f4efe4);
  --color-tmu07-ink: light-dark(oklch(20% 0.01 70), oklch(96% 0.012 88));
  --color-tmu07-ink-2: light-dark(#57503f, #b3a993);
  --color-tmu07-ink-2: light-dark(oklch(45% 0.018 78), oklch(76% 0.02 85));
  --color-tmu07-line: light-dark(#ddd4c1, #3b342a);
  --color-tmu07-line: light-dark(oklch(87% 0.02 85), oklch(33% 0.015 78));
  --color-tmu07-grid: light-dark(rgba(139, 74, 20, 0.22), rgba(232, 168, 106, 0.24));
  --color-tmu07-acc: light-dark(#8b4a14, #e8a86a);
  --color-tmu07-acc: light-dark(oklch(45% 0.12 55), oklch(79% 0.11 60));
  --font-tmu07-serif: ui-serif, Georgia, "Iowan Old Style", "Palatino Linotype", Palatino, serif;
  --font-tmu07-ui: system-ui, -apple-system, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
  --font-tmu07-mono: ui-monospace, SFMono-Regular, Menlo, Consolas, "Liberation Mono", monospace;
}
/* Root rule: the full-bleed contract, restated so the stage is sized before the browser build
   compiles. color-scheme drives every light-dark() token above. */

.tmu-07 {
  width: 100%;
  min-height: 100vh;
  min-height: 100svh;
  color-scheme: light dark;
  --tmu07-rhythm: 1.5rem;
}
/* Theme toggle: flip the scheme and every token follows. Mirrored for a dark OS. */

.tmu-07:has(#tmu07-theme:checked) {
  color-scheme: dark;
}

@media (prefers-color-scheme: dark) {
  .tmu-07:has(#tmu07-theme:checked) {
    color-scheme: light;
  }
}
```

## JavaScript
```js
(() => {
  const root = document.querySelector('.tmu-07');
  if (!root) return;
  const btn = root.querySelector('#tmu07-grid-btn');
  const article = root.querySelector('#tmu07-article');
  if (!btn || !article) return;

  const setGrid = (on) => {
    btn.setAttribute('aria-pressed', String(on));
    article.toggleAttribute('data-grid', on);
  };
  const onClick = () => setGrid(btn.getAttribute('aria-pressed') !== 'true');
  const onKey = (e) => {
    if (e.target.closest('input, textarea, select, [contenteditable]')) return;
    if (e.key.toLowerCase() === 'g' && !e.metaKey && !e.ctrlKey && !e.altKey) onClick();
  };

  btn.addEventListener('click', onClick);
  document.addEventListener('keydown', onKey);

  root.__tmuCleanup = () => {
    btn.removeEventListener('click', onClick);
    document.removeEventListener('keydown', onKey);
    setGrid(false);
  };
})();
```

How this works

Rhythm is what makes long-form text feel calm without any decoration. Set a rhythm unit — here 1.5rem — then make every line height and every block margin a multiple of it. Body copy at line-height: 1.5 on a 1rem base occupies exactly one unit per line, headings take two, and the gaps between blocks take one or two. The result is that the left column and the right column of a two-column layout land on the same lines, and a reader perceives order without being able to name it.

Draw the grid while you tune it. The overlay here is a repeating-linear-gradient with a 1px line every rhythm unit, toggled by a single button. This is a development tool, not a shipped feature — but it is the only reliable way to find the block that is 4px off, because the eye notices the disruption long before it can locate the cause.

Margin collapse is your ally, not a bug. Adjacent block margins collapse to the larger of the two, which is precisely what a rhythm system wants: a heading with margin-block: 2 units 1 unit after a paragraph with margin-block-end: 1 unit yields two units, not three. Switch to a flex or grid container with gap and collapse stops happening — which is fine, but you then own every gap explicitly. Pick one model per region and stay in it.

The honest limit: strict baseline grids fight real content. An image is whatever height it is. A code block has its own line height. An input is 44px because a finger needs 44px. Forcing all of those onto a per-line grid means padding hacks that break the moment content changes. The practical answer is rhythm at the block level: let each block start on the grid, and accept that its interior may not. That gets you nine-tenths of the perceived order for a tenth of the fragility — and if you need the last tenth, you are building a print layout, not a web page.

Make it yours

  • Retune the whole column with --tmu07-rhythm: 1.625rem on the root; the overlay follows.
  • Change the shortcut key in the JS onKey handler, or delete it — the button works alone.
  • Make the overlay default-on by rendering the article with data-grid and the button with aria-pressed="true".

Gotchas — read before shipping

  • A baseline grid abandoned the moment a real image appears is worse than no grid, because half the page now looks deliberate and half looks broken. Commit to block-level rhythm instead.
  • Mixing collapsed margins and flex gap in the same column silently doubles some gaps. Pick one spacing model per region.
  • Setting line-height in px breaks the rhythm as soon as the user changes their font size — the unit must be relative.
  • Shipping the overlay to production is a real mistake; it is a gradient on the reading surface and it reduces text contrast. Keep it behind a development flag.

Browser support

ChromeSafariFirefoxEdge
123+17.5+128+123+

The floor is set by light-dark() on the colour tokens (Chrome 123 / Safari 17.5 / Firefox 120) and by Tailwind v4's own generated sheet (cascade layers, @property, color-mix()). The technique itself works far further back.

Techniques used in this demo

Theme tokensset in @theme, used by a utility

Arbitraryneeds bracket syntax

Search CodeFronts

Loading…