Skip to content

Button ​

A clickable button or link element. Toggle Vue / CSS to switch the live demo and source together.

Basic ​

Default colors across the palette.

Vue SFC

Sizes ​

xs, sm, md, lg, xl, xxl.

Vue SFC

Colors ​

Pick a colour and every variant takes it. Solid, outline, tonal and text β€” solid fills with the color, outline uses it for the border and text, tonal is a soft tint of the same hue, text is bare until hovered.

Vue SFC

Rounded ​

One prop controls corners: none, the xs…xxl scale, or full. Leave it unset and the theme's per-size radius stands.

A step is a fixed radius, not one scaled by the button β€” rounded="md" is the same corner on an xs button as on an xxl one. none is a real 0 that beats the per-size radius, and full is a pill on a wide button and a circle on a square one.

That last part is how you make a FAB β€” a fixed square plus a full radius:

html
<mono-button width="56" height="56" rounded="full" icon-only color="primary">+</mono-button>

This replaced shape, round, circle and fab. The old fab also forced a fixed blue gradient, so it was the one button that could not take a color or a variant; the recipe above takes both.

Vue SFC

Text with icon ​

Icon slot with left or right position.

Vue SFC

Prepend & append ​

Two affix zones beside the label, holding an icon, arbitrary HTML, or a whole mono component. Give them content with slot="prepend" / slot="append", and configure each side with the matching prepend / append prop:

vue
<mono-button :append.prop="{ divider: true }">
  Download Template
  <span slot="append">βŒ„</span>
</mono-button>

