Skip to content

Button

Enhance any element with accessible button behavior, including keyboard support, interaction states, and proper ARIA semantics.

Why use this button proto?

The browser's native <button> element works great, but sometimes you need:

  • Consistent interaction states across hover, press, and focus for styling
  • Anchors with href/routerLink that need the accessibility and visual treatment of a button
  • Custom button components with button accessibility and custom styling
  • Loading states that keep focus on the button while it's temporarily disabled
  • Disabled tooltips that explain why a button is disabled

This primitive handles all of that while following the WAI-ARIA Button Pattern.

Import

ts
import {ProtoButton} from '@terse-ui/protos/button';

Accessibility Features

ProtoButton implements the WAI-ARIA Button Pattern:

FeatureNative <button>Non-native elements
RoleImplicitAdds role="button" (in the absence of set or apparent role)
KeyboardBrowser handlesEnter activates immediately, Space activates on release
Disableddisabled attraria-disabled="true" + event blocking
Tab orderdisabled removestabindex="-1" when disabled
Focus visible:focus-visibledata-focus-visible attribute

Examples

Loading States

Use disabledInteractive for buttons that enter a loading state after being clicked.

Why? When a button becomes disabled, it's removed from the tab order. Keyboard users lose their focus position and have to navigate back when loading completes. disabledInteractive keeps focus on the button throughout the loading cycle.

Loading example...

How it works

PropertyBehavior
disabled onlyUses native disabled attr, removed from tab order
disabled + disabledInteractiveUses aria-disabled, stays in tab order, blocks activation

This follows the APG guidance on focusability of disabled controls.

Styling

These are the recommended ways to style the button across different states:

css
[protoButton] {
  /* Base styles */
}

[protoButton][data-disabled] {
  /* Disabled (interactive or not) */
}

[protoButton]:not([data-disabled]):hover {
  /* Hovered */
}

[protoButton]:not([data-disabled]):active {
  /* Pressed */
}

[protoButton]:not([data-disabled='hard']):focus-visible {
  /* Focus visible (keep focus when soft disabled) */
}

API Reference

ProtoButton

Selector[protoButton]
Exported asprotoButton
Data attributesdata-disabled
ARIA bindingsaria-disabled

Inputs

InputTypeDefaultDescription
disabledboolean
disabledInteractiveboolean
tabIndexnumber
rolestring | null
typestring | null

Properties

PropertyTypeDescription
hardDisabledSignal<boolean> (readonly)
softDisabledSignal<boolean> (readonly)

Accessibility

Prefer native <button> elements for built-in browser accessibility. Native buttons receive the disabled attribute, which removes them from the tab order. Enabling disabledInteractive switches to aria-disabled="true" instead, keeping the button focusable while still blocking activation. Non-native elements always use aria-disabled for disabled state, and ProtoButton automatically adds role="button" and keyboard activation for them.

Keyboard Interactions

  • Enter: Activate the button.
  • Space: Activate the button (on key release).

Released under the MIT License.