elements
Demos UI Feed Discuss Install
Style System
  • Style
  • Themes
  • Behaviors
Forms
  • Button
  • ButtonGroup
  • Checkbox
  • ColorPicker
  • Combobox
  • Field
  • FileUpload
  • Input
  • InputGroup
  • InputOTP
  • Label
  • NativeSelect
  • NumberInput
  • PasswordInput
  • RadioGroup
  • Select
  • Slider
  • Switch
  • TagsInput
  • Textarea
  • Toggle
  • ToggleGroup
Overlays
  • AlertDialog
  • Command
  • ContextMenu
  • Dialog
  • Drawer
  • DropdownMenu
  • HoverCard
  • Menubar
  • NavigationMenu
  • Popover
  • Sheet
  • Toast
  • Tooltip
Display
  • Accordion
  • Alert
  • AspectRatio
  • Avatar
  • Badge
  • Breadcrumb
  • Card
  • Collapsible
  • Empty
  • Item
  • Kbd
  • Pagination
  • Progress
  • Separator
  • Skeleton
  • Spinner
  • Table
  • Tabs
  • Tree
Layout
  • Carousel
  • Resizable
  • ScrollArea
  • Sidebar
Dates
  • Calendar
  • DatePicker
  • DateRangePicker
  • TimePicker
Data
  • AreaChart
  • BarChart
  • Chart
  • DataTable
  • Kanban
  • LineChart
  • PieChart
  • RadarChart
  • RadialChart
  • VirtualList
Chat
  • Attachment
  • Message
  • MessageList
  • PromptInput
Elements UI / Forms / Button

Button

A button or a link that looks like one, in six variants and four sizes.

Variants

Six intents. primary is the accent; danger is for destructive actions.

clicks: 0
Code
<Button variant="primary">Primary</Button>
<Button variant="secondary">Secondary</Button>
<Button variant="outline">Outline</Button>
<Button variant="ghost">Ghost</Button>
<Button variant="link">Link</Button>
<Button variant="danger">Delete</Button>

Usage

import { Button } from "@elements/ui/button";

<Button variant="primary" onclick={() => save(form)}>Save changes</Button>
<Button variant="ghost" icon label="Close"><Icon name="x" /></Button>
<Button href="/docs" variant="link">Read the docs</Button>
<Button variant="primary" loading={state.saving}>Save</Button>

Examples

Sizes

Heights come from the theme's control tokens.

Code
<Button variant="primary" size="xs">Extra small</Button>
<Button variant="primary" size="sm">Small</Button>
<Button variant="primary">Medium</Button>
<Button variant="primary" size="lg">Large</Button>
<Button icon size="xs" label="Add"><Icon name="plus" /></Button>
<Button icon size="lg" label="Add"><Icon name="plus" /></Button>

Rounded

round makes a pill, or a circle with icon.

Code
<Button variant="primary" round>Subscribe</Button>
<Button icon round label="Next"><Icon name="arrow-right" /></Button>

Icons

icon makes a square button and needs a label. Icons beside text size themselves.

Code
<Button icon label="Settings"><Icon name="settings" /></Button>
<Button variant="ghost" icon label="More"><Icon name="more-horizontal" /></Button>
<Button variant="secondary"><Icon name="mail" /> Email</Button>
<Button variant="primary">Next <Icon name="arrow-right" /></Button>

Loading

The label is hidden, not removed, so the width holds. Focus stays and clicks are ignored.

Code
<Button variant="primary" loading={state.saving} onclick={() => save(state)}>
  Save changes
</Button>

Announce the busy label

Some screen readers do not announce a change to the text inside a button. Point aria-labelledby at the label, and its new text becomes the button's name. Both labels share one cell, so the width holds.

Code
<Button variant="primary" loading={state.submitting} attrs={{ "aria-labelledby": "submit-label" }}>
  <span id="submit-label" class="swap-label" data-state={state.submitting ? "on" : "off"}>
    <span class="swap-off">Submit</span>
    <span class="swap-on">Submitting</span>
  </span>
</Button>

Loading never resizes

Every variant and size, with and without an icon: entering and leaving loading keeps the box identical.

Code
<Button variant="primary" size="sm" loading={state.loading}>Save</Button>
<Button variant="outline" icon label="Refresh" loading={state.loading}><Icon name="loader" /></Button>

Disabled and links

href renders an <a>. A disabled link has no href and leaves the tab order.

Link as button Disabled link Focusable disabled link
Code
<Button disabled>Disabled</Button>
<Button variant="primary" disabled>Disabled</Button>
<Button href="#examples">Link as button</Button>
<Button href="#examples" disabled>Disabled link</Button>

Focusable when disabled

focusableWhenDisabled keeps a disabled button in the tab order with aria-disabled, so focus is not lost while a request runs.

clicks: 0
Code
<Button variant="primary" disabled={state.pending} focusableWhenDisabled
        onclick={() => submit(state)}>Submit</Button>

Right to left

Under dir=rtl the icon and label follow the reading direction.

Code
<div dir="rtl">
  <Button variant="secondary"><Icon name="mail" /> بريد</Button>
</div>

Other elements

buttonClass() gives any element the button look, such as a label for a file input.

Code
<label class={buttonClass("secondary")}>
  <input type="file" class="sr-only"> Upload
</label>

Accessible name and description

The name comes from the content, label, or aria-labelledby through attrs; aria-describedby adds a description.

Pro plan Billed yearly
Code
<Button attrs={{ "aria-labelledby": "plan-heading", "aria-describedby": "plan-note" }}>
  Choose
</Button>

In a form

type=submit submits on click and on Enter; loading blocks a second submit.

submitted: 0
Code
<form onsubmit={() => subscribe(state)}>
  <input type="email" value={state.email} placeholder="you@example.com">
  <Button type="submit" variant="primary" block loading={state.saving}>Subscribe</Button>
</form>

Invalid

aria-invalid marks a button that stands for a field with an error, such as a date picker's trigger.

Code
<Button attrs={{ "aria-invalid": "true" }}>Pick a date</Button>

Opens a dialog

APG: a button that opens a dialog ends its label with an ellipsis, so the label says more input follows.

Code
<Dialog title="Rename file">
  <Button slot="trigger">Rename…</Button>
  <input aria-label="Name" value="report.pdf">
  <DialogFooter>
    <DialogClose><Button>Cancel</Button></DialogClose>
    <DialogClose><Button variant="primary">Rename</Button></DialogClose>
  </DialogFooter>
</Dialog>
Reference Button in the manual Every part, attribute, slot, key and data attribute.
Next ButtonGroup
Product
Manual Pricing Compare
Company
History Contact
Legal
Privacy Policy Acceptable Use Terms License Agreement
© Elements Dev Corporation 2026