Button: smaller large size, and drop the label a pixel
Large was 47px tall, close enough to the old broken default that it still
read as a slab. Its padding comes down to 0.625rem/1.25rem, which puts it
at 43px and makes the ramp exactly regular — 0.375/0.5/0.625rem of
vertical padding, the horizontal exactly twice each, for 27/34/43px.
The label was also reading high in the box. It was not: measured on the
ink, a word like "Save" sat half a pixel *below* the geometric centre.
But the eye counts the empty descender space under a word as part of it,
so a label centred on its ink reads high anyway. --_shift gives it one
pixel more padding above than below; the two calcs cancel, so button
heights are unchanged and only the label moves.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019yEVAE5whghEqnU1g2tuX7
diff --git a/_components/button.md b/_components/button.md
index 5ec2fd6..64af107 100644
--- a/_components/button.md
+++ b/_components/button.md
@@ -40,6 +40,11 @@ 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.
+At every size the label sits one pixel below the geometric centre of the
+box. Centred exactly, a word with no descenders reads high — the eye counts
+the empty descender space under it as part of the word. The pixel is a
+deliberate correction, not a rounding error.
+
<figure class="example">
<figcaption>Sizes</figcaption>
<div>
diff --git a/_sass/components/button.css b/_sass/components/button.css
index 54f1aaf..8593328 100644
--- a/_sass/components/button.css
+++ b/_sass/components/button.css
@@ -23,14 +23,20 @@ button:not(.icon-button),
a.btn {
--_bg: transparent;
--_fg: var(--gl-color-text-primary);
- --_pad: 0.5rem 1rem;
+ --_pad-y: 0.5rem;
+ --_pad-x: 1rem;
+ --_shift: 1px;
--_font: var(--gl-step--1);
display: inline-flex;
align-items: center;
justify-content: center;
gap: var(--gl-space-2xs);
- padding: var(--_pad);
+ /* Optically centred, not geometrically: a mono label reads a shade high
+ in the box, so it gets one pixel more above it than below. The two
+ calcs cancel, so the button's height is exactly 2 × --_pad-y + the
+ line box either way. */
+ padding: calc(var(--_pad-y) + var(--_shift)) var(--_pad-x) calc(var(--_pad-y) - var(--_shift));
border: none;
border-radius: var(--gl-radius-md);
background: var(--_bg);
@@ -98,13 +104,13 @@ button:not(.icon-button):disabled,
--_fg: var(--gl-color-accent-fg);
}
-/* One ramp, three rungs — 0.375/0.5/0.75rem of vertical padding, each with
- the horizontal padding at roughly twice it. 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: 0.375rem 0.75rem; --_font: 0.8125rem; }
-:is(button:not(.icon-button), a.btn)[data-size="lg"] { --_pad: 0.75rem 1.375rem; --_font: var(--gl-step-0); }
+/* 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); }
:is(button:not(.icon-button), a.btn)[data-block] { width: 100%; }