HyIconButton
A compact, SSR-safe button component that displays only an icon, ideal for toolbars and space-constrained interfaces.
Key Features
- Dual mode: Works as both button and link (when href provided)
- Toggle button support with aria-pressed state management
- Five emphasis values: solid, soft, outlined, tinted, plain
- Two shapes: square (rounded corners) and round (circular)
- Full keyboard navigation support (Enter, Space, Escape)
- Comprehensive ARIA attributes for accessibility
- Custom events for interaction handling
- SSR-safe with proper hydration support
- High contrast and forced colors mode support
Accessibility
- Requires accessible label (label, aria-label, or aria-labelledby)
- Screen reader optimized with proper ARIA attributes
- Keyboard navigation with Enter, Space, and Escape keys
- Focus management with visible indicators
- Touch-friendly with proper target sizes
SSR Behavior
- Validation warnings only fire client-side
- Public methods (click, focus, blur) are safe to call at any time
- Properly hydrates with all attributes and state preserved
Examples
Basic Usage
<hy-icon-button name="settings" label="Open settings"></hy-icon-button>Soft emphasis
<hy-icon-button name="settings" label="Open settings" emphasis="soft"> </hy-icon-button>Outlined emphasis
<hy-icon-button name="settings" label="Open settings" emphasis="outlined"> </hy-icon-button>Round shape with soft emphasis
<hy-icon-button name="settings" label="Open settings" shape="round" emphasis="soft">
</hy-icon-button>Link Mode
<hy-icon-button
name="download"
href="/file.pdf"
download="document.pdf"
label="Download PDF"
></hy-icon-button>External Link with Security
<hy-icon-button
name="external-link"
href="https://example.com"
target="_blank"
label="Open external link"
>
</hy-icon-button>
<!-- Automatically adds rel="noreferrer noopener" for security -->Disabled State
<hy-icon-button name="delete" disabled label="Delete item (disabled)"> </hy-icon-button>Soft-disabled (tooltip explaining why stays reachable)
<hy-tooltip>
<hy-icon-button slot="trigger" name="delete" soft-disabled label="Remove font"> </hy-icon-button>
In use by a category — reassign it first.
</hy-tooltip>Toggle Button
<hy-icon-button
name="favorite-border"
label="Add to favorites"
pressed="false"
aria-describedby="fav-help"
>
</hy-icon-button>
<div id="fav-help">Click to toggle favorite status</div>Dropdown Trigger
<hy-icon-button
name="chevron-down"
label="Open menu"
aria-haspopup="menu"
aria-expanded="false"
aria-controls="dropdown-menu"
>
</hy-icon-button>
<div id="dropdown-menu" role="menu" hidden>Menu items...</div>Event Handling
const button = document.querySelector('hy-icon-button'); // Native click bubbles from the internal
<button>
— no custom event needed. // When disabled, click is gated (stopPropagation + preventDefault on
the host). button.addEventListener('click', (event) => { console.log('Button clicked:', event);
}); button.addEventListener('press-change', (event) => { console.log('Toggle state:',
event.detail.pressed); }); button.addEventListener('focus', (event) => { console.log('Button
focused'); }); button.addEventListener('escape', (event) => { console.log('Escape pressed - close
menu'); });
</button>Programmatic Control
const button = document.querySelector('hy-icon-button');
// Programmatically click
button.click();
// Programmatically focus
button.focus();
// Programmatically blur
button.blur();Toolbar with Mixed Appearances
<div class="toolbar" role="toolbar" aria-label="Document actions">
<hy-icon-button name="save" label="Save document"></hy-icon-button>
<hy-icon-button name="content-copy" label="Copy" emphasis="soft"></hy-icon-button>
<hy-icon-button name="share" label="Share" emphasis="outlined"></hy-icon-button>
</div>Click Gating
Native click from the internal <button> is composed: true and bubbles out of the shadow root naturally. When the component is disabled, a host-level listener calls stopPropagation() + preventDefault() so no click escapes.
Custom Events
press-change- Fired when toggle button's pressed state changesdetail.pressed: Boolean indicating new pressed state (true/false)detail.originalEvent: The original event that triggered the togglefocus- Fired when button receives focusdetail.originalEvent: The original FocusEventblur- Fired when button loses focusdetail.originalEvent: The original FocusEventescape- Fired when Escape key is pressed while focuseddetail.originalEvent: The original KeyboardEventUseful for closing associated menus or dialogs
Keyboard Support
Tab- Moves focus to/from the buttonShift + Tab- Moves focus in reverse orderEnter- Activates the button (works for both buttons and links)Space- Activates the button (buttons only, not links per HTML spec)Escape- Blurs the button and firesescapeevent
CSS Parts
### Styling with CSS Parts
```css
hy-icon-button::part(base) {
border-radius: 8px;
}
hy-icon-button::part(base):hover {
transform: scale(1.05);
}Methods
connectedCallback()
Called when the element is added to the DOM. Validates accessibility requirements (client-side only for SSR safety).
click()
Simulates a click on the icon button.
Behavior:
- Respects disabled state - disabled buttons won't trigger clicks
- For toggle buttons, toggles the pressed state
- Native
clickbubbles naturally; optionally firespress-changefor toggles - Safe to call during SSR or before first render
const iconButton = document.querySelector('hy-icon-button');
// Trigger click programmatically
iconButton.click();
// Listen for native click
iconButton.addEventListener('click', (e) => {
console.log('Button clicked!', e);
});const toggleButton = document.querySelector('hy-icon-button[pressed]');
toggleButton.click(); // Toggles pressed state
toggleButton.addEventListener('press-change', (e) => {
console.log('New state:', e.detail.pressed);
});focus()
Sets focus on the icon button.
Behavior:
- Shows focus ring for keyboard navigation visibility
- Makes button available for keyboard interaction
- Fires
focusevent when focused - Safe to call during SSR or before first render
Parameters:
options- Optional focus optionsoptions.preventScroll- If true, prevents scrolling to the focused element
const iconButton = document.querySelector('hy-icon-button');
// Focus the button
iconButton.focus();
// Listen for focus event
iconButton.addEventListener('focus', () => {
console.log('Button focused');
});const iconButton = document.querySelector('hy-icon-button');
// Focus without scrolling viewport
iconButton.focus({ preventScroll: true });function openDialog() {
dialog.showModal();
// Focus first button in dialog
dialog.querySelector('hy-icon-button').focus();
}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 icon button.
Behavior:
- Hides the focus ring
- Removes button from keyboard navigation flow
- Fires
blurevent when blurred - Safe to call during SSR or before first render
const iconButton = document.querySelector('hy-icon-button');
// Remove focus from button
iconButton.blur();
// Listen for blur event
iconButton.addEventListener('blur', () => {
console.log('Button lost focus');
});const menuButton = document.querySelector('hy-icon-button[aria-haspopup="menu"]');
menuButton.addEventListener('escape', () => {
closeMenu();
menuButton.blur(); // Remove focus after closing
});render()
Renders the icon button as either a button or link element. Handles all attributes, ARIA properties, and event listeners.