Pure TailwindMIT licensed

Tailwind Modular Type Scale with a Ratio Variable

One arbitrary property — [--tmu01-ratio:1.2] — rebuilds an entire type scale, because every text-tmu01-s* utility is minted from @theme inline as a live calc() of base × ratio. Three specimens run 1.200, 1.250 and 1.333 from one identical class list, with every computed size printed and drawn to scale.

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-01 relative w-full min-h-svh bg-tmu01-bg font-tmu01-ui text-base/normal text-tmu01-ink antialiased" aria-labelledby="tmu01-title">
  <div class="absolute top-4 right-4 z-10">
    <input id="tmu01-theme" type="checkbox" class="peer sr-only">
    <label for="tmu01-theme" class="grid size-11 cursor-pointer place-items-center rounded-full border border-tmu01-line bg-tmu01-surface text-tmu01-ink-2 shadow-[0_1px_2px_rgb(0_0_0/0.06)] transition-[color,rotate] duration-300 hover:text-tmu01-ink peer-checked:rotate-180 peer-focus-visible:outline-2 peer-focus-visible:outline-offset-2 peer-focus-visible:outline-tmu01-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-[78rem] gap-12 px-6 pt-20 pb-20 lg:px-12">
    <header class="grid max-w-[62ch] gap-3">
      <p class="inline-flex items-center gap-2.5 font-tmu01-mono text-xs font-medium uppercase tracking-[0.14em] text-tmu01-ink-2"><span class="tabular-nums text-tmu01-acc">01</span><span aria-hidden="true" class="h-px w-6 bg-tmu01-line"></span>Type scale</p>
      <h2 id="tmu01-title" class="text-[clamp(1.75rem,1.2rem+2vw,2.75rem)] font-semibold leading-[1.08] tracking-[-0.03em] text-balance">One ratio, every size</h2>
      <p class="text-[1.0625rem] leading-relaxed text-tmu01-ink-2 text-pretty">Each step is base &times; ratio<sup>n</sup>. Change one number and the whole hierarchy moves in proportion &mdash; three ratios, one identical class list.</p>
    </header>
    <div class="grid gap-6 grid-cols-[repeat(auto-fit,minmax(min(100%,17rem),1fr))]">
      <article aria-labelledby="tmu01-r1" class="relative grid min-w-0 content-start gap-6 rounded-xl border border-tmu01-line shadow-[0_1px_2px_rgb(20_24_28/0.05)] bg-tmu01-surface p-6 [--tmu01-ratio:1.2]">
        <header class="flex flex-wrap items-baseline gap-3 border-b border-tmu01-line pb-4">
          <h3 id="tmu01-r1" class="min-w-0 font-tmu01-mono text-[1.375rem] font-medium tracking-[-0.01em]">1.200</h3>
          <p class="min-w-0 text-xs uppercase tracking-[0.08em] text-tmu01-ink-2">Minor third &middot; dense UI</p>
        </header>
        <div class="grid min-w-0 gap-3 wrap-break-word">
          <p class="text-tmu01-s4 font-semibold leading-[1.05] tracking-[-0.028em]">Ranger</p>
          <p class="text-tmu01-s3 font-semibold leading-[1.15] tracking-[-0.018em]">Monthly recurring revenue</p>
          <p class="text-tmu01-s2 font-medium leading-[1.25]">Draft saved</p>
          <p class="max-w-[34ch] text-tmu01-s1 leading-normal text-tmu01-ink-2 text-pretty">A ratio this tight keeps a dense console readable, because adjacent steps stay close enough to sit in the same table row.</p>
          <p class="text-tmu01-s0 font-medium">Continue to payment</p>
        </div>
        <dl class="grid gap-1.5 border-t border-tmu01-line pt-4 font-tmu01-mono text-xs tabular-nums text-tmu01-ink-2">
          <div class="grid grid-cols-[2rem_minmax(0,1fr)_4.5rem] items-center gap-3"><dt>s4</dt><span aria-hidden="true" class="h-1 rounded-full bg-tmu01-line"><span class="block h-full rounded-full bg-tmu01-ink-2" style="width:65.7%"></span></span><dd class="text-right text-tmu01-ink">33.18px</dd></div>
          <div class="grid grid-cols-[2rem_minmax(0,1fr)_4.5rem] items-center gap-3"><dt>s3</dt><span aria-hidden="true" class="h-1 rounded-full bg-tmu01-line"><span class="block h-full rounded-full bg-tmu01-ink-2" style="width:54.7%"></span></span><dd class="text-right text-tmu01-ink">27.65px</dd></div>
          <div class="grid grid-cols-[2rem_minmax(0,1fr)_4.5rem] items-center gap-3"><dt>s2</dt><span aria-hidden="true" class="h-1 rounded-full bg-tmu01-line"><span class="block h-full rounded-full bg-tmu01-ink-2" style="width:45.6%"></span></span><dd class="text-right text-tmu01-ink">23.04px</dd></div>
          <div class="grid grid-cols-[2rem_minmax(0,1fr)_4.5rem] items-center gap-3"><dt>s1</dt><span aria-hidden="true" class="h-1 rounded-full bg-tmu01-line"><span class="block h-full rounded-full bg-tmu01-ink-2" style="width:38.0%"></span></span><dd class="text-right text-tmu01-ink">19.20px</dd></div>
          <div class="grid grid-cols-[2rem_minmax(0,1fr)_4.5rem] items-center gap-3"><dt>s0</dt><span aria-hidden="true" class="h-1 rounded-full bg-tmu01-line"><span class="block h-full rounded-full bg-tmu01-ink-2" style="width:31.7%"></span></span><dd class="text-right text-tmu01-ink">16.00px</dd></div>
        </dl>
      </article>
      <article aria-labelledby="tmu01-r2" class="relative grid min-w-0 content-start gap-6 rounded-xl border border-tmu01-acc shadow-[0_0_0_3px_var(--color-tmu01-ring),0_12px_32px_-18px_rgb(20_24_28/0.35)] bg-tmu01-surface p-6 [--tmu01-ratio:1.25]">
        <span class="absolute -top-3 left-6 rounded-full bg-tmu01-acc px-2.5 py-1 font-tmu01-mono text-[0.6875rem] font-semibold uppercase tracking-[0.08em] text-tmu01-acc-ink">Default</span>
        <header class="flex flex-wrap items-baseline gap-3 border-b border-tmu01-line pb-4">
          <h3 id="tmu01-r2" class="min-w-0 font-tmu01-mono text-[1.375rem] font-medium tracking-[-0.01em]">1.250</h3>
          <p class="min-w-0 text-xs uppercase tracking-[0.08em] text-tmu01-ink-2">Major third &middot; product UI</p>
        </header>
        <div class="grid min-w-0 gap-3 wrap-break-word">
          <p class="text-tmu01-s4 font-semibold leading-[1.05] tracking-[-0.028em]">Ranger</p>
          <p class="text-tmu01-s3 font-semibold leading-[1.15] tracking-[-0.018em]">Monthly recurring revenue</p>
          <p class="text-tmu01-s2 font-medium leading-[1.25]">Draft saved</p>
          <p class="max-w-[34ch] text-tmu01-s1 leading-normal text-tmu01-ink-2 text-pretty">The working default for most product interfaces: distinguishable steps that still leave room for four or five levels of hierarchy.</p>
          <p class="text-tmu01-s0 font-medium">Continue to payment</p>
        </div>
        <dl class="grid gap-1.5 border-t border-tmu01-line pt-4 font-tmu01-mono text-xs tabular-nums text-tmu01-ink-2">
          <div class="grid grid-cols-[2rem_minmax(0,1fr)_4.5rem] items-center gap-3"><dt>s4</dt><span aria-hidden="true" class="h-1 rounded-full bg-tmu01-line"><span class="block h-full rounded-full bg-tmu01-acc" style="width:77.3%"></span></span><dd class="text-right text-tmu01-ink">39.06px</dd></div>
          <div class="grid grid-cols-[2rem_minmax(0,1fr)_4.5rem] items-center gap-3"><dt>s3</dt><span aria-hidden="true" class="h-1 rounded-full bg-tmu01-line"><span class="block h-full rounded-full bg-tmu01-acc" style="width:61.8%"></span></span><dd class="text-right text-tmu01-ink">31.25px</dd></div>
          <div class="grid grid-cols-[2rem_minmax(0,1fr)_4.5rem] items-center gap-3"><dt>s2</dt><span aria-hidden="true" class="h-1 rounded-full bg-tmu01-line"><span class="block h-full rounded-full bg-tmu01-acc" style="width:49.5%"></span></span><dd class="text-right text-tmu01-ink">25.00px</dd></div>
          <div class="grid grid-cols-[2rem_minmax(0,1fr)_4.5rem] items-center gap-3"><dt>s1</dt><span aria-hidden="true" class="h-1 rounded-full bg-tmu01-line"><span class="block h-full rounded-full bg-tmu01-acc" style="width:39.6%"></span></span><dd class="text-right text-tmu01-ink">20.00px</dd></div>
          <div class="grid grid-cols-[2rem_minmax(0,1fr)_4.5rem] items-center gap-3"><dt>s0</dt><span aria-hidden="true" class="h-1 rounded-full bg-tmu01-line"><span class="block h-full rounded-full bg-tmu01-acc" style="width:31.7%"></span></span><dd class="text-right text-tmu01-ink">16.00px</dd></div>
        </dl>
      </article>
      <article aria-labelledby="tmu01-r3" class="relative grid min-w-0 content-start gap-6 rounded-xl border border-tmu01-line shadow-[0_1px_2px_rgb(20_24_28/0.05)] bg-tmu01-surface p-6 [--tmu01-ratio:1.333]">
        <header class="flex flex-wrap items-baseline gap-3 border-b border-tmu01-line pb-4">
          <h3 id="tmu01-r3" class="min-w-0 font-tmu01-mono text-[1.375rem] font-medium tracking-[-0.01em]">1.333</h3>
          <p class="min-w-0 text-xs uppercase tracking-[0.08em] text-tmu01-ink-2">Perfect fourth &middot; editorial</p>
        </header>
        <div class="grid min-w-0 gap-3 wrap-break-word">
          <p class="text-tmu01-s4 font-semibold leading-[1.05] tracking-[-0.028em]">Ranger</p>
          <p class="text-tmu01-s3 font-semibold leading-[1.15] tracking-[-0.018em]">Monthly recurring revenue</p>
          <p class="text-tmu01-s2 font-medium leading-[1.25]">Draft saved</p>
          <p class="max-w-[34ch] text-tmu01-s1 leading-normal text-tmu01-ink-2 text-pretty">Decisive on a landing page and hostile in a data table &mdash; by the fourth step the jump is 13px, which no dense interface can absorb.</p>
          <p class="text-tmu01-s0 font-medium">Continue to payment</p>
        </div>
        <dl class="grid gap-1.5 border-t border-tmu01-line pt-4 font-tmu01-mono text-xs tabular-nums text-tmu01-ink-2">
          <div class="grid grid-cols-[2rem_minmax(0,1fr)_4.5rem] items-center gap-3"><dt>s4</dt><span aria-hidden="true" class="h-1 rounded-full bg-tmu01-line"><span class="block h-full rounded-full bg-tmu01-ink-2" style="width:100.0%"></span></span><dd class="text-right text-tmu01-ink">50.53px</dd></div>
          <div class="grid grid-cols-[2rem_minmax(0,1fr)_4.5rem] items-center gap-3"><dt>s3</dt><span aria-hidden="true" class="h-1 rounded-full bg-tmu01-line"><span class="block h-full rounded-full bg-tmu01-ink-2" style="width:75.0%"></span></span><dd class="text-right text-tmu01-ink">37.91px</dd></div>
          <div class="grid grid-cols-[2rem_minmax(0,1fr)_4.5rem] items-center gap-3"><dt>s2</dt><span aria-hidden="true" class="h-1 rounded-full bg-tmu01-line"><span class="block h-full rounded-full bg-tmu01-ink-2" style="width:56.3%"></span></span><dd class="text-right text-tmu01-ink">28.44px</dd></div>
          <div class="grid grid-cols-[2rem_minmax(0,1fr)_4.5rem] items-center gap-3"><dt>s1</dt><span aria-hidden="true" class="h-1 rounded-full bg-tmu01-line"><span class="block h-full rounded-full bg-tmu01-ink-2" style="width:42.2%"></span></span><dd class="text-right text-tmu01-ink">21.33px</dd></div>
          <div class="grid grid-cols-[2rem_minmax(0,1fr)_4.5rem] items-center gap-3"><dt>s0</dt><span aria-hidden="true" class="h-1 rounded-full bg-tmu01-line"><span class="block h-full rounded-full bg-tmu01-ink-2" style="width:31.7%"></span></span><dd class="text-right text-tmu01-ink">16.00px</dd></div>
        </dl>
      </article>
    </div>
  </div>
