Pure CSSMIT licensed

Spacing Scale from a Single Base Unit

One base unit, six derived steps, and a rule that no component invents an off-scale value. A measured ruler prints every step, then the same card is built twice — once from the scale and once from hand-picked pixels — with the values labelled on both so the drift is visible rather than asserted.

Live Demo
Plain CSSTailwind v4
Try it

Untitled UI Pro: a 10,000+ component Figma UI kit · from $129 one-time

Untitled UI · Affiliate link

Get Untitled UI Pro

The code

<section class="mu-05" aria-labelledby="mu-05-title">
  <h2 class="mu-05__sr" id="mu-05-title">Spacing scale reference</h2>
  <div class="mu-05__theme">
    <input class="mu-05__theme-input" type="checkbox" id="mu-05-theme">
    <label class="mu-05__theme-label" for="mu-05-theme">
      <svg 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="mu-05__sr">Toggle dark theme</span>
    </label>
  </div>

  <div class="mu-05__stage">

    <section class="mu-05__ruler-wrap" aria-labelledby="mu-05-ruler-h">
      <h3 class="mu-05__sub" id="mu-05-ruler-h">The scale</h3>
      <ul class="mu-05__ruler">
        <li class="mu-05__tick" style="--mu-05-n: 1"><span class="mu-05__bar"></span><b>s1</b><i>8px</i></li>
        <li class="mu-05__tick" style="--mu-05-n: 2"><span class="mu-05__bar"></span><b>s2</b><i>16px</i></li>
        <li class="mu-05__tick" style="--mu-05-n: 3"><span class="mu-05__bar"></span><b>s3</b><i>24px</i></li>
        <li class="mu-05__tick" style="--mu-05-n: 4"><span class="mu-05__bar"></span><b>s4</b><i>32px</i></li>
        <li class="mu-05__tick" style="--mu-05-n: 6"><span class="mu-05__bar"></span><b>s5</b><i>48px</i></li>
        <li class="mu-05__tick" style="--mu-05-n: 8"><span class="mu-05__bar"></span><b>s6</b><i>64px</i></li>
      </ul>
    </section>

    <div class="mu-05__pair">
      <article class="mu-05__col" aria-labelledby="mu-05-bad-h">
        <div class="mu-05__col-head">
          <h3 class="mu-05__sub" id="mu-05-bad-h">Arbitrary values</h3>
          <p class="mu-05__col-note">13 &middot; 17 &middot; 22 &middot; 9 &middot; 31</p>
        </div>
        <div class="mu-05__card mu-05__card--bad">
          <p class="mu-05__card-kicker">Deployment</p>
          <h4 class="mu-05__card-title">Orbit API, region eu-west</h4>
          <p class="mu-05__card-body">Rolled out to 34 of 34 instances. No error budget consumed in the last hour.</p>
          <ul class="mu-05__card-meta">
            <li>Build 4c19f0</li>
            <li>Jordan Lee</li>
            <li>11 min ago</li>
          </ul>
          <div class="mu-05__card-actions">
            <button class="mu-05__btn mu-05__btn--primary" type="button">Promote to production</button>
            <button class="mu-05__btn" type="button">Roll back</button>
          </div>
        </div>
      </article>

      <article class="mu-05__col" aria-labelledby="mu-05-good-h">
        <div class="mu-05__col-head">
          <h3 class="mu-05__sub" id="mu-05-good-h">8px scale</h3>
          <p class="mu-05__col-note">8 &middot; 16 &middot; 24 &middot; 32</p>
        </div>
        <div class="mu-05__card mu-05__card--good">
          <p class="mu-05__card-kicker">Deployment</p>
          <h4 class="mu-05__card-title">Orbit API, region eu-west</h4>
          <p class="mu-05__card-body">Rolled out to 34 of 34 instances. No error budget consumed in the last hour.</p>
          <ul class="mu-05__card-meta">
            <li>Build 4c19f0</li>
            <li>Jordan Lee</li>
            <li>11 min ago</li>
          </ul>
          <div class="mu-05__card-actions">
            <button class="mu-05__btn mu-05__btn--primary" type="button">Promote to production</button>
            <button class="mu-05__btn" type="button">Roll back</button>
          </div>
        </div>
      </article>
    </div>
  </div>
