Skip to main content

Button

Render actions and links with loading, icons, and semantic roots.

Use Button for actions and explicit navigation. Choose the underlying element with as according to the action: a button for commands and an anchor for destinations.

Basic usage#

import { Button } from 'moraine'

export function Example() {
  return <Button>Save</Button>
}

Playground#

Props
Slots

Anatomy#

Button [component; slot=root; <button>]
├── leading [slot]
├── label [slot]
└── trailing [slot]

Usage#

Loading#

Use loading when application state owns the pending operation. loadingAuto is opt-in: when enabled, a promise returned by onClick drives the visual loading state. Loading suppresses additional activation without applying native disabled, so a focused button keeps its keyboard position. Explicit disabled still uses native disabled semantics where available.

import { Button } from 'moraine'
import { createSignal } from 'solid-js'

export function LoadingUsage() {
  const [loading, setLoading] = createSignal(false)

  const handleManual = () => {
    setLoading(true)
    setTimeout(() => setLoading(false), 1200)
  }

  return (
    <div class="flex flex-wrap gap-3 items-center">
      <Button loading={loading()} onClick={handleManual}>
        Controlled Loading
      </Button>
      <Button
        loadingAuto
        variant="outline"
        onClick={() => new Promise((resolve) => setTimeout(resolve, 1500))}
      >
        Auto Promise Loading
      </Button>
    </div>
  )
}

Icons and polymorphism#

Leading and trailing content can be icons or custom elements. Icon-only buttons need aria-label or another accessible name. Polymorphic roots without native link semantics receive button role and keyboard activation; anchors with href keep native link behavior. When a custom component forwards its root ref, Moraine resolves native button semantics from the rendered DOM element.

import { Button } from 'moraine'

export function IconsPolymorphism() {
  return (
    <div class="flex flex-wrap gap-3 items-center">
      <Button leading="i-lucide:plus">Create Project</Button>
      <Button variant="outline" trailing="i-lucide:external-link" as="a" href="#polymorphic">
        Documentation Link
      </Button>
      <Button variant="ghost" size="xs" aria-label="Settings" leading="i-lucide:settings" />
    </div>
  )
}

Examples#

Icon action with a hint#

import { Button, Icon, Tooltip } from 'moraine'
import { createSignal } from 'solid-js'

export function IconButtons() {
  const [bookmarked, setBookmarked] = createSignal(false)

  return (
    <div class="flex gap-3 items-center">
      <span class="text-sm">Release checklist</span>
      <Tooltip>
        <Tooltip.Trigger
          as={Button}
          variant="outline"
          size="icon-sm"
          aria-label={bookmarked() ? 'Remove bookmark' : 'Bookmark checklist'}
          onClick={() => setBookmarked((value) => !value)}
        >
          <Icon name={bookmarked() ? 'i-lucide:bookmark-check' : 'i-lucide:bookmark'} />
        </Tooltip.Trigger>
        <Tooltip.Content text={bookmarked() ? 'Remove bookmark' : 'Bookmark checklist'} />
      </Tooltip>
    </div>
  )
}

Attributes#

Attributes
data-disabledSlot: buttonDescription: Present when the component, slot, or item is disabled.
data-loadingSlot: buttonDescription: Present when the component or async operation is loading.

Props#

Props for the Button component. Renders a <button> element by default.

Prop