HyButton
A versatile, accessible button component supporting multiple variants, sizes, and visual appearances.
Key Features:
- Dual-mode rendering: Functions as both buttons (
<button>) and links (<a>) based onhrefproperty - Five-value
emphasisaxis (solid | soft | outlined | tinted | plain) — single-token, no compounds - Comprehensive accessibility support including ARIA attributes and keyboard navigation
- Loading states with visual feedback and screen reader announcements
- Flexible content model with prefix, suffix, and default slots
- Built-in popup/dropdown support with escape key handling
- CSS custom properties for extensive theming capabilities
Accessibility Highlights:
- Full keyboard navigation support (Enter, Space, Escape keys)
- Proper ARIA attributes for screen readers
- Loading state announcements via
aria-liveregions - Focus management for disabled and loading states
- Semantic role consistency between button and link modes
Performance Considerations:
- Lightweight DOM structure with minimal nesting
- Efficient event handling with proper cleanup
- CSS-only animations and transitions
- No unnecessary re-renders during property changes
Examples
Basic usage - Default neutral button
html
<hy-button>Click Me</hy-button>Primary action button with enhanced styling
html
<hy-button variant="brand" size="large" emphasis="soft"> Primary Action </hy-button>Link button with external navigation
html
<hy-button
href="https://example.com"
target="_blank"
rel="noopener noreferrer"
variant="success"
emphasis="outlined"
>
Visit External Site
</hy-button>Loading state with custom announcement
html
<hy-button loading loading-text="Saving your changes...">
<span slot="prefix">💾</span>
Save Document
</hy-button>Dropdown trigger with accessibility features
html
<hy-button
caret
variant="neutral"
emphasis="plain"
aria-haspopup="menu"
aria-expanded="false"
aria-controls="dropdown-menu"
>
More Options
<span slot="suffix">⚙️</span>
</hy-button>Form submission with validation
html
<hy-button type="submit" variant="brand" aria-describedby="submit-help"> Submit Form </hy-button>
<div id="submit-help">Review your information before submitting</div>Advanced accessibility configuration
html
<hy-button
aria-label="Open navigation menu"
aria-expanded="false"
aria-haspopup="menu"
aria-describedby="nav-help"
@click="${handleMenuToggle}"
@escape="${handleMenuClose}"
>
☰
</hy-button>
<div id="nav-help">Use arrow keys to navigate menu items</div>CSS Parts customization
css
hy-button::part(base) {
border-radius: 12px;
font-weight: 600;
letter-spacing: 0.025em;
}Single-token emphasis (no compounds — see no-compounds rule)
html
<hy-button emphasis="solid">Solid (default)</hy-button>
<hy-button emphasis="soft">Soft</hy-button>
<hy-button emphasis="outlined">Outlined</hy-button>
<hy-button emphasis="tinted">Tinted</hy-button>
<hy-button emphasis="plain">Plain</hy-button>Event handling with proper typing
css
// Native click bubbles from the internal element — gated when disabled/loading.
button.addEventListener('click', (event) => {
console.log('Button clicked:', event);
});
button.addEventListener('escape', (event: CustomEvent) => {
const { originalEvent } = event.detail;
closeAssociatedPopup();
});Click gating: Native click events bubble normally. When disabled or loading, clicks are stopped (stopPropagation + preventDefault) on the host so external
## Events
- **escape** - Emitted when Escape key is pressed on buttons with `aria-haspopup`.
**Detail:** `{originalEvent: KeyboardEvent}` - Contains the original keyboard event.
**Usage:** Ideal for closing associated popups, menus, or dialogs.
**Condition:** Only fired when `aria-haspopup` attribute is present.
## Slots
- **default** - Primary button content (text, inline elements).
**Usage:** Main button label; can contain text nodes and inline elements.
**Accessibility:** Content contributes to accessible name calculation.
- **prefix** - Content displayed before the main button text.
**Usage:** Icons, badges, or decorative elements that precede the label.
**Layout:** Positioned with appropriate spacing from main content.
- **suffix** - Content displayed after the main button text.
**Usage:** Icons, badges, or decorative elements that follow the label.
**Note:** Appears before the caret icon when both are present.
## Methods
### focus()
Sets focus on the underlying button or link element.
### setTabIndex()
Projects a tabindex onto the inner button — called by parent
roving-tabindex controllers so composite widgets (e.g. the
markdown-editor's format toolbar) can keep a single tab stop.
### blur()
Removes focus from the underlying button or link element.
### setDefaultSlotText()
Programmatically updates the button's default slot text content.
**Implementation Details:**
- Removes existing default slot content (text nodes and unslotted elements)
- Preserves slotted content (elements with `slot` attribute)
- Adds new text as a text node to maintain semantic structure
- Triggers reactive update cycle for proper re-rendering
**Use Cases:**
- Dynamic button text based on application state
- Internationalization and language switching
- Context-sensitive button labeling
- Programmatic button configuration
**Limitations:**
- Only affects text content, not HTML structure
- Overrides any existing default slot content
- Does not affect slotted elements (prefix/suffix)
**Alternative Approaches:**
- For complex content, prefer declarative slot usage
- For reactive text, consider binding to slot content directly
- For HTML content, manipulate slot children instead
**Parameters:**
- `text` - The new text content for the button's main label
```javascript
// Dynamic button text
const button = document.querySelector('hy-button');
button.setDefaultSlotText('Save Changes');
// Internationalization
button.setDefaultSlotText(i18n.t('buttons.save'));
// State-based labeling
button.setDefaultSlotText(isEditing ? 'Save' : 'Edit');render()
Main render method producing the complete button component.
Rendering Strategy:
- Link Mode (
hrefprovided + not disabled/loading): Renders as<a>element - Button Mode (default): Renders as
<button>element
Element Selection Logic:
- Links provide natural browser navigation and context menu support
- Disabled/loading links fall back to button mode to prevent navigation
- Both modes maintain consistent styling and behavior through CSS classes
Accessibility Implementation:
- Links include
role="button"for semantic consistency - Both modes receive identical ARIA attributes and keyboard handling
- Focus management remains consistent across rendering modes
- Screen readers receive appropriate element semantics
Attribute Management:
- Dynamic attributes use
ifDefined()directive for clean output - Boolean attributes are properly handled for both element types
- ARIA attributes are consistently applied across both modes
Event Binding:
- Both modes use identical event handlers for consistent behavior
- Link mode maintains keyboard accessibility through custom handlers
- Form integration works naturally with button elements