Button
Import
Section titled “Import”import { Button } from '@delightstack/components';Basic Usage
Section titled “Basic Usage”View code
<Button onclick={() => console.log('clicked')}> Click Me</Button>Examples
Section titled “Examples”Variants
Section titled “Variants”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>Color Schemes
Section titled “Color Schemes”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>Pill, Dense & Comfortable
Section titled “Pill, Dense & Comfortable”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>Icon Buttons
Section titled “Icon Buttons”Set icon for circular icon-only buttons. Combine with any variant or badge.
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>Disabled & Loading
Section titled “Disabled & Loading”Set loading to show a spinner whenever you drive the busy state yourself.
View code
<Button disabled>Disabled</Button><Button loading>Loading</Button>Icon Button Loading
Section titled “Icon Button Loading”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>Promise-Aware Click Handler
Section titled “Promise-Aware Click Handler”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>Manual Success Checkmark
Section titled “Manual Success Checkmark”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.
View code
<Button badge>Notifications</Button><Button badge="3" pill>Messages</Button>Link Button
Section titled “Link Button”When href is set, the button renders as an <a> element.
View code
<Button href="https://example.com" target="_blank"> Visit Site</Button>Dropdown Menu
Section titled “Dropdown Menu”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>Split Button with Dropdown
Section titled “Split Button with Dropdown”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>| Prop | Type | Default | Description |
|---|---|---|---|
size | '0000' | '000' | '00' | '0' | '1' | '2' | '3' | '4' | '5' | '6' | undefined | Font size tier, driving all dimensions via em units |
icon | boolean | false | Render as a circular icon-only button |
pill | boolean | false | Fully rounded corners |
transparent | boolean | false | No background or border (ghost style) |
translucent | boolean | false | Semi-transparent tinted background with backdrop blur |
outline | boolean | false | Transparent background with visible border |
grouped | boolean | false | Indicates the button is part of a group (border radius adjusted) |
error | boolean | false | Red/destructive color scheme |
success | boolean | false | Green/success color scheme |
accent | boolean | false | Accent/brand color scheme |
overlay | boolean | false | Dark overlay style with backdrop blur |
dense | boolean | false | Reduced padding/height for compact layouts |
comfortable | boolean | false | Increased padding/height for roomy layouts |
full_width | boolean | false | Stretch to container width |
full_height | boolean | false | Stretch to container height |
disable_ripple | boolean | false | Disable the ripple click effect |
href | string | undefined | Renders as <a> instead of <button> |
target | '_self' | '_blank' | '_parent' | '_top' | undefined | Link target attribute (when href is set) |
tooltip | string | '' | Hover tooltip text |
disabled | boolean | false | Disable interaction |
active | boolean | false | Visually pressed/selected state (useful in toggle groups) |
loading | boolean | undefined | Show the loading spinner manually. Leave unset to let a promise-returning onclick drive it |
loading_success | boolean | false | For the manual loading path: flash a success checkmark when loading goes true → false. The promise path shows it automatically on resolve |
badge | string | boolean | undefined | Badge indicator; true renders a dot, a string renders the text |
menu | Snippet<[{ close: () => void }]> | undefined | Popover dropdown content triggered by clicking the button |
show_chevron | boolean | false | Show a chevron icon (useful with menu) |
dropdown | Snippet<[{ close: () => void }]> | undefined | Content for a secondary dropdown trigger |
disable_dropdown | boolean | false | Disable and hide the secondary dropdown trigger |
popover_close_on_inside_click | boolean | false | Close the popover when clicking inside it |
popover_placement | Placement | 'bottom-end' | Floating UI placement for the popover |
popover_strategy | Strategy | 'fixed' | CSS positioning strategy for the popover |
popover_disable_initial_focus | boolean | false | Skip auto-focusing content when the popover opens |
children | Snippet<[{ isLoading: boolean; isLoadingSuccess: boolean }]> | undefined | Button label content (receives loading state) |
id | string | auto-generated | Element ID |
class | string | '' | Additional CSS classes |
style | string | '' | Inline CSS styles |
onclick | (e: MouseEvent) => void | Promise<void> | undefined | Click handler; returning a Promise enables automatic loading state |
Accessibility
Section titled “Accessibility”- Full keyboard activation via Space and Enter keys
- Clear focus ring using inset box-shadow
aria-haspopupandaria-expandedset automatically when amenusnippet is provided- Disabled state communicated via the
disabledattribute - Loading state disables pointer events and sets
tabindex="-1" - Ripple effect respects
prefers-reduced-motion