Skip to content

Button

import { Button } from '@delightstack/components';
View code
<Button onclick={() => console.log('clicked')}>
Click Me
</Button>

Combine variant booleans freely. The default appearance (solid action-colored) requires no variant props.

View code
<Button>Solid (default)</Button>
<Button outline>Outline</Button>
<Button transparent>Transparent</Button>
<Button translucent>Translucent</Button>

Apply semantic color schemes with the accent, error, or success props. These combine with any variant.

View code
<Button accent>Accent</Button>
<Button error>Error</Button>
<Button success>Success</Button>
<Button transparent error>Transparent Error</Button>
<Button outline accent>Outline Accent</Button>
<Button translucent success>Translucent Success</Button>

The size prop controls font size, which drives padding and dimensions via em units.

View code
<Button size="00">Extra Small</Button>
<Button size="0">Small</Button>
<Button size="1">Default</Button>
<Button size="2">Large</Button>
<Button size="3">Extra Large</Button>

dense and comfortable adjust the height and padding using the shared control sizing tokens, so a Button lines up with dense/comfortable Input and Select fields in the same form row.

View code
<Button dense>Dense</Button>
<Button>Default</Button>
<Button comfortable>Comfortable</Button>
<Button pill>Pill Button</Button>
<Button pill dense>Pill Dense</Button>

Set icon for circular icon-only buttons. Combine with any variant or badge.

5
View code
<Button icon><HeartIcon /></Button>
<Button icon outline><SettingsIcon /></Button>
<Button icon transparent><BellIcon /></Button>
<Button icon translucent><HeartIcon /></Button>
<Button icon accent><SettingsIcon /></Button>
<Button icon error><HeartIcon /></Button>
<Button icon badge><BellIcon /></Button>
<Button icon badge="5" accent><HeartIcon /></Button>

Set loading to show a spinner whenever you drive the busy state yourself.

View code
<Button disabled>Disabled</Button>
<Button loading>Loading</Button>

Icon buttons support the same loading feedback: the spinner (and the success checkmark) overlays the button while the icon scales away beneath it, and inherits the variant’s text color. Works with the loading prop or a promise-returning onclick — try the first button.

View code
<!-- promise-aware: spinner, then a confirming checkmark -->
<Button icon accent onclick={save}><UploadIcon /></Button>
<!-- prop-driven, any variant -->
<Button icon loading><SettingsIcon /></Button>
<Button icon accent loading><HeartIcon /></Button>
<Button icon error loading><HeartIcon /></Button>
<Button icon translucent loading><SettingsIcon /></Button>
<Button icon outline loading><SettingsIcon /></Button>

When onclick returns a Promise, the button manages its own loading feedback — no loading prop required:

  • Instant actions don’t flash. The spinner only appears if the promise is still pending after ~100ms; anything faster is treated as instant.
  • The spinner can’t blink. Once shown, it stays for at least ~1s, then eases out quickly.
  • Success is confirmed automatically. On resolve, a brief checkmark flashes; on reject, no checkmark is shown.

Try each button below: the first shows the spinner then a checkmark, the second resolves too fast to ever show a spinner, and the third shows a spinner but no checkmark because it rejects.

View code
<script>
import { Button } from '@delightstack/components';
async function handleSave(e) {
await api.save(data); // throws on failure -> no checkmark
}
</script>
<Button onclick={handleSave}>Save</Button>

When you drive loading yourself (e.g. from a store or form), set loading_success to flash the same confirming checkmark as loading returns to false. Leave it off and the spinner simply disappears.

View code
<script>
let loading = $state(false);
</script>
<Button loading={loading} loading_success>Save</Button>

Display a notification indicator. Pass true for a dot or a string for a count.

3
View code
<Button badge>Notifications</Button>
<Button badge="3" pill>Messages</Button>

When href is set, the button renders as an <a> element.

View code
<Button href="https://example.com" target="_blank">
Visit Site
</Button>

Pass a menu snippet to render a popover dropdown when the button is clicked.

View code
<Button show_chevron>
Options
{#snippet menu({ close })}
<button onclick={() => { edit(); close(); }}>Edit</button>
<button onclick={() => { remove(); close(); }}>Remove</button>
{/snippet}
</Button>

Use the dropdown snippet for a secondary dropdown trigger alongside the main button action.

View code
<Button onclick={primarySave}>
Save
{#snippet dropdown({ close })}
<button onclick={() => { saveAs(); close(); }}>Save As...</button>
<button onclick={() => { saveCopy(); close(); }}>Save Copy</button>
{/snippet}
</Button>
PropTypeDefaultDescription
size'0000' | '000' | '00' | '0' | '1' | '2' | '3' | '4' | '5' | '6'undefinedFont size tier, driving all dimensions via em units
iconbooleanfalseRender as a circular icon-only button
pillbooleanfalseFully rounded corners
transparentbooleanfalseNo background or border (ghost style)
translucentbooleanfalseSemi-transparent tinted background with backdrop blur
outlinebooleanfalseTransparent background with visible border
groupedbooleanfalseIndicates the button is part of a group (border radius adjusted)
errorbooleanfalseRed/destructive color scheme
successbooleanfalseGreen/success color scheme
accentbooleanfalseAccent/brand color scheme
overlaybooleanfalseDark overlay style with backdrop blur
densebooleanfalseReduced padding/height for compact layouts
comfortablebooleanfalseIncreased padding/height for roomy layouts
full_widthbooleanfalseStretch to container width
full_heightbooleanfalseStretch to container height
disable_ripplebooleanfalseDisable the ripple click effect
hrefstringundefinedRenders as <a> instead of <button>
target'_self' | '_blank' | '_parent' | '_top'undefinedLink target attribute (when href is set)
tooltipstring''Hover tooltip text
disabledbooleanfalseDisable interaction
activebooleanfalseVisually pressed/selected state (useful in toggle groups)
loadingbooleanundefinedShow the loading spinner manually. Leave unset to let a promise-returning onclick drive it
loading_successbooleanfalseFor the manual loading path: flash a success checkmark when loading goes truefalse. The promise path shows it automatically on resolve
badgestring | booleanundefinedBadge indicator; true renders a dot, a string renders the text
menuSnippet<[{ close: () => void }]>undefinedPopover dropdown content triggered by clicking the button
show_chevronbooleanfalseShow a chevron icon (useful with menu)
dropdownSnippet<[{ close: () => void }]>undefinedContent for a secondary dropdown trigger
disable_dropdownbooleanfalseDisable and hide the secondary dropdown trigger
popover_close_on_inside_clickbooleanfalseClose the popover when clicking inside it
popover_placementPlacement'bottom-end'Floating UI placement for the popover
popover_strategyStrategy'fixed'CSS positioning strategy for the popover
popover_disable_initial_focusbooleanfalseSkip auto-focusing content when the popover opens
childrenSnippet<[{ isLoading: boolean; isLoadingSuccess: boolean }]>undefinedButton label content (receives loading state)
idstringauto-generatedElement ID
classstring''Additional CSS classes
stylestring''Inline CSS styles
onclick(e: MouseEvent) => void | Promise<void>undefinedClick handler; returning a Promise enables automatic loading state
  • Full keyboard activation via Space and Enter keys
  • Clear focus ring using inset box-shadow
  • aria-haspopup and aria-expanded set automatically when a menu snippet is provided
  • Disabled state communicated via the disabled attribute
  • Loading state disables pointer events and sets tabindex="-1"
  • Ripple effect respects prefers-reduced-motion