Documentation

Documentation

Button

Interactive button component for actions, navigation, and form submissions.

The <dsgn-button> component provides interactive buttons with multiple visual appearances and sizes. Buttons can function as standard click targets, as links when an href is provided, or as form controls that submit and reset.

Usage

<dsgn-button appearance="solid">Click me</dsgn-button>

Import the component module in your application entrypoint:

import '@ds-gn/ui/button';

Playground

Props

Prop Type Default Description
appearance 'solid' | 'outline' | 'ghost' | 'link' | 'inline' | 'mono-solid' | 'mono-outline' | 'gradient' 'solid' Visual treatment.
button-weight 'normal' | 'medium' | 'semibold' | 'bold' 'semibold' Label font weight.
size 'sm' | 'md' | 'lg' 'md' Control size.
surface 'light' | 'dark' 'light' Semantic token surface to use.
href string '' When set, renders the component as a link (<a>).
target '_self' | '_blank' '_self' Target when rendered as a link. _blank automatically adds noopener noreferrer.
rel string '' Extra rel tokens for link mode, merged with the ones _blank forces.
icon-name IconName | '' '' Icon to render, by name. Requires the icon registry (see below).
icon-position 'leading' | 'trailing' | 'only' 'leading' Icon position relative to the label. Icon-only buttons require an aria-label.
icon SVGTemplateResult undefined Icon as a Lit template. Property-only (no attribute); wins over icon-name.
type 'button' | 'submit' | 'reset' 'button' Form behavior on activation. Ignored in link mode.
disabled boolean false Blocks activation and marks the control inert.
loading boolean false Busy state: spinner, aria-busy, activation blocked — but still focusable.
name string '' Submitted with the form when this button is the submitter.
value string '' Value paired with name on submission.

button-weight, icon-name, and icon-position are the attribute names; the matching JavaScript properties are buttonWeight, iconName, and iconPosition.

Slots

Slot Description
(default) Button label content.

Methods

Method Description
formDisabledCallback(disabled) Form-association lifecycle callback. The platform calls it when a <fieldset disabled> or form owner toggles the inherited disabled state.

Examples

Appearances

<dsgn-button appearance="solid">Solid</dsgn-button>
<dsgn-button appearance="outline">Outline</dsgn-button>
<dsgn-button appearance="ghost">Ghost</dsgn-button>
<dsgn-button appearance="link">Link</dsgn-button>
<dsgn-button appearance="inline">Inline</dsgn-button>
<dsgn-button appearance="mono-solid">Mono Solid</dsgn-button>
<dsgn-button appearance="mono-outline">Mono Outline</dsgn-button>
<dsgn-button appearance="gradient">Gradient</dsgn-button>

Sizes

<dsgn-button size="sm">Small</dsgn-button>
<dsgn-button size="md">Medium</dsgn-button>
<dsgn-button size="lg">Large</dsgn-button>
<dsgn-button href="/docs" appearance="solid">Go to docs</dsgn-button>

Icons

Two ways in. icon-name is markup, so it survives a CMS or a copy-paste — but names resolve through a registry you populate once at app entry:

import { Icon } from '@ds-gn/ui/icon';
import * as icons from '@ds-gn/ui/icon/icons';

Icon.registerIcons(icons); // or just the ones you use, to keep the bundle small
<dsgn-button icon-name="arrowRight" icon-position="trailing"
  >Get started</dsgn-button
>
<dsgn-button
  icon-name="search"
  icon-position="only"
  aria-label="Search"
></dsgn-button>

icon takes a Lit template instead. It needs no registry and tree-shakes to the one icon you import, but it can only be set from JS:

import { arrowRight } from '@ds-gn/ui/icon/icons';

html`<dsgn-button .icon=${arrowRight}>Get started</dsgn-button>`;

If both are set, icon wins.

Disabled and loading

<dsgn-button disabled>Disabled</dsgn-button>
<dsgn-button loading>Saving</dsgn-button>

Forms

<dsgn-button> is form-associated, so type="submit" and type="reset" reach an enclosing <form> even though the real <button> lives in the shadow root:

<form>
  <dsgn-input name="email" type="email"></dsgn-input>
  <dsgn-button type="submit" name="intent" value="subscribe"
    >Subscribe</dsgn-button
  >
</form>

The name/value pair is contributed only for the button that was activated, matching a native submit button. The form owner’s disabled state (e.g. a <fieldset disabled>) propagates to the component without touching its own disabled attribute, so re-enabling the fieldset restores it.

SubmitEvent.submitter is preserved: a form-associated custom element cannot be passed to requestSubmit(), so submission is driven by activating a throwaway native submit button inside the host. Handlers can recover the component from it:

form.addEventListener('submit', (event) => {
  const button = event.submitter?.closest('dsgn-button');
});

Disabled vs loading

  • disabled sets the native disabled attribute on the inner <button>. In link mode it drops href and sets aria-disabled="true" instead, since <a> has no native disabled state.
  • loading blocks activation and sets aria-busy="true" but deliberately leaves the control focusable, so keyboard focus is not lost while an async action runs. It swaps the icon for a spinner (or leads the label when there is no icon), and the spin honors prefers-reduced-motion. In link mode the href is dropped while busy — a click guard alone would not stop a middle-click’s auxclick from opening the target — and tabindex="0" keeps the anchor focusable.

Styling

Buttons use the semantic --radius-button token for their corner radius. If your application does not load the token theme stylesheet, they fall back to a fully rounded pill shape. Define --radius-button to customize the shape:

:root {
  --radius-button: 0.5rem;
}

Accessibility

  • Ensure the slot text describes the action (avoid ambiguous labels like “Click”). When href is set, the component renders an <a> and inherits link semantics.
  • Icon-only buttons (icon-position="only") require an aria-label.
  • For destructive actions, pair the button with surrounding context or add accessible description text.

Usage patterns

  • Use appearance="solid" for the main action in a view and appearance="outline" or appearance="ghost" for supporting actions.
  • Prefer href when navigating and omit href for in-page actions.
Esc