Button
Five variants, three sizes, and an icon-only form that insists on a name.
Preview
Code
The three render the same class names, the same ARIA and the same data attributes. That is checked when this site builds: a class in any of these samples that neither the markup contract nor the shipped stylesheet defines fails the build.
<button class="wsu-btn wsu-btn--primary" type="button">Add course</button>
<button class="wsu-btn wsu-btn--outline" type="button">
<svg class="wsu-icon" aria-hidden="true"><use href="/assets/wayne-ui/icons.svg#wsu-download"/></svg>
Export
</button>
<!-- Icon only: the name is not optional -->
<button class="wsu-btn wsu-btn--icon wsu-btn--ghost" type="button">
<span class="wsu-sr-only">Settings</span>
<svg class="wsu-icon" aria-hidden="true"><use href="/assets/wayne-ui/icons.svg#wsu-settings"/></svg>
</button>
<!-- Navigating, so an anchor -->
<a class="wsu-btn wsu-btn--primary" href="/courses/new">Add course</a><x-wsu::button variant="primary">Add course</x-wsu::button>
<x-wsu::button variant="outline" icon="download">Export</x-wsu::button>
<x-wsu::button variant="ghost" icon="settings" icon-only label="Settings" />
<x-wsu::button variant="primary" href="/courses/new">Add course</x-wsu::button>
<x-wsu::button variant="primary" type="submit">Submit request</x-wsu::button><Button variant="primary">Add course</Button>
<Button variant="outline">
<Icon name="download" /> Export
</Button>
<Button variant="ghost" icon label="Settings">
<Icon name="settings" />
</Button>
<Button as="a" href="/courses/new" variant="primary">Add course</Button>
<Button variant="primary" :loading="form.processing">Submit request</Button>Why it works this way
- A control that navigates must be an a, and a control that acts must be a button. Styling one to look like the other is fine. Using one where the other belongs breaks middle-click, open-in-new-tab, the screen reader's list of links and the difference between Enter and Space. 1.x shipped <a class="btn" href="#" onclick=…> throughout.
- type="button" is the default. An unqualified button inside a form is a submit button, and every Cancel that silently submits the form it was meant to abandon is that defect.
- An icon-only button without a label is meant to carry a .wsu-sr-only name instead of rendering unnamed. Blade raises InvalidArgumentException at render time, a hard failure a fresh template has no published callers to break. Vue cannot make label required without breaking every already-published caller that omits it on a plain, labeled button, so it instead warns in development when icon is set with no label; unlike Blade, an unnamed control still renders if the warning goes unread.
- Buttons take a 1px press on :active, at --wsu-duration-fast.
Accessibility
- Minimum 44px target from --wsu-size-tap-min (2.5.5).
- Icon-only buttons are meant to carry a .wsu-sr-only name (4.1.2); Blade enforces this at render time, Vue only warns about it in development.
- A disabled link is not a thing in HTML, so the href is dropped and aria-disabled is set instead.
Success criteria in brackets refer to WCAG 2.2. Every one of them is checked by axe-core in both themes at three viewport widths before this page can be published. See Accessibility for what the gates are.
Props
Blade
| Attribute | Default | What it does |
|---|---|---|
variant | 'ghost' | primary, accent, outline, ghost or danger. Anything else falls back to ghost. |
size | null | sm or lg. |
href | null | Renders an anchor instead of a button. |
icon / iconAfter | null | Icon name, before or after the label. |
iconOnly / label | false / null | label is required when iconOnly is set, or the component throws. |
type | 'button' | Set to submit deliberately, never by omission. |
disabled | false | Sets the native disabled attribute on a button; on a link it drops href and sets aria-disabled instead, since a disabled link is not a thing in HTML. |
Vue
| Prop | Type and default | What it does |
|---|---|---|
variant | String, 'primary' | primary, accent, outline, ghost or danger. |
size | String, '' | sm or lg. |
as / href | String, 'button' | Set as="a" to navigate. |
type | String, 'button' | Set to submit deliberately, never by omission. |
disabled | Boolean, false | Sets the native disabled attribute on a button; on an anchor (as="a") it drops href and sets aria-disabled instead, so the control stays focusable. |
icon / label | Boolean, false / String, '' | Icon-only when icon is set; label becomes the accessible name, or overrides it on any button. |
loading | Boolean, false | Adds a spinner and aria-busy. The label stays put so the name is stable. |