Skip to content

Dashboard Widget

DashboardSince 0.4.0
import { DashboardWidget } from '@absoluteui/components/dashboard-widget';
import '@absoluteui/components/dashboard-widget/style.css';

Using Angular? See Dashboard Widget for Angular.

A titled dashboard panel (widget container, collapsible widget, dashboard tile). DashboardWidget enhances a host element in place: a header with a heading, an optional subtitle, and action buttons (custom icon actions, collapse, maximize, remove) is inserted as the host’s first child. The host’s other children are the body; they are never moved, so framework-owned content is safe. Collapse is a disclosure button. Maximize shows the widget in the top layer as a modal dialog. Remove emits a cancelable event; the application removes the widget or its Dashboard Grid item.

  • Panels inside a Dashboard Grid (pass data-aui-item on the host).
  • Titled content blocks on a dashboard that users collapse, expand to full screen, or remove.
  • Generic content cards without widget actions: use Card.
  • Dialogs opened from elsewhere on the page: use Dialog.
  • Page sections that should not be landmarks: the host becomes role=region unless it already has another role.

Enhance a panel: heading, collapse, and maximize

Section titled “Enhance a panel: heading, collapse, and maximize”
import { DashboardWidget, type DashboardWidgetToggleDetail } from '@absoluteui/components/dashboard-widget';
const host = document.createElement('section');
const body = document.createElement('p');
body.textContent = '1,204 open tickets';
host.append(body);
document.body.append(host);
const widget = DashboardWidget.create(host, { title: 'Support queue', subtitle: 'Updated hourly', headingLevel: 2 });
host.addEventListener('aui-toggle', (event) => {
const { collapsed } = (event as CustomEvent<DashboardWidgetToggleDetail>).detail;
host.dataset.collapsed = String(collapsed && widget.collapsed);
});

Custom actions and a removable widget inside a Dashboard Grid

Section titled “Custom actions and a removable widget inside a Dashboard Grid”
import { DashboardGrid } from '@absoluteui/components/dashboard-grid';
import { DashboardWidget, type DashboardWidgetActionDetail, type DashboardWidgetRemoveDetail } from '@absoluteui/components/dashboard-widget';
const gridHost = document.createElement('div');
const widgetHost = document.createElement('section');
widgetHost.dataset.auiItem = 'sales';
gridHost.append(widgetHost);
document.body.append(gridHost);
const grid = DashboardGrid.create(gridHost, { label: 'Overview', items: [{ id: 'sales', x: 0, y: 0, w: 6, h: 3 }], editable: true });
const widget = DashboardWidget.create(widgetHost, {
title: 'Sales',
removable: true,
busy: true,
actions: [{ id: 'refresh', label: 'Refresh sales', icon: 'refresh' }],
});
widgetHost.addEventListener('aui-action', (event) => {
const { id } = (event as CustomEvent<DashboardWidgetActionDetail>).detail;
if (id === 'refresh') widget.update({ busy: true });
});
widgetHost.addEventListener('aui-remove', (event) => {
const { id } = (event as CustomEvent<DashboardWidgetRemoveDetail>).detail;
if (id !== null) grid.removeItem(id);
});
import { configureAbsoluteUI } from '@absoluteui/core';
configureAbsoluteUI({ components: { dashboardWidget: { maximizable: false } } });
import { DashboardWidget } from '@absoluteui/components/dashboard-widget';
import '@absoluteui/components/dashboard-widget/style.css';
const host = document.querySelector<HTMLElement>('#tickets');
if (host) {
DashboardWidget.create(host, { title: 'Open tickets', headingLevel: 2, removable: true, actions: [{ id: 'refresh', label: 'Refresh tickets', icon: 'refresh' }] });
host.addEventListener('aui-remove', () => {
host.remove();
});
}
import { DashboardWidget } from '@absoluteui/components/dashboard-widget';
const host = document.querySelector<HTMLElement>('#sales');
if (host) DashboardWidget.create(host, { title: 'Sales', subtitle: 'Last 30 days', removable: true });
<section class="aui-widget [aui-widget--collapsed] [aui-widget--maximized]" role="region" aria-labelledby="{title id}" [aria-busy]> (consumer host)
<div class="aui-widget__header"> (inserted first)
<div class="aui-widget__titles">
<h3 class="aui-widget__heading"><span class="aui-widget__title" id>Title</span></h3>
<p class="aui-widget__subtitle"> (hidden when empty)
</div>
<div class="aui-widget__actions">
<button class="aui-widget__action aui-widget__action--custom" data-action="custom:{id}"> (actions)
<button class="aui-widget__action aui-widget__toggle" aria-expanded> (collapsible)
<button class="aui-widget__action aui-widget__maximize"> (maximizable)
<button class="aui-widget__action aui-widget__remove"> (removable)
</div>
</div>
...body: the host's own children, untouched...
</section>
DashboardWidget.create(host: HTMLElement, options: DashboardWidgetOptions): DashboardWidget