</section>
.mu-05 {
  --mu-05-unit: 0.5rem;
  --mu-05-s1: calc(var(--mu-05-unit) * 1);
  --mu-05-s2: calc(var(--mu-05-unit) * 2);
  --mu-05-s3: calc(var(--mu-05-unit) * 3);
  --mu-05-s4: calc(var(--mu-05-unit) * 4);
  --mu-05-s5: calc(var(--mu-05-unit) * 6);
  --mu-05-s6: calc(var(--mu-05-unit) * 8);
  --mu-05-card-pad: var(--mu-05-s4);
  --mu-05-font: ui-monospace, SFMono-Regular, Menlo, Consolas, "Liberation Mono", monospace;
  --mu-05-ui: system-ui, -apple-system, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
  --mu-05-bg: #eef1ea;
  --mu-05-bg: oklch(94.5% 0.014 140);
  --mu-05-surface: #fbfcf9;
  --mu-05-surface: oklch(98.8% 0.006 140);
  --mu-05-ink: #161a15;
  --mu-05-ink: oklch(19.5% 0.012 140);
  --mu-05-ink-2: #4e564c;
  --mu-05-ink-2: oklch(44% 0.016 140);
  --mu-05-line: #ccd3c6;
  --mu-05-line: oklch(85% 0.018 140);
  --mu-05-acc: #1f6b3f;
  --mu-05-acc: oklch(46% 0.11 155);
  --mu-05-acc-ink: #ffffff;
  --mu-05-acc-ink: oklch(100% 0 0);
  width: 100%;
  min-height: 100vh;
  min-height: 100svh;
  display: block;
  position: relative;
  box-sizing: border-box;
  background: var(--mu-05-bg);
  color: var(--mu-05-ink);
  font-family: var(--mu-05-ui);
  line-height: 1.5;
  -webkit-text-size-adjust: 100%;
}

.mu-05 *,
.mu-05 *::before,
.mu-05 *::after {
  box-sizing: border-box;
}

@media (prefers-color-scheme: dark) {
  .mu-05 {
    --mu-05-bg: #101310;
    --mu-05-bg: oklch(17.5% 0.008 140);
    --mu-05-surface: #181d18;
    --mu-05-surface: oklch(22.5% 0.01 140);
    --mu-05-ink: #eef2ec;
    --mu-05-ink: oklch(95.5% 0.008 140);
    --mu-05-ink-2: #a5ad9f;
    --mu-05-ink-2: oklch(74% 0.014 140);
    --mu-05-line: #333a31;
    --mu-05-line: oklch(33% 0.014 140);
    --mu-05-acc: #6ed49a;
    --mu-05-acc: oklch(82% 0.13 155);
    --mu-05-acc-ink: #0b1a0f;
    --mu-05-acc-ink: oklch(16% 0.02 155);
  }
}

.mu-05[data-theme="dark"],
.mu-05:has(.mu-05__theme-input:checked) {
  --mu-05-bg: #101310;
  --mu-05-bg: oklch(17.5% 0.008 140);
  --mu-05-surface: #181d18;
  --mu-05-surface: oklch(22.5% 0.01 140);
  --mu-05-ink: #eef2ec;
  --mu-05-ink: oklch(95.5% 0.008 140);
  --mu-05-ink-2: #a5ad9f;
  --mu-05-ink-2: oklch(74% 0.014 140);
  --mu-05-line: #333a31;
  --mu-05-line: oklch(33% 0.014 140);
  --mu-05-acc: #6ed49a;
  --mu-05-acc: oklch(82% 0.13 155);
  --mu-05-acc-ink: #0b1a0f;
  --mu-05-acc-ink: oklch(16% 0.02 155);
}

@media (prefers-color-scheme: light) {
  .mu-05:has(.mu-05__theme-input:checked) {
    --mu-05-bg: #101310;
    --mu-05-bg: oklch(17.5% 0.008 140);
    --mu-05-surface: #181d18;
    --mu-05-surface: oklch(22.5% 0.01 140);
    --mu-05-ink: #eef2ec;
    --mu-05-ink: oklch(95.5% 0.008 140);
    --mu-05-ink-2: #a5ad9f;
    --mu-05-ink-2: oklch(74% 0.014 140);
    --mu-05-line: #333a31;
    --mu-05-line: oklch(33% 0.014 140);
    --mu-05-acc: #6ed49a;
    --mu-05-acc: oklch(82% 0.13 155);
    --mu-05-acc-ink: #0b1a0f;
    --mu-05-acc-ink: oklch(16% 0.02 155);
  }
}

