@dreamworld/dw-icon-button
v3.2.11
Published
## Why don't we use `mwc-icon-button`? - We created `dw-icon` to have extended icon set, sepecific to the app. But, those icons we can't be used with `mwc-icon-button`. - We found issue with `mwc-icon-button` that it can't be resized easily.
Keywords
Readme
dw-icon-button
A LitElement-based Web Component that renders a circular icon button with Material Design ripple effects, hover/focus state overlays, tooltip support, and extended icon set via dw-icon.
Why not mwc-icon-button?
- Custom icon sets registered with
dw-iconcannot be used withmwc-icon-button. mwc-icon-buttoncannot be resized easily.
1. User Guide
Installation & Setup
yarn add @dreamworld/dw-icon-buttonimport '@dreamworld/dw-icon-button/dw-icon-button.js';The component renders icons using icon font classes. Include the relevant font stylesheets in your HTML:
<!-- Material Icons (filled) -->
<link href="https://fonts.googleapis.com/css?family=Material+Icons&display=block" rel="stylesheet" />
<!-- Material Icons Outlined (for iconFont="OUTLINED") -->
<link href="https://fonts.googleapis.com/css?family=Material+Icons+Outlined&display=block" rel="stylesheet" />
<!-- Material Symbols Outlined (for symbol attribute) -->
<link href="https://fonts.googleapis.com/css2?family=Material+Symbols+Outlined" rel="stylesheet" />Basic Usage
<!-- Default icon button -->
<dw-icon-button icon="perm_media"></dw-icon-button>
<!-- Disabled -->
<dw-icon-button icon="perm_media" disabled></dw-icon-button>
<!-- Primary color -->
<dw-icon-button icon="perm_media" primary></dw-icon-button>
<!-- Secondary color -->
<dw-icon-button icon="perm_media" secondary></dw-icon-button>API Reference
Properties
| Property | Attribute | Type | Default | Reflected | Description |
|---|---|---|---|---|---|
| icon | icon | String | — | No | Name of the icon to render via dw-icon |
| disabled | disabled | Boolean | false | Yes | Disables the button. Sets tabindex="-1" and stops click event propagation |
| iconSize | icon-size | Number | undefined | No | Icon size in pixels. When unset, dw-icon uses its own default (24px) |
| buttonSize | button-size | Number | undefined | No | Explicit button width/height in pixels. Padding is computed as (buttonSize - iconSize) / 2. When unset, the button fills its parent |
| primary | primary | Boolean | false | Yes | Renders the icon and ripple using --mdc-theme-primary |
| secondary | secondary | Boolean | false | Yes | Renders the icon and ripple using --mdc-theme-secondary |
| title | title | String | — | No | Tooltip text. Shown on mouseenter when the button is not disabled and not on a touch device |
| disabledTitle | disabled-title | String | — | No | Tooltip text. Shown on mouseenter only when disabled is true |
| tipExtraOptions | — | Object | {} | No | Additional options object merged into dw-tooltip's extraOptions for the title tooltip |
| tipOffset | — | Array | [0, 8] | No | Displacement of the tooltip from the button as [skidding, distance]. Applied to both the title and disabledTitle tooltips |
| iconFont | icon-font | String | — | Yes | Icon font variant. Accepted values: FILLED, OUTLINED |
| symbol | symbol | Boolean | false | Yes | When true, renders using the Material Symbols font instead of Material Icons |
| placement | placement | String | 'top' | No | Tooltip position relative to the button. Accepts: top, bottom, left, right, with optional -start or -end suffix (e.g. top-start, left-end) |
Tooltip note:
titleanddisabledTitleare independent. Set both to show a tooltip in both enabled and disabled states.
Methods
| Method | Signature | Description |
|---|---|---|
| focus | focus(): void | Programmatically focuses the internal <button> element |
Events
No custom events are dispatched by this component. Standard DOM events (click, mousedown, mouseup, etc.) propagate from the host element normally, except when disabled is true — in that case, click events on the internal wrapper are stopped via stopImmediatePropagation.
Slots
Not supported. The component renders dw-icon internally within shadow DOM. Light DOM (slot) content is not accepted.
CSS Custom Properties
| Property | Default | Description |
|---|---|---|
| --dw-icon-color | (from dw-icon) | Icon color in the default state |
| --dw-icon-color-active | rgba(0, 0, 0, 0.87) | Icon color when the button is focused |
| --dw-icon-color-disabled | (from dw-icon) | Icon color when disabled is true |
| --dw-icon-button-background | transparent | Background color of the button element |
| --dw-icon-button-padding | 12px | Button padding. Automatically overridden when buttonSize is set |
| --mdc-theme-primary | — | Applied to icon color and ripple when primary attribute is set |
| --mdc-theme-secondary | — | Applied to icon color and ripple when secondary attribute is set |
| --mdc-theme-on-surface | — | Color used for hover/focus state overlays and ripple |
/* Example: custom-colored icon button */
dw-icon-button {
--dw-icon-color: #6200ee;
--dw-icon-color-active: #3700b3;
--dw-icon-button-background: #f5f5f5;
}Sizing
To size the button container using CSS (without the buttonSize property), set width and height directly on the element:
dw-icon-button {
width: 48px;
height: 48px;
}To use buttonSize for self-contained sizing with automatic padding:
<!-- 48px button with 24px icon → padding = (48 - 24) / 2 = 12px -->
<dw-icon-button icon="more_vert" buttonSize="48" iconSize="24"></dw-icon-button>Advanced Usage
Tooltip on enabled button
<dw-icon-button
icon="more_vert"
.title=${"More options"}
.placement=${"bottom"}
></dw-icon-button>Tooltip on disabled button
<dw-icon-button
icon="more_vert"
disabled
.disabledTitle=${"This action is unavailable"}
.placement=${"bottom"}
></dw-icon-button>Tooltip in both states
<dw-icon-button
icon="more_vert"
.title=${"More options"}
.disabledTitle=${"This action is unavailable"}
.placement=${"top"}
></dw-icon-button>Custom tooltip delay via tipExtraOptions
<dw-icon-button
icon="info"
.title=${"Help"}
.tipExtraOptions=${{ delay: [200, 0] }}
></dw-icon-button>Custom tooltip offset via tipOffset
<dw-icon-button
icon="info"
.title=${"Help"}
.tipOffset=${[0, 12]}
></dw-icon-button>Material Symbols (outlined)
<dw-icon-button
icon="signal_wifi_statusbar_not_connected"
iconFont="OUTLINED"
symbol
iconSize="48"
></dw-icon-button>Custom SVG icons (registered via DwIcon.addIcons)
import { DwIcon } from '@dreamworld/dw-icon/dw-icon.js';
import { svg } from 'lit';
DwIcon.addIcons({
my_custom_icon: svg`<svg viewBox="0 0 24 24">...</svg>`,
});<dw-icon-button icon="my_custom_icon"></dw-icon-button>Programmatic focus
const btn = document.querySelector('dw-icon-button');
btn.focus();2. Developer Guide / Architecture
Architecture Overview
| Concern | Implementation |
|---|---|
| Base class | DwIconButton extends buttonFocus(LitElement) |
| Composition pattern | Mixin (buttonFocus from @dreamworld/pwa-helpers) layered over LitElement |
| DOM model | Shadow DOM only; no light DOM slots |
| Icon rendering | Delegates to <dw-icon> inside shadow DOM |
| Tooltip rendering | Conditionally renders <dw-tooltip> in shadow DOM; two independent instances managed for title and disabledTitle |
Ripple State Machine
The ripple effect is driven entirely by CSS class toggling:
- Entry (
mousedown/touchstart):requestAnimationFrameaddsripple-entryclass → CSS appliesscale-in-ripple(225ms) andfade-in-ripple(75ms) keyframe animations on::after. - A
Promise(waitForEntryAnimation) resolves after 225ms, ensuring exit does not begin before entry completes. - Exit (
mouseup/mouseleave/touchend): AwaitswaitForEntryAnimation, then addsripple-exit→ CSS appliesfade-out-ripple(250ms). After 250ms, both classes are removed.
Touch Device Handling
isTouchDevice() (from @dreamworld/web-util) is called:
- In the constructor to set
_touchDeviceproperty (reflected astouch-deviceattribute). - In
render()to suppress thetitletooltip on touch devices.
The touch-device attribute disables hover overlays via the CSS rule:
:host(:not([disabled]):not([touch-device])) button:hover::before { opacity: 0.04; }Focus Effect Suppression
To distinguish keyboard focus from pointer/touch focus:
pointerdownsets_noFocusEffect = true→ addsno-focus-effectattribute.blurresets_noFocusEffect = false→ removes the attribute.
The attribute disables the focus overlay:
:host([no-focus-effect]) button:focus::before { opacity: 0; }This ensures the focus ring only appears for keyboard navigation, not after mouse or touch interaction.
Disabled Click Interception
When disabled is true, the internal _onTooltipContainerClick handler on the wrapper <div> calls e.stopImmediatePropagation(). This prevents click events from reaching parent elements even though the underlying <button> has tabindex="-1" (not disabled attribute), which is necessary to allow the disabledTitle tooltip to still receive pointer events.
Module Entry Point
The package exports a single ES module:
main: "dw-icon-button.js"
type: "module"The custom element dw-icon-button is registered via customElements.define at the end of dw-icon-button.js.
