Mushilu-San UI

Search components

PlaygroundStorybookGitHub
Primitives

Button

Triggers an action or navigation, with variants, sizes and a loading state.

Selector: button[muiButton], a[muiButton]

Import
import { Button } from '@mushilu-san/ui/primitives';
PreviewCode

Loading example…

Overview

Button is applied as an attribute to a native <button> or <a>, so it keeps the native role, form submission and keyboard behaviour for free.

Loading buttons stay in the tab order. Both disabled and loading buttons set aria-disabled="true" and swallow clicks. A disabled button is also taken out of the tab order (tabindex="-1"), while a loading one keeps focus so the state is announced.

When to use

  • Submitting a form or confirming a step.
  • Triggering an in-page action, such as opening a Dialog.
  • A call to action that navigates. Apply muiButton to an <a> so it is still a link.

When not to use

  • Switching between views of related content. Use Tabs instead.
  • Inline navigation inside a sentence. Use a plain link.

Examples

Sizes

Every size keeps a 44px minimum touch target.

PreviewCode

Loading example…

Loading and disabled

While loading is set the button sets aria-busy and ignores clicks.

PreviewCode

Loading example…

Use <a muiButton> when the action navigates.

PreviewCode

Loading example…

Guidelines

Do

  • Use one primary button per view for the main action.
  • Write labels as verbs that describe the outcome: "Save changes", not "OK".
  • Use destructive for actions that delete or cannot be undone, and confirm them.
  • Use loading for in-flight actions rather than disabling the button, so focus is not lost.

Don't

  • Do not put a muiButton on a <div> or <span>.
  • Do not rely on colour alone to distinguish destructive actions. Say it in the label.
  • Do not use a disabled button as the only explanation for why an action is unavailable. Tell the user why.

Accessibility

Roles and attributes

ElementRoleNotes
button[muiButton]button (native)
a[muiButton]link (native)
host—aria-disabled="true" when disabled or loading; aria-busy="true" while loading; tabindex="-1" when disabled.

Keyboard

KeysAction
TabShiftTabMoves focus to and from the button. Disabled buttons are skipped; loading buttons are not.
EnterSpaceActivates the button. Default action is prevented while disabled or loading.
  • Icon-only buttons need an aria-label that describes the action.
  • The loading spinner is decorative (aria-hidden). Change the label text if the state matters.

API

Button

Selector: button[muiButton], a[muiButton]

NameTypeDefaultDescription
variant'primary' | 'secondary' | 'ghost' | 'destructive''primary' Visual style: `primary` for the main action, `secondary` for supporting actions, `ghost` for low emphasis, `destructive` for irreversible actions. Default `primary`.
size'sm' | 'md' | 'lg''md' Button size. Every size keeps a 44px minimum touch target. Default `md`.
disabledbooleanfalse Disables the button. Sets `aria-disabled` and swallows clicks but keeps the element in the DOM. Accepts attribute presence, e.g. <… disabled>.
loadingbooleanfalse Shows a spinner and sets `aria-busy`. Clicks are ignored while loading. Accepts attribute presence, e.g. <… loading>.
NameTypeDescription
clickedMouseEvent Emits the native click event unless the button is disabled or loading.

CSS parts

  • ::part(root)

Tokens

  • --mui-color-primary
  • --mui-color-primary-hover
  • --mui-color-primary-active
  • --mui-color-danger
  • --mui-color-danger-hover
  • --mui-radius-md
  • --mui-touch-target
  • --mui-color-focus-ring