@media (prefers-color-scheme: dark) {
  .mu-05:has(.mu-05__theme-input:checked) {
    --mu-05-bg: #eef1ea;
    --mu-05-bg: oklch(94.5% 0.014 140);
    --mu-05-surface: #fbfcf9;
    --mu-05-surface: oklch(98.8% 0.006 140);
    --mu-05-ink: #161a15;
    --mu-05-ink: oklch(19.5% 0.012 140);
    --mu-05-ink-2: #4e564c;
    --mu-05-ink-2: oklch(44% 0.016 140);
    --mu-05-line: #ccd3c6;
    --mu-05-line: oklch(85% 0.018 140);
    --mu-05-acc: #1f6b3f;
    --mu-05-acc: oklch(46% 0.11 155);
    --mu-05-acc-ink: #ffffff;
    --mu-05-acc-ink: oklch(100% 0 0);
  }
}

.mu-05__sr {
  position: absolute;
  inline-size: 1px;
  block-size: 1px;
  padding: 0;
  margin: -1px;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
  border: 0;
}

.mu-05__theme {
  position: absolute;
  inset-block-start: 1rem;
  inset-inline-end: 1rem;
  z-index: 5;
}

.mu-05__theme-input {
  position: absolute;
  inset: 0;
  inline-size: 44px;
  block-size: 44px;
  margin: 0;
  opacity: 0;
  cursor: pointer;
}

.mu-05__theme-label {
  display: grid;
  place-items: center;
  inline-size: 44px;
  block-size: 44px;
  border: 1px solid var(--mu-05-line);
  border-radius: 4px;
  background: var(--mu-05-surface);
  color: var(--mu-05-ink-2);
  cursor: pointer;
}

.mu-05__theme-label svg {
  inline-size: 20px;
  block-size: 20px;
}

.mu-05__theme-input:focus-visible + .mu-05__theme-label {
  outline: 2px solid var(--mu-05-acc);
  outline-offset: 2px;
}

.mu-05__stage {
  max-inline-size: 74rem;
  margin-inline: auto;
  padding: var(--mu-05-s6) var(--mu-05-s3) var(--mu-05-s6);
  display: grid;
  gap: var(--mu-05-s5);
  grid-template-columns: minmax(0, 1fr);
}

.mu-05__sub {
  margin: 0;
  font-family: var(--mu-05-font);
  font-size: 0.75rem;
  font-weight: 500;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--mu-05-ink);
}

.mu-05__ruler-wrap {
  display: grid;
  gap: var(--mu-05-s3);
  min-inline-size: 0;
}

.mu-05__ruler {
  margin: 0;
  padding: 0;
  list-style: none;
  display: grid;
  gap: var(--mu-05-s1);
  min-inline-size: 0;
}

.mu-05__tick {
  display: grid;
  align-items: center;
  gap: var(--mu-05-s2);
  grid-template-columns: 2.25rem 3.5rem minmax(0, 1fr);
  font-family: var(--mu-05-font);
  font-size: 0.75rem;
  font-variant-numeric: tabular-nums;
  padding-block: 2px;
  min-inline-size: 0;
}

.mu-05__tick b {
  order: 1;
  font-weight: 500;
  color: var(--mu-05-ink);
}

.mu-05__tick i {
  order: 2;
  font-style: normal;
  color: var(--mu-05-ink-2);
}

.mu-05__bar {
  order: 3;
  display: block;
  block-size: 10px;
  inline-size: calc(var(--mu-05-unit) * var(--mu-05-n));
  background: var(--mu-05-acc);
  border-radius: 1px;
}

.mu-05__ruler-note code,
.mu-05__col-note code {
  font-family: var(--mu-05-font);
  font-size: 0.9em;
}

.mu-05__pair {
  display: grid;
  gap: var(--mu-05-s3);
  grid-template-columns: minmax(0, 1fr);
}

.mu-05__col {
  min-inline-size: 0;
  display: grid;
  gap: var(--mu-05-s2);
  align-content: start;
}

.mu-05__col-head {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  justify-content: space-between;
  gap: var(--mu-05-s1);
  padding-block-end: var(--mu-05-s1);
  border-block-end: 1px solid var(--mu-05-line);
}

.mu-05__col-note {
  margin: 0;
  font-family: var(--mu-05-font);
  font-size: 0.75rem;
  color: var(--mu-05-ink-2);
  font-variant-numeric: tabular-nums;
}

.mu-05__card {
  min-inline-size: 0;
  background: var(--mu-05-surface);
  border: 1px solid var(--mu-05-line);
  border-radius: 6px;
}

