Button — HTML & CSS
A themeable button with a ripple effect. Works on a <button> or an <a> (link button). Emits a change event carrying its data-value.
Wiring it up in JS (init, events, API, Vue) is on the JavaScript page.
Live example
Color
Size — xsm · sm · default · lg
Shape (no color) — r-0 · default · r-round
Compactness — the label ellipsizes, the icon never scales
Step 4 — narrow the browser window below 480px to see these collapse (it keys off the viewport, so a narrow container will not trigger it)
Last ui-button-change: —
Markup
data-value is the payload emitted on click. Text can sit directly in the button, but naming the parts with .UIb__icon / .UIb__label is what lets them survive a narrow screen (see Compactness) and a setValue({ label }) update:
<button class="UIb primary" data-value="save" type="button">Save</button>
<button class="UIb success g-1" data-value="ok" type="button">
<span class="UIb__icon">✓</span><span class="UIb__label">Confirm</span>
</button>
<!-- Link button -->
<a class="UIb success" href="/page/2" data-value="/page/2" role="button" tabindex="0">
Go
</a>Minimal markup
The smallest working Button — no color, no data-value, no icon slots:
<button class="UIb" type="button">Button</button>Compactness
A button has a fixed height, so a label that wraps to a second line is clipped. Rather than leave that to chance — and rather than patch every button that happens to be too long — the button defines what gives way, in order, when it runs out of room:
- Icons never do. Nothing inside the button shrinks by default, so an icon keeps its box instead of scaling down to make room for text.
- Padding and gap go first.
- The label ellipsizes. It is the only part allowed to shrink, and it never wraps.
- The label disappears — opt-in, below
480px.
The button will shrink, but never past a square (its min-width equals its height), so an icon always fits.
Steps 1–3 are automatic and need no classes. For the ellipsis and for step 4 the text must live in its own element — a bare text node cannot be truncated or hidden by CSS:
<button class="UIb primary" data-value="save" type="button">
<span class="UIb__icon">💾</span><span class="UIb__label">Save all changes</span>
</button>| Class | Goes on | Effect |
|---|---|---|
UIb__icon | a child | Keeps its size; centers its glyph or <svg>. |
UIb__label | a child | Shrinks and ellipsizes instead of wrapping. |
collapse | the button | Below 480px: hides the label, squares the button, re-centers the icon. |
ui-mobile-hide | any child | Below 480px: display: none. |
One icon and a label
collapse is the shorthand for the common case:
<button class="UIb success collapse" data-value="save" type="button">
<span class="UIb__icon">💾</span><span class="UIb__label">Save changes</span>
</button>Two icons, or an icon in an odd place
With two icons the library cannot guess which one carries the meaning — [💾] Save [▾] collapsed down to [▾] says nothing about what it does. That is a decision about content, so the markup makes it: leave collapse off and mark what should go.
<button class="UIb info g-1" data-value="export" type="button">
<span class="UIb__icon">⬆</span>
<span class="UIb__label ui-mobile-hide">Export report</span>
<span class="UIb__icon ui-mobile-hide">▾</span>
</button>ui-mobile-hide is a global utility, like ui-no-ripple — it works on any element, not only inside a button.
Accessibility is handled for you
A hidden label leaves the accessibility tree, so a collapsed button would lose its accessible name at exactly the width where it also has no visible text. Button.scan() mirrors .UIb__label into aria-label to keep the name. A hand-written aria-label always wins and is never overwritten.
One breakpoint, on purpose
There is a single threshold at 480px. Splitting HD from 4K buys nothing — the problems all live around 400px. Note it keys off the viewport, not the button's container.