divider draws a line between that affix and the button body; disabled deactivates just that side (it follows the button's own disabled by default).

Clicking an affix does not run the button. The zones render as siblings of the native control rather than inside it β€” a <button> may not contain interactive content, and anything nested in one has its clicks bubble into the button's own handler. Sitting outside is what lets an affix hold a real control and be clicked on its own, with no stopPropagation needed on your side. The affix's click also stops at the affix boundary, so the button's own @click never hears it β€” a split button's caret opens its menu without running the main action.

A loading button keeps its affixes live, so a busy main action can still have a working menu beside it; a disabled one dims them along with it.

These prop names shadow two DOM methods

prepend and append are methods on every element (ParentNode.append()). Declaring props with those names shadows them, so monoButtonEl.append(node) is no longer callable β€” reading it returns the config object instead.

Nothing inside mono-helper calls them (slot placement uses appendChild), so the component itself is unaffected. But consumer code or a third-party helper that does buttonEl.append(...) will break on a <mono-button>; use buttonEl.appendChild(node) there instead.

Vue SFC

States ​

Disabled, loading and full width (width="100%").

Vue SFC

Badges ​

Notification badge in four colors.

Vue SFC

Renders as <a> when href is set.

Vue SFC

Button type ​

button, submit, reset.

Vue SFC

Icon buttons ​

Icon-only buttons with optional badge or tooltip.

Vue SFC

Button with input ​

Buttons share the xs–xxl heights of the form controls, so they line up when paired inline.

Vue SFC

Throttle & debounce ​

Rate-limit clicks without writing a timer in your handler. throttle and debounce are backed by p-throttle and p-debounce, and each emits its own event when it actually executes.

Vue SFC

Both take the option object or a bare number shorthand (debounce="300"); a spinning button blocks, but a debounce still re-arms on repeat clicks so it runs once you stop.

Async loading ​

Bind the click's work as handler β€” its promise drives the button's own spinner, so there's no loading flag to manage. loading stays a read-only override, and loading-change reports the self-driven state.

However it is triggered, the spinner is drawn in the icon's place β€” a leading icon spins on the left, icon-position="right" spins on the right, and a button with no icon grows a leading one. The caption always stays readable, so a label you swap to "Saving..." is still there to read.

Vue SFC

Where the spinner goes ​

It replaces the icon rather than joining it, on whichever side the icon sits β€” so a busy button never shows an icon and a spinner at once. A button with no icon grows a leading spinner in the slot an icon would have used, and an icon-only button simply swaps its icon for the ring.

Vue SFC

Customized ​

Override per-element styling with the cssClass prop (Vue) or extra utility classes on the wrapper's class (CSS) β€” the mono-* attributes carry the props, class stays free for your own.

Vue SFC

Width & height ​

Set width, height, min-width, max-width, min-height or max-height β€” each takes a CSS string ("220px", "80%") or a number (px). Use width="100%" for a full-width button (replaces the old fullWidth).

Vue SFC

CSS Variables ​

Vue SFC

Themed through --mono-button-* custom properties (apply them on the element / inline β€” they win over the colorΓ—variant matrix and pierce the shadow boundary). Re-skin globally via the Basecoat tokens (--primary, --ring, --radius, …).

The button is a port of Basecoat's .btn β€” every size and variant rule cites the upstream block it mirrors (/* basecoat@1.0.2 styles/vega.css .btn[data-size='sm'] */ in button.css).

VariableDefaultControls
--mono-button-primary / -primary-foreground--primary / --primary-foregroundPrimary colour (the default fill) and its ink
--mono-button-secondary / -secondary-foreground--secondary / --secondary-foregroundThe secondary surface (shadcn's muted surface, not a second hue)
--mono-button-success / -danger / -warning / -info / -teal / -purple / -neutral / -dark (+ -foreground)matching token (--success, --destructive, …)Per-colour fill and ink
--mono-button-text / -background / -muted / -border--foreground / --background / --muted / --borderThe neutral surfaces light and text paint with
--mono-button-bg--primaryResolved background
--mono-button-color--primary-foregroundResolved text colour
--mono-button-border-colortransparentResolved border colour
--mono-button-shadownone (--mono-shadow-xs on light / outline)Resolved shadow
--mono-button-radius--mono-radius-md (xs/sm clamp to 8px / 10px)Corner radius β€” wins over a rounded step
--mono-button-ring-color--ring (the hue for outline / tonal)Focus ring colour (3px at 50%)
--mono-button-hover-bgresting bg at 80%Hover background
--mono-button-hover-color / -hover-border-color / -hover-shadowresting valuesHover ink / border / shadow
--mono-button-tint / -tint-hover--mono-mode-tint (10% / 20%, dark 20% / 30%)Tonal and text-hover alpha
--mono-button-<color>-soft / -soft-hoverthe colour at the tint alphasTonal fill at rest / on hover
--mono-button-font-weight / -text-transform--mono-font-weight-medium / noneLabel treatment (a flavor's uppercase reaches the shadow build through these)
--mono-button-plain-hover-bg / -plain-hover-border-colorthe tint / transparentHover of variant="text"
--mono-button-width / -justifyauto / centerHow much room the control takes and where its content sits. They exist so a component that EMBEDS buttons can turn one into a list row β€” button-dropdown sets 100% / flex-start on the <li>, which is the only route that also reaches the control in the shadow build
--mono-button-press-translate0 1pxThe :active nudge. 0 0 removes it β€” right for a row in a menu, wrong for a standalone button

Deprecated since the Basecoat port (accepted, no effect): --mono-button-<color>-lite(-hover) (tonal has no border), --mono-button-shadow-<color> (no glows), --mono-button-<color>-dark / -dark-deep, --mono-button-accent, --mono-button-light-bg(-2) (were gradient stops).

Types ​

Importimport { ButtonProps } from 'mono-helper'
PropValueDefaultDescription
size'xs' | 'sm' | 'md' | 'lg' | 'xl' | 'xxl'β€”Visual size of the button.
color'primary' | 'secondary' | 'success' | 'danger' | 'warning' | 'info' | 'teal' | 'purple' | 'neutral' | 'dark' | 'light'β€”Color theme of the button.
variant'outline' | 'solid' | 'tonal' | 'text'β€”Visual style variant (solid, outline, tonal, text). `text` has no background and no border at rest β€” just the coloured label β€” and takes the tonal look on hover or keyboard focus.
rounded'xs' | 'sm' | 'md' | 'lg' | 'xl' | 'xxl' | 'none' | 'full'β€”Corner radius β€” `none`, the `xs`…`xxl` scale, or `full`. Unset follows the theme's per-size radius.
disabledbooleanβ€”Disables interaction and dims the button.
loadingbooleanβ€”Shows a loading state and blocks clicks.
widthstring | numberβ€”Explicit sizing. Each accepts a CSS length string (`"220px"`, `"80%"`) or a number (interpreted as px). Use `width="100%"` for a full-width button.
heightstring | numberβ€”β€”
minWidthstring | numberβ€”β€”
maxWidthstring | numberβ€”β€”
minHeightstring | numberβ€”β€”
maxHeightstring | numberβ€”β€”
hrefstringβ€”Renders the button as an anchor with this URL.
targetstringβ€”Anchor target window when href is set.
type'button' | 'submit' | 'reset'β€”Native button type attribute.
iconPosition'left' | 'right'β€”Placement of the icon relative to the text.
prependstring | ButtonAffixPropsβ€”Config for the leading affix (`<span slot="prepend">`). The slot supplies the content; this only says how the zone behaves. Bind with `.prop`. NOTE: this name deliberately SHADOWS `ParentNode.prepend()` on the element. `monoButtonEl.prepend(node)` therefore stops being callable β€” reading it returns this config object instead of the DOM method. Nothing inside the library calls it (slot placement uses `appendChild`), but consumer code and third-party helpers that do will break on a `<mono-button>`.
appendstring | ButtonAffixPropsβ€”Config for the trailing affix (`<span slot="append">`). Bind with `.prop`. Shadows `ParentNode.append()` β€” see the note on `prepend`.
badgestringβ€”Badge text shown on the button.
badgeColor'red' | 'green' | 'orange' | 'cobalt'β€”Color of the badge.
iconOnlybooleanβ€”Renders an icon-only button without text.
glassbooleanβ€”Applies a translucent glass effect.
tooltipstringβ€”Tooltip text shown on hover.
ariaLabelTextstringβ€”Accessible label text for screen readers.
cssClassButtonCssClassβ€”Per-element class overrides.
throttlenull | string | number | ThrottleConfig | booleanβ€”Rate-limit clicks: at most `limit` executions per `interval` ms, the rest queued and replayed rather than dropped. A bare number is the interval. Emits `throttle` per execution.
debouncenull | string | number | DebounceConfig | booleanβ€”Collapse a burst of clicks into one execution, `wait` ms after the last. A bare number is the wait. Emits `debounce` per execution.
handlerButtonHandlerβ€”The work a click performs. Bind as a property (`:handler.prop="save"`) β€” it's what gets throttled/debounced, and its promise drives the spinner.
noAutoLoadingbooleanβ€”Opt out of the self-driven spinner; `loading` stays fully consumer-controlled.