Global configuration key: dashboardWidget (see Configuration).

Option Type Default Description
title string Heading text; names the region and the action buttons. Non-empty. Required.
headingLevel 2 | 3 | 4 | 5 | 6 3 Heading element level; changing it with update() throws. Set at creation only.
subtitle string Supporting text under the heading (hidden when empty).
actions readonly DashboardWidgetAction[] Extra header icon buttons { id, label, icon } (unique ids, non-empty labels); activation emits aui-action.
removable boolean false Show a remove button that emits the cancelable aui-remove.
collapsed boolean false Initial and controlled collapsed state (update() sets it without an event).
busy boolean false Set aria-busy=true on the host while content loads.
collapsible boolean true Show the collapse/expand button. Globally configurable.
maximizable boolean true Show the maximize/restore button. Globally configurable.
locale string "en-US" Locale of the action button names. Globally configurable. Inherited from common configuration.
direction 'ltr' | 'rtl' "ltr" Resolved with the other options; the layout itself follows the document direction (logical CSS). Globally configurable. Inherited from common configuration.
Property Type Description
host HTMLElement The enhanced host.
header HTMLDivElement The inserted header element (first child of the host).
collapsed boolean Collapsed state.
maximized boolean Maximized (modal) state.
Method Description
update(options: Partial<DashboardWidgetOptions>): void Merge, validate, and re-render the header (collapsed applies without an event). Throws when headingLevel changes.
setCollapsed(collapsed: boolean): void Collapse or expand without an event.
setMaximized(maximized: boolean): void Maximize into a modal dialog in the top layer, or restore; emits aui-maximize when the state changes.
destroy(): void Restore if maximized; remove the header, classes, aria-busy, aria-labelledby, and the region role it set. Body children stay.
Event Detail Bubbles Cancelable Description
aui-toggle DashboardWidgetToggleDetail { collapsed: boolean } No No The user activated the collapse/expand button.
aui-maximize DashboardWidgetMaximizeDetail { maximized: boolean } No No The widget was maximized or restored (button, Escape, or setMaximized).
aui-action DashboardWidgetActionDetail { id: string } Yes No The user activated a custom header action.
aui-remove DashboardWidgetRemoveDetail { id: string | null } Yes Yes The user activated the remove button; id is the host’s data-aui-item (its Dashboard Grid item) or null. The widget takes no default action: remove the element or call grid.removeItem(id).
Area Description
body (host children) Every host child other than the inserted header is the body. It stays where it is (framework-owned content is never moved) and is hidden with display: none while collapsed.

All rules live in the aui.components cascade layer, so application styles override them without specificity battles.

Class Purpose
aui-widget Host panel.
aui-widget--collapsed | --maximized State modifiers on the host.
aui-widget__header | __titles | __heading | __title | __subtitle Header, title group, heading element, title text (labels the region/dialog), subtitle.
aui-widget__actions Action button group.
aui-widget__action Every action button; modifiers aui-widget__action–custom, aui-widget__toggle, aui-widget__maximize, aui-widget__remove. data-action is custom:<id>, toggle, maximize, or remove.
Slot Default icon
collapse / expand chevron-down
maximize maximize
restore minimize
remove trash
custom actions (DashboardWidgetAction.icon) none

Tokens this stylesheet reads (verified in CI). Override them in a theme or with the Theme Designer.

--aui-border-width | --aui-color-backdrop | --aui-color-border | --aui-color-focus-ring | --aui-color-hover | --aui-color-surface | --aui-color-text | --aui-color-text-muted | --aui-duration-fast | --aui-easing-standard | --aui-focus-ring-offset | --aui-focus-ring-width | --aui-font-family-sans | --aui-font-size-md | --aui-font-size-sm | --aui-font-weight-semibold | --aui-line-height-tight | --aui-radius-lg | --aui-radius-sm | --aui-shadow-lg | --aui-shadow-sm | --aui-space-1 | --aui-space-2 | --aui-space-3 | --aui-space-6 | --aui-space-inset | --aui-touch-target-min

interface DashboardWidgetAction { id: string; label: string; icon: IconRef }
interface DashboardWidgetToggleDetail { collapsed: boolean }
interface DashboardWidgetMaximizeDetail { maximized: boolean }
interface DashboardWidgetActionDetail { id: string }
interface DashboardWidgetRemoveDetail { id: string | null }

Pattern: Labelled region with a heading and header buttons; collapse follows the WAI-ARIA APG Disclosure pattern; maximize follows the APG modal Dialog pattern (top layer through the Popover API).

