Button
A styled, accessible button. Dynamic state is expressed through real attributes — disabled becomes
a true disabled (or aria-disabled for link buttons), loading sets aria-busy, and it ships a
visible :focus-visible ring.
The examples are organised by layer: the class reference is the standalone
@oriui/css layer, and the Framework API is the @oriui/vue component. Every
example is live — flip its code between HTML (the standalone classes, also your htmx / Astro / Svelte /
plain-HTML usage), Vue; HTML is the default.
Classes
A button is a block class plus single-class token utilities — one class repoints one token, no base class needed. The Vue props in Framework API map 1:1 to these.
| Class | Type | Description |
|---|---|---|
ori-button | Block | Required base class. |
ori-variant_* | Style | fill · tonal · outline · text · plain |
ori-color_* | Color | primary · secondary · success · warn · danger · info · surface |
ori-button_* (size) | Size | xs · sm · md · lg · xl · xxl |
ori-font-size_* | Font | xs · sm · md · lg · xl · xxl (scales the label) |
ori-size-radius_* | Radius | zero · xs · sm · md · lg · xl · rounded |
ori-button_fluid · ori-button_icon | Layout | full-width · icon-only |
ori-button__icon · ori-button__text | Part | icon / label elements |
disabled · aria-busy · data-active | State | real attributes, not classes |
The non-fill variants (tonal / outline / text / plain) paint the label with the AA-safe
--ori-color-text tone rather than the raw role; fill keeps --ori-color-on for its solid
background — see Design tokens.
Variants
Five visual styles, all driven by the ori-variant_* single-class token.
<button class="ori-button ori-variant_fill ori-color_primary">Fill</button>
<!-- swap the variant: ori-variant_fill → _tonal / _outline / _text / _plain -->
<button class="ori-button ori-variant_outline ori-color_primary">Outline</button>
Colors
Every semantic role. surface / background are also available for neutral buttons.
<button class="ori-button ori-variant_fill ori-color_danger">danger</button>
Variant × color compose freely — e.g. a tonal danger button:
<button class="ori-button ori-variant_tonal ori-color_danger">Delete</button>
Sizes
xs → xxl. The size sugar ori-button_* drives the height; ori-font-size_* scales the label.
<button class="ori-button ori-button_sm ori-font-size_sm">sm</button>
<button class="ori-button ori-button_xl ori-font-size_xl">xl</button>
Radius
From zero to the default rounded (pill).
<button class="ori-button ori-size-radius_zero">zero</button>
With icon
Pass an SVG path to icon. iconPosition places it. For an icon-only square, pass icon withouttext (give it an aria-label) — icon mode is triggered by the explicit icon prop, so a slot-only or
text button is never forced into a square.
<button class="ori-button ori-button_icon ori-variant_tonal" aria-label="Add">
<i class="ori-icon" aria-hidden="true"
><svg viewBox="0 0 24 24"><path d="M11 13H5v-2h6V5h2v6h6v2h-6v6h-2z" /></svg
></i>
</button>
Loading
loading swaps the icon for a spinner, sets aria-busy="true", and blocks interaction.
<button class="ori-button" aria-busy="true">
<span class="ori-spinner ori-spinner_inline" role="status" aria-hidden="true"></span>
<span class="ori-button__text">Saving</span>
</button>
States
active paints the pressed look (data-active); disabled is the real attribute.
<button class="ori-button" data-active>Active</button> <button class="ori-button" disabled>Disabled</button>
Block
fluid stretches the button to the full width of its container.
<button class="ori-button ori-button_fluid">Full width</button>
Common patterns
A confirm / cancel pair and an icon toolbar — the everyday compositions.
<div style="display: flex; gap: 0.5rem">
<button class="ori-button ori-variant_text">Cancel</button>
<button class="ori-button ori-variant_fill">Save changes</button>
</div>
Accessibility
The accessibility contract holds across every layer — the standalone classes and the Vue component render the same attributes and keyboard behaviour.
- Renders a real
<button type="button">by default;as="a"(or a router link) switches the tag and usesaria-disabled+tabindex="-1"instead of the booleandisabled. loadingsetsaria-busy="true"; the spinner isaria-hidden. An icon-only button needs anaria-label.- Visible
:focus-visibleoutline; state lives in attributes, not classes.
| Key | Action |
|---|---|
Enter | Activates the button. |
Space | Activates the button (native only). |
Framework API
The props, events, slots, and polymorphism of the Vue component. The standalone CSS layer has no component API — its surface is the classes above. (Svelte bindings are planned.)
Props
| Prop | Type | Default | Description |
|---|---|---|---|
text | string | — | Label. A text (or slotted) button is a normal button, never an icon square. |
variant | 'fill' | 'tonal' | 'outline' | 'text' | 'plain' | 'fill' | Visual style. |
color | ThemeColor | 'primary' | Semantic role: primary · secondary · success · warn · danger · info · surface. |
size | ActionSize | 'md' | Height + label scale (xs–xxl). |
radius | RadiusSize | 'rounded' | Corner radius (zero–rounded). |
icon | string | — | SVG path for an icon; hidden while loading. icon with no text → an icon-only square (ori-button_icon). |
iconPosition | 'left' | 'right' | 'top' | 'bottom' | 'left' | Icon placement around the label. |
loading | boolean | false | Shows a spinner, sets aria-busy, and blocks interaction. |
disabled | boolean | false | Real disabled (button) or aria-disabled + tabindex="-1" (other tags). |
active | boolean | false | Pressed look via data-active. |
fluid | boolean | false | Full-width (block) button. |
as | string | Component | 'button' | Element or component to render (e.g. 'a', a router link). |
Events & attributes
OriButton declares no custom events. It doesn't set inheritAttrs: false, so native listeners
(@click, @focus, …) and attributes (type, aria-label, name, form, …) fall through to the
rendered element — the tag given by as.
Slots
| Slot | Description |
|---|---|
default | Replaces the built-in content (icon + text). Supply your own markup; you own its layout/spacing. |
Polymorphic (as)
Render any tag or component. As a non-<button>, disabled becomes aria-disabled + tabindex="-1"
instead of the boolean attribute.