Button
Basic button component, an enhanced native <button>.
Types
Button types
Sizes
Five sizes
size supports five tiers: xs / small / medium (default) / large / xl; invalid values fall back to medium with a warning.
Five sizes · primary
Five sizes · icon-only
Disabled & Loading
disabled disables the button; loading enters the loading state — the original label keeps its space (width stays unchanged) and a spinner is centered; pair with loading-text to show a loading message.
Disabled and loading states
Async auto loading
With loading="auto", the button automatically enters the loading state while the host oas-click handler returns a Promise, and exits after the Promise resolves or rejects.
Auto loading for async submit
Disabled but focusable
disabled-focusable renders a disabled look (desaturated + aria-disabled) without a native disabled attribute — still focusable and hoverable, ideal for wrapping in a tooltip that explains why it is disabled; clicks are intercepted by the component and oas-click is not dispatched.
Disabled but focusable (with tooltip)
Events
Click event
Clicking dispatches the oas-click CustomEvent (bubbles + composed); detail.originalEvent is the native MouseEvent.
Icon buttons
icon renders an icon before the text (reusing the oas-icon icon set, IconName); the spacing between the icon and text follows --oas-space-2.
Icon + text
Without text, the button becomes an equal-width square and needs an aria-label for an accessible name; when not set explicitly, the icon name is used as a fallback (e.g. icon="close" → aria-label="close"). It is recommended to provide an explicit label.
Icon-only buttons
Block
block makes the button fill the full width of its parent container.
Block buttons
Rounded
round applies a pill radius (--oas-radius-full, falling back to 999px when the token is unavailable).
Rounded buttons
Ghost
ghost renders a transparent background with an outline; the outline and text are colored by type and darken on hover.
Ghost buttons
Circle
circle turns the button into a circle; icon-only buttons combine equal-width and full rounding into a circle.
Circle buttons
Icon position
icon-position controls the icon/text order: start (default, icon on the left) or end (icon on the right).
Icon on the right
Dual icons
icon renders an icon before the text and icon-end renders a second icon after the text (both reuse the oas-icon set); they can coexist with icon-position.
Dual icons
Secondary text (compound two-row variant)
When the slot="description" secondary line is present, the button switches to a vertical two-row layout: the main row (icon + text) on top, the secondary line below (smaller, muted). Good for card entries, settings entries, upload entries — "main action + one line of explanation". Fully compatible with loading / icon / disabled / href.
Two-row button
Link button
Setting href renders a native link (<a>); target controls how it opens (_blank / _self etc.); download (file download) and rel (link relationship) are passed through.
Link buttons
Download and rel
Plain
plain uses a low-contrast, subtle style (a 12% tint of the semantic color as background with a transparent border and semantic-colored text), gentler on light backgrounds.
Plain buttons
Variant
variant controls the button shape, orthogonal to the type semantic color: solid (default filled) / outlined / dashed / filled (soft) / text / link. Legacy ghost equals outlined, plain equals filled.
Outlined / Dashed / Filled
Text / Link
Custom color
color overrides the type semantic color with any color value.
Color priority: --oas-button-bg (host-injected CSS variable, gradients allowed) > color attribute > type semantic color > default gray. In outlined / filled / dashed / text variants, color tints the border/text/light background; for solid buttons the text color is picked black or white by background luminance (stays readable in dark mode).
Custom color values are rendered as-is (never rewritten) — make sure the text/background contrast meets WCAG AA (4.5:1).
Custom color
Selected state (active / active-tint)
The active boolean attribute marks the selected state (controlled: the component does not toggle itself; the host writes back on oas-click). Selected visuals share the button-group selection anchor (aria-pressed). active-tint customizes the selected color — the 11 preset names resolve to theme tokens (light/dark adapted automatically), any CSS color is injected as-is, and the solid selected text color adapts to background luminance (contrast is the host's responsibility, WCAG AA 4.5:1).
Selected state
active-tint custom selected color
Toggle selection on click (controlled write-back)
Press feedback
wave enables a subtle press feedback (slight sink + darken, on by default); wave="false" disables it.
Press feedback
CJK auto spacing
auto-insert-space inserts a space between two consecutive CJK characters (typography optimization, off by default).
CJK auto spacing
Autofocus
autofocus gives the button focus after page load (native autofocus does not pierce Shadow DOM; the component forwards focus to the inner button on mount).
autofocus
Long content wrapping
Buttons are single-line by default (white-space: nowrap). With the explicit wrap attribute, long text wraps within a constrained width (parent container or width / max-width) and the box grows with the content (same height as default when it fits on one line).
wrap for long content
Touch targets on mobile
On touch devices (pointer: coarse) the button minimum height grows to 44px (--oas-touch-target-min): small sizes such as the 32px default or the 20px xs become 44px tall on touch, while sizes already ≥44px (e.g. xl) are unchanged; icon-only / circle buttons become 44×44 hit areas via aspect-ratio. Only the height grows — padding, font size, and border radius stay untouched; desktop (fine pointer) is unaffected.
Native form submission
html-type sets the native form behavior: button (default, no form behavior) / submit / reset. Buttons inside Shadow DOM do not participate in native form submission — the component bridges automatically: clicking triggers native submit/reset on the target form (the enclosing <form>, or the form id referenced by form); formaction / formmethod / formnovalidate / formtarget take effect through the native submitter mechanism (overriding the form's own action / method / novalidate / target). Submission is not triggered in disabled / loading state; in href link mode this attribute group is silently ignored.
Bridging notes (two boundaries): ① the bridge works via a transient native proxy submitter injected at click time — the form's
submitevent sees this proxy assubmitter, not theoas-button(the proxy carriesdata-oas-form-proxyfor identification); hosts with click delegation on the form area will see both the host's synthetic click and the proxy's native click bubble. ②html-type="submit"does not work insideoas-form: its real<form>lives in shadow DOM, so a light-DOM proxy finds no ancestor form — useoas-form'ssubmit()method /oas-submitevent instead.
Native form submission (html-type / form attributes)
Clicking "Submit" fires the native submit event (the demo calls preventDefault and shows the fields about to be submitted); "Reset" restores the initial input values and fires reset; "No-validate submit" demonstrates formnovalidate skipping required validation plus formaction/formmethod overriding the submission target.
Custom Loading Icon
slot="loading-icon" replaces the built-in spinner ring with custom content while loading (the spinner positioning is reused, so stable loading width etc. are unchanged); when absent, the built-in spinner shows. Commonly paired with oas-icon spin for a themed loading icon.
loading-icon slot
API
oas-button
Attributes
| Attribute | Description | Type | Default |
|---|---|---|---|
active | Selected state (controlled): present = selected; the host writes back on oas-click (the component does not toggle itself); selected visuals share the button-group selection anchor (aria-pressed), pair with active-tint for a custom tint | boolean | — |
active-tint | Selected-state tint: the 11 preset names resolve to --oas-preset-*-text tokens (one set per theme); any CSS color is injected as-is; solid selected text color (primary etc.) adapts to background luminance, contrast is the host’s responsibility | string | — |
auto-insert-space | CJK auto spacing: inserts a space between two consecutive CJK characters (off by default) | string | — |
autofocus | Autofocus: focuses the inner button on mount (native autofocus does not pierce Shadow DOM; the component forwards it) | boolean | — |
block | Fill the full width of the parent container (block level) | boolean | — |
circle | Circle button (icon-only, square + full rounding) | boolean | — |
color | Custom color: overrides the type semantic color (any color value) | string | — |
disabled | Disabled | boolean | — |
disabled-focusable | Visually disabled but stays focusable/hoverable (aria-disabled + click intercepted), for tooltips explaining why | boolean | — |
download | Passthrough download attribute in link mode (href) for file-download buttons | string | — |
form | Associated form id for buttons outside the target form (native form-attribute association) | string | — |
formaction | Target URL for this submission (overrides the form action) | string | — |
formmethod | HTTP method for this submission (get/post/dialog, overrides the form method) | string | — |
formnovalidate | Skip native validation for this submission only | boolean | — |
formtarget | Browsing target for the response (_blank/_self etc., overrides the form target) | string | — |
ghost | Ghost/outline style: transparent background + outline colored by type, darkens on hover | boolean | — |
href | Link address: renders a native <a> when set | string | — |
html-type | Native form behavior: button (default, none) / submit submits the containing or form-targeted form / reset resets it (bridged via a proxy submitter) | string | button |
icon | Icon name (reusing the oas-icon icon set); without text it becomes an equal-width square and uses the icon name as the fallback label | string | — |
icon-end | A second icon after the text (built-in icon name), works with icon/icon-position — e.g. left icon + right dropdown arrow | string | — |
icon-position | Icon position: start (default, left) / end (right) | string | start |
loading | Loading state | — | — |
loading-text | Text shown while loading (e.g. "Submitting…"); replaces the label content when set | string | — |
plain | Plain style: a 12% tinted background in the semantic color with text in its -text tone and a transparent border (no visible outline); equals variant="filled" | boolean | — |
rel | Passthrough rel attribute in link mode (pair noopener with target="_blank") | string | — |
round | Pill radius (--oas-radius-full / 999px) | boolean | — |
size | Size: xs / small / medium (default) / large / xl; invalid values fall back to medium with a warning | ButtonSize | medium |
target | How the link opens (_blank / _self etc.), with href | string | — |
type | Type | ButtonType | default |
variant | Shape (orthogonal to type): solid (default filled) / outlined / dashed / filled (soft) / text / link | ButtonVariant | '' | — |
wave | Press feedback: slight sink + darken (on by default); wave="false" disables | string | true |
wrap | Long-text wrapping: single-line nowrap by default; when enabled, content wraps within constrained widths and the box grows with it | boolean | — |
Events
| Event | Description |
|---|---|
oas-click | Click, detail: { originalEvent } |
Slots
| Name | Description |
|---|---|
| default | Button content (text / icon) |
description | Secondary text line (compound two-row variant): when present, the button switches to a vertical two-row layout (main row on top, secondary below, smaller muted text) |
loading-icon | Custom loading icon replacing the built-in spinner ring when slot content is present |
CSS Variables
| CSS Variable | Description | Default |
|---|---|---|
--oas-button-active-tint | Selected-state tint channel (injected by the active-tint attribute; preset names resolve to --oas-preset-*-text, raw colors as-is); consumed by selected text/border/soft and solid fills | var(--oas-color-primary) |
--oas-button-active-tint-on | Label color on solid selected fills (primary etc.): custom tints pick black/white by background luminance; falls back to --oas-color-text-on-primary when not injected | var(--oas-color-text-on-primary) |
--oas-button-active-tint-text | Text-safe variant of the selected tint: theme-aware deepen/lighten mix (toward near-black in light, near-white in dark; presets use the -text token directly) — labels on soft selected fills and text-variant selection | var(--oas-color-primary-text) |
--oas-button-bg | — | var(--oas-color-primary) |
--oas-button-color | — | var(--oas-color-text-primary) |
--oas-button-color-deep | Text-safe deepened variant of the custom color: the color-mix used for hover/active and outlined/dashed/filled text (injected by the component via --oas-deep-mix / --oas-deep-sink; inert without a custom color) | var(--btn-color, var(--oas-color-text-primary)) |
--oas-button-group-radius | — | var(--oas-radius-md) |
--oas-button-group-width | — | auto |
--oas-button-height | Button height override (falls back to the size-tier value by default); containers needing full-row-height buttons (e.g. swipe-cell actions) inject 100% via ::slotted (built into swipe-cell) | var(--oas-control-height-md) |
--oas-button-on-color | — | var(--oas-color-text-on-primary) |
--oas-glass-refraction | Liquid-glass edge refraction filter (active under data-glass, default none); for local opt-out override with an empty value (--oas-glass-refraction: ;)—not none (mixing none with button-state brightness() invalidates the whole filter declaration) | none |