Search...

Search documentation...

Button

Button

A styled <button> (or, with asChild, whatever element you hand it) — 6 variants, 4 text sizes, 4 icon-only sizes.

import { Button } from "@pompeitech/vesuvius-ui";

Variants

Sizes

The shared control scale is xs 28 px, sm 32 px, default 36 px, and lg 40 px. These heights align with Input, SelectTrigger, and MultiSelect.

Icon sizes

A dedicated icon size for each text size above, so an icon-only button can match whatever size button it sits next to:

Disabled

With an icon

Any <svg> child (including a Lucide icon) is auto-sized to size-4 and given the right gap from adjacent text — no wrapper span needed:

Loading state

There's no loading prop — compose it from disabled (to prevent a second submit and to drop the hover/focus states) plus a spinning icon, same as any other icon usage above:

Full width

asChild renders the button's styling on whatever single child element you pass it (via Radix Slot), instead of a <button> — useful for a router <Link> that should look like a button. The child becomes the actual rendered DOM element, so it needs to accept the same props a <button> would (className, event handlers, …), which any anchor or router link component does.

<Button asChild>
  <a href="/get-started">Get Started</a>
</Button>

API

PropTypeDefaultDescription
variant"default" | "secondary" | "destructive" | "outline" | "ghost" | "link""default"Visual style.
size"xs" | "sm" | "default" | "lg" | "icon-xs" | "icon-sm" | "icon" | "icon-lg""default"The icon-* sizes are square icon-only buttons matching each text size's height (pair with aria-label — see Accessibility below).
asChildbooleanfalseRender as the single child element instead of a <button>.

Plus every native <button> prop (onClick, disabled, type, form, …), and ref (forwarded to the underlying <button>, or to whatever element asChild's child renders as).

Accessibility

An icon-only button (any icon-* size) has no visible text for a screen reader to announce — always pair it with aria-label (see the icon-size example above). disabled on a native <button> already removes it from the tab order and announces it as unavailable; no extra aria-disabled needed on top of it unless you specifically need the element to stay focusable while disabled (a real accessibility technique for explaining why an action is unavailable, but not this component's default behavior). Every <button> (and anything else rendered with role="button", e.g. asChild) also gets cursor: pointer restored — Tailwind v4 dropped that from its own base reset.