VESYL UI

Button

Triggers an action. Pick the variant by how much attention the action deserves and the size by the surface it sits on.

Quick reference

Variants

Hover each swatch to see its interaction state.

Naming: a tone that shows at rest leads (destructive, destructive-secondary); a tone that only appears on interaction follows the base (ghost-destructive, soft-accent, outline-destructive). How a button rests — muted or rounded — is a modifier, not a variant.

ButtonMiniButtonvariantTierAt rest → on hoverUse for
accentPrimaryAccent fill → darkerThe one brand-forward CTA on a screen (Buy label, Create order)
defaultPrimaryPrimary fill → lighterThe neutral primary action (Save, Pay)
secondarySecondaryWhite chip + border → highlightSupporting actions beside the primary; toolbar actions
outlineSecondaryHairline → faint washQuieter supporting action
softTertiaryNeutral wash → stronger washTertiary filled control; MiniButton default
soft-accentTertiaryNeutral wash → blue tintAdditive chip (+) that hints intent on hover
soft-destructiveTertiaryNeutral wash → red tintRemove chip (×) that hints intent on hover
ghostTertiaryNothing → washDense toolbars, table rows, dialog Cancel
ghost-destructiveTertiaryNothing → red tonalTrash icon in a table row
ghost-successTertiaryNothing → green tonalAffirmative row action (mark as paid)
linkTertiaryText → underlineAction that reads as inline text
destructivePrimarySolid redIrreversible confirm (void label, delete store)
destructive-secondarySecondaryRed tonal → strongerRemove that sits among other actions
outline-destructiveTertiaryHairline → red tonal + red borderLow-emphasis remove with a visible edge

Every destructive and soft-tinted variant also shifts on keyboard focus, so the signal isn’t mouse-only. MiniButton takes the same variants and defaults to soft. Add muted to rest any non-filled variant at the muted text color.

Sizes

SwatchsizeBoxGlyphUse for
xs24px tall12pxPopovers, dense inline controls
sm32px tall16pxTable rows, cards, compact toolbars
md (default)36px tall16pxDialogs, forms, page headers
lg40px tall16pxRare — a standalone hero action
icon-xs24px square12pxIcon-only in dense rows
icon-sm32px square16pxIcon-only beside sm buttons
icon36px square16pxIcon-only beside md buttons
icon-lg40px square16pxIcon-only beside lg buttons

MiniButton sizes

A separate, text-relative scale: the box matches the line height of the text it rides next to.

SwatchsizeBoxGlyphRides next to
sm16px10pxtext-xs
md (default)20px12pxtext-sm (default UI text)
lg24px14pxtext-base
xl28px16pxtext-lg

Which button component?

NeedUse
Run an action on clickButton
Navigate to an in-app routeButtonLink — a real <a> with Button’s variants
External link or file download styled as a buttonButton with render={<a href="…" />}
Tiny ×, +, −, or trash next to text (chip, list row, toast)MiniButton
Async one-shot action that shows pending → success/error in placeActionButton
A label that opens a menu or popover (with chevron)DisclosureButton
Several related buttons joined into one barButtonGroup

Recipes

Default size. Cancel on the left as ghost, the primary action on the right. Label them Cancel and Save (not “Apply”). AlertDialogCancel renders outline by default.

Popover actions

Popovers step down to size="xs".

Table row actions

Keep rows quiet: sm for a labeled action; for icon-only ones, a muted ghost variant — ghost, ghost-success, or ghost-destructive.

Invoice #1042

For in-app navigation use ButtonLink. For an external URL or a file download, render the button as an anchor:

Async action

For a one-shot action that should show pending → success/error in place, use ActionButton. For a submit that just blocks while pending, pass loading (see Loading).

Choosing emphasis

Variants are a ladder, not a palette. Give a view one primary action and step everything else down, so the primary stays the obvious payoff.

  • Primary (default, accent, destructive) — one per view. Reach for accent only for a clearly affirmative, brand-forward action, and keep it to one per view.
  • Secondary (secondary, outline, destructive-secondary) — “not the main thing, but still a real button.”
  • Tertiary (soft*, ghost*, outline-destructive, link) — frequent, repeated, or inline actions that shouldn’t compete for attention.

With icons

Mark a leading or trailing icon with data-icon="inline-start" / "inline-end" so the padding tightens on that side.

Icon only

Use an icon* size and always pass an aria-label.

Modifiers

Boolean props that combine with any variant.

Rounded

Add rounded to fully round any button — a pill for text, a circle for icon-only. For small text-scaled chips, use MiniButton instead of hand-sizing an icon button.

Muted

muted rests the label or icon at the muted text color; the variant’s own hover and focus colors still apply. Use it for quiet, repeated controls — clear buttons inside inputs, icon actions in table rows — so they recede until reached for. It has no effect on filled variants (default, accent, destructive, destructive-secondary), which set their text against the fill.

MiniButton

MiniButton is a small, text-scaled icon button — the ×, +, −, or trash that rides next to text (a chip, list row, dialog, or toast). It’s a <Button rounded> preset on a text-relative size scale (MiniButton sizes), distinct from Button’s control-row sizes. Pass the glyph as children; pick emphasis with variant (defaults to soft). Always pass an aria-label.

In a row — the label owns the space, the button rides the trailing edge and reddens on hover:

ACME Instant Tunnel Paint

Loading

loading shows a spinner and disables the button until it clears.

Disabled

Props

Also accepts every native <button> attribute (type, onClick, aria-*, …).

PropTypeDefaultDescription
variantsee Variants"default"Emphasis and interaction style
sizesee Sizes"md"Height and padding, or square box for icon* sizes
roundedbooleanfalseFully round — pill for text, circle for icon-only
mutedbooleanfalseRest at the muted text color; hover/focus colors unchanged. No effect on filled variants
loadingbooleanfalseShows a spinner and disables the button
disabledbooleanfalseDisables interaction and dims the button
renderReactElement<button>Swap the rendered element (e.g. <a href>) while keeping button styling
classNamestring—Merged onto the button; prefer a variant or size over restyling