.mu-05__card p,
.mu-05__card h4 {
  margin: 0;
  min-inline-size: 0;
}

.mu-05__card-kicker {
  font-family: var(--mu-05-font);
  font-size: 0.6875rem;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--mu-05-ink-2);
}

.mu-05__card-title {
  font-size: 1.25rem;
  line-height: 1.2;
  letter-spacing: -0.018em;
  font-weight: 600;
}

.mu-05__card-body {
  font-size: 0.9375rem;
  color: var(--mu-05-ink-2);
  max-inline-size: 46ch;
  text-wrap: pretty;
}

.mu-05__card-meta {
  margin: 0;
  padding: 0;
  list-style: none;
  display: flex;
  flex-wrap: wrap;
  font-family: var(--mu-05-font);
  font-size: 0.75rem;
  color: var(--mu-05-ink-2);
}

.mu-05__card-actions {
  display: flex;
  flex-wrap: wrap;
}

.mu-05__btn {
  min-block-size: 44px;
  min-inline-size: 44px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  padding-inline: var(--mu-05-s2);
  border-radius: 4px;
  border: 1px solid var(--mu-05-line);
  background: transparent;
  color: var(--mu-05-ink);
  font: inherit;
  font-size: 0.875rem;
  font-weight: 500;
  cursor: pointer;
  transition: border-color 150ms ease;
}

.mu-05__btn:hover {
  border-color: var(--mu-05-ink);
}

.mu-05__btn:focus-visible {
  outline: 2px solid var(--mu-05-acc);
  outline-offset: 2px;
}

.mu-05__btn--primary {
  background: var(--mu-05-acc);
  border-color: var(--mu-05-acc);
  color: var(--mu-05-acc-ink);
}

.mu-05__card--bad {
  padding: 17px 13px 22px;
}

.mu-05__card--bad .mu-05__card-kicker {
  margin-block-end: 9px;
}

.mu-05__card--bad .mu-05__card-title {
  margin-block-end: 11px;
}

.mu-05__card--bad .mu-05__card-body {
  margin-block-end: 22px;
}

.mu-05__card--bad .mu-05__card-meta {
  gap: 6px 13px;
  margin-block-end: 31px;
}

.mu-05__card--bad .mu-05__card-actions {
  gap: 9px;
}

.mu-05__card--good {
  padding: var(--mu-05-card-pad);
  display: grid;
  gap: var(--mu-05-s2);
  align-content: start;
}

.mu-05__card--good .mu-05__card-meta {
  gap: var(--mu-05-s1) var(--mu-05-s2);
}

.mu-05__card--good .mu-05__card-actions {
  gap: var(--mu-05-s1);
  margin-block-start: var(--mu-05-s1);
}

@media (prefers-reduced-motion: reduce) {
  .mu-05__btn {
    transition: none;
  }
}

@media (min-width: 52rem) {
  .mu-05__pair {
    grid-template-columns: repeat(2, minmax(0, 1fr));
  }

  .mu-05__stage {
    padding-inline: var(--mu-05-s5);
  }
}
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 Minimalist UI 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: Spacing Scale from a Single Base Unit
Source: https://codefronts.com/design-styles/css-minimalist-ui/spacing-scale-from-a-single-base-unit/

