Button

uistable

Button — extracted 1:1 from WEM-App.

npx shadcn add @krawma/button

npm: class-variance-authority@^0.7.1, radix-ui@^1.6.1 · registry: @krawma/utils

Preview

Variants
defaultoutlinesecondaryghostdestructivelink
Sizes
defaultxssmlgiconicon-xsicon-smicon-lg
States
hoverfocus-visibleactivedisabledaria-invalid
Keyboard
EnterSpace
Depends on
utils

Purpose

The primary action trigger. Use for anything that performs an action (submit, open, confirm). For navigation that changes the URL, render a link — either an <a> via asChild, or the link variant when it should look like a button-styled link.

Anatomy

Single element: <Button>. With asChild, it forwards its styling/behavior to a child (e.g. a next/link <a> or a Radix trigger) instead of rendering a <button>.

Style

  • variant (default default): default (primary, filled) · outline · secondary · ghost (chromeless, for toolbars/menus) · destructive (muted danger — used on destructive actions) · link (inline, underline-on-hover).
  • size (default default): default · xs · sm · lg, plus square icon · icon-xs · icon-sm · icon-lg for icon-only buttons.
  • Colors come from tokens (bg-primary, bg-destructive/10, …); never restyle with ad-hoc classes — add a variant instead.

Behavior & functionality

  • Renders a native <button> (type defaults to the browser default — pass type="button" inside forms to avoid accidental submits).
  • Cursor: cursor-pointer on all variants (including link and asChild links) — Tailwind v4 resets native <button> to cursor:default, so the base restores the pointer. disabled shows no pointer (pointer-events-none).
  • Active press yields a subtle translate; disabled disables pointer events.
  • Icon children are auto-sized; use data-[icon=inline-start|inline-end] slots for padding-aware icon buttons.

Accessibility

  • Role button; operable with Enter and Space.
  • Visible focus via focus-visible ring (--ring).
  • Icon-only buttons MUST have an accessible name (aria-label or visually hidden text).
  • disabled is both non-interactive and visually muted; aria-invalid styling is supported for form-trigger cases.

Do / Don't

  • ✅ Use asChild to make a link look/behave like a button — don't nest <a> in <button>.
  • ✅ Use destructive for delete/remove; pair with a confirm (AlertDialog) for irreversible actions.
  • ❌ Don't pass raw color/size classes to change appearance — pick or add a variant.
  • ❌ Don't use link variant for real buttons that don't navigate.