Button
Primary action. Variants, tones and sizes.
Usage
Import the component and use it. No setup, no config — it ships fully styled.
Variants
Four visual treatments via the variant prop.
Colors
Semantic tones, all driven by --sve-* tokens.
Sizes
Three sizes from the size prop.
States
Disabled is fully styled and non-interactive. Icons compose inline.
As a link
Pass href and it renders an <a> instead
of a <button>, with the same variants and sizes.
Use it whenever activating the control takes the user somewhere. Do not reach
for onclick={() => (window.location.href = '/x')} — that is not
a link. It cannot be middle-clicked or opened in a new tab, shows no URL on hover, is announced
as a button rather than a link, and does nothing at all until JavaScript has run.
target="_blank" gets rel="noopener noreferrer" automatically. Without it, the opened page can reach back through window.opener and navigate the tab the user came from. Passing your
own rel always wins.
href together with disabled renders a <span aria-disabled="true">, not an anchor. <a> has no disabled attribute, and an <a aria-disabled> still takes a tab stop and is still announced as
a link — so it invites the user to follow something that goes nowhere.
Loading
loading shows a spinner and sets aria-busy while an action is in flight. Activation is blocked, so a double
submit is not possible.
It stays focusable. disabled is the obvious choice and
the wrong one: a disabled element loses focus, so a keyboard user who just pressed Enter is dropped
back to the top of the document with no idea anything happened.
The children stay on screen, so the button keeps its width and nothing around it shifts. The
spinner is aria-hidden — aria-busy already
carries the state — and a visually hidden loadingLabel is what gets
announced. Say what is happening: "Saving your changes" beats "Loading". It respects prefers-reduced-motion.
Props
| Prop | Type | Default |
|---|---|---|
variant | 'solid' | 'outline' | 'ghost' | 'flat' | 'solid' |
color | 'primary' | 'secondary' | 'success' | 'warning' | 'danger' | 'default' | 'default' |
size | 'sm' | 'md' | 'lg' | 'md' |
href Render an anchor instead of a button. Pass this whenever activating the control takes the user somewhere. A `<button>` that navigates in its `onclick` cannot be opened in a new tab or middle-clicked, shows no URL on hover, is announced as a button rather than a link, and does nothing at all until JavaScript has run. | string | — |
target Anchor target. Only meaningful with `href`. | HTMLAnchorAttributes['target'] | — |
rel Anchor rel. Set automatically for `target="_blank"` unless you pass it. | string | — |
loading Shows a spinner and marks the button busy while an action is in flight. It stays FOCUSABLE on purpose. `disabled` would be the obvious choice and it is the wrong one: a disabled element loses focus, so a keyboard user who just pressed Enter is dropped back to the top of the document with no idea anything happened. Activation is blocked instead, and `aria-busy` tells assistive technology the control is working. Only meaningful on a button; a link navigates rather than performing work. | boolean | false |
loadingLabel Announced while `loading`. Say what is happening, not that something is. | string | 'Loading' |
class | string | — |
children | Snippet | — |
disabled html | boolean | null | undefined | — |
onclick html | MouseEventHandler<HTMLButtonElement> | null | undefined | — |
Plus every native <button> attribute via prop spreading.