Button
StableTriggers actions or submits forms. Six variants, four sizes, loading state, icon support, and asChild for link rendering — all built in.
InteractiveForm elementVhyxSeal
Interactive example
Click to trigger loading state
Import
tsx
import { Button } from '@vhyxui/react'Variants
Six semantic variants covering every use case.
All 6 variants
Sizes
Four sizes mapping to --vhyx-size-* height tokens.
xs, sm, md, lg
States
Loading
Disabled
Icon + text
Icon only — requires aria-label
asChild — renders as anchor
Props
| Prop | Type | Default | Description |
|---|---|---|---|
variant | 'primary' | 'secondary' | 'outline' | 'ghost' | 'destructive' | 'link' | 'primary' | Visual style of the button. |
size | 'xs' | 'sm' | 'md' | 'lg' | 'md' | Size of the button, maps to --vhyx-size-* height tokens. |
loading | boolean | false | When true, shows a spinner and disables interaction. |
icon | React.ReactNode | — | Optional icon element rendered alongside button text. |
iconPosition | 'left' | 'right' | 'left' | Side the icon appears on. |
iconOnly | boolean | false | Renders icon only. Requires aria-label. |
asChild | boolean | false | Renders as the child element via Slot. Use for navigation links. |
contract | Partial<ComponentContract> | — | VhyxSeal contract override — merged with the default. |
disabled | boolean | — | Disables the button (from HTMLButtonElement). |
onClick | React.MouseEventHandler<HTMLButtonElement> | — | Click handler (from HTMLButtonElement). |
className | string | — | Additional CSS classes appended to the button. |
Button also accepts all standard HTMLButtonElement attributes.
Accessibility
- Renders as native
<button>— no role override needed. -
aria-busyset automatically whenloading=true. - Focus ring via
:focus-visible— visible on keyboard, hidden on mouse. -
iconOnlywarns in development ifaria-labelis missing. -
asChildprevents nested interactive elements — correct DOM structure. -
disabledattribute propagated — no separatearia-disabledneeded.
Keyboard navigation
| Key | Action |
|---|---|
| EnterorSpace | Activates the button (triggers onClick). |
| Tab | Moves focus to the next focusable element. |
| Shift + Tab | Moves focus to the previous focusable element. |
Agent contract
Default VhyxSeal contract shipped with every Button. The destructive variant auto-upgrades safetyLevel, destructive, and requiresConfirmation.
Default contract
{
"type": "action",
"intent": "trigger-action",
"description": "Triggers an action or submits a form when activated by the user",
"requires": [],
"requiredPermissions": [],
"consequence": "Triggers the associated action",
"affects": [],
"reversible": false,
"safetyLevel": "low",
"requiresConfirmation": false,
"destructive": false,
"contractVersion": "0.0.1",
"fingerprint": "vhyxs_6ecebcad"
}Override fields via the contract prop — merged with the default:
tsx
<Button contract={{ intent: 'submit-order', requiresConfirmation: true }}>
Place Order
</Button>Theming
Override these CSS tokens to theme the Button without touching component code.
--vhyx-color-accentPrimary variant background
--vhyx-color-accent-hoverPrimary hover state
--vhyx-color-accent-activePrimary active state
--vhyx-color-dangerDestructive variant background
--vhyx-color-danger-hoverDestructive hover state
--vhyx-size-xs/sm/md/lgButton height per size
--vhyx-radius-mdBorder radius
--vhyx-shadow-focusFocus ring
--vhyx-duration-instantActive press scale transition
--vhyx-weight-mediumFont weight
css
:root {
--vhyx-color-accent: #7c3aed; /* purple primary buttons */
--vhyx-color-accent-hover: #6d28d9;
--vhyx-radius-md: 9999px; /* pill-shaped buttons */
}Examples
Form submit with loading state
Controlled loading — typical form submission pattern
Confirm / Cancel action group
Paired primary + outline
Destructive action
Delete pattern — contract auto-upgrades to safetyLevel: high