One base unit, six derived steps, and a rule that no component invents an off-scale value. A measured ruler prints every step, then the same card is built twice &mdash; once from the scale and once from hand-picked pixels &mdash; with the values labelled on both so the drift is visible rather than asserted.
## HTML
```html
<section class="mu-05" aria-labelledby="mu-05-title">
  <h2 class="mu-05__sr" id="mu-05-title">Spacing scale reference</h2>
  <div class="mu-05__theme">
    <input class="mu-05__theme-input" type="checkbox" id="mu-05-theme">
    <label class="mu-05__theme-label" for="mu-05-theme">
      <svg 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="mu-05__sr">Toggle dark theme</span>
    </label>
  </div>

  <div class="mu-05__stage">

    <section class="mu-05__ruler-wrap" aria-labelledby="mu-05-ruler-h">
      <h3 class="mu-05__sub" id="mu-05-ruler-h">The scale</h3>
      <ul class="mu-05__ruler">
        <li class="mu-05__tick" style="--mu-05-n: 1"><span class="mu-05__bar"></span><b>s1</b><i>8px</i></li>
        <li class="mu-05__tick" style="--mu-05-n: 2"><span class="mu-05__bar"></span><b>s2</b><i>16px</i></li>
        <li class="mu-05__tick" style="--mu-05-n: 3"><span class="mu-05__bar"></span><b>s3</b><i>24px</i></li>
        <li class="mu-05__tick" style="--mu-05-n: 4"><span class="mu-05__bar"></span><b>s4</b><i>32px</i></li>
        <li class="mu-05__tick" style="--mu-05-n: 6"><span class="mu-05__bar"></span><b>s5</b><i>48px</i></li>
        <li class="mu-05__tick" style="--mu-05-n: 8"><span class="mu-05__bar"></span><b>s6</b><i>64px</i></li>
      </ul>
    </section>

    <div class="mu-05__pair">
      <article class="mu-05__col" aria-labelledby="mu-05-bad-h">
        <div class="mu-05__col-head">
          <h3 class="mu-05__sub" id="mu-05-bad-h">Arbitrary values</h3>
          <p class="mu-05__col-note">13 &middot; 17 &middot; 22 &middot; 9 &middot; 31</p>
        </div>
        <div class="mu-05__card mu-05__card--bad">
          <p class="mu-05__card-kicker">Deployment</p>
          <h4 class="mu-05__card-title">Orbit API, region eu-west</h4>
          <p class="mu-05__card-body">Rolled out to 34 of 34 instances. No error budget consumed in the last hour.</p>
          <ul class="mu-05__card-meta">
            <li>Build 4c19f0</li>
            <li>Jordan Lee</li>
            <li>11 min ago</li>
          </ul>
          <div class="mu-05__card-actions">
            <button class="mu-05__btn mu-05__btn--primary" type="button">Promote to production</button>
            <button class="mu-05__btn" type="button">Roll back</button>
          </div>
        </div>
      </article>

      <article class="mu-05__col" aria-labelledby="mu-05-good-h">
        <div class="mu-05__col-head">
          <h3 class="mu-05__sub" id="mu-05-good-h">8px scale</h3>
          <p class="mu-05__col-note">8 &middot; 16 &middot; 24 &middot; 32</p>
        </div>
        <div class="mu-05__card mu-05__card--good">
          <p class="mu-05__card-kicker">Deployment</p>
          <h4 class="mu-05__card-title">Orbit API, region eu-west</h4>
          <p class="mu-05__card-body">Rolled out to 34 of 34 instances. No error budget consumed in the last hour.</p>
          <ul class="mu-05__card-meta">
            <li>Build 4c19f0</li>
            <li>Jordan Lee</li>
            <li>11 min ago</li>
          </ul>
          <div class="mu-05__card-actions">
            <button class="mu-05__btn mu-05__btn--primary" type="button">Promote to production</button>
            <button class="mu-05__btn" type="button">Roll back</button>
          </div>
        </div>
      </article>
    </div>
  </div>
</section>
```
## CSS
```css
.mu-05 {
  --mu-05-unit: 0.5rem;
  --mu-05-s1: calc(var(--mu-05-unit) * 1);
  --mu-05-s2: calc(var(--mu-05-unit) * 2);
  --mu-05-s3: calc(var(--mu-05-unit) * 3);
  --mu-05-s4: calc(var(--mu-05-unit) * 4);
  --mu-05-s5: calc(var(--mu-05-unit) * 6);
  --mu-05-s6: calc(var(--mu-05-unit) * 8);
  --mu-05-card-pad: var(--mu-05-s4);
  --mu-05-font: ui-monospace, SFMono-Regular, Menlo, Consolas, "Liberation Mono", monospace;
  --mu-05-ui: system-ui, -apple-system, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
  --mu-05-bg: #eef1ea;
  --mu-05-bg: oklch(94.5% 0.014 140);
  --mu-05-surface: #fbfcf9;
  --mu-05-surface: oklch(98.8% 0.006 140);
  --mu-05-ink: #161a15;
  --mu-05-ink: oklch(19.5% 0.012 140);
  --mu-05-ink-2: #4e564c;
  --mu-05-ink-2: oklch(44% 0.016 140);
  --mu-05-line: #ccd3c6;
  --mu-05-line: oklch(85% 0.018 140);
  --mu-05-acc: #1f6b3f;
  --mu-05-acc: oklch(46% 0.11 155);
  --mu-05-acc-ink: #ffffff;
  --mu-05-acc-ink: oklch(100% 0 0);
  width: 100%;
  min-height: 100vh;
  min-height: 100svh;
  display: block;
  position: relative;
  box-sizing: border-box;
  background: var(--mu-05-bg);
  color: var(--mu-05-ink);
  font-family: var(--mu-05-ui);
  line-height: 1.5;
  -webkit-text-size-adjust: 100%;
}

.mu-05 *,
.mu-05 *::before,
.mu-05 *::after {
  box-sizing: border-box;
}

@media (prefers-color-scheme: dark) {
  .mu-05 {
    --mu-05-bg: #101310;
    --mu-05-bg: oklch(17.5% 0.008 140);
    --mu-05-surface: #181d18;
    --mu-05-surface: oklch(22.5% 0.01 140);
    --mu-05-ink: #eef2ec;
    --mu-05-ink: oklch(95.5% 0.008 140);
    --mu-05-ink-2: #a5ad9f;
    --mu-05-ink-2: oklch(74% 0.014 140);
    --mu-05-line: #333a31;
    --mu-05-line: oklch(33% 0.014 140);
    --mu-05-acc: #6ed49a;
    --mu-05-acc: oklch(82% 0.13 155);
    --mu-05-acc-ink: #0b1a0f;
    --mu-05-acc-ink: oklch(16% 0.02 155);
  }
}

.mu-05[data-theme="dark"],
.mu-05:has(.mu-05__theme-input:checked) {
  --mu-05-bg: #101310;
  --mu-05-bg: oklch(17.5% 0.008 140);
  --mu-05-surface: #181d18;
  --mu-05-surface: oklch(22.5% 0.01 140);
  --mu-05-ink: #eef2ec;
  --mu-05-ink: oklch(95.5% 0.008 140);
  --mu-05-ink-2: #a5ad9f;
  --mu-05-ink-2: oklch(74% 0.014 140);
  --mu-05-line: #333a31;
  --mu-05-line: oklch(33% 0.014 140);
  --mu-05-acc: #6ed49a;
  --mu-05-acc: oklch(82% 0.13 155);
  --mu-05-acc-ink: #0b1a0f;
  --mu-05-acc-ink: oklch(16% 0.02 155);
}

@media (prefers-color-scheme: light) {
  .mu-05:has(.mu-05__theme-input:checked) {
    --mu-05-bg: #101310;
    --mu-05-bg: oklch(17.5% 0.008 140);
    --mu-05-surface: #181d18;
    --mu-05-surface: oklch(22.5% 0.01 140);
    --mu-05-ink: #eef2ec;
    --mu-05-ink: oklch(95.5% 0.008 140);
    --mu-05-ink-2: #a5ad9f;
    --mu-05-ink-2: oklch(74% 0.014 140);
    --mu-05-line: #333a31;
    --mu-05-line: oklch(33% 0.014 140);
    --mu-05-acc: #6ed49a;
    --mu-05-acc: oklch(82% 0.13 155);
    --mu-05-acc-ink: #0b1a0f;
    --mu-05-acc-ink: oklch(16% 0.02 155);
  }
}

@media (prefers-color-scheme: dark) {
  .mu-05:has(.mu-05__theme-input:checked) {
    --mu-05-bg: #eef1ea;
    --mu-05-bg: oklch(94.5% 0.014 140);
    --mu-05-surface: #fbfcf9;
    --mu-05-surface: oklch(98.8% 0.006 140);
    --mu-05-ink: #161a15;
    --mu-05-ink: oklch(19.5% 0.012 140);
    --mu-05-ink-2: #4e564c;
    --mu-05-ink-2: oklch(44% 0.016 140);
    --mu-05-line: #ccd3c6;
    --mu-05-line: oklch(85% 0.018 140);
    --mu-05-acc: #1f6b3f;
    --mu-05-acc: oklch(46% 0.11 155);
    --mu-05-acc-ink: #ffffff;
    --mu-05-acc-ink: oklch(100% 0 0);
  }
}

.mu-05__sr {
  position: absolute;
  inline-size: 1px;
  block-size: 1px;
  padding: 0;
  margin: -1px;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
  border: 0;
}

.mu-05__theme {
  position: absolute;
  inset-block-start: 1rem;
  inset-inline-end: 1rem;
  z-index: 5;
}

.mu-05__theme-input {
  position: absolute;
  inset: 0;
  inline-size: 44px;
  block-size: 44px;
  margin: 0;
  opacity: 0;
  cursor: pointer;
}

.mu-05__theme-label {
  display: grid;
  place-items: center;
  inline-size: 44px;
  block-size: 44px;
  border: 1px solid var(--mu-05-line);
  border-radius: 4px;
  background: var(--mu-05-surface);
  color: var(--mu-05-ink-2);
  cursor: pointer;
}

.mu-05__theme-label svg {
  inline-size: 20px;
  block-size: 20px;
}

.mu-05__theme-input:focus-visible + .mu-05__theme-label {
  outline: 2px solid var(--mu-05-acc);
  outline-offset: 2px;
}

.mu-05__stage {
  max-inline-size: 74rem;
  margin-inline: auto;
  padding: var(--mu-05-s6) var(--mu-05-s3) var(--mu-05-s6);
  display: grid;
  gap: var(--mu-05-s5);
  grid-template-columns: minmax(0, 1fr);
}

.mu-05__sub {
  margin: 0;
  font-family: var(--mu-05-font);
  font-size: 0.75rem;
  font-weight: 500;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--mu-05-ink);
}

.mu-05__ruler-wrap {
  display: grid;
  gap: var(--mu-05-s3);
  min-inline-size: 0;
}

.mu-05__ruler {
  margin: 0;
  padding: 0;
  list-style: none;
  display: grid;
  gap: var(--mu-05-s1);
  min-inline-size: 0;
}

.mu-05__tick {
  display: grid;
  align-items: center;
  gap: var(--mu-05-s2);
  grid-template-columns: 2.25rem 3.5rem minmax(0, 1fr);
  font-family: var(--mu-05-font);
  font-size: 0.75rem;
  font-variant-numeric: tabular-nums;
  padding-block: 2px;
  min-inline-size: 0;
}

.mu-05__tick b {
  order: 1;
  font-weight: 500;
  color: var(--mu-05-ink);
}

.mu-05__tick i {
  order: 2;
  font-style: normal;
  color: var(--mu-05-ink-2);
}

.mu-05__bar {
  order: 3;
  display: block;
  block-size: 10px;
  inline-size: calc(var(--mu-05-unit) * var(--mu-05-n));
  background: var(--mu-05-acc);
  border-radius: 1px;
}

.mu-05__ruler-note code,
.mu-05__col-note code {
  font-family: var(--mu-05-font);
  font-size: 0.9em;
}

.mu-05__pair {
  display: grid;
  gap: var(--mu-05-s3);
  grid-template-columns: minmax(0, 1fr);
}

.mu-05__col {
  min-inline-size: 0;
  display: grid;
  gap: var(--mu-05-s2);
  align-content: start;
}

.mu-05__col-head {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  justify-content: space-between;
  gap: var(--mu-05-s1);
  padding-block-end: var(--mu-05-s1);
  border-block-end: 1px solid var(--mu-05-line);
}

.mu-05__col-note {
  margin: 0;
  font-family: var(--mu-05-font);
  font-size: 0.75rem;
  color: var(--mu-05-ink-2);
  font-variant-numeric: tabular-nums;
}

.mu-05__card {
  min-inline-size: 0;
  background: var(--mu-05-surface);
  border: 1px solid var(--mu-05-line);
  border-radius: 6px;
}

.mu-05__card p,
.mu-05__card h4 {
  margin: 0;
  min-inline-size: 0;
}

.mu-05__card-kicker {
  font-family: var(--mu-05-font);
  font-size: 0.6875rem;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--mu-05-ink-2);
}

.mu-05__card-title {
  font-size: 1.25rem;
  line-height: 1.2;
  letter-spacing: -0.018em;
  font-weight: 600;
}

.mu-05__card-body {
  font-size: 0.9375rem;
  color: var(--mu-05-ink-2);
  max-inline-size: 46ch;
  text-wrap: pretty;
}

.mu-05__card-meta {
  margin: 0;
  padding: 0;
  list-style: none;
  display: flex;
  flex-wrap: wrap;
  font-family: var(--mu-05-font);
  font-size: 0.75rem;
  color: var(--mu-05-ink-2);
}

.mu-05__card-actions {
  display: flex;
  flex-wrap: wrap;
}

.mu-05__btn {
  min-block-size: 44px;
  min-inline-size: 44px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  padding-inline: var(--mu-05-s2);
  border-radius: 4px;
  border: 1px solid var(--mu-05-line);
  background: transparent;
  color: var(--mu-05-ink);
  font: inherit;
  font-size: 0.875rem;
  font-weight: 500;
  cursor: pointer;
  transition: border-color 150ms ease;
}

.mu-05__btn:hover {
  border-color: var(--mu-05-ink);
}

.mu-05__btn:focus-visible {
  outline: 2px solid var(--mu-05-acc);
  outline-offset: 2px;
}

.mu-05__btn--primary {
  background: var(--mu-05-acc);
  border-color: var(--mu-05-acc);
  color: var(--mu-05-acc-ink);
}

.mu-05__card--bad {
  padding: 17px 13px 22px;
}

.mu-05__card--bad .mu-05__card-kicker {
  margin-block-end: 9px;
}

.mu-05__card--bad .mu-05__card-title {
  margin-block-end: 11px;
}

.mu-05__card--bad .mu-05__card-body {
  margin-block-end: 22px;
}

.mu-05__card--bad .mu-05__card-meta {
  gap: 6px 13px;
  margin-block-end: 31px;
}

.mu-05__card--bad .mu-05__card-actions {
  gap: 9px;
}

.mu-05__card--good {
  padding: var(--mu-05-card-pad);
  display: grid;
  gap: var(--mu-05-s2);
  align-content: start;
}

.mu-05__card--good .mu-05__card-meta {
  gap: var(--mu-05-s1) var(--mu-05-s2);
}

.mu-05__card--good .mu-05__card-actions {
  gap: var(--mu-05-s1);
  margin-block-start: var(--mu-05-s1);
}

@media (prefers-reduced-motion: reduce) {
  .mu-05__btn {
    transition: none;
  }
}

@media (min-width: 52rem) {
  .mu-05__pair {
    grid-template-columns: repeat(2, minmax(0, 1fr));
  }

  .mu-05__stage {
    padding-inline: var(--mu-05-s5);
  }
}
```

