Meowland docs
Workbench

Component reference

Button

Triggers one action, in one of six levels and three sizes.

Preview

Sizes, levels and unavailable states

The same action across three sizes and six levels: primary (black and white), secondary, quiet, accent soft fill (about current activity), stop (interrupting running work) and confirming a dangerous action. Buttons can be triggered from the keyboard; disabled controls skip Tab and produce no receipt.

packages/meowtail/stories/actions.stories.tsxline 22tsx
render() {
  const feedback = signal("Choose an action; the receipt here only happens locally.");
  const actions = [
    ["primary", "Save changes"],
    ["outline", "View diff"],
    ["quiet", "Handle later"],
    ["danger", "Discard draft"],
    ["accent", "View progress"],
    ["stop", "Stop processing"],
  ] as const;
  return (
    <div className="workshop-stack workshop-constrained">
      {(["small", "default", "large"] as const).map((size) => (
        <Specimen
          label={
            { small: "Compact toolbar", default: "Regular actions", large: "Priority actions" }[
              size
            ]
          }
          align="start"
        >
          <div className="workshop-foundation-action-grid">
            {actions.map(([variant, label]) => (
              <Button
                size={size}
                variant={variant}
                onClick={() => {
                  feedback.value = `Selected: ${label} (${size})`;
                }}
              >
                {label}
              </Button>
            ))}
            <IconButton
              size={size}
              label={`${size} search`}
              onClick={() => {
                feedback.value = `Selected search (${size})`;
              }}
            >
              <Icon name="search" />
            </IconButton>
          </div>
          <div className="workshop-foundation-action-grid">
            {actions.map(([variant, label]) => (
              <Button size={size} variant={variant} disabled>
                {label}
              </Button>
            ))}
            <IconButton size={size} label={`${size} search unavailable`} disabled>
              <Icon name="search" />
            </IconButton>
          </div>
        </Specimen>
      ))}
      <Specimen label="Add entries" align="start">
        <div className="workshop-row">
          <AddButton
            onClick={() => {
              feedback.value = "Selected add";
            }}
          />
          <AddButton
            variant="outline"
            size="small"
            onClick={() => {
              feedback.value = "Selected add attachment";
            }}
          >
            Add attachment
          </AddButton>
          <AddButton variant="quiet" size="large" disabled>
            Add after the work completes
          </AddButton>
        </div>
      </Specimen>
      <Note role="status">{feedback}</Note>
    </div>
  );
}

Installation

bun add meowtail

Usage

Taken from the examples on this page.

usage.tsxtsx
import { Button } from "meowtail";

<Button>Save changes</Button>

Examples

The other stories that show this component.

Action hierarchy

Four real button variants plus the disabled state. Use Tab to see focus; disabled buttons cannot trigger actions.

packages/meowtail/stories/foundation.stories.tsxline 39tsx
render: () => (
  <Measure width="narrow">
    <Stack data-button-composition>
      <Stack as="header" gap="compact">
        <h2 style={{ margin: 0, fontSize: uiTokens.type.title, fontWeight: 600 }}>
          Ready to deliver this change
        </h2>
        <span style={{ color: uiTokens.ink.secondary }}>
          The primary action is clear and supporting actions step back. Every action is demoed
          locally only.
        </span>
      </Stack>
      <ActionRow>
        <Button data-action="Saved the local sample.">Save changes</Button>
        <Button variant="outline" data-action="Diff sample selected.">
          View diff
        </Button>
        <Button variant="quiet" data-action="Continue editing.">
          Continue editing
        </Button>
      </ActionRow>
      <Stack as="section" gap="compact" aria-label="Actions not yet available">
        <span style={{ color: uiTokens.ink.secondary, fontSize: uiTokens.type.caption }}>
          No submission target selected yet
        </span>
        <ActionRow>
          <Button disabled>Submit changes</Button>
          <Button variant="outline" disabled>
            Preview submission
          </Button>
          <Button variant="quiet" disabled>
            Undo
          </Button>
          <Button variant="danger" disabled>
            Delete
          </Button>
        </ActionRow>
      </Stack>
      <span
        style={{ color: uiTokens.ink.secondary, fontSize: uiTokens.type.caption }}
        role="status"
        data-feedback
      >
        Choose an action to see immediate feedback.
      </span>
      <Inline as="section" gap="normal" aria-label="Discard draft demo">
        <LayoutItem grow>
          <Stack gap="compact">
            <h2 style={{ margin: 0, fontSize: uiTokens.type.body, fontWeight: 600 }}>
              Start over
            </h2>
            <span style={{ color: uiTokens.ink.secondary, fontSize: uiTokens.type.caption }}>
              Only demonstrates discarding; no real file is deleted.
            </span>
          </Stack>
        </LayoutItem>
        <LayoutItem shrink={false}>
          <Button
            variant="danger"
            data-action="Discarded the draft demo; no real file was deleted."
          >
            Discard local draft
          </Button>
        </LayoutItem>
      </Inline>
    </Stack>
  </Measure>
)

Sizes and long text

Small, default, and large sizes plus a longer action name to observe wrapping and hit areas on a narrow canvas.

packages/meowtail/stories/foundation.stories.tsxline 131tsx
render: () => (
  <div className="workshop-stack workshop-constrained">
    {(["small", "default", "large"] as const).map((size, index) => (
      <Specimen
        label={["Compact toolbar", "Regular actions", "Primary entry"][index]}
        align="start"
      >
        <div className="workshop-row">
          <Button size={size}>Save changes</Button>
          <Button size={size} variant="outline">
            View work outcomes
          </Button>
          <IconButton size={size} label={`${size} search`}>
            <Icon name="search" />
          </IconButton>
          <IconButton size={size} label={`${size} close`} disabled>
            <Icon name="x" />
          </IconButton>
        </div>
      </Specimen>
    ))}
    <Button size="large" variant="quiet">
      View all work outcomes awaiting review in the current project
    </Button>
  </div>
)

API reference

Props

PropTypeDefault
variant
"primary" | "quiet" | "danger" | "outline" | "accent" | "stop""primary"
size
"small" | "default" | "large""default"

Also takes className, children and the attributes and events of its root element.

DOM parts

root

Registry

The component is registered as button in the meowtail component registry.