</section>
@import "tailwindcss";

@theme {
  --color-tmu01-bg: light-dark(#eef0f2, #0f1215);
  --color-tmu01-bg: light-dark(oklch(94.6% 0.005 240), oklch(17% 0.008 250));
  --color-tmu01-surface: light-dark(#ffffff, #171b1f);
  --color-tmu01-surface: light-dark(oklch(100% 0 0), oklch(22% 0.009 250));
  --color-tmu01-ink: light-dark(#14181c, #eef1f4);
  --color-tmu01-ink: light-dark(oklch(19% 0.012 250), oklch(95% 0.004 250));
  --color-tmu01-ink-2: light-dark(#4c565f, #a3adb6);
  --color-tmu01-ink-2: light-dark(oklch(43% 0.018 250), oklch(74% 0.012 250));
  --color-tmu01-line: light-dark(#c9cfd5, #333c44);
  --color-tmu01-line: light-dark(oklch(84% 0.008 250), oklch(33% 0.012 250));
  --color-tmu01-acc: light-dark(#1d5cff, #7fa6ff);
  --color-tmu01-acc: light-dark(oklch(52% 0.21 264), oklch(76% 0.14 264));
  --color-tmu01-ring: light-dark(#d9e3ff, #1f2d52);
  --color-tmu01-ring: light-dark(oklch(91% 0.04 264), oklch(30% 0.07 264));
  --color-tmu01-acc-ink: light-dark(#ffffff, #0f1215);
  --color-tmu01-acc-ink: light-dark(oklch(100% 0 0), oklch(17% 0.008 250));
  --font-tmu01-ui: system-ui, -apple-system, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
  --font-tmu01-mono: ui-monospace, SFMono-Regular, Menlo, Consolas, "Liberation Mono", monospace;
}
/* inline: each utility carries its own calc(), so it resolves on the element that uses it. */

@theme inline {
  --text-tmu01-s0: var(--tmu01-base);
  --text-tmu01-s1: calc(var(--tmu01-base) * var(--tmu01-ratio));
  --text-tmu01-s2: calc(var(--tmu01-base) * var(--tmu01-ratio) * var(--tmu01-ratio));
  --text-tmu01-s3: calc(var(--tmu01-base) * var(--tmu01-ratio) * var(--tmu01-ratio) * var(--tmu01-ratio));
  --text-tmu01-s4: calc(var(--tmu01-base) * var(--tmu01-ratio) * var(--tmu01-ratio) * var(--tmu01-ratio) * var(--tmu01-ratio));
}
/* 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-01 {
  width: 100%;
  min-height: 100vh;
  min-height: 100svh;
  color-scheme: light dark;
  --tmu01-ratio: 1.25;
  --tmu01-base: clamp(0.9375rem, 0.9rem + 0.25vw, 1rem);
}
/* Theme toggle: flip the scheme and every token follows. Mirrored for a dark OS. */

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

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

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-tmu01-bg: light-dark(#eef0f2, #0f1215);
  --color-tmu01-bg: light-dark(oklch(94.6% 0.005 240), oklch(17% 0.008 250));
  --color-tmu01-surface: light-dark(#ffffff, #171b1f);
  --color-tmu01-surface: light-dark(oklch(100% 0 0), oklch(22% 0.009 250));
  --color-tmu01-ink: light-dark(#14181c, #eef1f4);
  --color-tmu01-ink: light-dark(oklch(19% 0.012 250), oklch(95% 0.004 250));
  --color-tmu01-ink-2: light-dark(#4c565f, #a3adb6);
  --color-tmu01-ink-2: light-dark(oklch(43% 0.018 250), oklch(74% 0.012 250));
  --color-tmu01-line: light-dark(#c9cfd5, #333c44);
  --color-tmu01-line: light-dark(oklch(84% 0.008 250), oklch(33% 0.012 250));
  --color-tmu01-acc: light-dark(#1d5cff, #7fa6ff);
  --color-tmu01-acc: light-dark(oklch(52% 0.21 264), oklch(76% 0.14 264));
  --color-tmu01-ring: light-dark(#d9e3ff, #1f2d52);
  --color-tmu01-ring: light-dark(oklch(91% 0.04 264), oklch(30% 0.07 264));
  --color-tmu01-acc-ink: light-dark(#ffffff, #0f1215);
  --color-tmu01-acc-ink: light-dark(oklch(100% 0 0), oklch(17% 0.008 250));
  --font-tmu01-ui: system-ui, -apple-system, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
  --font-tmu01-mono: ui-monospace, SFMono-Regular, Menlo, Consolas, "Liberation Mono", monospace;
}
/* inline: each utility carries its own calc(), so it resolves on the element that uses it. */

@theme inline {
  --text-tmu01-s0: var(--tmu01-base);
  --text-tmu01-s1: calc(var(--tmu01-base) * var(--tmu01-ratio));
  --text-tmu01-s2: calc(var(--tmu01-base) * var(--tmu01-ratio) * var(--tmu01-ratio));
  --text-tmu01-s3: calc(var(--tmu01-base) * var(--tmu01-ratio) * var(--tmu01-ratio) * var(--tmu01-ratio));
  --text-tmu01-s4: calc(var(--tmu01-base) * var(--tmu01-ratio) * var(--tmu01-ratio) * var(--tmu01-ratio) * var(--tmu01-ratio));
}
/* 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-01 {
  width: 100%;
  min-height: 100vh;
  min-height: 100svh;
  color-scheme: light dark;
  --tmu01-ratio: 1.25;
  --tmu01-base: clamp(0.9375rem, 0.9rem + 0.25vw, 1rem);
}
/* Theme toggle: flip the scheme and every token follows. Mirrored for a dark OS. */

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

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

What changed in the port

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

  • A real bug fixed in the port. The CSS original chained --s1: calc(var(--s0) * var(--ratio)) on the root, so the strips' inline ratio override never reached the steps — custom properties resolve where they are declared, and all three specimens actually rendered at 1.25. @theme inline puts the calc() inside each utility, so it resolves on the element and the three specimens now genuinely differ.
  • The chain became explicit products (base * r * r * r) rather than a step-references-step chain: an inlined utility cannot reference a sibling step that was never declared on the element.
  • UI upgrade: a visible header states the rule, the default strip gains a focus-style ring and a Default badge, and every printed value gets a to-scale bar so the jump between ratios reads before you read the numbers.
  • UI upgrade: the theme toggle is a sr-only peer rather than an invisible input laid over the label, so hover and focus both style the visible control.
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 Modular Type Scale with a Ratio Variable
Source: https://codefronts.com/design-styles/tailwind-css-minimalist-ui/tailwind-modular-type-scale-ratio-variable/

One arbitrary property &mdash; [--tmu01-ratio:1.2] &mdash; rebuilds an entire type scale, because every text-tmu01-s* utility is minted from @theme inline as a live calc() of base &times; ratio. Three specimens run 1.200, 1.250 and 1.333 from one identical class list, with every computed size printed and drawn to scale.
## HTML
```html
<section class="tmu-01 relative w-full min-h-svh bg-tmu01-bg font-tmu01-ui text-base/normal text-tmu01-ink antialiased" aria-labelledby="tmu01-title">
  <div class="absolute top-4 right-4 z-10">
    <input id="tmu01-theme" type="checkbox" class="peer sr-only">
    <label for="tmu01-theme" class="grid size-11 cursor-pointer place-items-center rounded-full border border-tmu01-line bg-tmu01-surface text-tmu01-ink-2 shadow-[0_1px_2px_rgb(0_0_0/0.06)] transition-[color,rotate] duration-300 hover:text-tmu01-ink peer-checked:rotate-180 peer-focus-visible:outline-2 peer-focus-visible:outline-offset-2 peer-focus-visible:outline-tmu01-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-[78rem] gap-12 px-6 pt-20 pb-20 lg:px-12">
    <header class="grid max-w-[62ch] gap-3">
      <p class="inline-flex items-center gap-2.5 font-tmu01-mono text-xs font-medium uppercase tracking-[0.14em] text-tmu01-ink-2"><span class="tabular-nums text-tmu01-acc">01</span><span aria-hidden="true" class="h-px w-6 bg-tmu01-line"></span>Type scale</p>
      <h2 id="tmu01-title" class="text-[clamp(1.75rem,1.2rem+2vw,2.75rem)] font-semibold leading-[1.08] tracking-[-0.03em] text-balance">One ratio, every size</h2>
      <p class="text-[1.0625rem] leading-relaxed text-tmu01-ink-2 text-pretty">Each step is base &times; ratio<sup>n</sup>. Change one number and the whole hierarchy moves in proportion &mdash; three ratios, one identical class list.</p>
    </header>
    <div class="grid gap-6 grid-cols-[repeat(auto-fit,minmax(min(100%,17rem),1fr))]">
      <article aria-labelledby="tmu01-r1" class="relative grid min-w-0 content-start gap-6 rounded-xl border border-tmu01-line shadow-[0_1px_2px_rgb(20_24_28/0.05)] bg-tmu01-surface p-6 [--tmu01-ratio:1.2]">
        <header class="flex flex-wrap items-baseline gap-3 border-b border-tmu01-line pb-4">
          <h3 id="tmu01-r1" class="min-w-0 font-tmu01-mono text-[1.375rem] font-medium tracking-[-0.01em]">1.200</h3>
          <p class="min-w-0 text-xs uppercase tracking-[0.08em] text-tmu01-ink-2">Minor third &middot; dense UI</p>
        </header>
        <div class="grid min-w-0 gap-3 wrap-break-word">
          <p class="text-tmu01-s4 font-semibold leading-[1.05] tracking-[-0.028em]">Ranger</p>
          <p class="text-tmu01-s3 font-semibold leading-[1.15] tracking-[-0.018em]">Monthly recurring revenue</p>
          <p class="text-tmu01-s2 font-medium leading-[1.25]">Draft saved</p>
          <p class="max-w-[34ch] text-tmu01-s1 leading-normal text-tmu01-ink-2 text-pretty">A ratio this tight keeps a dense console readable, because adjacent steps stay close enough to sit in the same table row.</p>
          <p class="text-tmu01-s0 font-medium">Continue to payment</p>
        </div>
        <dl class="grid gap-1.5 border-t border-tmu01-line pt-4 font-tmu01-mono text-xs tabular-nums text-tmu01-ink-2">
          <div class="grid grid-cols-[2rem_minmax(0,1fr)_4.5rem] items-center gap-3"><dt>s4</dt><span aria-hidden="true" class="h-1 rounded-full bg-tmu01-line"><span class="block h-full rounded-full bg-tmu01-ink-2" style="width:65.7%"></span></span><dd class="text-right text-tmu01-ink">33.18px</dd></div>
          <div class="grid grid-cols-[2rem_minmax(0,1fr)_4.5rem] items-center gap-3"><dt>s3</dt><span aria-hidden="true" class="h-1 rounded-full bg-tmu01-line"><span class="block h-full rounded-full bg-tmu01-ink-2" style="width:54.7%"></span></span><dd class="text-right text-tmu01-ink">27.65px</dd></div>
          <div class="grid grid-cols-[2rem_minmax(0,1fr)_4.5rem] items-center gap-3"><dt>s2</dt><span aria-hidden="true" class="h-1 rounded-full bg-tmu01-line"><span class="block h-full rounded-full bg-tmu01-ink-2" style="width:45.6%"></span></span><dd class="text-right text-tmu01-ink">23.04px</dd></div>
          <div class="grid grid-cols-[2rem_minmax(0,1fr)_4.5rem] items-center gap-3"><dt>s1</dt><span aria-hidden="true" class="h-1 rounded-full bg-tmu01-line"><span class="block h-full rounded-full bg-tmu01-ink-2" style="width:38.0%"></span></span><dd class="text-right text-tmu01-ink">19.20px</dd></div>
          <div class="grid grid-cols-[2rem_minmax(0,1fr)_4.5rem] items-center gap-3"><dt>s0</dt><span aria-hidden="true" class="h-1 rounded-full bg-tmu01-line"><span class="block h-full rounded-full bg-tmu01-ink-2" style="width:31.7%"></span></span><dd class="text-right text-tmu01-ink">16.00px</dd></div>
        </dl>
      </article>
      <article aria-labelledby="tmu01-r2" class="relative grid min-w-0 content-start gap-6 rounded-xl border border-tmu01-acc shadow-[0_0_0_3px_var(--color-tmu01-ring),0_12px_32px_-18px_rgb(20_24_28/0.35)] bg-tmu01-surface p-6 [--tmu01-ratio:1.25]">
        <span class="absolute -top-3 left-6 rounded-full bg-tmu01-acc px-2.5 py-1 font-tmu01-mono text-[0.6875rem] font-semibold uppercase tracking-[0.08em] text-tmu01-acc-ink">Default</span>
        <header class="flex flex-wrap items-baseline gap-3 border-b border-tmu01-line pb-4">
          <h3 id="tmu01-r2" class="min-w-0 font-tmu01-mono text-[1.375rem] font-medium tracking-[-0.01em]">1.250</h3>
          <p class="min-w-0 text-xs uppercase tracking-[0.08em] text-tmu01-ink-2">Major third &middot; product UI</p>
        </header>
        <div class="grid min-w-0 gap-3 wrap-break-word">
          <p class="text-tmu01-s4 font-semibold leading-[1.05] tracking-[-0.028em]">Ranger</p>
          <p class="text-tmu01-s3 font-semibold leading-[1.15] tracking-[-0.018em]">Monthly recurring revenue</p>
          <p class="text-tmu01-s2 font-medium leading-[1.25]">Draft saved</p>
          <p class="max-w-[34ch] text-tmu01-s1 leading-normal text-tmu01-ink-2 text-pretty">The working default for most product interfaces: distinguishable steps that still leave room for four or five levels of hierarchy.</p>
          <p class="text-tmu01-s0 font-medium">Continue to payment</p>
        </div>
        <dl class="grid gap-1.5 border-t border-tmu01-line pt-4 font-tmu01-mono text-xs tabular-nums text-tmu01-ink-2">
          <div class="grid grid-cols-[2rem_minmax(0,1fr)_4.5rem] items-center gap-3"><dt>s4</dt><span aria-hidden="true" class="h-1 rounded-full bg-tmu01-line"><span class="block h-full rounded-full bg-tmu01-acc" style="width:77.3%"></span></span><dd class="text-right text-tmu01-ink">39.06px</dd></div>
          <div class="grid grid-cols-[2rem_minmax(0,1fr)_4.5rem] items-center gap-3"><dt>s3</dt><span aria-hidden="true" class="h-1 rounded-full bg-tmu01-line"><span class="block h-full rounded-full bg-tmu01-acc" style="width:61.8%"></span></span><dd class="text-right text-tmu01-ink">31.25px</dd></div>
          <div class="grid grid-cols-[2rem_minmax(0,1fr)_4.5rem] items-center gap-3"><dt>s2</dt><span aria-hidden="true" class="h-1 rounded-full bg-tmu01-line"><span class="block h-full rounded-full bg-tmu01-acc" style="width:49.5%"></span></span><dd class="text-right text-tmu01-ink">25.00px</dd></div>
          <div class="grid grid-cols-[2rem_minmax(0,1fr)_4.5rem] items-center gap-3"><dt>s1</dt><span aria-hidden="true" class="h-1 rounded-full bg-tmu01-line"><span class="block h-full rounded-full bg-tmu01-acc" style="width:39.6%"></span></span><dd class="text-right text-tmu01-ink">20.00px</dd></div>
          <div class="grid grid-cols-[2rem_minmax(0,1fr)_4.5rem] items-center gap-3"><dt>s0</dt><span aria-hidden="true" class="h-1 rounded-full bg-tmu01-line"><span class="block h-full rounded-full bg-tmu01-acc" style="width:31.7%"></span></span><dd class="text-right text-tmu01-ink">16.00px</dd></div>
        </dl>
      </article>
      <article aria-labelledby="tmu01-r3" class="relative grid min-w-0 content-start gap-6 rounded-xl border border-tmu01-line shadow-[0_1px_2px_rgb(20_24_28/0.05)] bg-tmu01-surface p-6 [--tmu01-ratio:1.333]">
        <header class="flex flex-wrap items-baseline gap-3 border-b border-tmu01-line pb-4">
          <h3 id="tmu01-r3" class="min-w-0 font-tmu01-mono text-[1.375rem] font-medium tracking-[-0.01em]">1.333</h3>
          <p class="min-w-0 text-xs uppercase tracking-[0.08em] text-tmu01-ink-2">Perfect fourth &middot; editorial</p>
        </header>
        <div class="grid min-w-0 gap-3 wrap-break-word">
          <p class="text-tmu01-s4 font-semibold leading-[1.05] tracking-[-0.028em]">Ranger</p>
          <p class="text-tmu01-s3 font-semibold leading-[1.15] tracking-[-0.018em]">Monthly recurring revenue</p>
          <p class="text-tmu01-s2 font-medium leading-[1.25]">Draft saved</p>
          <p class="max-w-[34ch] text-tmu01-s1 leading-normal text-tmu01-ink-2 text-pretty">Decisive on a landing page and hostile in a data table &mdash; by the fourth step the jump is 13px, which no dense interface can absorb.</p>
          <p class="text-tmu01-s0 font-medium">Continue to payment</p>
        </div>
        <dl class="grid gap-1.5 border-t border-tmu01-line pt-4 font-tmu01-mono text-xs tabular-nums text-tmu01-ink-2">
          <div class="grid grid-cols-[2rem_minmax(0,1fr)_4.5rem] items-center gap-3"><dt>s4</dt><span aria-hidden="true" class="h-1 rounded-full bg-tmu01-line"><span class="block h-full rounded-full bg-tmu01-ink-2" style="width:100.0%"></span></span><dd class="text-right text-tmu01-ink">50.53px</dd></div>
          <div class="grid grid-cols-[2rem_minmax(0,1fr)_4.5rem] items-center gap-3"><dt>s3</dt><span aria-hidden="true" class="h-1 rounded-full bg-tmu01-line"><span class="block h-full rounded-full bg-tmu01-ink-2" style="width:75.0%"></span></span><dd class="text-right text-tmu01-ink">37.91px</dd></div>
          <div class="grid grid-cols-[2rem_minmax(0,1fr)_4.5rem] items-center gap-3"><dt>s2</dt><span aria-hidden="true" class="h-1 rounded-full bg-tmu01-line"><span class="block h-full rounded-full bg-tmu01-ink-2" style="width:56.3%"></span></span><dd class="text-right text-tmu01-ink">28.44px</dd></div>
          <div class="grid grid-cols-[2rem_minmax(0,1fr)_4.5rem] items-center gap-3"><dt>s1</dt><span aria-hidden="true" class="h-1 rounded-full bg-tmu01-line"><span class="block h-full rounded-full bg-tmu01-ink-2" style="width:42.2%"></span></span><dd class="text-right text-tmu01-ink">21.33px</dd></div>
          <div class="grid grid-cols-[2rem_minmax(0,1fr)_4.5rem] items-center gap-3"><dt>s0</dt><span aria-hidden="true" class="h-1 rounded-full bg-tmu01-line"><span class="block h-full rounded-full bg-tmu01-ink-2" style="width:31.7%"></span></span><dd class="text-right text-tmu01-ink">16.00px</dd></div>
        </dl>
      </article>
    </div>
  </div>
</section>
```
## CSS
```css
@import "tailwindcss";

@theme {
  --color-tmu01-bg: light-dark(#eef0f2, #0f1215);
  --color-tmu01-bg: light-dark(oklch(94.6% 0.005 240), oklch(17% 0.008 250));
  --color-tmu01-surface: light-dark(#ffffff, #171b1f);
  --color-tmu01-surface: light-dark(oklch(100% 0 0), oklch(22% 0.009 250));
  --color-tmu01-ink: light-dark(#14181c, #eef1f4);
  --color-tmu01-ink: light-dark(oklch(19% 0.012 250), oklch(95% 0.004 250));
  --color-tmu01-ink-2: light-dark(#4c565f, #a3adb6);
  --color-tmu01-ink-2: light-dark(oklch(43% 0.018 250), oklch(74% 0.012 250));
  --color-tmu01-line: light-dark(#c9cfd5, #333c44);
  --color-tmu01-line: light-dark(oklch(84% 0.008 250), oklch(33% 0.012 250));
  --color-tmu01-acc: light-dark(#1d5cff, #7fa6ff);
  --color-tmu01-acc: light-dark(oklch(52% 0.21 264), oklch(76% 0.14 264));
  --color-tmu01-ring: light-dark(#d9e3ff, #1f2d52);
  --color-tmu01-ring: light-dark(oklch(91% 0.04 264), oklch(30% 0.07 264));
  --color-tmu01-acc-ink: light-dark(#ffffff, #0f1215);
  --color-tmu01-acc-ink: light-dark(oklch(100% 0 0), oklch(17% 0.008 250));
  --font-tmu01-ui: system-ui, -apple-system, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
  --font-tmu01-mono: ui-monospace, SFMono-Regular, Menlo, Consolas, "Liberation Mono", monospace;
}
/* inline: each utility carries its own calc(), so it resolves on the element that uses it. */

@theme inline {
  --text-tmu01-s0: var(--tmu01-base);
  --text-tmu01-s1: calc(var(--tmu01-base) * var(--tmu01-ratio));
  --text-tmu01-s2: calc(var(--tmu01-base) * var(--tmu01-ratio) * var(--tmu01-ratio));
  --text-tmu01-s3: calc(var(--tmu01-base) * var(--tmu01-ratio) * var(--tmu01-ratio) * var(--tmu01-ratio));
  --text-tmu01-s4: calc(var(--tmu01-base) * var(--tmu01-ratio) * var(--tmu01-ratio) * var(--tmu01-ratio) * var(--tmu01-ratio));
}
/* 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-01 {
  width: 100%;
  min-height: 100vh;
  min-height: 100svh;
  color-scheme: light dark;
  --tmu01-ratio: 1.25;
  --tmu01-base: clamp(0.9375rem, 0.9rem + 0.25vw, 1rem);
}
/* Theme toggle: flip the scheme and every token follows. Mirrored for a dark OS. */

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

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

How this works

A ratio is a decision; a list of pixel values is twelve decisions you will contradict later. Set one base size and one ratio, then derive every step by multiplication. The result is that adjacent sizes are always distinguishable by the same proportional amount, which is exactly what makes hierarchy read without any other signal. Hand-picked sizes drift — 14, 16, 18, 20, 24 has three different intervals in it, and the 18 will end up doing nothing that the 16 was not already doing.

The mechanism is a chain of calc(), not maths you do in your head. Each step multiplies the previous step by --mu-01-ratio. Because custom properties resolve at computed-value time, redeclaring the ratio on any element recalculates every step inside it — which is how the three specimens here run three different scales from one identical rule set. Nothing is duplicated, and there is no build step.

Fluidity belongs on the base, not on every step. Put a clamp() on the base size and every derived step scales with the viewport automatically, in proportion. Writing clamp() on each step separately is the common mistake: the steps then interpolate at different rates and the scale you designed only exists at two viewport widths.

The trap, stated plainly. A large ratio looks decisive in a heading specimen and is unusable in a dense UI. At 1.333 the jump from body to the step above it is 5px at 16px base but 21px four steps up, so a table header and a page title end up almost the same size or absurdly far apart. Interface work generally wants 1.125 to 1.250, with editorial and landing pages taking the larger ratios. If your product has both — a marketing site and a dense console — you want two ratios, not one compromise ratio that serves neither.

Make it yours

  • Change the default ratio in the root rule (--tmu01-ratio: 1.25) and the whole scale rebuilds.
  • Add a step: --text-tmu01-s5: calc(var(--tmu01-base) * var(--tmu01-ratio) * var(--tmu01-ratio) * var(--tmu01-ratio) * var(--tmu01-ratio) * var(--tmu01-ratio)); inside @theme inline.
  • Scope a different ratio to any subtree — a marketing hero, a dense table — with one [--tmu01-ratio:1.125] class.
  • Retheme via the --color-tmu01-* tokens; each holds its light and dark value in one light-dark().

Gotchas — read before shipping

  • A ratio chosen from a heading specimen fails in a dense table. Test the scale on your smallest real UI text, not on a hero headline, or you will ship a scale whose lower steps are indistinguishable.
  • Rounding to whole pixels breaks the ratio. Let the browser keep the fractional value — 19.2px is correct and snapping it to 19px compounds into visible drift by the fourth step.
  • Applying clamp() to every step instead of the base makes the steps interpolate independently, so the proportional relationship you designed only holds at the clamp endpoints.
  • More steps is not more system. Five to seven steps that each have a job beats twelve where four are never used and two are used interchangeably.

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…