1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
---
title: Button
summary: >-
  No border, no ornament — hierarchy is ink density, not an outline. Five
  variants, three sizes, one geometry.
---

## Variants

`default` is quiet — no fill at rest, a flat tint of the stock on hover, the
same gesture as a CardRow. `primary` is the accent fill — one committing
action per view, at most. `dark` is reserved for the single most committing
action in a screen, typically a final Save; it should be rare. `ghost` is
the same quiet block one step further back — text-secondary instead of
text-primary — for dismissals and tertiary actions. `danger` is destructive
and irreversible only.

<figure class="example">
<figcaption>Variants</figcaption>
<div>
<button>Cancel</button>
<button data-variant="primary">Save</button>
<button data-variant="dark">Finish setup</button>
<button data-variant="ghost">Dismiss</button>
<button data-variant="danger">Delete</button>
</div>
<details open><summary>Markup</summary>
<pre><code>&lt;button&gt;Cancel&lt;/button&gt;
&lt;button data-variant=&quot;primary&quot;&gt;Save&lt;/button&gt;
&lt;button data-variant=&quot;dark&quot;&gt;Finish setup&lt;/button&gt;
&lt;button data-variant=&quot;ghost&quot;&gt;Dismiss&lt;/button&gt;
&lt;button data-variant=&quot;danger&quot;&gt;Delete&lt;/button&gt;</code></pre>
</details>
</figure>

## Sizes

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. [IconButton](<%= relative_url('/components/icon-button/') %>)
builds its square from the same two rungs, so the two are exactly the same
height at every width and in every size — a button and an icon button in a
toolbar line up without either being told the other's measurements.

<figure class="example">
<figcaption>Sizes</figcaption>
<div>
<button data-variant="primary" data-size="sm">Small</button>
<button data-variant="primary">Medium</button>
<button data-variant="primary" data-size="lg">Large</button>
</div>
<details><summary>Markup</summary>
<pre><code>&lt;button data-variant=&quot;primary&quot; data-size=&quot;sm&quot;&gt;Small&lt;/button&gt;
&lt;button data-variant=&quot;primary&quot;&gt;Medium&lt;/button&gt;
&lt;button data-variant=&quot;primary&quot; data-size=&quot;lg&quot;&gt;Large&lt;/button&gt;</code></pre>
</details>
</figure>

## States

Disabled buttons drop to 50% opacity and keep their variant colors, so a
disabled primary still reads as the primary.

<figure class="example">
<figcaption>States</figcaption>
<div>
<button data-variant="primary" disabled="">Disabled</button>
<button aria-disabled="true">Not yet available</button>
</div>
<details><summary>Markup</summary>
<pre><code>&lt;button data-variant=&quot;primary&quot; disabled=&quot;&quot;&gt;Disabled&lt;/button&gt;
&lt;button aria-disabled=&quot;true&quot;&gt;Not yet available&lt;/button&gt;</code></pre>
</details>
</figure>

<div class="gl-callout">
  <p class="type-label">disabled vs. aria-disabled</p>
  <p>
    A <code>disabled</code> button is removed from the tab order, so a reader
    navigating by keyboard never learns it is there. When a button is
    temporarily unavailable and the reason matters, use
    <code>aria-disabled="true"</code> instead — it stays focusable and can
    carry an explanation.
  </p>
</div>

## With an icon

Set `--gl-icon` to one of the [icon tokens](<%= relative_url('/foundations/icons/') %>).
The button draws it before the label, at the label's size and color, with
the button's own `--gl-space-2xs` gap.

<figure class="example">
<figcaption>Icon</figcaption>
<div>
<button data-variant="primary" style="--gl-icon: var(--gl-icon-plus)">
  New
</button>
</div>
<details><summary>Markup</summary>
<pre><code>&lt;button data-variant=&quot;primary&quot; style=&quot;--gl-icon: var(--gl-icon-plus)&quot;&gt;
  New
&lt;/button&gt;</code></pre>
</details>
</figure>

## Groups

`[role="group"]` is already the correct ARIA role for a toolbar of related
buttons, so it doubles as the styling hook — no wrapper class.

<figure class="example">
<figcaption>Button group</figcaption>
<div>
<div role="group" aria-label="View">
  <button data-size="sm">List</button>
  <button data-size="sm">Board</button>
  <button data-size="sm">Calendar</button>
</div>
</div>
<details><summary>Markup</summary>
<pre><code>&lt;div role=&quot;group&quot; aria-label=&quot;View&quot;&gt;
  &lt;button data-size=&quot;sm&quot;&gt;List&lt;/button&gt;
  &lt;button data-size=&quot;sm&quot;&gt;Board&lt;/button&gt;
  &lt;button data-size=&quot;sm&quot;&gt;Calendar&lt;/button&gt;
&lt;/div&gt;</code></pre>
</details>
</figure>

## Full width

<figure class="example">
<figcaption>Block</figcaption>
<div data-layout="stack">
<button data-variant="primary" data-block>Take a photo</button>
</div>
<details><summary>Markup</summary>
<pre><code>&lt;button data-variant=&quot;primary&quot; data-block&gt;Take a photo&lt;/button&gt;</code></pre>
</details>
</figure>

## Links that act like buttons

An `<a>` with `.btn` is fine when the thing it does is navigate. A `<button>`
is required when it changes something. `.btn` exists only because a bare `a`
selector would catch every ordinary prose link on the site — it is the one
class Button needs, and only anchors reach for it.

<figure class="example">
<figcaption>Anchor as button</figcaption>
<div>
<a class="btn" data-variant="primary" href="/gloss/components/icon-button/">Next: IconButton</a>
<a class="btn" data-variant="ghost" href="/gloss/foundations/color/">Back to color</a>
</div>
<details><summary>Markup</summary>
<pre><code>&lt;a class=&quot;btn&quot; data-variant=&quot;primary&quot; href=&quot;/gloss/components/icon-button/&quot;&gt;Next: IconButton&lt;/a&gt;
&lt;a class=&quot;btn&quot; data-variant=&quot;ghost&quot; href=&quot;/gloss/foundations/color/&quot;&gt;Back to color&lt;/a&gt;</code></pre>
</details>
</figure>

## API

<table>
  <thead><tr><th scope="col">Element / attribute</th><th scope="col">Values</th><th scope="col">Notes</th></tr></thead>
  <tbody>
    <tr><td><code>button</code></td><td></td><td>Required base. A bare <code>&lt;button&gt;</code> is styled directly — no class. Use <code>&lt;a class="btn"&gt;</code> instead when the control navigates.</td></tr>
    <tr><td><code>data-variant</code></td><td><code>primary | dark | ghost | danger</code></td><td>Omit for the default, quiet look — no fill at rest.</td></tr>
    <tr><td><code>data-size</code></td><td><code>sm | lg</code></td><td>Omit for the default (medium) size. Padding, plus one step of font size at each end.</td></tr>
    <tr><td><code>data-block</code></td><td>boolean</td><td>Full width of its container.</td></tr>
    <tr><td><code>--gl-icon</code></td><td>an icon token</td><td>Draws the icon before the label. See <a href="<%= relative_url('/foundations/icons/') %>">Icons</a>.</td></tr>
    <tr><td><code>disabled</code></td><td>boolean</td><td>Removes the button from the tab order.</td></tr>
    <tr><td><code>aria-disabled</code></td><td><code>"true"</code></td><td>Looks disabled, stays focusable.</td></tr>
    <tr><td><code>[role="group"]</code></td><td></td><td>Wrap adjacent buttons in a group to join their radii; a hairline divider separates them, since the buttons themselves carry no border.</td></tr>
  </tbody>
</table>