---
outline: deep
---

# Button

Buttons are used to trigger actions such as submitting forms, confirming dialogs, or navigating. They are the primary interactive control in any interface.

**`.l-button`** — Native HTML Element

## Options

### Sizes

Add `data-size="sm"`, `data-size="lg"`, or `data-size="xl"`. Default is md. Only height and padding scale — the label stays 14px so a taller button reads as a larger touch target, not a louder label. For a larger label, override `--font-size`.

```html
<button
  class="l-button"
  data-size="sm"
>
  Button sm
</button>
<button class="l-button">Button md</button>
<button
  class="l-button"
  data-size="lg"
>
  Button lg
</button>
<button
  class="l-button"
  data-size="xl"
>
  Button xl
</button>
```

### Primary

Add `data-variant="primary"`.

```html
<button
  class="l-button"
  data-variant="primary"
>
  Primary
</button>
<button
  class="l-button"
  data-variant="primary"
  disabled
>
  Primary disabled
</button>
```

### Destructive

Add `data-variant="destructive"`.

```html
<button
  class="l-button"
  data-variant="destructive"
>
  Delete
</button>
<button
  class="l-button"
  data-variant="destructive"
  disabled
>
  Delete
</button>
```

### Icon-only

Auto-detected when containing a single `<svg>`, `<l-icon>`, or `<iconify-icon>`.

```html
<button
  class="l-button"
  aria-label="Settings"
>
  <iconify-icon icon="lucide:settings"></iconify-icon>
</button>
<button
  class="l-button"
  data-variant="primary"
  aria-label="Add"
>
  <iconify-icon icon="lucide:plus"></iconify-icon>
</button>
```

### Disabled

Native `disabled` attribute.

```html
<button
  class="l-button"
  disabled
>
  Disabled
</button>
<button
  class="l-button"
  data-variant="primary"
  disabled
>
  Disabled primary
</button>
```

### As link

Use an `<a>` element instead of `<button>`.

```html
<a
  class="l-button"
  href="#link"
  >Link button</a
>
<a
  class="l-button"
  data-variant="primary"
  href="#link"
  >Primary link</a
>
```

### Press effect

Add `data-press-effect` attribute.

```html
<button
  class="l-button"
  data-press-effect
>
  Press me
</button>
<button
  class="l-button"
  data-variant="primary"
  data-press-effect
>
  Press me
</button>
```

### Loading

Add an `<l-spinner>` inside the button. The spinner inherits the button's text color.

```html
<button
  class="l-button"
  disabled
>
  <l-spinner></l-spinner>
  Loading…
</button>
<button
  class="l-button"
  data-variant="primary"
  disabled
>
  <l-spinner></l-spinner>
  Loading…
</button>
```

## Examples

### With icon and text

```html
<button class="l-button">
  <iconify-icon icon="lucide:download"></iconify-icon>
  Download
</button>
<button
  class="l-button"
  data-variant="primary"
>
  <iconify-icon icon="lucide:send"></iconify-icon>
  Send
</button>
```

### Button group

Wrap related buttons in [`<l-button-group>`](./button-group) to join them into a single segmented control.

```html
<l-button-group label="Text formatting">
  <button
    class="l-button"
    aria-label="Bold"
  >
    <iconify-icon icon="lucide:bold"></iconify-icon>
  </button>
  <button
    class="l-button"
    aria-label="Italic"
  >
    <iconify-icon icon="lucide:italic"></iconify-icon>
  </button>
  <button
    class="l-button"
    aria-label="Underline"
  >
    <iconify-icon icon="lucide:underline"></iconify-icon>
  </button>
</l-button-group>
```

### Form actions

```html
<div class="flex gap-2 justify-end">
  <button class="l-button">Cancel</button>
  <button
    class="l-button"
    data-variant="primary"
  >
    Save changes
  </button>
</div>
```

## Accessibility

### Criteria

- **Color** — Text and background colors meet AA contrast ratios across all variants
- **Focus state** — Visible 2px focus ring for keyboard users
- **Hover state** — Clear visual change on hover
- **Active state** — Visual feedback on press
- **Disabled state** — Reduced opacity, `cursor: not-allowed`, not focusable when disabled
- **Icon-only** — Icon-only buttons must have `aria-label` for an accessible name
- **Role** — Uses native `<button>` or `<a>` — no extra ARIA role needed

### Rules

- Use a native `<button>` element — never a `<div>` or `<span>`
- Always add `aria-label` to icon-only buttons
- Include `type="button"` to prevent form submission (except for submit buttons)
- When using `<a>` as a link button, add `role="button"` only if it triggers an action rather than navigating

### Keyboard interactions

- `Enter` — Activates the button
- `Space` — Activates the button
- `Tab` — Moves focus to the next focusable element
- `Shift + Tab` — Moves focus to the previous focusable element

## API reference

### Importing

```css
@import 'luxen-ui/css/button';
```

### Attributes & Properties

- **disabled** — Disables the button.
- **command** — Invoker command (`show-modal`, `close`, `show-popover`, etc.).
- **commandfor** — ID of the target element for `command`.
- **data-variant**: `primary | destructive` — Visual variant. Default is the secondary style.
- **data-size**: `sm | lg | xl` — Control size. Default is md.
- **data-icon-only** — Icon-only mode (square button, width equals height). Auto-detected for a single icon child.
- **data-press-effect** — Press effect (scale + translate on active).

### CSS classes

- `.l-button` — Base button style.

### CSS custom properties

- `--height` (default: `36px`) — Button height (md size).
- `--padding-inline` (default: `0.625rem`) — Horizontal padding.
- `--background-color` — Background color.
- `--background-color-hover` — Background color on hover.
- `--background-color-active` — Background color on press.
- `--text-color` — Text color.
- `--text-color-hover` — Text color on hover.
- `--border-color` — Border color.
- `--border-color-hover` — Border color on hover.
- `--font-size` (default: `0.875rem`) — Label font size. Stays 14px across all sizes; override to opt into a larger label.