Key Context Action
Tab / Shift+Tab Any Moves through the header buttons and body content in DOM order.
Enter / Space Collapse button Toggles the body (aria-expanded); emits aui-toggle. Focus stays on the button.
Enter / Space Maximize button Maximizes into a modal dialog; focus moves to the restore button and is trapped inside the widget; page scroll is locked.
Escape Maximized Restores the widget (topmost overlay first); focus returns to the maximize button.
Tab / Shift+Tab Maximized Cycles within the widget (focus trap).
Enter / Space Remove or custom action button Emits aui-remove (cancelable) or aui-action.
Element Role
host region (set unless the host already has another role); dialog while maximized
heading heading (native h2-h6)
action buttons button (native)
icons none (decorative)
Attribute Element Value
aria-labelledby host id of the title text (region and dialog name)
aria-modal host “true” while maximized (role=dialog)
aria-busy host “true” while busy
aria-expanded collapse button “true” when expanded, “false” when collapsed
aria-label and title action buttons Localized ‘Collapse {title}’ / ‘Expand {title}’, ‘Maximize {title}’ / ‘Restore {title}’, ‘Remove {title}’, or the custom action label

Header buttons show a focus-ring-width outline in color-focus-ring. Re-rendering keeps focus on the same action. Maximize moves focus to the restore button; restore returns it to the maximize button.

Criterion Level Note
1.3.1 Info and Relationships A
2.1.1 Keyboard A
2.1.2 No Keyboard Trap A
2.4.3 Focus Order A
2.4.6 Headings and Labels AA
2.4.7 Focus Visible AA
2.5.8 Target Size (Minimum) AA
4.1.2 Name, Role, Value A

Automated test results: Accessibility and compliance.

  • The region is named by the visible title; action names include the title, so several widgets on one page stay distinguishable.
  • Collapse hides the body with display: none, so hidden content is removed from the accessibility tree and tab order.
  • Maximized, outside pointer presses do not restore the widget; Escape and the restore button do.
  • The collapse button is not shown while maximized.
  • Manual screen-reader verification (NVDA, JAWS, VoiceOver) is pending.
  • The collapse button has no aria-controls (the body is every host child except the header).
  • Many widgets on a page create many region landmarks; give hosts another role (for example role=group) when that is too noisy.

expanded, collapsed, maximized (modal), busy, action hover, action focus-visible.

From Trigger To
expanded collapse button collapsed; aui-toggle { collapsed: true }
collapsed expand button expanded; aui-toggle { collapsed: false }
any maximize button / setMaximized(true) maximized: role="dialog", aria-modal, top layer, focus trapped, scroll locked; aui-maximize
maximized Escape, restore button, setMaximized(false) previous role restored; focus to the maximize button; aui-maximize
any update({ collapsed }), setCollapsed() collapsed state set, no event
  • Action names include the title, so several widgets remain distinguishable.
  • Collapsed bodies are display: none: removed from the accessibility tree and tab order.
  • Maximized, focus is trapped and outside presses do not restore; Escape and the restore button do (WCAG 2.1.2: no trap without an exit).
  • The collapse button is hidden while maximized. Manual screen-reader verification is pending.

Fills its container and scrolls its content (overflow: auto). Actions wrap. Maximized, the widget is fixed over the viewport inset by space-6. Action buttons are at least touch-target-min.

Logical properties throughout; the collapsed chevron rotation is mirrored.

Role tokens only; maximized uses shadow-lg and a color-backdrop backdrop. Forced colors: CanvasText border. The chevron transition is removed under reduced motion.

configureAbsoluteUI({ components: { dashboardWidget: { maximizable: false } } });

Global options: collapsible, maximizable, plus inherited locale and direction. Changes apply to existing widgets immediately.

Instance options override global and theme defaults. All options are dynamic except headingLevel.

Built-in actions use chevron-down (collapse), maximize / minimize (maximize / restore), and trash (remove). Custom actions take any IconRef.

Title, subtitle, and labels use textContent and attributes; the body is never read or re-parsed. CSP-safe.

No persistence, network access, or telemetry. Store collapsed state from aui-toggle if needed.

The header is rebuilt on every update() (a handful of buttons); the body is untouched. One configuration subscription per instance; maximize adds a focus trap, a scroll lock, and an overlay registration only while maximized. Not separately budgeted: covered by the Dashboard Grid budget (create with 50 items < 40 ms in headless Chromium, e2e performance test).

Dashboards of up to 50 widgets (the Dashboard Grid target).

  • A host with its own role (other than region) keeps it; aria-labelledby is still set.
  • Without HTMLElement.showPopover the maximized widget is fixed-positioned only, not in the top layer.
  • Re-rendering keeps focus on the same action button.
  • aui-remove does nothing by itself; preventDefault() only matters to other listeners.
Code Category Cause
DASHBOARD_WIDGET_OPTIONS configuration Empty title; headingLevel outside 2..6 or changed by update(); non-boolean flags; invalid or duplicate action ids or empty labels.
  • Maximize depends on the Popover API for the top layer.
  • No aria-controls on the collapse button.
  • headingLevel is init-only.
  • Manual screen-reader verification is pending.
  • HTML in the title or subtitle.
  • Non-modal full-screen mode.