Diwa Design System

Button Pure

A minimal, transparent button — icon and label only, without background or border. Use it for inline actions, contextual links, and secondary affordances where a full button would be too heavy.

Icon-only buttons

When hideLabel=true, the label slot is visually hidden using an sr-only technique but stays in the DOM so screen readers can announce the accessible name. Always provide descriptive slot text:

<diwa-button-pure hide-label icon="x">
  Close dialog
</diwa-button-pure>

<!-- Or use the label prop for a shorter syntax -->
<diwa-button-pure hide-label icon="x" label="Close dialog" />

Keyboard interaction

KeyAction
TabMove focus to the button
Shift + TabMove focus to the previous focusable element
EnterActivate the button
SpaceActivate the button

Screen reader behaviour

  • aria-busy="true" is set automatically when loading=true.
  • • When rendered as <a> (via href), disabled/loading state sets aria-disabled="true" and removes the tab stop (tabIndex=-1).
  • • The icon is wrapped in aria-hidden="true" — purely decorative, not announced by screen readers.
  • • The spinner replaces the icon during loading and is also aria-hidden="true"; the aria-busy attribute signals the async state to AT.
  • shadow: { delegatesFocus: true } ensures :focus-visible and .focus() work correctly when called on the host element.

WCAG 2.2 compliance

1.4.3 Contrast (Minimum) — AAPass

Button label text and icon foreground colours use --diwa-text-primary; meet the 4.5:1 minimum contrast ratio against the component background in both themes.

1.4.11 Non-text Contrast — AAPass

The focus ring (--diwa-border-focus) meets ≥ 3:1 contrast against adjacent surface colours.

2.1.1 Keyboard — APass

All button actions are activatable via Enter and Space. When rendered as <a>, Enter activates it.

2.4.7 Focus Visible — AAPass

A tokenized focus outline (var(--diwa-focus-ring-width) solid --diwa-border-focus) is shown on the host element via :focus-visible using delegatesFocus.

4.1.2 Name, Role, Value — APass

role, aria-label (icon-only), aria-busy, and aria-disabled are managed automatically by the component.

Best practices

  • Reduced motion — all CSS transitions inside the shadow DOM respond to prefers-reduced-motion: reduce and are suppressed automatically.
  • Icon-only labels are mandatory — when no visible text label is present, always set label on the host so screen readers announce a meaningful name.
  • Loading state — setting loading automatically adds aria-busy="true" — do not manage this attribute manually.