How this works

A spacing scale exists so that spacing stops being a decision. Pick one base unit — 4px or 8px in practice — and derive every gap, padding and margin from it. The value of this is not tidiness: it is that two developers working on two components in two different weeks produce spacing that matches, without talking to each other. That is the entire promise of a token system, and spacing is where it pays out fastest.

Use gap, not margins. A margin belongs to a child and describes a relationship the child cannot see. gap belongs to the container, which is the thing that actually knows how its children relate. Practically, gap-based spacing survives a reorder, a deletion and a conditional render, where a margin-based rhythm collapses the moment the first or last child changes.

Print the values while you are learning the scale. The ruler in this demo is generated from the same properties the cards consume, so it can never document a value the components do not use. On the comparison below it, the left card uses 13px, 17px, 22px and 9px — all plausible, all chosen individually, and together they read as slightly broken in a way that is very hard to diagnose after the fact. The right card uses 8, 16, 24 and 32, and reads as deliberate.

The trap: too many steps is the same as having none. If your scale offers 2, 4, 6, 8, 10, 12, 14, 16, 20, 24, 28, 32, 40, 48, then every value is on the scale and the scale constrains nothing. Six or seven steps with a clear jump between them forces a real choice — and when a component genuinely needs an in-between value, that is a signal the component is doing something unusual and worth looking at, which is exactly the signal a dense scale destroys.

