Button: pad from the space scale again, one rung lower
Reading --gl-space-* is the point of the scale; the fix for the default
outgrowing the large size is to pad from a smaller rung, not to leave the
scale for literals. Medium takes space-2xs/space-xs, small drops to
space-3xs/space-2xs, and large keeps medium's vertical rung and widens to
space-s, parting from it on type size instead.

That last detail is what holds the order at every width. Both fluid rungs
grow with the viewport, so two sizes separated only by which rung they pad
with can cross over on a wide screen — which is exactly what the original
bug was. Separated by a type step, they cannot.

Heights run 25 / 35–38 / 39–43px, fluid, ordered everywhere. Medium no
longer lands on IconButton's fixed 34px box; the Button page says so
rather than claiming an alignment that only held for fixed padding.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019yEVAE5whghEqnU1g2tuX7
change
commit 15f86dbf6683825c7341443901e6fb7ec459f850
author Claude <noreply@anthropic.com>
date
parent f38e9256
diff --git a/_components/button.md b/_components/button.md
index 5ec2fd6..0088415 100644
--- a/_components/button.md
+++ b/_components/button.md
@@ -35,10 +35,16 @@ and irreversible only.
 
 ## Sizes
 
-Sizes move padding, and at the two ends of the ramp one step of font size
-with it — weight, radius, and letter-spacing never move. Medium is the
-default, and it lands within a pixel of a default IconButton's 34px box, so
-a button and an icon button in the same row line up.
+Sizes move one rung down the space scale, and at the two ends of the ramp
+one step of font size with it — weight, radius, and letter-spacing never
+move. Medium and large pad themselves with the same rung and part on type
+size instead; that is what holds their order at every width, since the
+space scale is fluid and the size on the wider rung would otherwise
+overtake the one on the narrower.
+
+Being fluid, a medium button runs 35–38px tall depending on the width of
+the window, a little over IconButton's fixed 34px box. The two sit close
+together in a row but do not lock to each other.
 
 <figure class="example">
 <figcaption>Sizes</figcaption>
diff --git a/_foundations/space.md b/_foundations/space.md
index 3046660..c9f2cde 100644
--- a/_foundations/space.md
+++ b/_foundations/space.md
@@ -160,8 +160,3 @@ rather than a decorative flourish. Nothing pill-shaped except true pills.
   `:last-child` reset.
 - **A value not on the scale is a bug**, not a nudge. If a gap looks wrong,
   the scale is wrong and should change for everybody.
-- **A control's own padding is not on this scale.** Button, Tag, Badge, and
-  Tabs size themselves with a small fixed ramp of their own. This scale is
-  fluid, and a control that grows with the viewport stops matching the
-  controls beside it — the default Button was once taller than the large
-  one for exactly that reason.
diff --git a/_sass/components/button.css b/_sass/components/button.css
index feab235..f260521 100644
--- a/_sass/components/button.css
+++ b/_sass/components/button.css
@@ -6,10 +6,11 @@
    dark, ghost, danger. Sizes: sm, md (default), lg — padding and font step
    only, weight and radius stay put.
 
-   The three sizes share one literal padding ramp rather than reading the
-   space scale: --gl-space-xs/-s are layout gaps, and being fluid they grew
-   the default button past the large one on a wide viewport. Control padding
-   is its own small, fixed ramp — see the sizes block at the bottom.
+   The default used to pad itself with --gl-space-xs/-s, which are the rungs
+   a layout reaches for between two controls, not inside one: on a wide
+   viewport they grew the default button taller than the large one. Every
+   size now sits one rung further down the same scale — see the block at
+   the bottom.
 
    The contract is the element itself: a bare <button> is styled directly, no
    class required. An <a> that acts like a button (navigates rather than
@@ -23,8 +24,8 @@ button:not(.icon-button),
 a.btn {
   --_bg: transparent;
   --_fg: var(--gl-color-text-primary);
-  --_pad-y: 0.5rem;
-  --_pad-x: 1rem;
+  --_pad-y: var(--gl-space-2xs);
+  --_pad-x: var(--gl-space-xs);
   --_font: var(--gl-step--1);
 
   display: inline-flex;
@@ -99,13 +100,13 @@ button:not(.icon-button):disabled,
   --_fg: var(--gl-color-accent-fg);
 }
 
-/* One ramp, three rungs — 0.375/0.5/0.625rem of vertical padding, the
-   horizontal exactly twice each. Medium lands within a pixel of
-   IconButton's default 2.125rem box (its label is fluid, that box is not),
-   so a Button and an IconButton in the same toolbar line up without either
-   being told the other's size. */
-:is(button:not(.icon-button), a.btn)[data-size="sm"] { --_pad-y: 0.375rem; --_pad-x: 0.75rem; --_font: 0.8125rem; }
-:is(button:not(.icon-button), a.btn)[data-size="lg"] { --_pad-y: 0.625rem; --_pad-x: 1.25rem; --_font: var(--gl-step-0); }
+/* Each size is one step of the space scale wide and, at the ends, one step
+   of the type scale. Medium and large share a rung vertically and part on
+   type size, which is what keeps them in order at every viewport width: the
+   scale is fluid, so two sizes that differed only by which fluid rung they
+   padded with could — and did — cross over on a wide screen. */
+:is(button:not(.icon-button), a.btn)[data-size="sm"] { --_pad-y: var(--gl-space-3xs); --_pad-x: var(--gl-space-2xs); --_font: 0.8125rem; }
+:is(button:not(.icon-button), a.btn)[data-size="lg"] { --_pad-x: var(--gl-space-s); --_font: var(--gl-step-0); }
 
 :is(button:not(.icon-button), a.btn)[data-block] { width: 100%; }