Make it yours

  • Change --mu-05-unit from 0.5rem to 0.25rem for a 4px grid; every step, gap and padding follows.
  • Add or remove a step by editing the --mu-05-s* chain — nothing else references raw numbers.
  • Switch the ramp to a non-linear one (1, 2, 3, 5, 8) by changing the multipliers for a more editorial rhythm.
  • Set --mu-05-card-pad to a different step to test how much of the card's calm is padding rather than type.
  • Delete the ruler block to ship just the two cards as a before/after teaching pair.
  • Recolour by editing the two --mu-05-acc declarations, hex first then oklch.

Gotchas — read before shipping

  • A scale with fourteen steps constrains nothing. Six or seven steps with visible jumps is what makes the system a decision rather than a menu.
  • Per-child margins break the moment a child is conditionally rendered — the first or last gap disappears. Put the spacing on the container with gap.
  • Mixing the scale with one-off values is worse than having no scale, because the eye reads the off-scale value as an error rather than an intent.
  • Deriving steps in px instead of rem means the rhythm ignores the user's font-size preference, so a zoomed-in layout keeps hairline spacing around huge text.

Browser support

ChromeSafariFirefoxEdge
114+17.5+121+114+

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

oklch() and the :has() theme toggle set the floor. The scale mechanism — custom properties, calc() and gap on grid and flex — has been Baseline since 2020 (gap on flexbox landed in Chrome 84, Safari 14.1, Firefox 63), so the spacing system itself works in engines far older than this floor. Older engines take the sRGB hex declared first and keep the operating-system colour scheme.

Techniques used in this demo

Search CodeFronts

Loading…