# REALM UI components

<!-- Generated by scripts/build-skill.mjs from packages/ui/components and apps/site/content/components. Do not edit. -->

Every `ui-*` component in `@realmorg/ui` 2.0.0-alpha.2: its file, tags, a usage snippet and its API. This file is long. Find the component in the index, then read only its lines (or search for its heading, such as `## Dialog`).

Load the theme once per page, then either the whole bundle or one file per component. Each file imports the files it depends on, and every file is fetched once.

```html
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@realmorg/ui@alpha/dist/theme.css">

<!-- everything -->
<import-element from="https://cdn.jsdelivr.net/npm/@realmorg/ui@alpha/dist/ui.html"></import-element>

<!-- or only what the page uses -->
<import-element from="https://cdn.jsdelivr.net/npm/@realmorg/ui@alpha/components/button.html"></import-element>
<import-element from="https://cdn.jsdelivr.net/npm/@realmorg/ui@alpha/components/dialog.html"></import-element>
```

Conventions shared by every component:

- Attributes are typed props. Variants and sizes are attributes: `<ui-button variant="outline" size="sm">`.
- Compound parts place themselves. `<ui-dialog-title>` goes inside `<ui-dialog-header>` with no `slot` attribute.
- Form controls are form-associated: they submit with `name`, reset, validate and follow `<fieldset disabled>`.
- Events use native names where one fits (`change`, `input`, `toggle`, `select`, `close`, `cancel`), so `onchange` works anywhere.
- Style from outside with `::part(name)`, the theme tokens and states such as `ui-button:state(attr-variant-is-ghost)`. Do not put `style="…"` on `ui-*` elements.

## Index

### Forms

| Component | Lines | File | Tags |
| --- | --- | --- | --- |
| [Button](#button) | 120-181 | `button.html` | `ui-button` |
| [Button Group](#button-group) | 183-238 | `button-group.html` | `ui-button-group`, `ui-button-group-text`, `ui-button-group-separator` |
| [Calendar](#calendar) | 240-293 | `calendar.html` | `ui-calendar` |
| [Checkbox](#checkbox) | 295-360 | `checkbox.html` | `ui-checkbox` |
| [Combobox](#combobox) | 362-435 | `combobox.html` | `ui-combobox`, `ui-combobox-empty` |
| [Field](#field) | 437-499 | `field.html` | `ui-field`, `ui-field-content`, `ui-field-description`, `ui-field-group`, `ui-field-set`, `ui-field-legend` |
| [Input](#input) | 501-563 | `input.html` | `ui-input` |
| [Input Group](#input-group) | 565-634 | `input-group.html` | `ui-input-group`, `ui-input-group-addon` |
| [Input OTP](#input-otp) | 636-689 | `input-otp.html` | `ui-input-otp` |
| [Label](#label) | 691-732 | `label.html` | `ui-label` |
| [Radio Group](#radio-group) | 734-797 | `radio-group.html` | `ui-radio-group` |
| [Select](#select) | 799-877 | `select.html` | `ui-select` |
| [Slider](#slider) | 879-933 | `slider.html` | `ui-slider` |
| [Switch](#switch) | 935-996 | `switch.html` | `ui-switch` |
| [Textarea](#textarea) | 998-1051 | `textarea.html` | `ui-textarea` |
| [Toggle](#toggle) | 1053-1111 | `toggle.html` | `ui-toggle` |
| [Toggle Group](#toggle-group) | 1113-1188 | `toggle-group.html` | `ui-toggle-group` |

### Display

| Component | Lines | File | Tags |
| --- | --- | --- | --- |
| [Alert](#alert) | 1190-1224 | `alert.html` | `ui-alert`, `ui-alert-title`, `ui-alert-description` |
| [Avatar](#avatar) | 1226-1280 | `avatar.html` | `ui-avatar`, `ui-avatar-image`, `ui-avatar-fallback`, `ui-avatar-group`, `ui-avatar-group-count` |
| [Badge](#badge) | 1282-1315 | `badge.html` | `ui-badge` |
| [Card](#card) | 1317-1384 | `card.html` | `ui-card`, `ui-card-header`, `ui-card-title`, `ui-card-description`, `ui-card-action`, `ui-card-content`, `ui-card-footer` |
| [Chart](#chart) | 1386-1431 | `chart.html` | `ui-chart` |
| [Empty](#empty) | 1433-1496 | `empty.html` | `ui-empty`, `ui-empty-header`, `ui-empty-media`, `ui-empty-title`, `ui-empty-description`, `ui-empty-content` |
| [Item](#item) | 1498-1552 | `item.html` | `ui-item`, `ui-item-group`, `ui-item-media`, `ui-item-content`, `ui-item-title`, `ui-item-description`, `ui-item-actions`, `ui-item-header`, `ui-item-footer` |
| [Kbd](#kbd) | 1554-1592 | `kbd.html` | `ui-kbd`, `ui-kbd-group` |
| [Progress](#progress) | 1594-1626 | `progress.html` | `ui-progress` |
| [Separator](#separator) | 1628-1668 | `separator.html` | `ui-separator` |
| [Skeleton](#skeleton) | 1670-1705 | `skeleton.html` | `ui-skeleton` |
| [Spinner](#spinner) | 1707-1738 | `spinner.html` | `ui-spinner` |
| [Table](#table) | 1740-1795 | `table.html` | `ui-table` |

### Overlays

| Component | Lines | File | Tags |
| --- | --- | --- | --- |
| [Alert Dialog](#alert-dialog) | 1797-1893 | `alert-dialog.html` | `ui-alert-dialog`, `ui-alert-dialog-trigger`, `ui-alert-dialog-header`, `ui-alert-dialog-title`, `ui-alert-dialog-description`, `ui-alert-dialog-footer`, `ui-alert-dialog-action`, `ui-alert-dialog-cancel` |
| [Command](#command) | 1895-1964 | `command.html` | `ui-command`, `ui-command-empty` |
| [Context Menu](#context-menu) | 1966-2023 | `context-menu.html` | `ui-context-menu`, `ui-context-menu-trigger`, `ui-context-menu-content` |
| [Dialog](#dialog) | 2025-2128 | `dialog.html` | `ui-dialog`, `ui-dialog-trigger`, `ui-dialog-close`, `ui-dialog-header`, `ui-dialog-title`, `ui-dialog-description`, `ui-dialog-footer` |
| [Drawer](#drawer) | 2130-2213 | `drawer.html` | `ui-drawer`, `ui-drawer-trigger`, `ui-drawer-close`, `ui-drawer-header`, `ui-drawer-title`, `ui-drawer-description`, `ui-drawer-footer` |
| [Dropdown Menu](#dropdown-menu) | 2215-2315 | `dropdown-menu.html` | `ui-dropdown-menu`, `ui-dropdown-menu-trigger`, `ui-dropdown-menu-content`, `ui-menu-item`, `ui-menu-checkbox-item`, `ui-menu-radio-group`, `ui-menu-radio-item`, `ui-menu-label`, `ui-menu-group`, `ui-menu-separator`, `ui-menu-shortcut` |
| [Hover Card](#hover-card) | 2317-2373 | `hover-card.html` | `ui-hover-card`, `ui-hover-card-trigger`, `ui-hover-card-content` |
| [Popover](#popover) | 2375-2453 | `popover.html` | `ui-popover`, `ui-popover-trigger`, `ui-popover-content`, `ui-popover-header`, `ui-popover-title`, `ui-popover-description` |
| [Sheet](#sheet) | 2455-2546 | `sheet.html` | `ui-sheet`, `ui-sheet-trigger`, `ui-sheet-close`, `ui-sheet-header`, `ui-sheet-title`, `ui-sheet-description`, `ui-sheet-footer` |
| [Toaster](#toaster) | 2548-2607 | `toaster.html` | `ui-toaster` |
| [Tooltip](#tooltip) | 2609-2658 | `tooltip.html` | `ui-tooltip`, `ui-tooltip-trigger`, `ui-tooltip-content` |

### Navigation

| Component | Lines | File | Tags |
| --- | --- | --- | --- |
| [Accordion](#accordion) | 2660-2728 | `accordion.html` | `ui-accordion`, `ui-accordion-item`, `ui-accordion-trigger`, `ui-accordion-content` |
| [Breadcrumb](#breadcrumb) | 2730-2795 | `breadcrumb.html` | `ui-breadcrumb`, `ui-breadcrumb-list`, `ui-breadcrumb-item`, `ui-breadcrumb-link`, `ui-breadcrumb-page`, `ui-breadcrumb-separator`, `ui-breadcrumb-ellipsis` |
| [Collapsible](#collapsible) | 2797-2852 | `collapsible.html` | `ui-collapsible`, `ui-collapsible-trigger`, `ui-collapsible-content` |
| [Menubar](#menubar) | 2854-2919 | `menubar.html` | `ui-menubar`, `ui-menubar-menu`, `ui-menubar-trigger`, `ui-menubar-content` |
| [Navigation Menu](#navigation-menu) | 2921-3006 | `navigation-menu.html` | `ui-navigation-menu`, `ui-navigation-menu-list`, `ui-navigation-menu-item`, `ui-navigation-menu-trigger`, `ui-navigation-menu-content`, `ui-navigation-menu-link` |
| [Pagination](#pagination) | 3008-3046 | `pagination.html` | `ui-pagination` |
| [Sidebar](#sidebar) | 3048-3253 | `sidebar.html` | `ui-sidebar-layout`, `ui-sidebar`, `ui-sidebar-header`, `ui-sidebar-footer`, `ui-sidebar-content`, `ui-sidebar-group`, `ui-sidebar-group-label`, `ui-sidebar-menu`, `ui-sidebar-menu-item`, `ui-sidebar-menu-button`, `ui-sidebar-menu-badge`, `ui-sidebar-trigger`, `ui-sidebar-inset` |
| [Tabs](#tabs) | 3255-3350 | `tabs.html` | `ui-tabs`, `ui-tabs-list`, `ui-tabs-trigger`, `ui-tabs-content` |

### Layout

| Component | Lines | File | Tags |
| --- | --- | --- | --- |
| [Aspect Ratio](#aspect-ratio) | 3352-3387 | `aspect-ratio.html` | `ui-aspect-ratio` |
| [Carousel](#carousel) | 3389-3481 | `carousel.html` | `ui-carousel`, `ui-carousel-content`, `ui-carousel-item`, `ui-carousel-previous`, `ui-carousel-next` |
| [Direction](#direction) | 3483-3528 | `direction.html` | `ui-direction` |
| [Resizable](#resizable) | 3530-3600 | `resizable.html` | `ui-resizable`, `ui-resizable-panel`, `ui-resizable-handle` |
| [Scroll Area](#scroll-area) | 3602-3664 | `scroll-area.html` | `ui-scroll-area` |

### Chat and content

| Component | Lines | File | Tags |
| --- | --- | --- | --- |
| [Attachment](#attachment) | 3666-3709 | `attachment.html` | `ui-attachment`, `ui-attachment-media`, `ui-attachment-actions` |
| [Bubble](#bubble) | 3711-3753 | `bubble.html` | `ui-bubble`, `ui-bubble-reactions` |
| [Code Block](#code-block) | 3755-3787 | `code-block.html` | `ui-code-block` |
| [Marker](#marker) | 3789-3824 | `marker.html` | `ui-marker` |
| [Message](#message) | 3826-3879 | `message.html` | `ui-message`, `ui-message-avatar`, `ui-message-header`, `ui-message-footer` |
| [Message Scroller](#message-scroller) | 3881-3976 | `message-scroller.html` | `ui-message-scroller` |

<a id="button"></a>

## Button

A form-associated button with variants and sizes that can submit or reset a form, open popovers and invoke commands on dialogs. [Docs and demos](https://realm.js.org/components/button/).

- File: `button.html`, import with `<import-element from="https://cdn.jsdelivr.net/npm/@realmorg/ui@alpha/components/button.html"></import-element>`
- Tags: `ui-button`

```html runnable
<import-element from="https://cdn.jsdelivr.net/npm/@realmorg/ui@alpha/components/button.html"></import-element>

<ui-button>Button</ui-button>
```

### Attributes

| Attribute | Type | Default | Description |
| --- | --- | --- | --- |
| `variant` | `"default"` \| `"destructive"` \| `"outline"` \| `"secondary"` \| `"ghost"` \| `"link"` | `"default"` | Visual style. |
| `size` | `"default"` \| `"xs"` \| `"sm"` \| `"lg"` \| `"icon"` \| `"icon-xs"` \| `"icon-sm"` \| `"icon-lg"` | `"default"` | Height and padding. The `icon` sizes make a square button. |
| `type` | `"button"` \| `"submit"` \| `"reset"` | `"button"` | What the button does in its form. |
| `disabled` | boolean | `false` | Disables the button. Reflects to the attribute. Also true while the button is inside a disabled `<fieldset>`. |
| `aria-label` | string | | Accessible name, passed to the inner button. Use it for icon-only buttons. Property name: `label`. |
| `aria-description` | string | | Accessible description, passed to the inner button. `ui-tooltip` sets it from its content. Property name: `description`. |
| `popovertarget` | string | | Id of a popover element to toggle. |
| `popovertargetaction` | `"toggle"` \| `"show"` \| `"hide"` | | Action taken on the popover target. |
| `commandfor` | string | | Id of the element that receives `command`. |
| `command` | string | | Command to invoke, such as `show-modal`, `close`, `request-close`, `toggle-popover`, `show-popover`, `hide-popover` or a custom `--command`. |

### Events

`ui-button` dispatches no events of its own. Listen for `click` as on any element. When native invoker commands are
not supported, the button dispatches a cancelable `command` event on the `commandfor` target with `command` and
`source` properties. Calling `preventDefault()` stops the built-in action.

### Slots

| Slot | Description |
| --- | --- |
| (default) | The label and any icons. Slotted `<svg>` elements are sized to `calc(var(--spacing) * 4)`. |

### CSS parts

| Part | Description |
| --- | --- |
| `button` | The inner native `<button>`. |

### CSS states and custom properties

| Name | Description |
| --- | --- |
| `--ui-radius-start` | Overrides the start corner radii. `ui-button-group` sets it to `0` to join buttons. |
| `--ui-radius-end` | Overrides the end corner radii. |
| `:state(attr-variant-is-*)`, `:state(attr-size-is-*)` | Set for the current variant and size, for example `ui-button:state(attr-variant-is-outline)`. |

### Forms

`ui-button` is form-associated through `ElementInternals`, so it finds its form even across shadow roots.
`type="submit"` calls `requestSubmit()` on the form, which runs constraint validation and fires `submit`.
`type="reset"` calls `reset()`. The button itself adds no name or value to the submitted data, and the `submit`
event's `submitter` is `null`.

<a id="button-group"></a>

## Button Group

Joins adjacent buttons or toggles into one connected control, such as a toolbar or a split button. [Docs and demos](https://realm.js.org/components/button-group/).

- File: `button-group.html`, import with `<import-element from="https://cdn.jsdelivr.net/npm/@realmorg/ui@alpha/components/button-group.html"></import-element>`
- Tags: `ui-button-group`, `ui-button-group-text`, `ui-button-group-separator`

```html runnable
<import-element from="https://cdn.jsdelivr.net/npm/@realmorg/ui@alpha/components/button-group.html"></import-element>
<import-element from="https://cdn.jsdelivr.net/npm/@realmorg/ui@alpha/components/button.html"></import-element>

<ui-button-group aria-label="Text alignment">
  <ui-button variant="outline">Left</ui-button>
  <ui-button variant="outline">Center</ui-button>
  <ui-button variant="outline">Right</ui-button>
</ui-button-group>
```

### Parts

| Element | Fills | Renders |
| --- | --- | --- |
| `ui-button-group` | | A `role="group"` row or column that joins its children's corners. |
| `ui-button-group-text` | the default slot of `ui-button-group` | A bordered, muted label joined like a button. |
| `ui-button-group-separator` | the default slot of `ui-button-group` | A one-pixel `role="separator"` line between two buttons. |

### Attributes

#### ui-button-group

| Attribute | Type | Default | Description |
| --- | --- | --- | --- |
| `orientation` | `"horizontal"` \| `"vertical"` | `"horizontal"` | Lays the children out in a row or a column. |

#### ui-button-group-separator

| Attribute | Type | Default | Description |
| --- | --- | --- | --- |
| `orientation` | `"vertical"` \| `"horizontal"` | `"vertical"` | Direction of the line. Use `"horizontal"` in a vertical group. Sets `aria-orientation`. |

### Slots

| Slot | Description |
| --- | --- |
| (default) | The grouped buttons. Every child except the first gets `--ui-radius-start: 0` and a `-1px` start margin so borders overlap. Every child except the last gets `--ui-radius-end: 0`. |

### CSS states and custom properties

| Name | Description |
| --- | --- |
| `:state(attr-orientation-is-vertical)` | Set when `orientation="vertical"`. |

The group sets `--ui-radius-start` and `--ui-radius-end` on its children. `ui-button`, `ui-toggle` and
`ui-button-group-text` read them. Other children can use them for their own corner radii.

<a id="calendar"></a>

## Calendar

A month grid date picker that submits an ISO date with its form, with min and max limits, locale formatting and keyboard navigation. [Docs and demos](https://realm.js.org/components/calendar/).

- File: `calendar.html`, import with `<import-element from="https://cdn.jsdelivr.net/npm/@realmorg/ui@alpha/components/calendar.html"></import-element>`
- Tags: `ui-calendar`
- Imports for you: `button.html`

```html runnable
<import-element from="https://cdn.jsdelivr.net/npm/@realmorg/ui@alpha/components/calendar.html"></import-element>

<ui-calendar></ui-calendar>
```

### Attributes

| Attribute | Type | Default | Description |
| --- | --- | --- | --- |
| `value` | string | `""` | Selected date as `YYYY-MM-DD`. Empty means no selection. |
| `month` | string | | Month shown first, as `YYYY-MM`. Used when the calendar is created. Defaults to the month of `value`, or the current month. |
| `min` | string | | Earliest selectable date, `YYYY-MM-DD`. |
| `max` | string | | Latest selectable date, `YYYY-MM-DD`. |
| `locale` | string | | Locale for the month title and weekday names. Defaults to the browser locale. |

### Properties

| Property | Type | Description |
| --- | --- | --- |
| `value` | string | The selected date. Setting it updates the selection and the submitted value. |

### Events

| Event | Detail | Description |
| --- | --- | --- |
| `change` | none | Fired after the user picks a day. |

### CSS parts

| Part | Description |
| --- | --- |
| `header` | The row with the previous and next month buttons and the month title. |
| `grid` | The `<table>` holding the weekday headings and days. |
| `day` | Each day `<button>`. It carries `data-date`, plus `data-outside` for days of the neighbouring months, `data-today` for today, `aria-selected="true"` for the selected day and `disabled` outside `min`/`max`. |

`::part(day)` accepts pseudo-classes such as `:hover` and `:disabled` but not attribute selectors, so the
selected, today and outside colours come from the theme tokens (`--primary`, `--accent`, `--muted-foreground`).

### Forms

`ui-calendar` is form-associated. With a `name`, the form submits `value` (`YYYY-MM-DD`), or nothing when no date is
selected. `form.reset()` restores the `value` attribute, and the browser's form state restore brings back a
selected date after navigation.

<a id="checkbox"></a>

## Checkbox

A form-associated checkbox with a built-in label, indeterminate state and native validation. [Docs and demos](https://realm.js.org/components/checkbox/).

- File: `checkbox.html`, import with `<import-element from="https://cdn.jsdelivr.net/npm/@realmorg/ui@alpha/components/checkbox.html"></import-element>`
- Tags: `ui-checkbox`

```html runnable
<import-element from="https://cdn.jsdelivr.net/npm/@realmorg/ui@alpha/components/checkbox.html"></import-element>

<ui-checkbox>Accept terms and conditions</ui-checkbox>
```

### Attributes

| Attribute | Type | Default | Description |
| --- | --- | --- | --- |
| `checked` | boolean | `false` | Whether the box is checked. The attribute is the initial and reset state. The property follows the user. |
| `value` | string | | Value submitted when checked. Without it the form submits `on`. |
| `indeterminate` | boolean | `false` | Shows the mixed state. Visual only. Cleared when the user toggles the box. |
| `disabled` | boolean | `false` | Disables the box. Also true inside a disabled `<fieldset>`. |
| `required` | boolean | `false` | The form is invalid until the box is checked. |
| `aria-label` | string | | Accessible name when there is no visible text. Property name: `label`. |

The `name` attribute is read by the form, as on any form-associated element.

### Properties

| Property | Type | Description |
| --- | --- | --- |
| `checked` | boolean | Current state. Updated when the user toggles the box. Setting it updates the box and the submitted value. |
| `value`, `indeterminate`, `disabled`, `required` | | Mirror the attributes above. |

### Events

| Event | Detail | Description |
| --- | --- | --- |
| `change` | none | Bubbles from the element after the user toggles the box. `checked` is already updated. |
| `input` | none | The native `input` event of the inner checkbox, which is composed and reaches the page. |

### Slots

| Slot | Description |
| --- | --- |
| (default) | The label text. It is rendered inside a native `<label>` for the inner checkbox. |

### CSS parts

| Part | Description |
| --- | --- |
| `control` | The inner `<input type="checkbox">`. |
| `label` | The `<label>` around the slotted text. |

### CSS states

| State | Description |
| --- | --- |
| `:state(attr-checked)` | Set while the box is checked, for example `ui-checkbox:state(attr-checked)`. |

### Forms

`ui-checkbox` is form-associated. When checked it submits `value`, or `on` without one. When unchecked it submits
nothing. Validity is mirrored from the inner checkbox, so `required` takes part in `checkValidity()` and
`reportValidity()`. `form.reset()` restores the `checked` attribute, and a disabled `<fieldset>` disables the box.

<a id="combobox"></a>

## Combobox

A searchable, form-associated select that filters native option children as you type and submits the chosen value. [Docs and demos](https://realm.js.org/components/combobox/).

- File: `combobox.html`, import with `<import-element from="https://cdn.jsdelivr.net/npm/@realmorg/ui@alpha/components/combobox.html"></import-element>`
- Tags: `ui-combobox`, `ui-combobox-empty`

```html runnable
<import-element from="https://cdn.jsdelivr.net/npm/@realmorg/ui@alpha/components/combobox.html"></import-element>

<ui-combobox aria-label="Framework" placeholder="Select a framework…" width="60">
  <option value="realm">REALM</option>
  <option value="lit">Lit</option>
  <option value="stencil">Stencil</option>
  <option value="fast">FAST</option>
  <option value="vanilla">Vanilla custom elements</option>
</ui-combobox>
```

### Parts

| Part | Slot | Renders |
| --- | --- | --- |
| `ui-combobox` | | The text input with a chevron and the popover list. Fills its container's width unless `width` is set. |
| `<option>` | (data) | One choice in the list, with a check mark when chosen. |
| `ui-combobox-empty` | `empty` | The text shown when nothing matches, in place of "No results found.". |

### Attributes

| Attribute | Type | Default | Description |
| --- | --- | --- | --- |
| `value` | string | `""` | The `value` of the chosen option. |
| `placeholder` | string | `"Select…"` | Placeholder of the text input. |
| `width` | number | | Fixed width in spacing units, where `n` means `calc(var(--spacing) * n)`. Without it the combobox fills its container. |
| `disabled` | boolean | `false` | Disables the input. Also true inside a disabled `<fieldset>`. |
| `required` | boolean | `false` | The form is invalid until an option is chosen. |
| `aria-label` | string | | Accessible name, passed to the inner input. Property name: `label`. |

### Properties

| Property | Type | Description |
| --- | --- | --- |
| `value` | string | The chosen value. Setting it selects the matching option. |

### Events

| Event | Detail | Description |
| --- | --- | --- |
| `change` | none | Fired after the user chooses an option. |

### Child elements

| Element | Attributes read | Description |
| --- | --- | --- |
| `<option>` | `value`, `label` (or text), `disabled` | One choice. Options are hidden in place and rendered in the list. Adding, removing or changing options updates the list. |
| `ui-combobox-empty` | | Rich content for the empty state. |

### CSS parts

| Part | Description |
| --- | --- |
| `field` | The bordered box around the input and chevron. |
| `input` | The inner text `<input role="combobox">`. |
| `list` | The popover `role="listbox"` element. |
| `option` | Each rendered option (`role="option"`). |
| `empty` | The paragraph shown when nothing matches. |

### Forms

`ui-combobox` is form-associated. With a `name`, it submits `value`, or an empty string when nothing is chosen. With
`required` and no value it reports `valueMissing` with the message "Please select an item." and the validation
bubble points at the input. `form.reset()` restores the `value` attribute.

<a id="field"></a>

## Field

Layout wrappers that pair a form control with its label, description and error message, and group related fields under a legend. [Docs and demos](https://realm.js.org/components/field/).

- File: `field.html`, import with `<import-element from="https://cdn.jsdelivr.net/npm/@realmorg/ui@alpha/components/field.html"></import-element>`
- Tags: `ui-field`, `ui-field-content`, `ui-field-description`, `ui-field-group`, `ui-field-set`, `ui-field-legend`

```html runnable
<import-element from="https://cdn.jsdelivr.net/npm/@realmorg/ui@alpha/components/field.html"></import-element>
<import-element from="https://cdn.jsdelivr.net/npm/@realmorg/ui@alpha/components/input.html"></import-element>
<import-element from="https://cdn.jsdelivr.net/npm/@realmorg/ui@alpha/components/label.html"></import-element>

<ui-field>
  <ui-label for="field-email">Email</ui-label>
  <ui-input id="field-email" type="email" placeholder="m@example.com"></ui-input>
  <ui-field-description>We only use it to send receipts.</ui-field-description>
</ui-field>
```

### Parts

| Part | Slot | Renders |
| --- | --- | --- |
| `ui-field` | | One field: label, control, description and error, stacked (or in a row when horizontal). Role `group`. |
| `ui-label` | (default) | The label of a `ui-field`. A plain child. See [Label](https://realm.js.org/components/label/). |
| `ui-field-content` | (default) | A column that keeps a label and description together beside the control in a horizontal field. |
| `ui-field-description` | (default) | Help text in muted `var(--text-sm)`. Goes in `ui-field`, `ui-field-content` or `ui-field-set`. |
| `ui-field-group` | (default) | A column of fields with a gap of `calc(var(--spacing) * 7)`. |
| `ui-field-set` | | A group of fields under a legend. Role `group`. |
| `ui-field-legend` | `legend` | The heading of a `ui-field-set`. Assigns itself to the `legend` slot, so you do not set `slot`. |

Parts show in source order. Every part's default slot takes any content.

### ui-field attributes

| Attribute | Type | Default | Description |
| --- | --- | --- | --- |
| `orientation` | `"vertical"` \| `"horizontal"` | `"vertical"` | Stacks the parts, or puts them on one row. |
| `invalid` | boolean | `false` | Colours the label with `--destructive`. |
| `error` | string | | Error message shown below the field. Hidden when empty. |

### ui-field-legend attributes

| Attribute | Type | Default | Description |
| --- | --- | --- | --- |
| `variant` | `"label"` | | Uses `var(--text-sm)` instead of `var(--text-base)`. |

The other parts have no attributes.

### CSS parts

| Element | Part | Description |
| --- | --- | --- |
| `ui-field` | `error` | The error paragraph (`role="alert"`). |

### CSS states

| Element | State | Description |
| --- | --- | --- |
| `ui-field` | `:state(attr-invalid)` | Set while `invalid` is present. |
| `ui-field` | `:state(attr-orientation-is-horizontal)` | Set for the horizontal layout. |

<a id="input"></a>

## Input

A form-associated single-line text input that wraps a native input, with native types, constraints and validation. [Docs and demos](https://realm.js.org/components/input/).

- File: `input.html`, import with `<import-element from="https://cdn.jsdelivr.net/npm/@realmorg/ui@alpha/components/input.html"></import-element>`
- Tags: `ui-input`

```html runnable
<import-element from="https://cdn.jsdelivr.net/npm/@realmorg/ui@alpha/components/input.html"></import-element>

<ui-input aria-label="Email" type="email" placeholder="Email"></ui-input>
```

### Attributes

| Attribute | Type | Default | Description |
| --- | --- | --- | --- |
| `value` | string | `""` | Current value. The attribute is the initial and reset value. |
| `type` | `"text"` \| `"email"` \| `"password"` \| `"number"` \| `"search"` \| `"tel"` \| `"url"` \| `"date"` \| `"time"` \| `"datetime-local"` \| `"month"` \| `"week"` \| `"color"` | `"text"` | Native input type. |
| `placeholder` | string | | Placeholder text. |
| `autocomplete` | string | | Native autofill hint. |
| `inputmode` | string | | Virtual keyboard hint. |
| `pattern` | string | | Regular expression the value must match. |
| `min` | string | | Minimum for number and date types. |
| `max` | string | | Maximum for number and date types. |
| `step` | string | | Step for number and date types. |
| `minlength` | number | | Minimum length. |
| `maxlength` | number | | Maximum length. |
| `disabled` | boolean | `false` | Disables the input. Also true inside a disabled `<fieldset>`. |
| `readonly` | boolean | `false` | Makes the value read-only. |
| `required` | boolean | `false` | The form is invalid while the value is empty. |
| `invalid` | boolean | `false` | Sets `aria-invalid="true"` and the error style. Does not affect validity. |
| `aria-label` | string | | Accessible name, passed to the inner input. Property name: `label`. |

### Properties

| Property | Type | Description |
| --- | --- | --- |
| `value` | string | Current value. Updated as the user types. Setting it updates the field and the submitted value. |

Every attribute above is also a property with the same name in camelCase (`label` for `aria-label`).

### Events

| Event | Detail | Description |
| --- | --- | --- |
| `input` | none | The inner input's native `input` event, which is composed and reaches the page. |
| `change` | none | Dispatched from the element (bubbling) when the inner input fires `change`. |

### CSS parts

| Part | Description |
| --- | --- |
| `input` | The inner native `<input>`. |

### Forms

`ui-input` is form-associated. It submits `value` under its `name`. Validity is mirrored from the inner input, so
`required`, `type`, `pattern`, `minlength`, `maxlength`, `min`, `max` and `step` all take part in `checkValidity()`
and `reportValidity()`, and the validation bubble points at the inner field. Enter submits the form for the
text-like types. `form.reset()` restores the `value` attribute, and a disabled `<fieldset>` disables the input.

<a id="input-group"></a>

## Input Group

A form-associated text input with start and end addons inside one border, for prefixes, suffixes, icons and inline buttons. [Docs and demos](https://realm.js.org/components/input-group/).

- File: `input-group.html`, import with `<import-element from="https://cdn.jsdelivr.net/npm/@realmorg/ui@alpha/components/input-group.html"></import-element>`
- Tags: `ui-input-group`, `ui-input-group-addon`

```html runnable
<import-element from="https://cdn.jsdelivr.net/npm/@realmorg/ui@alpha/components/input-group.html"></import-element>

<ui-input-group aria-label="Website" placeholder="example.com">
  <ui-input-group-addon>https://</ui-input-group-addon>
</ui-input-group>
```

### Parts

| Part | Slot | Renders |
| --- | --- | --- |
| `ui-input-group` | (root) | The bordered box with the native `<input>`. |
| `ui-input-group-addon` | (default) | Text, icons or buttons before or after the input, in muted colour. |

The default slot of each part takes any content.

### Attributes

#### ui-input-group

| Attribute | Type | Default | Description |
| --- | --- | --- | --- |
| `value` | string | `""` | Current value. The attribute is the initial and reset value. |
| `type` | `"text"` \| `"email"` \| `"password"` \| `"number"` \| `"search"` \| `"tel"` \| `"url"` | `"text"` | Native input type. |
| `placeholder` | string | | Placeholder text. |
| `disabled` | boolean | `false` | Disables the input. Also true inside a disabled `<fieldset>`. |
| `required` | boolean | `false` | The form is invalid while the value is empty. |
| `name` | string | | Form field name the value is submitted under. |
| `aria-label` | string | | Accessible name, passed to the inner input. Property name: `label`. |

#### ui-input-group-addon

| Attribute | Type | Default | Description |
| --- | --- | --- | --- |
| `align` | `"inline-start"` \| `"inline-end"` | `"inline-start"` | Places the addon before or after the input. |

### Properties

| Property | Type | Description |
| --- | --- | --- |
| `value` | string | Current value. Updated as the user types. Setting it updates the field. |

### Events

| Event | Detail | Description |
| --- | --- | --- |
| `input` | none | The inner input's native `input` event (composed). |
| `change` | none | Dispatched from the element (bubbling) when the inner input fires `change`. |

### CSS parts

| Part | Description |
| --- | --- |
| `input` | The inner native `<input>`. |

### Forms

`ui-input-group` is form-associated. It submits the input's `value` under its `name`. Addon content is never
submitted. Validity comes from the inner input (`required` and `type`), Enter submits the form, `form.reset()`
restores the `value` attribute, and a disabled `<fieldset>` disables the input.

<a id="input-otp"></a>

## Input OTP

A one-time code input with one box per character, paste support and a complete event, that submits the whole code with its form. [Docs and demos](https://realm.js.org/components/input-otp/).

- File: `input-otp.html`, import with `<import-element from="https://cdn.jsdelivr.net/npm/@realmorg/ui@alpha/components/input-otp.html"></import-element>`
- Tags: `ui-input-otp`

```html runnable
<import-element from="https://cdn.jsdelivr.net/npm/@realmorg/ui@alpha/components/input-otp.html"></import-element>

<ui-input-otp></ui-input-otp>
```

### Attributes

| Attribute | Type | Default | Description |
| --- | --- | --- | --- |
| `value` | string | `""` | The code entered so far. The attribute is the initial and reset value. |
| `length` | number | `6` | Number of boxes. An integer from 1 to 12. |
| `pattern` | string | `"[0-9]"` | Regular expression that a single character must match. |
| `separator` | number | `0` | Adds a gap before every box whose index is a multiple of this number. `0` means no gaps. |
| `disabled` | boolean | `false` | Disables every box. Also true inside a disabled `<fieldset>`. |
| `required` | boolean | `false` | The form is invalid until all boxes are filled. |

### Properties

| Property | Type | Description |
| --- | --- | --- |
| `value` | string | The current code. Setting it fills the boxes. |

### Events

| Event | Detail | Description |
| --- | --- | --- |
| `change` | none | Fired after each typed, pasted or deleted character. |
| `complete` | `{ value: string }` | Fired when the code reaches `length` characters. |

`complete` is a custom event name. Handle it with `oncomplete` inside a component's markup, as in the example above,
or with `addEventListener("complete", …)`.

### CSS parts

| Part | Description |
| --- | --- |
| `group` | The row of boxes (`role="group"`). |
| `slot` | Each character box. Boxes that start a new group carry `data-gap`. |

### Forms

`ui-input-otp` is form-associated and submits `value` under its `name`, even when it is incomplete. With `required`
it reports `valueMissing` with the message "Enter the full code." until all boxes are filled. The validation bubble
points at the first empty box, which takes focus. `form.reset()` restores the `value` attribute.

<a id="label"></a>

## Label

A text label that names a form control by id, focuses it on click and toggles checkboxes and switches. [Docs and demos](https://realm.js.org/components/label/).

- File: `label.html`, import with `<import-element from="https://cdn.jsdelivr.net/npm/@realmorg/ui@alpha/components/label.html"></import-element>`
- Tags: `ui-label`

```html runnable
<import-element from="https://cdn.jsdelivr.net/npm/@realmorg/ui@alpha/components/input.html"></import-element>
<import-element from="https://cdn.jsdelivr.net/npm/@realmorg/ui@alpha/components/label.html"></import-element>

<ui-label for="label-email">Email</ui-label>
<ui-input id="label-email" type="email" placeholder="m@example.com"></ui-input>
```

### Attributes

| Attribute | Type | Default | Description |
| --- | --- | --- | --- |
| `for` | string | | Id of the control to label, looked up in the label's own document or shadow root. |

### Parts

| Part | Slot | Renders |
| --- | --- | --- |
| `ui-label` | (default) | The label text, and optionally an icon, in a `<span>` that handles the click. Inside `ui-field` it is a plain child. |

The default slot takes any content.

### CSS parts

| Part | Description |
| --- | --- |
| `label` | The `<span>` around the slotted text. It handles the click. |

### CSS states

| State | Description |
| --- | --- |
| `:state(attr-for)` | Set while `for` is present. The label then shows a pointer cursor. |

<a id="radio-group"></a>

## Radio Group

A form-associated set of radio buttons built from native option children, for choosing exactly one value. [Docs and demos](https://realm.js.org/components/radio-group/).

- File: `radio-group.html`, import with `<import-element from="https://cdn.jsdelivr.net/npm/@realmorg/ui@alpha/components/radio-group.html"></import-element>`
- Tags: `ui-radio-group`

```html runnable
<import-element from="https://cdn.jsdelivr.net/npm/@realmorg/ui@alpha/components/radio-group.html"></import-element>

<ui-radio-group aria-label="Plan" value="pro">
  <option value="free">Free</option>
  <option value="pro">Pro</option>
  <option value="team">Team</option>
</ui-radio-group>
```

### Attributes

| Attribute | Type | Default | Description |
| --- | --- | --- | --- |
| `value` | string | | The `value` of the chosen option. |
| `orientation` | `"vertical"` \| `"horizontal"` | `"vertical"` | Lays the radios out in a column or a row. |
| `disabled` | boolean | `false` | Disables every radio. Also true inside a disabled `<fieldset>`. |
| `required` | boolean | `false` | The form is invalid until an option is chosen. |

### Properties

| Property | Type | Description |
| --- | --- | --- |
| `value` | string | The chosen value. Setting it checks the matching radio. |

### Events

| Event | Detail | Description |
| --- | --- | --- |
| `change` | none | Fired from the group after the user chooses a radio. |

### Child elements

| Element | Attributes read | Description |
| --- | --- | --- |
| `<option>` | `value`, `label` (or text), `disabled` | One radio. Options stay hidden in place. Adding or removing options updates the radios. |

### CSS parts

| Part | Description |
| --- | --- |
| `item` | The `<label>` around each radio and its text. |
| `control` | Each native `<input type="radio">`. |

### CSS states

| State | Description |
| --- | --- |
| `:state(attr-orientation-is-horizontal)` | Set for the horizontal layout. |

### Forms

`ui-radio-group` is form-associated. It submits `value` under its `name`, or nothing while no option is chosen. With
`required` and no value it reports `valueMissing` with the message "Please select an option.", and the validation
bubble points at the first enabled radio, which takes focus. `form.reset()` restores the `value` attribute.

<a id="select"></a>

## Select

A styled, form-associated native select that mirrors its option children, with an optional placeholder and validation. [Docs and demos](https://realm.js.org/components/select/).

- File: `select.html`, import with `<import-element from="https://cdn.jsdelivr.net/npm/@realmorg/ui@alpha/components/select.html"></import-element>`
- Tags: `ui-select`

```html runnable
<import-element from="https://cdn.jsdelivr.net/npm/@realmorg/ui@alpha/components/select.html"></import-element>

<ui-select aria-label="Fruit" width="48">
  <option value="apple">Apple</option>
  <option value="banana">Banana</option>
  <option value="blueberry">Blueberry</option>
  <option value="grapes">Grapes</option>
</ui-select>
```

### Parts

| Part | Slot | Renders |
| --- | --- | --- |
| `ui-select` | | A native `<select>` styled like an input, with a chevron. Fills its container's width unless `width` is set. |
| `<option>` | (data) | One choice, copied into the native select. |

The select keeps the native `<option>` children rather than separate item parts, so the platform picker, keyboard
handling and mobile UI keep working.

### Attributes

| Attribute | Type | Default | Description |
| --- | --- | --- | --- |
| `value` | string | `""` | The `value` of the chosen option. |
| `placeholder` | string | | Text of an empty, unselectable first entry. Without it, the first or `selected` option is chosen automatically. |
| `size` | `"default"` \| `"sm"` | `"default"` | Height of the select. |
| `width` | number | | Fixed width in spacing units, where `n` means `calc(var(--spacing) * n)`. Without it the select fills its container. |
| `disabled` | boolean | `false` | Disables the select. Also true inside a disabled `<fieldset>`. |
| `required` | boolean | `false` | The form is invalid while the placeholder is shown. |
| `aria-label` | string | | Accessible name, passed to the inner select. Property name: `label`. |

### Properties

| Property | Type | Description |
| --- | --- | --- |
| `value` | string | The chosen value. Setting it selects the matching option. |

### Events

| Event | Detail | Description |
| --- | --- | --- |
| `change` | none | Dispatched from the element (bubbling) after the user picks an option. |
| `input` | none | The inner select's native `input` event (composed). |

### Child elements

| Element | Attributes read | Description |
| --- | --- | --- |
| `<option>` | `value`, `label` (or text), `disabled`, `selected` | One choice. Options are copied into the inner select and kept in sync. `selected` only picks the initial option when there is no `placeholder` and no `value`. `<optgroup>` is not supported. |

### CSS parts

| Part | Description |
| --- | --- |
| `control` | The inner native `<select>`. |

### CSS states

| State | Description |
| --- | --- |
| `:state(attr-size-is-sm)` | Set for the small size. |

### Forms

`ui-select` is form-associated. It submits `value` under its `name` (an empty string while the placeholder is
shown). Validity is mirrored from the inner select, so `required` with a placeholder reports `valueMissing` and the
validation bubble points at the select. `form.reset()` restores the `value` attribute. Without one, the select goes
back to the placeholder, or to the first or `selected` option.

<a id="slider"></a>

## Slider

A form-associated range slider built on a native range input, with a filled track and numeric value. [Docs and demos](https://realm.js.org/components/slider/).

- File: `slider.html`, import with `<import-element from="https://cdn.jsdelivr.net/npm/@realmorg/ui@alpha/components/slider.html"></import-element>`
- Tags: `ui-slider`

```html runnable
<import-element from="https://cdn.jsdelivr.net/npm/@realmorg/ui@alpha/components/slider.html"></import-element>

<ui-slider aria-label="Volume" value="50"></ui-slider>
```

### Attributes

| Attribute | Type | Default | Description |
| --- | --- | --- | --- |
| `value` | number | `0` | Current value. The attribute is the initial and reset value. |
| `min` | number | `0` | Lowest value. |
| `max` | number | `100` | Highest value. |
| `step` | number | `1` | Granularity of the value. |
| `disabled` | boolean | `false` | Disables the slider. Also true inside a disabled `<fieldset>`. |
| `aria-label` | string | | Accessible name, passed to the inner input. Property name: `label`. |

### Properties

| Property | Type | Description |
| --- | --- | --- |
| `value` | number | Current value as a number. Updated while the thumb moves. Setting it moves the thumb. |

### Events

| Event | Detail | Description |
| --- | --- | --- |
| `input` | none | The inner range input's native `input` event (composed), fired while dragging. |
| `change` | none | Dispatched from the element (bubbling) when the user releases the thumb or commits a key press. |

### CSS parts

| Part | Description |
| --- | --- |
| `control` | The inner native `<input type="range">`. |

### CSS custom properties

| Property | Description |
| --- | --- |
| `--state-range-percent` | Set on the host to the filled share of the track, for example `40%`. Read-only. Use it to draw a matching tooltip or label. |

### Forms

`ui-slider` is form-associated and submits `value` under its `name`. `form.reset()` restores the `value` attribute,
and a disabled `<fieldset>` disables it.

<a id="switch"></a>

## Switch

A form-associated on/off switch built on a native checkbox with the switch role, for settings that apply immediately. [Docs and demos](https://realm.js.org/components/switch/).

- File: `switch.html`, import with `<import-element from="https://cdn.jsdelivr.net/npm/@realmorg/ui@alpha/components/switch.html"></import-element>`
- Tags: `ui-switch`

```html runnable
<import-element from="https://cdn.jsdelivr.net/npm/@realmorg/ui@alpha/components/switch.html"></import-element>

<ui-switch>Airplane mode</ui-switch>
```

### Attributes

| Attribute | Type | Default | Description |
| --- | --- | --- | --- |
| `checked` | boolean | `false` | Whether the switch is on. The attribute is the initial and reset state. |
| `value` | string | | Value submitted when on. Without it the form submits `on`. |
| `size` | `"default"` \| `"sm"` | `"default"` | Track and thumb size. |
| `disabled` | boolean | `false` | Disables the switch. Also true inside a disabled `<fieldset>`. |
| `aria-label` | string | | Accessible name when there is no visible text. Property name: `label`. |

### Properties

| Property | Type | Description |
| --- | --- | --- |
| `checked` | boolean | Current state. Updated when the user toggles. Setting it moves the switch. |

### Events

| Event | Detail | Description |
| --- | --- | --- |
| `change` | none | Bubbles from the element after the user toggles. `checked` is already updated. |
| `input` | none | The inner checkbox's native `input` event (composed). |

### Slots

| Slot | Description |
| --- | --- |
| (default) | The label text, rendered inside a native `<label>` for the switch. |

### CSS parts

| Part | Description |
| --- | --- |
| `control` | The inner `<input type="checkbox" role="switch">`. |
| `label` | The `<label>` around the slotted text. |

### CSS states

| State | Description |
| --- | --- |
| `:state(attr-checked)` | Set while the switch is on. |
| `:state(attr-size-is-sm)` | Set for the small size. |

### Forms

`ui-switch` is form-associated. When on it submits `value`, or `on` without one. When off it submits nothing.
`form.reset()` restores the `checked` attribute.

<a id="textarea"></a>

## Textarea

A form-associated multi-line text field that wraps a native textarea and grows with its content. [Docs and demos](https://realm.js.org/components/textarea/).

- File: `textarea.html`, import with `<import-element from="https://cdn.jsdelivr.net/npm/@realmorg/ui@alpha/components/textarea.html"></import-element>`
- Tags: `ui-textarea`

```html runnable
<import-element from="https://cdn.jsdelivr.net/npm/@realmorg/ui@alpha/components/textarea.html"></import-element>

<ui-textarea aria-label="Message" placeholder="Type your message here."></ui-textarea>
```

### Attributes

| Attribute | Type | Default | Description |
| --- | --- | --- | --- |
| `value` | string | `""` | Current text. The attribute is the initial and reset value. |
| `placeholder` | string | | Placeholder text. |
| `rows` | number | `3` | Visible lines before the field grows. An integer of at least 1. |
| `minlength` | number | | Minimum length. |
| `maxlength` | number | | Maximum length. |
| `disabled` | boolean | `false` | Disables the textarea. Also true inside a disabled `<fieldset>`. |
| `readonly` | boolean | `false` | Makes the text read-only. |
| `required` | boolean | `false` | The form is invalid while the text is empty. |
| `invalid` | boolean | `false` | Sets `aria-invalid="true"` and the error style. Does not affect validity. |
| `aria-label` | string | | Accessible name, passed to the inner textarea. Property name: `label`. |

### Properties

| Property | Type | Description |
| --- | --- | --- |
| `value` | string | Current text. Updated as the user types. Setting it updates the field. |

### Events

| Event | Detail | Description |
| --- | --- | --- |
| `input` | none | The inner textarea's native `input` event (composed). |
| `change` | none | Dispatched from the element (bubbling) when the inner textarea fires `change`, after it loses focus. |

### CSS parts

| Part | Description |
| --- | --- |
| `textarea` | The inner native `<textarea>`. |

### Forms

`ui-textarea` is form-associated and submits `value` under its `name`. Validity is mirrored from the inner
textarea, so `required`, `minlength` and `maxlength` take part in `checkValidity()` and `reportValidity()`.
`form.reset()` restores the `value` attribute, and a disabled `<fieldset>` disables the field.

<a id="toggle"></a>

## Toggle

A two-state button that stays pressed or unpressed, for options such as bold text or a pinned item. [Docs and demos](https://realm.js.org/components/toggle/).

- File: `toggle.html`, import with `<import-element from="https://cdn.jsdelivr.net/npm/@realmorg/ui@alpha/components/toggle.html"></import-element>`
- Tags: `ui-toggle`

```html runnable
<import-element from="https://cdn.jsdelivr.net/npm/@realmorg/ui@alpha/components/toggle.html"></import-element>

<ui-toggle aria-label="Bold">
  <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" aria-hidden="true"><path d="M6 12h9a4 4 0 0 1 0 8H6V4h8a4 4 0 0 1 0 8" /></svg>
</ui-toggle>
```

### Attributes

| Attribute | Type | Default | Description |
| --- | --- | --- | --- |
| `pressed` | boolean | `false` | Whether the toggle is pressed. Reflects to the attribute. |
| `variant` | `"default"` \| `"outline"` | `"default"` | Visual style. `outline` adds a border. |
| `size` | `"default"` \| `"sm"` \| `"lg"` | `"default"` | Height and padding. |
| `disabled` | boolean | `false` | Disables the button. |
| `value` | string | | Identifies the toggle inside a `ui-toggle-group`. |
| `aria-label` | string | | Accessible name, passed to the inner button. Use it for icon-only toggles. Property name: `label`. |

### Properties

| Property | Type | Description |
| --- | --- | --- |
| `pressed` | boolean | Current state. Flipped by each click. Setting it updates the button. |

### Events

| Event | Detail | Description |
| --- | --- | --- |
| `change` | none | Fired after each click, once `pressed` has flipped. Inside a `ui-toggle-group`, the group stops it and fires its own `change`. |

### Slots

| Slot | Description |
| --- | --- |
| (default) | The label and icons. Slotted `<svg>` elements are sized to `calc(var(--spacing) * 4)`. |

### CSS parts

| Part | Description |
| --- | --- |
| `button` | The inner native `<button>`. |

### CSS states and custom properties

| Name | Description |
| --- | --- |
| `:state(attr-pressed)` | Set while the toggle is pressed. |
| `:state(attr-variant-is-outline)`, `:state(attr-size-is-sm)`, `:state(attr-size-is-lg)` | Set for the current variant and size. |
| `--ui-radius-start`, `--ui-radius-end` | Override the start and end corner radii. Set by `ui-toggle-group` and `ui-button-group`. |

<a id="toggle-group"></a>

## Toggle Group

A form-associated set of toggles where one or several can be pressed, submitting the pressed values as one field. [Docs and demos](https://realm.js.org/components/toggle-group/).

- File: `toggle-group.html`, import with `<import-element from="https://cdn.jsdelivr.net/npm/@realmorg/ui@alpha/components/toggle-group.html"></import-element>`
- Tags: `ui-toggle-group`
- Imports for you: `toggle.html`

```html runnable
<import-element from="https://cdn.jsdelivr.net/npm/@realmorg/ui@alpha/components/toggle-group.html"></import-element>
<import-element from="https://cdn.jsdelivr.net/npm/@realmorg/ui@alpha/components/toggle.html"></import-element>

<ui-toggle-group value="left" aria-label="Text alignment">
  <ui-toggle value="left" aria-label="Align left">
    <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" aria-hidden="true"><path d="M3 6h18M3 12h10M3 18h14" /></svg>
  </ui-toggle>
  <ui-toggle value="center" aria-label="Align center">
    <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" aria-hidden="true"><path d="M3 6h18M7 12h10M5 18h14" /></svg>
  </ui-toggle>
  <ui-toggle value="right" aria-label="Align right">
    <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" aria-hidden="true"><path d="M3 6h18M11 12h10M7 18h14" /></svg>
  </ui-toggle>
</ui-toggle-group>
```

### Attributes

| Attribute | Type | Default | Description |
| --- | --- | --- | --- |
| `type` | `"single"` \| `"multiple"` | `"single"` | Whether one or several toggles can be pressed. |
| `value` | string | `""` | Pressed toggle values, separated by commas. The attribute is the initial and reset value. |
| `orientation` | `"horizontal"` \| `"vertical"` | `"horizontal"` | Layout direction, and which arrow keys move focus. |

### Properties

| Property | Type | Description |
| --- | --- | --- |
| `value` | string | Pressed values as a comma-separated string. Setting it presses the matching toggles. |

### Events

| Event | Detail | Description |
| --- | --- | --- |
| `change` | none | Fired after a toggle in the group is clicked and `value` is updated. |

### Slots

| Slot | Description |
| --- | --- |
| (default) | The `ui-toggle` elements. Every child except the first gets `--ui-radius-start: 0`, and every child except the last gets `--ui-radius-end: 0`. |

### Child elements

| Element | Attributes read | Description |
| --- | --- | --- |
| `ui-toggle` | `value`, `pressed` | The group sets each toggle's `pressed` from its own `value`. A toggle's value must not contain a comma. |

### CSS parts

| Part | Description |
| --- | --- |
| `group` | The `display: contents` wrapper that carries the `focusgroup` attribute. |

### CSS states

| State | Description |
| --- | --- |
| `:state(attr-orientation-is-vertical)` | Set for the vertical layout. |

### Forms

`ui-toggle-group` is form-associated and submits `value` under its `name`, including an empty string when nothing
is pressed. `form.reset()` restores the `value` attribute and the pressed toggles follow it. The group has no
`disabled` attribute. Disable individual toggles instead.

<a id="alert"></a>

## Alert

A callout box with an optional icon, a title and a description, for messages that need the reader's attention. [Docs and demos](https://realm.js.org/components/alert/).

- File: `alert.html`, import with `<import-element from="https://cdn.jsdelivr.net/npm/@realmorg/ui@alpha/components/alert.html"></import-element>`
- Tags: `ui-alert`, `ui-alert-title`, `ui-alert-description`

```html runnable
<import-element from="https://cdn.jsdelivr.net/npm/@realmorg/ui@alpha/components/alert.html"></import-element>

<ui-alert>
  <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="10"/><path d="m9 12 2 2 4-4"/></svg>
  <ui-alert-title>Changes saved</ui-alert-title>
  <ui-alert-description>Your profile was updated. It can take a minute to show everywhere.</ui-alert-description>
</ui-alert>
```

### Parts

| Part | Slot | Renders |
| --- | --- | --- |
| `ui-alert` | | The bordered box. A two-column grid: a leading `<svg>` icon in the first column, everything else in the second. |
| `<svg>` | (default) | Optional icon as a direct child of `ui-alert`. Sized to `calc(var(--spacing) * 4)` and spans the title and description rows. |
| `ui-alert-title` | (default) | The heading line, in medium weight. |
| `ui-alert-description` | (default) | Muted text under the title (tinted destructive in the destructive variant). A grid, so `<p>` and `<ul>` stack. |

Each part has a single default slot that takes any content.

### ui-alert attributes

| Attribute | Type | Default | Description |
| --- | --- | --- | --- |
| `variant` | `"default"` \| `"destructive"` | `"default"` | Colour scheme of the alert. |

<a id="avatar"></a>

## Avatar

A round image of a user or entity, with a text fallback while the image loads or when it fails. [Docs and demos](https://realm.js.org/components/avatar/).

- File: `avatar.html`, import with `<import-element from="https://cdn.jsdelivr.net/npm/@realmorg/ui@alpha/components/avatar.html"></import-element>`
- Tags: `ui-avatar`, `ui-avatar-image`, `ui-avatar-fallback`, `ui-avatar-group`, `ui-avatar-group-count`

```html runnable
<import-element from="https://cdn.jsdelivr.net/npm/@realmorg/ui@alpha/components/avatar.html"></import-element>

<ui-avatar>
  <ui-avatar-image src="data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 64 64'%3E%3Crect width='64' height='64' fill='%236366f1'/%3E%3Ccircle cx='32' cy='26' r='12' fill='%23e0e7ff'/%3E%3Cpath d='M10 60c4-14 14-20 22-20s18 6 22 20' fill='%23e0e7ff'/%3E%3C/svg%3E" alt="Ada Lovelace"></ui-avatar-image>
  <ui-avatar-fallback>AL</ui-avatar-fallback>
</ui-avatar>
<ui-avatar>
  <ui-avatar-fallback>CN</ui-avatar-fallback>
</ui-avatar>
```

### Parts

| Part | Slot | Renders |
| --- | --- | --- |
| `ui-avatar` | (root) | The circle (or rounded square) that clips its content. |
| `ui-avatar-image` | (default) | The `<img>`, hidden until it has loaded and removed when loading fails. |
| `ui-avatar-fallback` | (default) | Initials or an icon on a muted background, shown until the image loads and when it fails. |
| `ui-avatar-group` | (root) | A row that overlaps its avatars, each with a ring in the background colour. |
| `ui-avatar-group-count` | (default) | A muted 32px circle for a count such as "+3", inside `ui-avatar-group`. |

The default slot of each part takes any content.

### Attributes

#### ui-avatar

| Attribute | Type | Default | Description |
| --- | --- | --- | --- |
| `size` | `"sm"` \| `"default"` \| `"lg"` | `"default"` | 24px, 32px or 40px square. |
| `shape` | `"circle"` \| `"square"` | `"circle"` | `square` uses `var(--radius-md)` corners. |

#### ui-avatar-image

| Attribute | Type | Default | Description |
| --- | --- | --- | --- |
| `src` | string | | Image URL. |
| `alt` | string | `""` | Alternative text for the image. |

### CSS parts

| Part | Element | Description |
| --- | --- | --- |
| `image` | `ui-avatar-image` | The `<img>`. |
| `fallback` | `ui-avatar-fallback` | The `<span>` around the fallback content. |

<a id="badge"></a>

## Badge

A small inline label for a status, a count or a category. [Docs and demos](https://realm.js.org/components/badge/).

- File: `badge.html`, import with `<import-element from="https://cdn.jsdelivr.net/npm/@realmorg/ui@alpha/components/badge.html"></import-element>`
- Tags: `ui-badge`

```html runnable
<import-element from="https://cdn.jsdelivr.net/npm/@realmorg/ui@alpha/components/badge.html"></import-element>

<ui-badge>Badge</ui-badge>
<ui-badge variant="secondary">Secondary</ui-badge>
<ui-badge variant="destructive">Destructive</ui-badge>
<ui-badge variant="outline">Outline</ui-badge>
```

### Parts

| Part | Slot | Renders |
| --- | --- | --- |
| `ui-badge` | (default) | An inline label. Its default slot takes any content: text and an optional `<svg>` icon. |

### Attributes

| Attribute | Type | Default | Description |
| --- | --- | --- | --- |
| `variant` | `"default"` \| `"secondary"` \| `"destructive"` \| `"outline"` | `"default"` | Colour scheme of the badge. |
| `shape` | `"default"` \| `"pill"` \| `"round"` | `"default"` | `pill` is fully rounded. `round` is a 20px circle for counts, with tabular numbers. |

### CSS parts

None. The badge renders only a slot, so style the element itself.

<a id="card"></a>

## Card

A bordered container with a header, body and footer for grouping related content and actions. [Docs and demos](https://realm.js.org/components/card/).

- File: `card.html`, import with `<import-element from="https://cdn.jsdelivr.net/npm/@realmorg/ui@alpha/components/card.html"></import-element>`
- Tags: `ui-card`, `ui-card-header`, `ui-card-title`, `ui-card-description`, `ui-card-action`, `ui-card-content`, `ui-card-footer`

```html runnable
<import-element from="https://cdn.jsdelivr.net/npm/@realmorg/ui@alpha/components/button.html"></import-element>
<import-element from="https://cdn.jsdelivr.net/npm/@realmorg/ui@alpha/components/card.html"></import-element>
<import-element from="https://cdn.jsdelivr.net/npm/@realmorg/ui@alpha/components/field.html"></import-element>
<import-element from="https://cdn.jsdelivr.net/npm/@realmorg/ui@alpha/components/input.html"></import-element>
<import-element from="https://cdn.jsdelivr.net/npm/@realmorg/ui@alpha/components/label.html"></import-element>

<ui-card>
  <ui-card-header>
    <ui-card-title>Create project</ui-card-title>
    <ui-card-description>Deploy your new project in one click.</ui-card-description>
  </ui-card-header>
  <ui-card-content>
    <ui-field>
      <ui-label for="card-project-name">Name</ui-label>
      <ui-input id="card-project-name" placeholder="Name of your project"></ui-input>
    </ui-field>
  </ui-card-content>
  <ui-card-footer justify="between">
    <ui-button variant="outline">Cancel</ui-button>
    <ui-button>Deploy</ui-button>
  </ui-card-footer>
</ui-card>
```

### Parts

| Part | Slot | Renders |
| --- | --- | --- |
| `ui-card` | | The bordered panel. A flex column with a gap between its parts. Fills its container's width. |
| `ui-card-header` | (default) | A grid for the title, description and action, with horizontal padding. |
| `ui-card-title` | (default) | A heading (level 3 unless `level` says otherwise) in `var(--text-base)` semibold. Place it in `ui-card-header`. |
| `ui-card-description` | (default) | Supporting text in muted `var(--text-sm)`. Place it in `ui-card-header`. |
| `ui-card-action` | (default) | Content in the header's second column, aligned to the end and spanning the title and description rows. |
| `ui-card-content` | (default) | The body, with horizontal padding. |
| `ui-card-footer` | (default) | A row of actions at the bottom, with a gap and horizontal padding. |

Every part has a single default slot that takes any content.

### ui-card-title attributes

| Attribute | Type | Default | Description |
| --- | --- | --- | --- |
| `level` | number (1 to 6) | `3` | The heading level. Use `1` for a card that is the page's main content and `2` for cards directly under the page heading. |

### ui-card-footer attributes

| Attribute | Type | Default | Description |
| --- | --- | --- | --- |
| `justify` | `"between"` \| `"end"` | | Spreads the children apart, or pushes them to the end. Without it they start at the left. |
| `orientation` | `"vertical"` | | Stacks the children and stretches them to the full width. |

The other parts have no attributes.

### CSS parts

| Element | Part | Description |
| --- | --- | --- |
| `ui-card-title` | `title` | The heading element (`role="heading"` with `aria-level`) around the title text. |

<a id="chart"></a>

## Chart

A small SVG bar, line or area chart drawn from a list of data objects, with one or more series. [Docs and demos](https://realm.js.org/components/chart/).

- File: `chart.html`, import with `<import-element from="https://cdn.jsdelivr.net/npm/@realmorg/ui@alpha/components/chart.html"></import-element>`
- Tags: `ui-chart`

```html runnable
<import-element from="https://cdn.jsdelivr.net/npm/@realmorg/ui@alpha/components/chart.html"></import-element>

<ui-chart x="month" y="desktop" label="Desktop visitors per month"
  data='[{"month":"Jan","desktop":186},{"month":"Feb","desktop":305},{"month":"Mar","desktop":237},{"month":"Apr","desktop":73},{"month":"May","desktop":209},{"month":"Jun","desktop":214}]'>
</ui-chart>
```

### Attributes

| Attribute | Type | Default | Description |
| --- | --- | --- | --- |
| `type` | `"bar"` \| `"line"` \| `"area"` | `"bar"` | How the series are drawn. |
| `data` | array of objects | `[]` | The data points, one object per category. Accepts JSON in the attribute, or an array through the `data` property or a `_data` binding. |
| `x` | string | `"label"` | Field used for the category labels on the horizontal axis. |
| `y` | string | `"value"` | Field, or comma-separated fields, plotted as series. Non-numeric values count as `0`. |
| `height` | number | `240` | Height of the drawing in SVG units, minimum `80`. The width is 600 units and the chart scales to its container. |
| `label` | string | `"Chart"` | Accessible name of the chart image. |

### Parts

`ui-chart` has no child parts and no slots. It draws everything from its attributes.

### CSS parts

| Part | Description |
| --- | --- |
| `svg` | The chart `<svg>`. |
| `legend` | The legend under the chart. It has content only when `y` names more than one series. |

### CSS custom properties

| Property | Description |
| --- | --- |
| `--chart-1` … `--chart-5` | Series colours, in order. Defined in `theme.css`. Override them on an ancestor, such as a wrapping `<div>`. |

The axis labels use the element's `color` (muted foreground by default) and the grid lines use `var(--border)`.

<a id="empty"></a>

## Empty

A placeholder for an empty list, search result or section, with an icon, a message and actions. [Docs and demos](https://realm.js.org/components/empty/).

- File: `empty.html`, import with `<import-element from="https://cdn.jsdelivr.net/npm/@realmorg/ui@alpha/components/empty.html"></import-element>`
- Tags: `ui-empty`, `ui-empty-header`, `ui-empty-media`, `ui-empty-title`, `ui-empty-description`, `ui-empty-content`

```html runnable
<import-element from="https://cdn.jsdelivr.net/npm/@realmorg/ui@alpha/components/button.html"></import-element>
<import-element from="https://cdn.jsdelivr.net/npm/@realmorg/ui@alpha/components/empty.html"></import-element>

<ui-empty>
  <ui-empty-header>
    <ui-empty-media variant="icon">
      <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M20 20a2 2 0 0 0 2-2V8a2 2 0 0 0-2-2h-7.9a2 2 0 0 1-1.69-.9L9.6 3.9A2 2 0 0 0 7.93 3H4a2 2 0 0 0-2 2v13a2 2 0 0 0 2 2Z"/></svg>
    </ui-empty-media>
    <ui-empty-title>No projects yet</ui-empty-title>
    <ui-empty-description>You have not created any projects. Get started by creating your first one.</ui-empty-description>
  </ui-empty-header>
  <ui-empty-content>
    <div>
      <ui-button>Create project</ui-button>
      <ui-button variant="outline">Import project</ui-button>
    </div>
  </ui-empty-content>
</ui-empty>
```

### Parts

| Part | Slot | Renders |
| --- | --- | --- |
| `ui-empty` | | The dashed, centred box. A flex column with a gap between header and content. |
| `ui-empty-header` | (default) | A centred column for media, title and description, capped at `calc(var(--spacing) * 96)`. |
| `ui-empty-media` | (default) | A wrapper above the title for an icon, avatar or image. Place it in `ui-empty-header`. |
| `ui-empty-title` | (default) | A heading (level 3 unless `level` says otherwise) in `var(--text-lg)`. Place it in `ui-empty-header`. |
| `ui-empty-description` | (default) | Supporting text in muted `var(--text-sm)`. Place it in `ui-empty-header`. |
| `ui-empty-content` | (default) | Actions or inputs under the header, in a centred column capped at `calc(var(--spacing) * 96)`. |

Every part has a single default slot that takes any content.

### ui-empty-media attributes

| Attribute | Type | Default | Description |
| --- | --- | --- | --- |
| `variant` | `"icon"` | | Shows the content in a muted, rounded `calc(var(--spacing) * 10)` square. Without it the media is a plain wrapper. |

A slotted `<svg>` is sized to `calc(var(--spacing) * 6)`.

### ui-empty-title attributes

| Attribute | Type | Default | Description |
| --- | --- | --- | --- |
| `level` | number, 1 to 6 | `3` | The heading level, so the title fits the page outline. |

The other parts have no attributes.

### CSS parts

| Element | Part | Description |
| --- | --- | --- |
| `ui-empty-title` | `title` | The heading around the title text. |

<a id="item"></a>

## Item

A row with media, a title, a description and actions, for lists of people, files, settings or notifications. [Docs and demos](https://realm.js.org/components/item/).

- File: `item.html`, import with `<import-element from="https://cdn.jsdelivr.net/npm/@realmorg/ui@alpha/components/item.html"></import-element>`
- Tags: `ui-item`, `ui-item-group`, `ui-item-media`, `ui-item-content`, `ui-item-title`, `ui-item-description`, `ui-item-actions`, `ui-item-header`, `ui-item-footer`

```html runnable
<import-element from="https://cdn.jsdelivr.net/npm/@realmorg/ui@alpha/components/button.html"></import-element>
<import-element from="https://cdn.jsdelivr.net/npm/@realmorg/ui@alpha/components/item.html"></import-element>

<ui-item variant="outline">
  <ui-item-content>
    <ui-item-title>Basic item</ui-item-title>
    <ui-item-description>A simple item with a title and a description.</ui-item-description>
  </ui-item-content>
  <ui-item-actions>
    <ui-button variant="outline" size="sm">Action</ui-button>
  </ui-item-actions>
</ui-item>
```

### Parts

| Part | Slot | Renders |
| --- | --- | --- |
| `ui-item` | | One row, laid out as a wrapping flex container with a gap. |
| `ui-item-group` | | A column of items with the role `list`. |
| `ui-item-media` | (default) | A non-shrinking box for an icon, image or avatar. Slotted `<svg>` is sized to `calc(var(--spacing) * 4)`. |
| `ui-item-content` | (default) | A flex column that fills the space between media and actions. |
| `ui-item-title` | (default) | The main line, in medium weight. Place it in `ui-item-content`. |
| `ui-item-description` | (default) | Secondary text in muted colour, clamped to two lines. Place it in `ui-item-content`. |
| `ui-item-actions` | (default) | A row of buttons or icons with a small gap. |
| `ui-item-header` | (default) | A full-width row above the rest, children spread apart. |
| `ui-item-footer` | (default) | A full-width row below the rest, children spread apart. |

`ui-item` lays its parts out in source order. Put media first, then content, then actions. Every part has a single
default slot that takes any content.

### ui-item attributes

| Attribute | Type | Default | Description |
| --- | --- | --- | --- |
| `variant` | `"default"` \| `"outline"` \| `"muted"` | `"default"` | Border and background style. |
| `size` | `"default"` \| `"sm"` | `"default"` | Padding and gap. |

### ui-item-media attributes

| Attribute | Type | Default | Description |
| --- | --- | --- | --- |
| `variant` | `"default"` \| `"icon"` \| `"image"` | `"default"` | `icon` is a `calc(var(--spacing) * 8)` bordered, muted box. `image` is a `calc(var(--spacing) * 10)` rounded box whose `<img>` covers it. `default` is a plain wrapper. |

The other parts have no attributes.

<a id="kbd"></a>

## Kbd

Styled keyboard keys and key combinations for shortcuts in menus, buttons, tooltips and help text. [Docs and demos](https://realm.js.org/components/kbd/).

- File: `kbd.html`, import with `<import-element from="https://cdn.jsdelivr.net/npm/@realmorg/ui@alpha/components/kbd.html"></import-element>`
- Tags: `ui-kbd`, `ui-kbd-group`

```html runnable
<import-element from="https://cdn.jsdelivr.net/npm/@realmorg/ui@alpha/components/kbd.html"></import-element>

<ui-kbd-group>
  <ui-kbd>⌘</ui-kbd>
  <ui-kbd>⇧</ui-kbd>
  <ui-kbd>⌥</ui-kbd>
  <ui-kbd>⌃</ui-kbd>
</ui-kbd-group>
<ui-kbd-group>
  <ui-kbd>Ctrl</ui-kbd>
  <span>+</span>
  <ui-kbd>B</ui-kbd>
</ui-kbd-group>
```

### Parts

| Part | Slot | Renders |
| --- | --- | --- |
| `ui-kbd` | (default) | One key in a small muted box. Its default slot takes the key label: a character, a word, or a small `<svg>` icon. |
| `ui-kbd-group` | (default) | An inline row of keys with a small gap. Its default slot takes the `ui-kbd` elements and any separators such as `+` or `then`. |

### Attributes

Neither element has attributes.

### CSS parts

None. Both elements render only a slot.

<a id="progress"></a>

## Progress

A horizontal bar showing how far a task has got, built on the native progress element. [Docs and demos](https://realm.js.org/components/progress/).

- File: `progress.html`, import with `<import-element from="https://cdn.jsdelivr.net/npm/@realmorg/ui@alpha/components/progress.html"></import-element>`
- Tags: `ui-progress`

```html runnable
<import-element from="https://cdn.jsdelivr.net/npm/@realmorg/ui@alpha/components/progress.html"></import-element>

<ui-progress value="60" aria-label="Progress"></ui-progress>
```

### Attributes

| Attribute | Type | Default | Description |
| --- | --- | --- | --- |
| `value` | number | none | Current value, minimum `0`. When it is left out the bar is indeterminate. |
| `max` | number | `100` | Value that means complete, minimum `1`. |
| `aria-label` | string | | Accessible name, passed to the inner `<progress>`. Property name: `label`. |

### Parts

`ui-progress` has no child parts. Its default slot takes fallback text inside the native `<progress>`, for example
`45%`, which browsers that support `<progress>` do not render.

### CSS parts

| Part | Description |
| --- | --- |
| `bar` | The native `<progress>` element. Its height is `calc(var(--spacing) * 2)`. |

<a id="separator"></a>

## Separator

A thin horizontal or vertical line that divides content, with the ARIA separator role. [Docs and demos](https://realm.js.org/components/separator/).

- File: `separator.html`, import with `<import-element from="https://cdn.jsdelivr.net/npm/@realmorg/ui@alpha/components/separator.html"></import-element>`
- Tags: `ui-separator`

```html runnable
<import-element from="https://cdn.jsdelivr.net/npm/@realmorg/ui@alpha/components/separator.html"></import-element>

<div>
  <strong>REALM UI</strong>
  <p>Native custom elements for every app.</p>
</div>
<ui-separator></ui-separator>
<div>
  <span>Blog</span>
  <ui-separator orientation="vertical"></ui-separator>
  <span>Docs</span>
  <ui-separator orientation="vertical"></ui-separator>
  <span>Source</span>
</div>
```

### Parts

| Part | Slot | Renders |
| --- | --- | --- |
| `ui-separator` | (default) | A one pixel line in `var(--border)`, 100% wide when horizontal. It has no slot and takes no content. |

### Attributes

| Attribute | Type | Default | Description |
| --- | --- | --- | --- |
| `orientation` | `"horizontal"` \| `"vertical"` | `"horizontal"` | Direction of the line. Also sets `aria-orientation`. |

### CSS parts

None. The separator renders no shadow content. Style the element itself.

<a id="skeleton"></a>

## Skeleton

A pulsing placeholder block that shows the shape of content while it loads. [Docs and demos](https://realm.js.org/components/skeleton/).

- File: `skeleton.html`, import with `<import-element from="https://cdn.jsdelivr.net/npm/@realmorg/ui@alpha/components/skeleton.html"></import-element>`
- Tags: `ui-skeleton`

```html runnable
<import-element from="https://cdn.jsdelivr.net/npm/@realmorg/ui@alpha/components/skeleton.html"></import-element>

<ui-skeleton shape="circle" width="12" height="12"></ui-skeleton>
<div>
  <ui-skeleton width="62" height="4"></ui-skeleton>
  <ui-skeleton width="50" height="4"></ui-skeleton>
</div>
```

### Parts

| Part | Slot | Renders |
| --- | --- | --- |
| `ui-skeleton` | (default) | A pulsing block in `var(--accent)` with `var(--radius-md)` corners. It has no slot and takes no content. |

### Attributes

| Attribute | Type | Default | Description |
| --- | --- | --- | --- |
| `width` | number | none | Width in spacing units (`calc(var(--spacing) * width)`). Without it the skeleton fills its container. |
| `height` | number | none | Height in spacing units. |
| `shape` | `"circle"` | none | Fully rounded corners, and the skeleton does not shrink in a flex row. Use with equal `width` and `height`. |

### CSS parts

None. The skeleton renders no shadow content.

<a id="spinner"></a>

## Spinner

A rotating loading indicator with the status role and an accessible label. [Docs and demos](https://realm.js.org/components/spinner/).

- File: `spinner.html`, import with `<import-element from="https://cdn.jsdelivr.net/npm/@realmorg/ui@alpha/components/spinner.html"></import-element>`
- Tags: `ui-spinner`

```html runnable
<import-element from="https://cdn.jsdelivr.net/npm/@realmorg/ui@alpha/components/spinner.html"></import-element>

<ui-spinner></ui-spinner>
```

### Parts

| Part | Slot | Renders |
| --- | --- | --- |
| `ui-spinner` | (default) | A rotating arc drawn with an inline `<svg>`. It has no slot and takes no content. |

### Attributes

| Attribute | Type | Default | Description |
| --- | --- | --- | --- |
| `size` | number | `16` | Width and height in pixels. Minimum `8`. |
| `label` | string | `"Loading"` | Accessible name of the spinner. |
| `variant` | `"default"` \| `"primary"` \| `"muted"` \| `"destructive"` | `"default"` | Colour of the arc. `default` uses `currentColor`. |

### CSS parts

None. Set `color` on the element to use a colour outside the variants.

<a id="table"></a>

## Table

A data table rendered from a list of columns and rows, plus the pattern for building a custom table from component state. [Docs and demos](https://realm.js.org/components/table/).

- File: `table.html`, import with `<import-element from="https://cdn.jsdelivr.net/npm/@realmorg/ui@alpha/components/table.html"></import-element>`
- Tags: `ui-table`

```html runnable
<import-element from="https://cdn.jsdelivr.net/npm/@realmorg/ui@alpha/components/table.html"></import-element>

<ui-table
  caption="A list of your recent invoices."
  columns='[{"key":"invoice","label":"Invoice"},{"key":"status","label":"Status"},{"key":"method","label":"Method"},{"key":"amount","label":"Amount"}]'
  rows='[{"id":"INV001","invoice":"INV001","status":"Paid","method":"Credit card","amount":"$250.00"},{"id":"INV002","invoice":"INV002","status":"Pending","method":"PayPal","amount":"$150.00"},{"id":"INV003","invoice":"INV003","status":"Unpaid","method":"Bank transfer","amount":"$350.00"}]'>
</ui-table>
```

### Attributes

| Attribute | Type | Default | Description |
| --- | --- | --- | --- |
| `columns` | array of `{ key: string, label?: string }` | `[]` | The columns, in order. `key` is required and selects the value from each row. `label` is the header text and falls back to `key`. Accepts JSON in the attribute, or an array through the `columns` property or a `_columns` binding. |
| `rows` | array of objects | `[]` | The rows. Each object's values are looked up by column key. Missing values render as empty cells. A row's `id`, when present, keys the row so updates reuse its DOM. Otherwise the index is used. Accepts JSON or an array, like `columns`. |
| `caption` | string | none | Text for the table's `<caption>`, shown under the table. The caption is hidden when this is not set. |

### Properties

`columns` and `rows` are also properties. Assigning a new array re-renders the table:

```html
<ui-table id="people"></ui-table>
<script>
  const table = document.getElementById("people");
  table.columns = [{ key: "name", label: "Name" }, { key: "email", label: "Email" }];
  table.rows = [{ id: 1, name: "Ada", email: "ada@example.com" }];
</script>
```

### Parts

`ui-table` has no child parts and no slots. It renders everything from `columns`, `rows` and `caption`. For cells
with markup, use a native table as shown above.

### CSS parts

| Part | Description |
| --- | --- |
| `table` | The native `<table>`. |
| `head` | Each header cell (`<th>`). |
| `row` | Each body row (`<tr>`). |
| `cell` | Each body cell (`<td>`). |

Rows get a bottom border and a muted background on hover. Cells do not wrap. Long tables scroll horizontally
inside the element.

<a id="alert-dialog"></a>

## Alert Dialog

A modal dialog with the alertdialog role that interrupts the user with a decision and only closes when an action is chosen. [Docs and demos](https://realm.js.org/components/alert-dialog/).

- File: `alert-dialog.html`, import with `<import-element from="https://cdn.jsdelivr.net/npm/@realmorg/ui@alpha/components/alert-dialog.html"></import-element>`
- Tags: `ui-alert-dialog`, `ui-alert-dialog-trigger`, `ui-alert-dialog-header`, `ui-alert-dialog-title`, `ui-alert-dialog-description`, `ui-alert-dialog-footer`, `ui-alert-dialog-action`, `ui-alert-dialog-cancel`
- Imports for you: `button.html`

```html runnable
<import-element from="https://cdn.jsdelivr.net/npm/@realmorg/ui@alpha/components/alert-dialog.html"></import-element>
<import-element from="https://cdn.jsdelivr.net/npm/@realmorg/ui@alpha/components/button.html"></import-element>

<ui-alert-dialog>
  <ui-alert-dialog-trigger><ui-button variant="outline">Delete account</ui-button></ui-alert-dialog-trigger>
  <ui-alert-dialog-header>
    <ui-alert-dialog-title>Are you absolutely sure?</ui-alert-dialog-title>
    <ui-alert-dialog-description>This permanently deletes your account and removes your data from our servers.</ui-alert-dialog-description>
  </ui-alert-dialog-header>
  <ui-alert-dialog-footer>
    <ui-alert-dialog-cancel>Cancel</ui-alert-dialog-cancel>
    <ui-alert-dialog-action value="delete" variant="destructive">Delete account</ui-alert-dialog-action>
  </ui-alert-dialog-footer>
</ui-alert-dialog>
```

### Parts

| Part | Slot | Renders |
| --- | --- | --- |
| `ui-alert-dialog` | | The modal `<dialog>` with `role="alertdialog"`, a grid with a gap between the header, content and footer. |
| `ui-alert-dialog-trigger` | `trigger` | Its content, outside the dialog. A click on it focuses the element inside and opens the dialog. |
| `ui-alert-dialog-header` | `header` | A column for the title and description. |
| `ui-alert-dialog-title` | (default) | An `<h2>` in `var(--text-lg)` semibold. Its text becomes the dialog's accessible name. |
| `ui-alert-dialog-description` | (default) | A `<p>` in muted `var(--text-sm)`. Its text becomes the dialog's accessible description. |
| `ui-alert-dialog-footer` | `footer` | A row of actions, aligned to the end and wrapped on narrow screens. |
| `ui-alert-dialog-cancel` | (default) | An outline `ui-button`. Closes the dialog with its `value`. |
| `ui-alert-dialog-action` | (default) | A `ui-button` in the chosen `variant`. Closes the dialog with its `value`. |

Parts with a slot assign themselves to it, so you do not write `slot`. Other content goes between the header and the
footer.

### ui-alert-dialog attributes

| Attribute | Type | Default | Description |
| --- | --- | --- | --- |
| `open` | boolean | `false` | Whether the dialog is open. Setting it opens the dialog modally. Reflects. |

### ui-alert-dialog-action attributes

| Attribute | Type | Default | Description |
| --- | --- | --- | --- |
| `value` | string | `"action"` | Stored in `returnValue` when the action closes the dialog. |
| `variant` | `"default"` \| `"destructive"` | `"default"` | The button variant. |

### ui-alert-dialog-cancel attributes

| Attribute | Type | Default | Description |
| --- | --- | --- | --- |
| `value` | string | `"cancel"` | Stored in `returnValue` when the cancel button closes the dialog. |

### Properties and methods

| Name | Description |
| --- | --- |
| `showModal()` | Opens the dialog modally. |
| `show()` | Same as `showModal()`. |
| `close(value?)` | Closes the dialog. When `value` is given it becomes `returnValue`. |
| `returnValue` | Read-only. The `value` of the chosen action or cancel part, or the value passed to `close(value)`. |
| `open` | Boolean property mirroring the attribute. |

### Commands

| Command | Action |
| --- | --- |
| `--show-modal` | Opens the dialog. |
| `--close` | Closes the dialog. |
| `--request-close` | Same as `--close`, because the alert dialog has no cancel step. |

`ui-button` also accepts the commands without the `--` prefix. A plain `<button>` needs the prefix.

### Events

| Event | Detail | Description |
| --- | --- | --- |
| `close` | none | Fired after the dialog has closed. Does not bubble. |

### CSS parts

| Element | Part | Description |
| --- | --- | --- |
| `ui-alert-dialog` | `dialog` | The native `<dialog>` element. |
| `ui-alert-dialog-title` | `title` | The `<h2>`. |
| `ui-alert-dialog-description` | `description` | The `<p>`. |
| `ui-alert-dialog-action`, `ui-alert-dialog-cancel` | `button` | The inner `ui-button`. |
| `ui-alert-dialog-trigger` | `trigger` | The wrapper that listens for clicks. |

<a id="command"></a>

## Command

A searchable list of commands with groups, shortcuts, an empty state and keyboard navigation, used inline or as a command palette inside a dialog. [Docs and demos](https://realm.js.org/components/command/).

- File: `command.html`, import with `<import-element from="https://cdn.jsdelivr.net/npm/@realmorg/ui@alpha/components/command.html"></import-element>`
- Tags: `ui-command`, `ui-command-empty`

```html runnable
<import-element from="https://cdn.jsdelivr.net/npm/@realmorg/ui@alpha/components/command.html"></import-element>
<import-element from="https://cdn.jsdelivr.net/npm/@realmorg/ui@alpha/components/toaster.html"></import-element>

<ui-command onselect="document.dispatchEvent(new CustomEvent('toast', { detail: { title: event.detail.label } }))">
  <optgroup label="Suggestions">
    <option value="calendar">Calendar</option>
    <option value="emoji">Search emoji</option>
    <option value="calculator">Calculator</option>
  </optgroup>
  <optgroup label="Settings">
    <option value="profile" data-shortcut="⌘P">Profile</option>
    <option value="billing" data-shortcut="⌘B">Billing</option>
    <option value="settings" data-shortcut="⌘S">Settings</option>
  </optgroup>
</ui-command>
<ui-toaster></ui-toaster>
```

### Parts

| Part | Slot | Renders |
| --- | --- | --- |
| `ui-command` | | A bordered column with the search input above the filtered list. Fills its container's width. |
| `<optgroup label="…">` | (data) | A group with the label as its heading. Groups after the first are separated by a line. |
| `<option>` | (data) | One row in the list, with the `data-shortcut` hint at the end. |
| `ui-command-empty` | `empty` | The text shown when nothing matches, in place of "No results found.". |

`<option>` and `<optgroup>` are hidden and used only as data. Changing them updates the list. Options
outside a group are listed without a heading.

### ui-command attributes

| Attribute | Type | Default | Description |
| --- | --- | --- | --- |
| `placeholder` | string | `"Type a command or search..."` | Placeholder of the search input. |

### Option attributes

| Attribute | Description |
| --- | --- |
| `value` | Sent in `event.detail.value`. Also matched by the search. Defaults to the option text. |
| `label` | Text shown in the list and matched by the search. Defaults to the option text. |
| `data-shortcut` | Shortcut hint shown at the end of the row. Display only. It does not bind a key. |

### Events

| Event | Detail | Description |
| --- | --- | --- |
| `select` | `{ value, label }` | Fired when an option is clicked or Enter is pressed on the highlighted option. Bubbles and is composed. |

### CSS parts

| Part | Description |
| --- | --- |
| `search` | The row with the search icon and input. |
| `input` | The search `<input>`. |
| `list` | The scrolling list (`role="listbox"`). |
| `item` | Each option row (`role="option"`). |
| `shortcut` | The shortcut hint in a row. |
| `empty` | The paragraph shown when nothing matches. |

<a id="context-menu"></a>

## Context Menu

A menu of actions that opens at the pointer when the user right-clicks an area, sharing its items with the dropdown menu. [Docs and demos](https://realm.js.org/components/context-menu/).

- File: `context-menu.html`, import with `<import-element from="https://cdn.jsdelivr.net/npm/@realmorg/ui@alpha/components/context-menu.html"></import-element>`
- Tags: `ui-context-menu`, `ui-context-menu-trigger`, `ui-context-menu-content`

```html runnable
<import-element from="https://cdn.jsdelivr.net/npm/@realmorg/ui@alpha/components/context-menu.html"></import-element>
<import-element from="https://cdn.jsdelivr.net/npm/@realmorg/ui@alpha/components/dropdown-menu.html"></import-element>
<import-element from="https://cdn.jsdelivr.net/npm/@realmorg/ui@alpha/components/toaster.html"></import-element>

<ui-context-menu onselect="document.dispatchEvent(new CustomEvent('toast', { detail: { title: 'Chose ' + event.detail.value } }))">
  <ui-context-menu-trigger><div>Right click here</div></ui-context-menu-trigger>
  <ui-context-menu-content>
    <ui-menu-item value="back">Back <ui-menu-shortcut>⌘[</ui-menu-shortcut></ui-menu-item>
    <ui-menu-item value="forward" disabled>Forward <ui-menu-shortcut>⌘]</ui-menu-shortcut></ui-menu-item>
    <ui-menu-item value="reload">Reload <ui-menu-shortcut>⌘R</ui-menu-shortcut></ui-menu-item>
    <ui-menu-separator></ui-menu-separator>
    <ui-menu-checkbox-item value="bookmarks" checked>Show Bookmarks</ui-menu-checkbox-item>
    <ui-menu-separator></ui-menu-separator>
    <ui-menu-label>People</ui-menu-label>
    <ui-menu-radio-group value="pedro">
      <ui-menu-radio-item value="pedro">Pedro Duarte</ui-menu-radio-item>
      <ui-menu-radio-item value="colm">Colm Tuite</ui-menu-radio-item>
    </ui-menu-radio-group>
  </ui-context-menu-content>
</ui-context-menu>
<ui-toaster></ui-toaster>
```

### Parts

| Part | Slot | Renders |
| --- | --- | --- |
| `ui-context-menu` | | Its parts, with no box of its own. |
| `ui-context-menu-trigger` | (default) | A block around the area. A `contextmenu` event inside it opens the menu at the pointer. |
| `ui-context-menu-content` | (default) | A bordered `popover="auto"` panel with `role="menu"`, positioned at the pointer with `position: fixed`. |
| `ui-menu-*` | (default) | Items, labels, groups, separators and shortcuts. See [Dropdown Menu](https://realm.js.org/components/dropdown-menu/). |

None of the parts has attributes of its own. See [Dropdown Menu](https://realm.js.org/components/dropdown-menu/) for the item attributes
(`value`, `disabled`, `variant`, `checked`).

### Events

| Event | Element | Detail | Description |
| --- | --- | --- | --- |
| `select` | `ui-menu-item`, `ui-menu-radio-item` | `{ value }` | Fired when an enabled item is chosen. Bubbles to `ui-context-menu`. The menu closes afterwards. |
| `select` | `ui-menu-checkbox-item` | `{ value, checked }` | Same, with the new checked state. |

### CSS parts

| Element | Part | Description |
| --- | --- | --- |
| `ui-context-menu-trigger` | `trigger` | The wrapper that listens for `contextmenu`. |
| `ui-context-menu-content` | `menu` | The focusgroup wrapper around the items. |

<a id="dialog"></a>

## Dialog

A modal dialog built on the native dialog element, with a trigger, header, title, description, footer, close parts and optional side placement. [Docs and demos](https://realm.js.org/components/dialog/).

- File: `dialog.html`, import with `<import-element from="https://cdn.jsdelivr.net/npm/@realmorg/ui@alpha/components/dialog.html"></import-element>`
- Tags: `ui-dialog`, `ui-dialog-trigger`, `ui-dialog-close`, `ui-dialog-header`, `ui-dialog-title`, `ui-dialog-description`, `ui-dialog-footer`

```html runnable
<import-element from="https://cdn.jsdelivr.net/npm/@realmorg/ui@alpha/components/button.html"></import-element>
<import-element from="https://cdn.jsdelivr.net/npm/@realmorg/ui@alpha/components/dialog.html"></import-element>
<import-element from="https://cdn.jsdelivr.net/npm/@realmorg/ui@alpha/components/field.html"></import-element>
<import-element from="https://cdn.jsdelivr.net/npm/@realmorg/ui@alpha/components/input.html"></import-element>
<import-element from="https://cdn.jsdelivr.net/npm/@realmorg/ui@alpha/components/label.html"></import-element>

<ui-dialog>
  <ui-dialog-trigger><ui-button variant="outline">Edit profile</ui-button></ui-dialog-trigger>
  <ui-dialog-header>
    <ui-dialog-title>Edit profile</ui-dialog-title>
    <ui-dialog-description>Make changes to your profile here. Click save when you are done.</ui-dialog-description>
  </ui-dialog-header>
  <ui-field>
    <ui-label for="dialog-profile-name">Name</ui-label>
    <ui-input id="dialog-profile-name" value="Pedro Duarte"></ui-input>
  </ui-field>
  <ui-dialog-footer>
    <ui-dialog-close><ui-button variant="outline">Cancel</ui-button></ui-dialog-close>
    <ui-dialog-close value="saved"><ui-button>Save changes</ui-button></ui-dialog-close>
  </ui-dialog-footer>
</ui-dialog>
```

### Parts

| Part | Slot | Renders |
| --- | --- | --- |
| `ui-dialog` | | The modal `<dialog>`, a grid with a gap between the header, content and footer, plus the close button. |
| `ui-dialog-trigger` | `trigger` | Its content, outside the dialog. A click on it focuses the element inside and opens the dialog. |
| `ui-dialog-header` | `header` | A column for the title and description at the top of the dialog. |
| `ui-dialog-title` | (default) | An `<h2>` in `var(--text-lg)` semibold. Its text becomes the dialog's accessible name. |
| `ui-dialog-description` | (default) | A `<p>` in muted `var(--text-sm)`. Its text becomes the dialog's accessible description. |
| `ui-dialog-footer` | `footer` | A row of actions at the bottom, aligned to the end and wrapped on narrow screens. |
| `ui-dialog-close` | (default) | Its content. A click on it closes the dialog without firing `cancel`. |

Parts with a slot assign themselves to it, so you do not write `slot`. Everything else goes in the default slot, the
dialog body, in source order.

### ui-dialog attributes

| Attribute | Type | Default | Description |
| --- | --- | --- | --- |
| `open` | boolean | `false` | Whether the dialog is open. Setting it opens the dialog modally. Reflects. |
| `side` | `"center"` \| `"top"` \| `"right"` \| `"bottom"` \| `"left"` | `"center"` | Where the dialog sits. Any value other than `center` attaches it to that edge as a full-height or full-width panel. |
| `hide-close` | boolean | `false` | Hides the close button in the top corner. Property name: `hideClose`. |
| `persistent` | boolean | `false` | Ignores Escape and backdrop clicks. The close button and `requestClose()` still work. |
| `label` | string | | Accessible name when there is no `ui-dialog-title`. |
| `description` | string | | Accessible description when there is no `ui-dialog-description`. |

### ui-dialog-close attributes

| Attribute | Type | Default | Description |
| --- | --- | --- | --- |
| `value` | string | | Stored in the dialog's `returnValue` when it closes. Leave it out to keep the previous value. |

### Properties and methods

| Name | Description |
| --- | --- |
| `showModal()` | Opens the dialog modally. |
| `show()` | Same as `showModal()`. The dialog is always modal. |
| `close(value?)` | Closes the dialog. When `value` is given it becomes `returnValue`. Does not fire `cancel`. |
| `requestClose(value?)` | Fires `cancel`, then closes with `value` unless the event was cancelled. |
| `returnValue` | Read-only. The value passed to the last `close(value)` or `ui-dialog-close`, or `""`. |
| `open` | Boolean property mirroring the attribute. |

### Commands

| Command | Action |
| --- | --- |
| `--show-modal` | Opens the dialog. |
| `--close` | Closes the dialog. |
| `--request-close` | Same as `requestClose()`. |

`ui-button` also accepts `show-modal`, `close` and `request-close` without the prefix.

### Events

| Event | Detail | Description |
| --- | --- | --- |
| `cancel` | none | Fired before the dialog closes from Escape, a backdrop click, the close button, `requestClose()` or `--request-close`. Cancelable. Does not bubble. |
| `close` | none | Fired after the dialog has closed, however it was closed. Does not bubble. |

### CSS parts

| Element | Part | Description |
| --- | --- | --- |
| `ui-dialog` | `dialog` | The native `<dialog>` element. Style the backdrop with `ui-dialog::part(dialog)::backdrop`. |
| `ui-dialog` | `body` | Wrapper around the default slot. |
| `ui-dialog` | `close` | The close button in the top corner. |
| `ui-dialog-title` | `title` | The `<h2>`. |
| `ui-dialog-description` | `description` | The `<p>`. |
| `ui-dialog-trigger` | `trigger` | The wrapper that listens for clicks. |
| `ui-dialog-close` | `close` | The wrapper that listens for clicks. |

<a id="drawer"></a>

## Drawer

A modal panel that slides up from the bottom edge with a drag handle, or in from another edge, for mobile-friendly menus and forms. [Docs and demos](https://realm.js.org/components/drawer/).

- File: `drawer.html`, import with `<import-element from="https://cdn.jsdelivr.net/npm/@realmorg/ui@alpha/components/drawer.html"></import-element>`
- Tags: `ui-drawer`, `ui-drawer-trigger`, `ui-drawer-close`, `ui-drawer-header`, `ui-drawer-title`, `ui-drawer-description`, `ui-drawer-footer`

```html runnable
<import-element from="https://cdn.jsdelivr.net/npm/@realmorg/ui@alpha/components/button.html"></import-element>
<import-element from="https://cdn.jsdelivr.net/npm/@realmorg/ui@alpha/components/drawer.html"></import-element>
<import-element from="https://cdn.jsdelivr.net/npm/@realmorg/ui@alpha/components/slider.html"></import-element>

<ui-drawer>
  <ui-drawer-trigger><ui-button variant="outline">Set daily goal</ui-button></ui-drawer-trigger>
  <ui-drawer-header>
    <ui-drawer-title>Move goal</ui-drawer-title>
    <ui-drawer-description>Set your daily activity goal.</ui-drawer-description>
  </ui-drawer-header>
  <ui-slider value="350" min="200" max="600" step="10" aria-label="Calories per day"></ui-slider>
  <ui-drawer-footer>
    <ui-drawer-close><ui-button>Submit</ui-button></ui-drawer-close>
    <ui-drawer-close><ui-button variant="outline">Cancel</ui-button></ui-drawer-close>
  </ui-drawer-footer>
</ui-drawer>
```

### Parts

| Part | Slot | Renders |
| --- | --- | --- |
| `ui-drawer` | | The modal `<dialog>` with the drag handle, a grid with a gap between the header, content and footer. |
| `ui-drawer-trigger` | `trigger` | Its content, outside the drawer. A click on it focuses the element inside and opens the drawer. |
| `ui-drawer-header` | `header` | A column for the title and description. |
| `ui-drawer-title` | (default) | An `<h2>` in `var(--text-base)` semibold. Its text becomes the drawer's accessible name. |
| `ui-drawer-description` | (default) | A `<p>` in muted `var(--text-sm)`. Its text becomes the drawer's accessible description. |
| `ui-drawer-footer` | `footer` | A column of full-width actions at the bottom. |
| `ui-drawer-close` | (default) | Its content. A click on it closes the drawer. |

Parts with a slot assign themselves to it, so you do not write `slot`. Everything else goes in the body in source
order.

### ui-drawer attributes

| Attribute | Type | Default | Description |
| --- | --- | --- | --- |
| `open` | boolean | `false` | Whether the drawer is open. Setting it opens the drawer modally. Reflects. |
| `side` | `"bottom"` \| `"top"` \| `"left"` \| `"right"` | `"bottom"` | The edge the drawer slides in from. |

### Properties and methods

| Name | Description |
| --- | --- |
| `showModal()` | Opens the drawer. |
| `show()` | Same as `showModal()`. |
| `close()` | Closes the drawer. |
| `open` | Boolean property mirroring the attribute. |

### Commands

| Command | Action |
| --- | --- |
| `--show-modal` | Opens the drawer. |
| `--close` | Closes the drawer. |
| `--request-close` | Same as `--close`. |

`ui-button` also accepts the commands without the `--` prefix. A plain `<button>` needs the prefix.

### Events

| Event | Detail | Description |
| --- | --- | --- |
| `close` | none | Fired after the drawer has closed. Does not bubble. |

### CSS parts

| Element | Part | Description |
| --- | --- | --- |
| `ui-drawer` | `dialog` | The native `<dialog>` element. |
| `ui-drawer` | `handle` | The drag handle. Visible only when `side` is `bottom`. |
| `ui-drawer` | `body` | Wrapper around the default slot. |
| `ui-drawer-title` | `title` | The `<h2>`. |
| `ui-drawer-description` | `description` | The `<p>`. |

<a id="dropdown-menu"></a>

## Dropdown Menu

A menu of actions that opens from a trigger, with labels, groups, separators, shortcuts, checkbox and radio items, keyboard navigation and a select event. [Docs and demos](https://realm.js.org/components/dropdown-menu/).

- File: `dropdown-menu.html`, import with `<import-element from="https://cdn.jsdelivr.net/npm/@realmorg/ui@alpha/components/dropdown-menu.html"></import-element>`
- Tags: `ui-dropdown-menu`, `ui-dropdown-menu-trigger`, `ui-dropdown-menu-content`, `ui-menu-item`, `ui-menu-checkbox-item`, `ui-menu-radio-group`, `ui-menu-radio-item`, `ui-menu-label`, `ui-menu-group`, `ui-menu-separator`, `ui-menu-shortcut`

```html runnable
<import-element from="https://cdn.jsdelivr.net/npm/@realmorg/ui@alpha/components/button.html"></import-element>
<import-element from="https://cdn.jsdelivr.net/npm/@realmorg/ui@alpha/components/dropdown-menu.html"></import-element>
<import-element from="https://cdn.jsdelivr.net/npm/@realmorg/ui@alpha/components/toaster.html"></import-element>

<ui-dropdown-menu onselect="document.dispatchEvent(new CustomEvent('toast', { detail: { title: 'Selected ' + event.detail.value } }))">
  <ui-dropdown-menu-trigger><ui-button variant="outline">Open menu</ui-button></ui-dropdown-menu-trigger>
  <ui-dropdown-menu-content>
    <ui-menu-label>My Account</ui-menu-label>
    <ui-menu-group>
      <ui-menu-item value="profile">Profile <ui-menu-shortcut>⇧⌘P</ui-menu-shortcut></ui-menu-item>
      <ui-menu-item value="billing">Billing <ui-menu-shortcut>⌘B</ui-menu-shortcut></ui-menu-item>
      <ui-menu-item value="settings">Settings <ui-menu-shortcut>⌘S</ui-menu-shortcut></ui-menu-item>
    </ui-menu-group>
    <ui-menu-separator></ui-menu-separator>
    <ui-menu-item value="logout" variant="destructive">Log out <ui-menu-shortcut>⇧⌘Q</ui-menu-shortcut></ui-menu-item>
  </ui-dropdown-menu-content>
</ui-dropdown-menu>
<ui-toaster></ui-toaster>
```

### Parts

| Part | Slot | Renders |
| --- | --- | --- |
| `ui-dropdown-menu` | | Its parts, with no box of its own. |
| `ui-dropdown-menu-trigger` | (default) | An inline block around the trigger. A click on it focuses the element inside and toggles the menu. |
| `ui-dropdown-menu-content` | (default) | A bordered `popover="auto"` panel with `role="menu"`, anchored to the trigger. |
| `ui-menu-item` | (default) | A row with `role="menuitem"`: an optional leading `<svg>`, the label and an optional shortcut. |
| `ui-menu-checkbox-item` | (default) | A row with `role="menuitemcheckbox"` and a check mark when `checked`. |
| `ui-menu-radio-group` | (default) | A group (`role="group"`) that keeps one radio item checked. |
| `ui-menu-radio-item` | (default) | A row with `role="menuitemradio"` and a dot when checked. Place it in `ui-menu-radio-group`. |
| `ui-menu-label` | (default) | A heading row in `var(--text-sm)` medium. Not focusable. |
| `ui-menu-group` | (default) | A block with `role="group"` around related items. |
| `ui-menu-separator` | (default) | A one-pixel line with `role="separator"`. |
| `ui-menu-shortcut` | (default) | Muted `var(--text-xs)` text pushed to the end of its item. Place it in an item. |

The `ui-menu-*` parts work the same in `ui-context-menu-content` and `ui-menubar-content`.

### ui-dropdown-menu-content attributes

| Attribute | Type | Default | Description |
| --- | --- | --- | --- |
| `placement` | `"bottom-start"` \| `"bottom-end"` \| `"top-start"` \| `"top-end"` | `"bottom-start"` | Where the menu opens relative to the trigger. |

### ui-menu-item attributes

| Attribute | Type | Default | Description |
| --- | --- | --- | --- |
| `value` | string | | Sent as `event.detail.value` when the item is chosen. |
| `disabled` | boolean | `false` | Ignores clicks and keys, sets `aria-disabled` and skips the item in keyboard navigation. |
| `variant` | `"default"` \| `"destructive"` | `"default"` | Destructive items use the destructive colour. |

### ui-menu-checkbox-item attributes

| Attribute | Type | Default | Description |
| --- | --- | --- | --- |
| `value` | string | | Sent in `event.detail.value`. |
| `checked` | boolean | `false` | Whether the item is checked. Toggled when chosen. Reflects. |
| `disabled` | boolean | `false` | Ignores clicks and keys and skips the item in keyboard navigation. |

### ui-menu-radio-group and ui-menu-radio-item attributes

| Element | Attribute | Type | Default | Description |
| --- | --- | --- | --- | --- |
| `ui-menu-radio-group` | `value` | string | | The checked item's value. Updated when an item is chosen. Reflects. |
| `ui-menu-radio-item` | `value` | string | | The item's value. |
| `ui-menu-radio-item` | `disabled` | boolean | `false` | Ignores clicks and keys and skips the item in keyboard navigation. |

### Events

| Event | Element | Detail | Description |
| --- | --- | --- | --- |
| `select` | `ui-menu-item`, `ui-menu-radio-item` | `{ value }` | Fired when an enabled item is clicked or activated with Enter or Space. Bubbles and is composed, so you can listen on `ui-dropdown-menu`. The menu closes afterwards. |
| `select` | `ui-menu-checkbox-item` | `{ value, checked }` | Same, with the new checked state. |

### CSS parts

| Element | Part | Description |
| --- | --- | --- |
| `ui-dropdown-menu-trigger` | `trigger` | The wrapper that listens for clicks. It is the anchor for positioning. |
| `ui-dropdown-menu-content` | `menu` | The focusgroup wrapper around the items. |
| `ui-menu-checkbox-item`, `ui-menu-radio-item` | `indicator` | The check mark or dot. |

### CSS states

| Name | Description |
| --- | --- |
| `ui-menu-item:state(attr-disabled)` | Matches disabled items. |
| `ui-menu-item:state(attr-variant-is-destructive)` | Matches destructive items. |
| `ui-menu-checkbox-item:state(attr-checked)` | Matches checked checkbox items. Radio items have the same state. |
| `ui-dropdown-menu-content:popover-open` | Matches while the menu is open. |

<a id="hover-card"></a>

## Hover Card

A card that previews the content behind a link when the pointer rests on it or it receives keyboard focus. [Docs and demos](https://realm.js.org/components/hover-card/).

- File: `hover-card.html`, import with `<import-element from="https://cdn.jsdelivr.net/npm/@realmorg/ui@alpha/components/hover-card.html"></import-element>`
- Tags: `ui-hover-card`, `ui-hover-card-trigger`, `ui-hover-card-content`

```html runnable
<import-element from="https://cdn.jsdelivr.net/npm/@realmorg/ui@alpha/components/avatar.html"></import-element>
<import-element from="https://cdn.jsdelivr.net/npm/@realmorg/ui@alpha/components/button.html"></import-element>
<import-element from="https://cdn.jsdelivr.net/npm/@realmorg/ui@alpha/components/hover-card.html"></import-element>

<ui-hover-card>
  <ui-hover-card-trigger><ui-button variant="link">@realmorg</ui-button></ui-hover-card-trigger>
  <ui-hover-card-content width="80">
    <div>
      <ui-avatar><ui-avatar-fallback>R</ui-avatar-fallback></ui-avatar>
      <div>
        <strong>@realmorg</strong>
        <span>Native-first custom elements, written as HTML.</span>
        <span>Joined December 2021</span>
      </div>
    </div>
  </ui-hover-card-content>
</ui-hover-card>
```

### Parts

| Part | Slot | Renders |
| --- | --- | --- |
| `ui-hover-card` | | Its parts, with no box of its own. Holds the delays. |
| `ui-hover-card-trigger` | (default) | An inline block around the trigger. Pointer enter and focus schedule the card to open. Pointer leave and focus out schedule it to close. |
| `ui-hover-card-content` | (default) | A bordered `popover="manual"` panel with padding, anchored to the trigger. The pointer over it keeps it open. |

### ui-hover-card attributes

| Attribute | Type | Default | Description |
| --- | --- | --- | --- |
| `open-delay` | number | `500` | Milliseconds before the card opens. Minimum `0`. Property name: `openDelay`. |
| `close-delay` | number | `300` | Milliseconds before the card closes. Minimum `0`. Property name: `closeDelay`. |

### ui-hover-card-content attributes

| Attribute | Type | Default | Description |
| --- | --- | --- | --- |
| `placement` | `"bottom"` \| `"top"` \| `"left"` \| `"right"` | `"bottom"` | Side of the trigger the card appears on. |
| `width` | number | `64` | Card width in spacing units, where `n` means `calc(var(--spacing) * n)`. |

### CSS parts

| Element | Part | Description |
| --- | --- | --- |
| `ui-hover-card-trigger` | `trigger` | The wrapper that listens for pointer and focus events. |
| `ui-hover-card-content` | `content` | The padded box inside the card. |

<a id="popover"></a>

## Popover

Floating content anchored to a trigger, built on the native popover attribute with light dismiss and placement. [Docs and demos](https://realm.js.org/components/popover/).

- File: `popover.html`, import with `<import-element from="https://cdn.jsdelivr.net/npm/@realmorg/ui@alpha/components/popover.html"></import-element>`
- Tags: `ui-popover`, `ui-popover-trigger`, `ui-popover-content`, `ui-popover-header`, `ui-popover-title`, `ui-popover-description`

```html runnable
<import-element from="https://cdn.jsdelivr.net/npm/@realmorg/ui@alpha/components/button.html"></import-element>
<import-element from="https://cdn.jsdelivr.net/npm/@realmorg/ui@alpha/components/field.html"></import-element>
<import-element from="https://cdn.jsdelivr.net/npm/@realmorg/ui@alpha/components/input.html"></import-element>
<import-element from="https://cdn.jsdelivr.net/npm/@realmorg/ui@alpha/components/label.html"></import-element>
<import-element from="https://cdn.jsdelivr.net/npm/@realmorg/ui@alpha/components/popover.html"></import-element>

<ui-popover>
  <ui-popover-trigger><ui-button variant="outline">Open popover</ui-button></ui-popover-trigger>
  <ui-popover-content width="80">
    <ui-popover-header>
      <ui-popover-title>Dimensions</ui-popover-title>
      <ui-popover-description>Set the dimensions for the layer.</ui-popover-description>
    </ui-popover-header>
    <ui-field orientation="horizontal">
      <ui-label for="popover-width">Width</ui-label>
      <ui-input id="popover-width" value="100%"></ui-input>
    </ui-field>
    <ui-field orientation="horizontal">
      <ui-label for="popover-height">Height</ui-label>
      <ui-input id="popover-height" value="25px"></ui-input>
    </ui-field>
  </ui-popover-content>
</ui-popover>
```

### Parts

| Part | Slot | Renders |
| --- | --- | --- |
| `ui-popover` | | Its parts, with no box of its own. |
| `ui-popover-trigger` | (default) | An inline block around the trigger. A click on it focuses the element inside and toggles the content, anchored to the trigger. |
| `ui-popover-content` | (default) | A bordered card with padding, a gap between children and `popover="auto"`. |
| `ui-popover-header` | (default) | A column for the title and description, with a small gap. |
| `ui-popover-title` | (default) | An `<h3>` in `var(--text-sm)` medium. |
| `ui-popover-description` | (default) | A `<p>` in muted `var(--text-sm)`. |

### ui-popover-content attributes

| Attribute | Type | Default | Description |
| --- | --- | --- | --- |
| `placement` | `"bottom"` \| `"top"` \| `"left"` \| `"right"` | `"bottom"` | Side of the anchor the popover appears on. It flips when there is not enough room. |
| `width` | number | `72` | Width in spacing units, where `n` means `calc(var(--spacing) * n)`. |
| `popover` | `"auto"` \| `"manual"` | `"auto"` | The native popover attribute. Set on connect when missing. |

### Properties and methods

`ui-popover-content` has the native popover methods: `showPopover(options?)`, `hidePopover()` and
`togglePopover(options?)`. Use `:popover-open` to check whether it is open.

### Events

| Event | Element | Detail | Description |
| --- | --- | --- | --- |
| `beforetoggle` | `ui-popover-content` | `ToggleEvent` | Native. Fired before the popover opens or closes. Cancelable when opening. |
| `toggle` | `ui-popover-content` | `ToggleEvent` | Native. Fired after it opens or closes. `event.newState` is `"open"` or `"closed"`. |

### CSS parts

| Element | Part | Description |
| --- | --- | --- |
| `ui-popover-trigger` | `trigger` | The wrapper that listens for clicks. |
| `ui-popover-title` | `title` | The `<h3>`. |
| `ui-popover-description` | `description` | The `<p>`. |

### CSS states

| Name | Description |
| --- | --- |
| `ui-popover-content:popover-open` | Native pseudo-class that matches while the popover is shown. |

<a id="sheet"></a>

## Sheet

A modal panel that slides in from an edge of the screen, for navigation, filters or secondary forms. [Docs and demos](https://realm.js.org/components/sheet/).

- File: `sheet.html`, import with `<import-element from="https://cdn.jsdelivr.net/npm/@realmorg/ui@alpha/components/sheet.html"></import-element>`
- Tags: `ui-sheet`, `ui-sheet-trigger`, `ui-sheet-close`, `ui-sheet-header`, `ui-sheet-title`, `ui-sheet-description`, `ui-sheet-footer`
- Imports for you: `dialog.html`

```html runnable
<import-element from="https://cdn.jsdelivr.net/npm/@realmorg/ui@alpha/components/button.html"></import-element>
<import-element from="https://cdn.jsdelivr.net/npm/@realmorg/ui@alpha/components/field.html"></import-element>
<import-element from="https://cdn.jsdelivr.net/npm/@realmorg/ui@alpha/components/input.html"></import-element>
<import-element from="https://cdn.jsdelivr.net/npm/@realmorg/ui@alpha/components/label.html"></import-element>
<import-element from="https://cdn.jsdelivr.net/npm/@realmorg/ui@alpha/components/sheet.html"></import-element>

<ui-sheet>
  <ui-sheet-trigger><ui-button variant="outline">Open sheet</ui-button></ui-sheet-trigger>
  <ui-sheet-header>
    <ui-sheet-title>Edit profile</ui-sheet-title>
    <ui-sheet-description>Make changes to your profile here. Click save when you are done.</ui-sheet-description>
  </ui-sheet-header>
  <ui-field>
    <ui-label for="sheet-profile-name">Name</ui-label>
    <ui-input id="sheet-profile-name" value="Pedro Duarte"></ui-input>
  </ui-field>
  <ui-field>
    <ui-label for="sheet-profile-username">Username</ui-label>
    <ui-input id="sheet-profile-username" value="@peduarte"></ui-input>
  </ui-field>
  <ui-sheet-footer>
    <ui-sheet-close><ui-button>Save changes</ui-button></ui-sheet-close>
  </ui-sheet-footer>
</ui-sheet>
```

### Parts

| Part | Slot | Renders |
| --- | --- | --- |
| `ui-sheet` | | A `ui-dialog` attached to the `side` edge. |
| `ui-sheet-trigger` | `trigger` | Its content, outside the sheet. A click on it focuses the element inside and opens the sheet. |
| `ui-sheet-header` | `header` | A column for the title and description at the top. |
| `ui-sheet-title` | (default) | An `<h2>` in `var(--text-lg)` semibold. Its text becomes the sheet's accessible name. |
| `ui-sheet-description` | (default) | A `<p>` in muted `var(--text-sm)`. Its text becomes the sheet's accessible description. |
| `ui-sheet-footer` | `footer` | A row of actions at the bottom, aligned to the end. |
| `ui-sheet-close` | (default) | Its content. A click on it closes the sheet. |

Parts with a slot assign themselves to it, so you do not write `slot`. Everything else goes in the body in source
order.

### ui-sheet attributes

| Attribute | Type | Default | Description |
| --- | --- | --- | --- |
| `open` | boolean | `false` | Whether the sheet is open. Setting it opens the sheet modally. Reflects. |
| `side` | `"top"` \| `"right"` \| `"bottom"` \| `"left"` | `"right"` | The edge the sheet is attached to. |

### Properties and methods

| Name | Description |
| --- | --- |
| `showModal()` | Opens the sheet. |
| `show()` | Same as `showModal()`. |
| `close()` | Closes the sheet. |
| `requestClose()` | Fires `cancel` on the inner dialog, then closes unless it was cancelled. |
| `open` | Boolean property mirroring the attribute. |

### Commands

| Command | Action |
| --- | --- |
| `--show-modal` | Opens the sheet. |
| `--close` | Closes the sheet. |
| `--request-close` | Same as `requestClose()`. |

`ui-button` also accepts the commands without the `--` prefix. A plain `<button>` needs the prefix.

### Events

| Event | Detail | Description |
| --- | --- | --- |
| `close` | none | Fired after the sheet has closed. Does not bubble. |

### CSS parts

| Element | Part | Description |
| --- | --- | --- |
| `ui-sheet` | `dialog` | The inner `ui-dialog` element. Its own parts are not exported. |
| `ui-sheet-title` | `title` | The `<h2>`. |
| `ui-sheet-description` | `description` | The `<p>`. |

<a id="toaster"></a>

## Toaster

A live region that shows short, auto-dismissing notifications triggered by a document event or a method call. [Docs and demos](https://realm.js.org/components/toaster/).

- File: `toaster.html`, import with `<import-element from="https://cdn.jsdelivr.net/npm/@realmorg/ui@alpha/components/toaster.html"></import-element>`
- Tags: `ui-toaster`

```html runnable
<import-element from="https://cdn.jsdelivr.net/npm/@realmorg/ui@alpha/components/button.html"></import-element>
<import-element from="https://cdn.jsdelivr.net/npm/@realmorg/ui@alpha/components/toaster.html"></import-element>

<ui-button variant="outline" onclick="document.dispatchEvent(new CustomEvent('toast', { detail: { title: 'Event created', description: 'Sunday, December 3 at 9:00 AM' } }))">Show toast</ui-button>
<ui-toaster></ui-toaster>
```

### Parts

| Part | Slot | Renders |
| --- | --- | --- |
| `ui-toaster` | | The fixed toast list. Each toast is rendered from the data passed to `toast()` or the `toast` event, so there are no toast parts to write. |

### Attributes

| Attribute | Type | Default | Description |
| --- | --- | --- | --- |
| `position` | `"bottom-right"` \| `"bottom-left"` \| `"bottom-center"` \| `"top-right"` \| `"top-left"` \| `"top-center"` | `"bottom-right"` | Where toasts appear. |
| `duration` | number | `4000` | Default milliseconds before a toast closes. `0` keeps toasts open until dismissed. Minimum `0`. |

### Toast options

The `detail` of a `toast` event and the argument of `toast()` take the same fields. All are optional.

| Field | Type | Default | Description |
| --- | --- | --- | --- |
| `title` | string | `""` | Bold first line. |
| `description` | string | `""` | Muted second line. Hidden when empty. |
| `variant` | `"default"` \| `"destructive"` | `"default"` | Destructive toasts use the destructive colour. |
| `duration` | number | the `duration` attribute | Milliseconds before this toast closes. `0` keeps it open. |

### Properties and methods

| Name | Description |
| --- | --- |
| `toast(options?)` | Shows a toast and returns its numeric id. |
| `dismiss(id)` | Removes the toast with that id. Unknown ids are ignored. |

### Events

| Event | Target | Detail | Description |
| --- | --- | --- | --- |
| `toast` | `document` | toast options | Listened for, not dispatched. Every connected `ui-toaster` shows a toast for each event. |

### CSS parts

| Part | Description |
| --- | --- |
| `region` | The `<ol>` that holds the toasts (`popover="manual"`, `aria-live="polite"`). |
| `toast` | Each toast `<li>`. Destructive toasts have `data-variant="destructive"`. |

<a id="tooltip"></a>

## Tooltip

A short text label that appears next to an element on hover or keyboard focus, built on a manual popover with anchor positioning. [Docs and demos](https://realm.js.org/components/tooltip/).

- File: `tooltip.html`, import with `<import-element from="https://cdn.jsdelivr.net/npm/@realmorg/ui@alpha/components/tooltip.html"></import-element>`
- Tags: `ui-tooltip`, `ui-tooltip-trigger`, `ui-tooltip-content`

```html runnable
<import-element from="https://cdn.jsdelivr.net/npm/@realmorg/ui@alpha/components/button.html"></import-element>
<import-element from="https://cdn.jsdelivr.net/npm/@realmorg/ui@alpha/components/tooltip.html"></import-element>

<ui-tooltip>
  <ui-tooltip-trigger><ui-button variant="outline">Hover me</ui-button></ui-tooltip-trigger>
  <ui-tooltip-content>Add to library</ui-tooltip-content>
</ui-tooltip>
```

### Parts

| Part | Slot | Renders |
| --- | --- | --- |
| `ui-tooltip` | | Its parts, with no box of its own. |
| `ui-tooltip-trigger` | (default) | An inline block around the trigger. Pointer enter and focus show the tooltip. Pointer leave, focus out and Escape hide it. |
| `ui-tooltip-content` | (default) | A `popover="manual"` element with `role="tooltip"`, in `var(--text-xs)` on the foreground colour, anchored to the trigger. |

### ui-tooltip attributes

| Attribute | Type | Default | Description |
| --- | --- | --- | --- |
| `delay` | number | `300` | Milliseconds before the tooltip opens. Minimum `0`. |

### ui-tooltip-content attributes

| Attribute | Type | Default | Description |
| --- | --- | --- | --- |
| `placement` | `"top"` \| `"bottom"` \| `"left"` \| `"right"` | `"top"` | Side of the trigger the tooltip appears on. |

### CSS parts

| Element | Part | Description |
| --- | --- | --- |
| `ui-tooltip-trigger` | `trigger` | The wrapper that listens for pointer and focus events. |

### CSS states

| Name | Description |
| --- | --- |
| `ui-tooltip-content:popover-open` | Matches while the tooltip is shown. |

<a id="accordion"></a>

## Accordion

A vertically stacked set of collapsible sections built on native details and summary, with one or several sections open at a time. [Docs and demos](https://realm.js.org/components/accordion/).

- File: `accordion.html`, import with `<import-element from="https://cdn.jsdelivr.net/npm/@realmorg/ui@alpha/components/accordion.html"></import-element>`
- Tags: `ui-accordion`, `ui-accordion-item`, `ui-accordion-trigger`, `ui-accordion-content`

```html runnable
<import-element from="https://cdn.jsdelivr.net/npm/@realmorg/ui@alpha/components/accordion.html"></import-element>

<ui-accordion>
  <ui-accordion-item open>
    <ui-accordion-trigger>Is it accessible?</ui-accordion-trigger>
    <ui-accordion-content>
      Yes. Each item is a native details element, so it works with the keyboard and screen readers.
    </ui-accordion-content>
  </ui-accordion-item>
  <ui-accordion-item>
    <ui-accordion-trigger>Is it styled?</ui-accordion-trigger>
    <ui-accordion-content>
      Yes. It uses the theme tokens, so it follows your colours, radii and dark mode.
    </ui-accordion-content>
  </ui-accordion-item>
  <ui-accordion-item>
    <ui-accordion-trigger>Is it animated?</ui-accordion-trigger>
    <ui-accordion-content>The chevron rotates when an item opens.</ui-accordion-content>
  </ui-accordion-item>
</ui-accordion>
```

### Parts

| Element | Fills | Renders |
| --- | --- | --- |
| `ui-accordion` | | The container. Fills its container's width. |
| `ui-accordion-item` | the default slot of `ui-accordion` | A native `<details>` with a `<summary>` row and a chevron. |
| `ui-accordion-trigger` | the `<summary>` of its `ui-accordion-item` | The summary row's content. Accepts inline markup. |
| `ui-accordion-content` | the body of its `ui-accordion-item` | The content shown when the item is open, with bottom padding. |

### Attributes

#### ui-accordion

| Attribute | Type | Default | Description |
| --- | --- | --- | --- |
| `type` | `"single"` \| `"multiple"` | `"single"` | In `single` mode, opening an item closes every other open item in the accordion. |

#### ui-accordion-item

| Attribute | Type | Default | Description |
| --- | --- | --- | --- |
| `open` | boolean | `false` | Whether the item is open. Reflects. |
| `disabled` | boolean | `false` | Stops the user from opening or closing the item. Setting `open` from script still works. |

`ui-accordion-trigger` and `ui-accordion-content` have no attributes.

### Events

| Event | Element | Description |
| --- | --- | --- |
| `toggle` | `ui-accordion-item` | Fires when the user opens or closes the item. Bubbles. Read `event.target.open`. Setting `open` from script, including the automatic closing in `single` mode, does not fire it. |

### CSS parts

| Part | Element | Description |
| --- | --- | --- |
| `summary` | `ui-accordion-item` | The native `<summary>` row. |

<a id="breadcrumb"></a>

## Breadcrumb

A trail of links showing where the current page sits in the site hierarchy. [Docs and demos](https://realm.js.org/components/breadcrumb/).

- File: `breadcrumb.html`, import with `<import-element from="https://cdn.jsdelivr.net/npm/@realmorg/ui@alpha/components/breadcrumb.html"></import-element>`
- Tags: `ui-breadcrumb`, `ui-breadcrumb-list`, `ui-breadcrumb-item`, `ui-breadcrumb-link`, `ui-breadcrumb-page`, `ui-breadcrumb-separator`, `ui-breadcrumb-ellipsis`

```html runnable
<import-element from="https://cdn.jsdelivr.net/npm/@realmorg/ui@alpha/components/breadcrumb.html"></import-element>

<ui-breadcrumb>
  <ui-breadcrumb-list>
    <ui-breadcrumb-item><ui-breadcrumb-link href="#">Home</ui-breadcrumb-link></ui-breadcrumb-item>
    <ui-breadcrumb-separator></ui-breadcrumb-separator>
    <ui-breadcrumb-item><ui-breadcrumb-link href="#">Components</ui-breadcrumb-link></ui-breadcrumb-item>
    <ui-breadcrumb-separator></ui-breadcrumb-separator>
    <ui-breadcrumb-item><ui-breadcrumb-page>Breadcrumb</ui-breadcrumb-page></ui-breadcrumb-item>
  </ui-breadcrumb-list>
</ui-breadcrumb>
```

### Parts

| Element | Fills | Renders |
| --- | --- | --- |
| `ui-breadcrumb` | | A `<nav>` landmark named by `label`. |
| `ui-breadcrumb-list` | the `<nav>` of `ui-breadcrumb` | An `<ol>` that lays out items and separators in a wrapping row of small muted text. |
| `ui-breadcrumb-item` | the `<ol>` of `ui-breadcrumb-list` | A list item (`role="listitem"`). |
| `ui-breadcrumb-link` | a `ui-breadcrumb-item` | An `<a>` that turns the foreground colour on hover. |
| `ui-breadcrumb-page` | a `ui-breadcrumb-item` | The current page: a `<span>` with `aria-current="page"` in the foreground colour. |
| `ui-breadcrumb-separator` | the `<ol>`, between items | A chevron, or its own content. Hidden from assistive technology. |
| `ui-breadcrumb-ellipsis` | a `ui-breadcrumb-item` or a button | Three dots with a visually hidden label (default "More"). |

### Attributes

#### ui-breadcrumb

| Attribute | Type | Default | Description |
| --- | --- | --- | --- |
| `label` | string | `"Breadcrumb"` | Accessible name of the `<nav>` landmark. |

#### ui-breadcrumb-link

| Attribute | Type | Default | Description |
| --- | --- | --- | --- |
| `href` | string | | Link target. |

The other parts have no attributes.

### Slots

| Slot | Element | Description |
| --- | --- | --- |
| (default) | `ui-breadcrumb-separator` | Replaces the chevron, for example with `/`. |
| (default) | `ui-breadcrumb-ellipsis` | The visually hidden label. Defaults to "More". |

### CSS parts

| Part | Element | Description |
| --- | --- | --- |
| `list` | `ui-breadcrumb-list` | The `<ol>`. |
| `link` | `ui-breadcrumb-link` | The `<a>`. |
| `page` | `ui-breadcrumb-page` | The `<span>` with `aria-current="page"`. |
| `label` | `ui-breadcrumb-ellipsis` | The visually hidden label. |

<a id="collapsible"></a>

## Collapsible

A single section that shows and hides its content when its trigger is activated, built on native details and summary. [Docs and demos](https://realm.js.org/components/collapsible/).

- File: `collapsible.html`, import with `<import-element from="https://cdn.jsdelivr.net/npm/@realmorg/ui@alpha/components/collapsible.html"></import-element>`
- Tags: `ui-collapsible`, `ui-collapsible-trigger`, `ui-collapsible-content`

```html runnable
<import-element from="https://cdn.jsdelivr.net/npm/@realmorg/ui@alpha/components/collapsible.html"></import-element>
<import-element from="https://cdn.jsdelivr.net/npm/@realmorg/ui@alpha/components/item.html"></import-element>

<ui-collapsible>
  <ui-collapsible-trigger>
    3 starred repositories
    <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" aria-hidden="true"><path d="m7 15 5 5 5-5M7 9l5-5 5 5" /></svg>
  </ui-collapsible-trigger>
  <ui-collapsible-content>
    <ui-item variant="outline" size="sm">realm/core</ui-item>
    <ui-item variant="outline" size="sm">realm/ui</ui-item>
    <ui-item variant="outline" size="sm">realm/site</ui-item>
  </ui-collapsible-content>
</ui-collapsible>
```

### Parts

| Element | Fills | Renders |
| --- | --- | --- |
| `ui-collapsible` | | A native `<details>`. Fills its container's width. |
| `ui-collapsible-trigger` | the `<summary>` of `ui-collapsible` | A row with its content spread to both ends, in small semibold text. Icons are sized to `calc(var(--spacing) * 4)`. |
| `ui-collapsible-content` | the body of `ui-collapsible` | A column with `calc(var(--spacing) * 2)` gaps, shown when open. |

### Attributes

#### ui-collapsible

| Attribute | Type | Default | Description |
| --- | --- | --- | --- |
| `open` | boolean | `false` | Whether the content is shown. Reflects. |
| `disabled` | boolean | `false` | Stops the user from toggling. Setting `open` from script still works. |

`ui-collapsible-trigger` and `ui-collapsible-content` have no attributes.

### Events

| Event | Element | Description |
| --- | --- | --- |
| `toggle` | `ui-collapsible` | Fires when the user opens or closes it. Bubbles. Read `event.target.open`. Not fired when `open` is set from script. |

### CSS parts

| Part | Element | Description |
| --- | --- | --- |
| `trigger` | `ui-collapsible` | The native `<summary>`. |

<a id="menubar"></a>

## Menubar

A horizontal bar of menus, like the File and Edit menus of a desktop application, with triggers, menus and shared menu items. [Docs and demos](https://realm.js.org/components/menubar/).

- File: `menubar.html`, import with `<import-element from="https://cdn.jsdelivr.net/npm/@realmorg/ui@alpha/components/menubar.html"></import-element>`
- Tags: `ui-menubar`, `ui-menubar-menu`, `ui-menubar-trigger`, `ui-menubar-content`

```html runnable
<import-element from="https://cdn.jsdelivr.net/npm/@realmorg/ui@alpha/components/dropdown-menu.html"></import-element>
<import-element from="https://cdn.jsdelivr.net/npm/@realmorg/ui@alpha/components/menubar.html"></import-element>

<ui-menubar>
  <ui-menubar-menu>
    <ui-menubar-trigger>File</ui-menubar-trigger>
    <ui-menubar-content>
      <ui-menu-item value="new-tab">New Tab <ui-menu-shortcut>⌘T</ui-menu-shortcut></ui-menu-item>
      <ui-menu-item value="new-window">New Window <ui-menu-shortcut>⌘N</ui-menu-shortcut></ui-menu-item>
      <ui-menu-separator></ui-menu-separator>
      <ui-menu-item value="print">Print… <ui-menu-shortcut>⌘P</ui-menu-shortcut></ui-menu-item>
    </ui-menubar-content>
  </ui-menubar-menu>
  <ui-menubar-menu>
    <ui-menubar-trigger>Edit</ui-menubar-trigger>
    <ui-menubar-content>
      <ui-menu-item value="undo">Undo <ui-menu-shortcut>⌘Z</ui-menu-shortcut></ui-menu-item>
      <ui-menu-item value="redo">Redo <ui-menu-shortcut>⇧⌘Z</ui-menu-shortcut></ui-menu-item>
      <ui-menu-separator></ui-menu-separator>
      <ui-menu-item value="cut">Cut</ui-menu-item>
      <ui-menu-item value="copy">Copy</ui-menu-item>
      <ui-menu-item value="paste" disabled>Paste</ui-menu-item>
    </ui-menubar-content>
  </ui-menubar-menu>
  <ui-menubar-menu>
    <ui-menubar-trigger>View</ui-menubar-trigger>
    <ui-menubar-content>
      <ui-menu-checkbox-item value="bookmarks" checked>Always Show Bookmarks Bar</ui-menu-checkbox-item>
      <ui-menu-separator></ui-menu-separator>
      <ui-menu-item value="reload">Reload <ui-menu-shortcut>⌘R</ui-menu-shortcut></ui-menu-item>
      <ui-menu-item value="fullscreen">Toggle Fullscreen</ui-menu-item>
    </ui-menubar-content>
  </ui-menubar-menu>
</ui-menubar>
```

### Parts

| Part | Slot | Renders |
| --- | --- | --- |
| `ui-menubar` | | The bordered bar, `role="menubar"`, with a gap between menus. |
| `ui-menubar-menu` | (default) | One menu: its trigger and content, with no box of its own. |
| `ui-menubar-trigger` | (default) | A `<button>` with `role="menuitem"` and `aria-haspopup="menu"` in `var(--text-sm)` medium. A click toggles its menu. |
| `ui-menubar-content` | (default) | A bordered `popover="auto"` panel with `role="menu"`, anchored under the trigger. |
| `ui-menu-*` | (default) | Items, labels, groups, separators and shortcuts. See [Dropdown Menu](https://realm.js.org/components/dropdown-menu/). |

None of the menubar parts has attributes of its own. See [Dropdown Menu](https://realm.js.org/components/dropdown-menu/) for the item
attributes and the `select` event.

### CSS parts

| Element | Part | Description |
| --- | --- | --- |
| `ui-menubar` | `bar` | The focusgroup wrapper around the slot. It uses `display: contents`, so it does not affect layout. |
| `ui-menubar-trigger` | `trigger` | The `<button>`. |
| `ui-menubar-content` | `menu` | The focusgroup wrapper around the items. |

<a id="navigation-menu"></a>

## Navigation Menu

A site navigation bar of links and triggers that open popover panels of further links on hover or click. [Docs and demos](https://realm.js.org/components/navigation-menu/).

- File: `navigation-menu.html`, import with `<import-element from="https://cdn.jsdelivr.net/npm/@realmorg/ui@alpha/components/navigation-menu.html"></import-element>`
- Tags: `ui-navigation-menu`, `ui-navigation-menu-list`, `ui-navigation-menu-item`, `ui-navigation-menu-trigger`, `ui-navigation-menu-content`, `ui-navigation-menu-link`

```html runnable
<import-element from="https://cdn.jsdelivr.net/npm/@realmorg/ui@alpha/components/navigation-menu.html"></import-element>

<ui-navigation-menu>
  <ui-navigation-menu-list>
    <ui-navigation-menu-item>
      <ui-navigation-menu-trigger>Getting started</ui-navigation-menu-trigger>
      <ui-navigation-menu-content width="72">
        <ui-navigation-menu-link href="#">
          <strong>Introduction</strong>
          <span>What REALM is and how it works.</span>
        </ui-navigation-menu-link>
        <ui-navigation-menu-link href="#">
          <strong>Installation</strong>
          <span>Add REALM to a page or a build.</span>
        </ui-navigation-menu-link>
      </ui-navigation-menu-content>
    </ui-navigation-menu-item>
    <ui-navigation-menu-item>
      <ui-navigation-menu-trigger>Components</ui-navigation-menu-trigger>
      <ui-navigation-menu-content width="80" columns="2">
        <ui-navigation-menu-link href="#">Alert Dialog</ui-navigation-menu-link>
        <ui-navigation-menu-link href="#">Hover Card</ui-navigation-menu-link>
        <ui-navigation-menu-link href="#">Progress</ui-navigation-menu-link>
        <ui-navigation-menu-link href="#">Tabs</ui-navigation-menu-link>
      </ui-navigation-menu-content>
    </ui-navigation-menu-item>
    <ui-navigation-menu-item>
      <ui-navigation-menu-link href="#" current>Docs</ui-navigation-menu-link>
    </ui-navigation-menu-item>
  </ui-navigation-menu-list>
</ui-navigation-menu>
```

### Parts

| Element | Fills | Renders |
| --- | --- | --- |
| `ui-navigation-menu` | | A `<nav>` landmark named by `label`. |
| `ui-navigation-menu-list` | the `<nav>` of `ui-navigation-menu` | A `<ul>` laying out the items in a row, with arrow-key navigation. |
| `ui-navigation-menu-item` | the `<ul>` of `ui-navigation-menu-list` | A list item (`role="listitem"`) and, when it has content, the popover panel. |
| `ui-navigation-menu-trigger` | a `ui-navigation-menu-item` | A `<button>` with a chevron and `aria-expanded` that opens the item's panel. |
| `ui-navigation-menu-content` | the popover panel of its `ui-navigation-menu-item` | A grid of links inside the panel. |
| `ui-navigation-menu-link` | a `ui-navigation-menu-item` or a `ui-navigation-menu-content` | An `<a>`. At the top level it looks like a trigger. Inside content it is a block row, where a first child is the title and a last child the muted description. |

### Attributes

#### ui-navigation-menu

| Attribute | Type | Default | Description |
| --- | --- | --- | --- |
| `label` | string | `"Main"` | Accessible name of the `<nav>` landmark. |

#### ui-navigation-menu-content

| Attribute | Type | Default | Description |
| --- | --- | --- | --- |
| `width` | number | | Width of the content in spacing units, so `width="72"` is `calc(var(--spacing) * 72)`. |
| `columns` | number | `1` | Number of equal grid columns for the links. |

#### ui-navigation-menu-link

| Attribute | Type | Default | Description |
| --- | --- | --- | --- |
| `href` | string | | Link target. |
| `current` | boolean | `false` | Highlights the link and sets `aria-current="page"`. |

`ui-navigation-menu-list`, `ui-navigation-menu-item` and `ui-navigation-menu-trigger` have no attributes.

### CSS parts

| Part | Element | Description |
| --- | --- | --- |
| `list` | `ui-navigation-menu-list` | The `<ul>`. |
| `panel` | `ui-navigation-menu-item` | The popover panel around the content. |
| `trigger` | `ui-navigation-menu-trigger` | The `<button>`. |
| `link` | `ui-navigation-menu-link` | The `<a>`. |

<a id="pagination"></a>

## Pagination

Page navigation with previous and next controls, numbered pages and ellipses, driven by a page number and a page count. [Docs and demos](https://realm.js.org/components/pagination/).

- File: `pagination.html`, import with `<import-element from="https://cdn.jsdelivr.net/npm/@realmorg/ui@alpha/components/pagination.html"></import-element>`
- Tags: `ui-pagination`

```html runnable
<import-element from="https://cdn.jsdelivr.net/npm/@realmorg/ui@alpha/components/pagination.html"></import-element>

<ui-pagination page="5" total="10"></ui-pagination>
```

### Attributes

| Attribute | Type | Default | Description |
| --- | --- | --- | --- |
| `page` | number | `1` | The current page. Integer, at least 1. Updated by the component when `href` is not set. |
| `total` | number | `1` | Number of pages. Integer, at least 1. |
| `siblings` | number | `1` | Pages shown on each side of the current page. Integer, at least 0. |
| `href` | string | | URL pattern for page links. `{page}` is replaced by the page number. When not set, the controls are buttons that update `page`. |
| `label` | string | `"Pagination"` | Accessible name of the `<nav>` landmark. |

### Events

| Event | Detail | Description |
| --- | --- | --- |
| `change` | `{ page: number }` | Fires before the page changes, from Previous, Next or a page number. Bubbles and is cancelable. `preventDefault()` keeps the current page and, in `href` mode, stops the link from navigating. Not fired for the current page or a page out of range. |

### CSS parts

| Part | Description |
| --- | --- |
| `list` | The `<ul>` that lays out the controls. |
| `previous` | The Previous control: a `<button>`, or an `<a>` in `href` mode. |
| `next` | The Next control: a `<button>`, or an `<a>` in `href` mode. |
| `page` | Each page number: a `<button>`, or an `<a>` in `href` mode. The current one has `aria-current="page"`. |

<a id="sidebar"></a>

## Sidebar

An application shell with a collapsible side navigation, grouped menus, a toggle button, a keyboard shortcut and an off-canvas mode on small screens. [Docs and demos](https://realm.js.org/components/sidebar/).

- File: `sidebar.html`, import with `<import-element from="https://cdn.jsdelivr.net/npm/@realmorg/ui@alpha/components/sidebar.html"></import-element>`
- Tags: `ui-sidebar-layout`, `ui-sidebar`, `ui-sidebar-header`, `ui-sidebar-footer`, `ui-sidebar-content`, `ui-sidebar-group`, `ui-sidebar-group-label`, `ui-sidebar-menu`, `ui-sidebar-menu-item`, `ui-sidebar-menu-button`, `ui-sidebar-menu-badge`, `ui-sidebar-trigger`, `ui-sidebar-inset`
- Imports for you: `button.html`

```html runnable
<import-element from="https://cdn.jsdelivr.net/npm/@realmorg/ui@alpha/components/avatar.html"></import-element>
<import-element from="https://cdn.jsdelivr.net/npm/@realmorg/ui@alpha/components/button.html"></import-element>
<import-element from="https://cdn.jsdelivr.net/npm/@realmorg/ui@alpha/components/dropdown-menu.html"></import-element>
<import-element from="https://cdn.jsdelivr.net/npm/@realmorg/ui@alpha/components/sidebar.html"></import-element>

<style>
  .sidebar-demo {
    width: min(calc(var(--spacing) * 160), calc(100vw - var(--spacing) * 28));
    height: calc(var(--spacing) * 96);
    overflow: hidden;
    contain: layout paint;
    border: 1px solid var(--border);
    border-radius: var(--radius-lg);
  }
  .sidebar-demo ui-sidebar-layout {
    height: 100%;
    min-height: 0;
  }
  .sidebar-demo ui-sidebar-layout::part(sidebar) {
    height: 100%;
  }
  .sidebar-demo header {
    display: flex;
    align-items: center;
    gap: calc(var(--spacing) * 2);
    padding: calc(var(--spacing) * 2) calc(var(--spacing) * 3);
    border-bottom: 1px solid var(--border);
    font-size: var(--text-sm);
    font-weight: var(--font-weight-medium);
  }
  .sidebar-demo .brand {
    display: flex;
    align-items: center;
    gap: calc(var(--spacing) * 2);
    font-size: var(--text-sm);
    font-weight: var(--font-weight-semibold);
    white-space: nowrap;
    overflow: hidden;
  }
  .sidebar-demo .logo {
    display: grid;
    place-items: center;
    flex-shrink: 0;
    width: calc(var(--spacing) * 8);
    height: calc(var(--spacing) * 8);
    border-radius: var(--radius-md);
    background: var(--primary);
    color: var(--primary-foreground);
  }
  .sidebar-demo .body {
    padding: calc(var(--spacing) * 4);
    font-size: var(--text-sm);
    color: var(--muted-foreground);
  }
</style>
<div class="sidebar-demo">
  <ui-sidebar-layout>
    <ui-sidebar>
      <ui-sidebar-header>
        <div class="brand"><span class="logo">A</span><span>Acme Inc</span></div>
      </ui-sidebar-header>
      <ui-sidebar-content>
        <ui-sidebar-group>
          <ui-sidebar-group-label>Platform</ui-sidebar-group-label>
          <ui-sidebar-menu>
            <ui-sidebar-menu-item>
              <ui-sidebar-menu-button href="#dashboard" current label="Dashboard">
                <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" aria-hidden="true"><path d="M3 10.5 12 3l9 7.5V21H3z" /></svg>
                Dashboard
              </ui-sidebar-menu-button>
            </ui-sidebar-menu-item>
            <ui-sidebar-menu-item>
              <ui-sidebar-menu-button href="#inbox" label="Inbox">
                <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" aria-hidden="true"><path d="M22 12h-6l-2 3h-4l-2-3H2M5.5 5h13L22 12v7H2v-7z" /></svg>
                Inbox
              </ui-sidebar-menu-button>
              <ui-sidebar-menu-badge>12</ui-sidebar-menu-badge>
            </ui-sidebar-menu-item>
            <ui-sidebar-menu-item>
              <ui-sidebar-menu-button href="#calendar" label="Calendar">
                <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" aria-hidden="true"><rect x="3" y="4" width="18" height="17" rx="2" /><path d="M3 10h18M8 2v4M16 2v4" /></svg>
                Calendar
              </ui-sidebar-menu-button>
            </ui-sidebar-menu-item>
          </ui-sidebar-menu>
        </ui-sidebar-group>
        <ui-sidebar-group>
          <ui-sidebar-group-label>Settings</ui-sidebar-group-label>
          <ui-sidebar-menu>
            <ui-sidebar-menu-item>
              <ui-sidebar-menu-button href="#team" label="Team">
                <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" aria-hidden="true"><circle cx="9" cy="8" r="4" /><path d="M2 21a7 7 0 0 1 14 0M16 4a4 4 0 0 1 0 8M22 21a7 7 0 0 0-4-6.3" /></svg>
                Team
              </ui-sidebar-menu-button>
            </ui-sidebar-menu-item>
            <ui-sidebar-menu-item>
              <ui-sidebar-menu-button href="#billing" label="Billing">
                <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" aria-hidden="true"><rect x="2" y="5" width="20" height="14" rx="2" /><path d="M2 10h20" /></svg>
                Billing
              </ui-sidebar-menu-button>
            </ui-sidebar-menu-item>
          </ui-sidebar-menu>
        </ui-sidebar-group>
      </ui-sidebar-content>
      <ui-sidebar-footer>
        <ui-dropdown-menu>
          <ui-dropdown-menu-trigger>
            <ui-button variant="ghost" size="sm"><ui-avatar><ui-avatar-fallback>JD</ui-avatar-fallback></ui-avatar> Jane Doe</ui-button>
          </ui-dropdown-menu-trigger>
          <ui-dropdown-menu-content placement="top-start">
            <ui-menu-item value="profile">Profile</ui-menu-item>
            <ui-menu-item value="logout" variant="destructive">Log out</ui-menu-item>
          </ui-dropdown-menu-content>
        </ui-dropdown-menu>
      </ui-sidebar-footer>
    </ui-sidebar>
    <ui-sidebar-inset>
      <header>
        <ui-sidebar-trigger></ui-sidebar-trigger>
        Dashboard
      </header>
      <div class="body">Press the trigger, or Ctrl+B (⌘B on macOS), to collapse the sidebar to icons.</div>
    </ui-sidebar-inset>
  </ui-sidebar-layout>
</div>
```

### Parts

| Element | Fills | Renders |
| --- | --- | --- |
| `ui-sidebar-layout` | | The shell, with an `<aside>` for the sidebar, the inset beside it, and a backdrop on small screens. |
| `ui-sidebar` | the `<aside>` of `ui-sidebar-layout` | The panel, a full-height column. |
| `ui-sidebar-header` | the top of `ui-sidebar` | A padded column for a logo or team switcher. |
| `ui-sidebar-content` | the middle of `ui-sidebar` | A scrolling `<nav>` around the groups. |
| `ui-sidebar-footer` | the bottom of `ui-sidebar` | A padded column for a user menu. |
| `ui-sidebar-group` | `ui-sidebar-content` | A `role="group"` section, named by its label. |
| `ui-sidebar-group-label` | `ui-sidebar-group` | The small muted heading. Hidden when collapsed. |
| `ui-sidebar-menu` | `ui-sidebar-group` | A `<ul>` of menu items with Up and Down arrow navigation. |
| `ui-sidebar-menu-item` | the `<ul>` of `ui-sidebar-menu` | A list item (`role="listitem"`). |
| `ui-sidebar-menu-button` | `ui-sidebar-menu-item` | The item's `<a>`, with icon and text. Icons are sized to `calc(var(--spacing) * 4)`. When collapsed, only the icon shows. |
| `ui-sidebar-menu-badge` | `ui-sidebar-menu-item` | Short text, such as a count, at the end of the item. Hidden when collapsed. |
| `ui-sidebar-trigger` | anywhere inside `ui-sidebar-layout` | A ghost icon `ui-button` labelled "Toggle sidebar". |
| `ui-sidebar-inset` | the main area of `ui-sidebar-layout` | A `<main>` panel with rounded corners next to the sidebar. |

### Attributes

#### ui-sidebar-layout

| Attribute | Type | Default | Description |
| --- | --- | --- | --- |
| `collapsed` | boolean | `false` | Collapses the sidebar to an icon rail (`calc(var(--spacing) * 12)` wide) and hides labels, badges and group labels. Reflects. Ignored on small screens, where the sidebar is off-canvas instead. |
| `persist` | string | | A `localStorage` key. When set, `collapsed` is restored from it on connect and saved to it on every change. |

#### ui-sidebar-menu-button

| Attribute | Type | Default | Description |
| --- | --- | --- | --- |
| `href` | string | | Link target. Without it, the item is a button activated by Enter and Space. |
| `current` | boolean | `false` | Marks the current page with a highlight, medium weight and `aria-current="page"` on the link. |
| `label` | string | | Sets the link's `title`, which is the tooltip when the sidebar is collapsed. |

The other parts have no attributes.

### Properties and methods

| Member | Element | Description |
| --- | --- | --- |
| `toggle()` | `ui-sidebar-layout` | On wide screens, flips `collapsed`. On small screens, opens or closes the off-canvas sidebar. |

### Events

| Event | Element | Description |
| --- | --- | --- |
| `select` | `ui-sidebar-menu-button` | Fires when the button is clicked. Bubbles and is cancelable. `preventDefault()` stops the link from following its `href`. |
| `sidebartoggle` | `ui-sidebar-trigger` | Fires when the trigger is clicked. Bubbles to the enclosing `ui-sidebar-layout`, which calls `toggle()`. |

### CSS parts

| Part | Element | Description |
| --- | --- | --- |
| `sidebar` | `ui-sidebar-layout` | The `<aside>` holding the sidebar. Sticky and `100dvh` tall by default. |
| `backdrop` | `ui-sidebar-layout` | The overlay behind the off-canvas sidebar on small screens. Clicking it closes the sidebar. |
| `content` | `ui-sidebar-content` | The scrolling `<nav>`. |
| `menu` | `ui-sidebar-menu` | The `<ul>`. |
| `button` | `ui-sidebar-menu-button` | The `<a>`. |
| `main` | `ui-sidebar-inset` | The `<main>` panel. |

### CSS states and custom properties

- `ui-sidebar-layout:state(attr-collapsed)` matches while collapsed (the same as `[collapsed]`).
- `ui-sidebar-layout:state(state-layout-mobile)` matches when the viewport is narrower than 768px, and
  `:state(state-layout-open)` while the off-canvas sidebar is open.
- The layout sets `--ui-sidebar-width` and `--ui-sidebar-label` (`block` or `none`) for its descendants. Group
  labels and badges use `--ui-sidebar-label` to hide when collapsed.

<a id="tabs"></a>

## Tabs

A set of panels shown one at a time, switched with a row or column of tab buttons. [Docs and demos](https://realm.js.org/components/tabs/).

- File: `tabs.html`, import with `<import-element from="https://cdn.jsdelivr.net/npm/@realmorg/ui@alpha/components/tabs.html"></import-element>`
- Tags: `ui-tabs`, `ui-tabs-list`, `ui-tabs-trigger`, `ui-tabs-content`

```html runnable
<import-element from="https://cdn.jsdelivr.net/npm/@realmorg/ui@alpha/components/card.html"></import-element>
<import-element from="https://cdn.jsdelivr.net/npm/@realmorg/ui@alpha/components/field.html"></import-element>
<import-element from="https://cdn.jsdelivr.net/npm/@realmorg/ui@alpha/components/input.html"></import-element>
<import-element from="https://cdn.jsdelivr.net/npm/@realmorg/ui@alpha/components/label.html"></import-element>
<import-element from="https://cdn.jsdelivr.net/npm/@realmorg/ui@alpha/components/tabs.html"></import-element>

<ui-tabs value="account">
  <ui-tabs-list>
    <ui-tabs-trigger value="account">Account</ui-tabs-trigger>
    <ui-tabs-trigger value="password">Password</ui-tabs-trigger>
  </ui-tabs-list>
  <ui-tabs-content value="account">
    <ui-card>
      <ui-card-header>
        <ui-card-title>Account</ui-card-title>
        <ui-card-description>Make changes to your account here.</ui-card-description>
      </ui-card-header>
      <ui-card-content>
        <ui-field><ui-label for="tabs-name">Name</ui-label><ui-input id="tabs-name" value="Pedro Duarte"></ui-input></ui-field>
      </ui-card-content>
    </ui-card>
  </ui-tabs-content>
  <ui-tabs-content value="password">
    <ui-card>
      <ui-card-header>
        <ui-card-title>Password</ui-card-title>
        <ui-card-description>Change your password here.</ui-card-description>
      </ui-card-header>
      <ui-card-content>
        <ui-field><ui-label for="tabs-password">Current password</ui-label><ui-input id="tabs-password" type="password"></ui-input></ui-field>
      </ui-card-content>
    </ui-card>
  </ui-tabs-content>
</ui-tabs>
```

### Parts

| Element | Fills | Renders |
| --- | --- | --- |
| `ui-tabs` | | A flex container for the list and the panels. Fills its container's width. |
| `ui-tabs-list` | the default slot of `ui-tabs` | A `role="tablist"` pill around the triggers, with `aria-orientation` and arrow-key navigation. |
| `ui-tabs-trigger` | the default slot of `ui-tabs-list` | A `<button role="tab">` with `aria-selected` and a roving `tabindex`. |
| `ui-tabs-content` | the default slot of `ui-tabs` | The panel, with `role="tabpanel"`. Hidden unless its `value` is selected. |

### Attributes

#### ui-tabs

| Attribute | Type | Default | Description |
| --- | --- | --- | --- |
| `value` | string | | The `value` of the selected trigger. If it is missing, unknown or names a disabled trigger, the first enabled trigger is selected. Updated when the user selects a tab. |
| `orientation` | `"horizontal"` \| `"vertical"` | `"horizontal"` | Lays the list out as a row above the panels or a column beside them, and sets which arrow keys move between tabs. |

#### ui-tabs-trigger

| Attribute | Type | Default | Description |
| --- | --- | --- | --- |
| `value` | string | | Identifies the tab. Selecting it shows the `ui-tabs-content` with the same `value`. |
| `disabled` | boolean | `false` | Disables the tab. |

#### ui-tabs-content

| Attribute | Type | Default | Description |
| --- | --- | --- | --- |
| `value` | string | | The `value` of the trigger that shows this panel. |

`ui-tabs-list` has no attributes.

### Events

| Event | Element | Description |
| --- | --- | --- |
| `change` | `ui-tabs` | Fires when the user selects a different tab, by click or keyboard. Read the new `value` from the element. Bubbles. |

### Child elements

`ui-tabs` watches its `ui-tabs-list` and `ui-tabs-content` children and the `value` and `disabled` attributes of
the triggers and panels, so tabs can be added, removed or renamed at any time.

### CSS parts

| Part | Element | Description |
| --- | --- | --- |
| `list` | `ui-tabs-list` | The `role="tablist"` container. |
| `trigger` | `ui-tabs-trigger` | The tab button. The selected one has `aria-selected="true"`. |

<a id="aspect-ratio"></a>

## Aspect Ratio

A box that keeps its content at a fixed width-to-height ratio, for images, video and embeds. [Docs and demos](https://realm.js.org/components/aspect-ratio/).

- File: `aspect-ratio.html`, import with `<import-element from="https://cdn.jsdelivr.net/npm/@realmorg/ui@alpha/components/aspect-ratio.html"></import-element>`
- Tags: `ui-aspect-ratio`

```html runnable
<import-element from="https://cdn.jsdelivr.net/npm/@realmorg/ui@alpha/components/aspect-ratio.html"></import-element>

<ui-aspect-ratio ratio="16 / 9">
  <div>16 / 9</div>
</ui-aspect-ratio>
```

### Parts

| Part | Slot | Renders |
| --- | --- | --- |
| `ui-aspect-ratio` | (default) | A full-width block at the given ratio that clips overflow. Its default slot takes any content. `<img>`, `<video>` and `<iframe>` fill the box and use `object-fit: cover`. |

### Attributes

| Attribute | Type | Default | Description |
| --- | --- | --- | --- |
| `ratio` | string | `"16 / 9"` | Width-to-height ratio, as a CSS `aspect-ratio` value. |

### CSS parts

None. The box renders only a slot.

### CSS states and custom properties

The ratio is applied through the reactive custom property `--attr-ratio`, which mirrors the `ratio` attribute.

<a id="carousel"></a>

## Carousel

A scroll-snapping slideshow with previous and next buttons, arrow-key navigation and swipe support. [Docs and demos](https://realm.js.org/components/carousel/).

- File: `carousel.html`, import with `<import-element from="https://cdn.jsdelivr.net/npm/@realmorg/ui@alpha/components/carousel.html"></import-element>`
- Tags: `ui-carousel`, `ui-carousel-content`, `ui-carousel-item`, `ui-carousel-previous`, `ui-carousel-next`
- Imports for you: `button.html`

```html runnable
<import-element from="https://cdn.jsdelivr.net/npm/@realmorg/ui@alpha/components/aspect-ratio.html"></import-element>
<import-element from="https://cdn.jsdelivr.net/npm/@realmorg/ui@alpha/components/card.html"></import-element>
<import-element from="https://cdn.jsdelivr.net/npm/@realmorg/ui@alpha/components/carousel.html"></import-element>

<ui-carousel>
  <ui-carousel-content>
    <ui-carousel-item>
      <ui-card>
        <ui-card-content>
          <ui-aspect-ratio ratio="1"><div>1</div></ui-aspect-ratio>
        </ui-card-content>
      </ui-card>
    </ui-carousel-item>
    <ui-carousel-item>
      <ui-card>
        <ui-card-content>
          <ui-aspect-ratio ratio="1"><div>2</div></ui-aspect-ratio>
        </ui-card-content>
      </ui-card>
    </ui-carousel-item>
    <ui-carousel-item>
      <ui-card>
        <ui-card-content>
          <ui-aspect-ratio ratio="1"><div>3</div></ui-aspect-ratio>
        </ui-card-content>
      </ui-card>
    </ui-carousel-item>
    <ui-carousel-item>
      <ui-card>
        <ui-card-content>
          <ui-aspect-ratio ratio="1"><div>4</div></ui-aspect-ratio>
        </ui-card-content>
      </ui-card>
    </ui-carousel-item>
    <ui-carousel-item>
      <ui-card>
        <ui-card-content>
          <ui-aspect-ratio ratio="1"><div>5</div></ui-aspect-ratio>
        </ui-card-content>
      </ui-card>
    </ui-carousel-item>
  </ui-carousel-content>
  <ui-carousel-previous></ui-carousel-previous>
  <ui-carousel-next></ui-carousel-next>
</ui-carousel>
```

### Parts

| Part | Slot | Renders |
| --- | --- | --- |
| `ui-carousel-content` | (default) | The scroll-snapping track (`part="track"`). It is focusable and the arrow keys move between slides. |
| `ui-carousel-item` | (default) of `ui-carousel-content` | One slide, the full width (or height) of the track, borders and padding included. |
| `ui-carousel-previous` | (default) | An outline icon button that scrolls to the previous slide. Left of the track, or above it when vertical. |
| `ui-carousel-next` | (default) | An outline icon button that scrolls to the next slide. Right of the track, or below it when vertical. |

The parts place themselves in the carousel's grid, so their source order does not matter. The default slot of
`ui-carousel-item` takes any content.

### Attributes

#### ui-carousel

| Attribute | Type | Default | Description |
| --- | --- | --- | --- |
| `orientation` | `"horizontal"` \| `"vertical"` | `"horizontal"` | Scroll and snap direction. A vertical carousel needs a `height`. |
| `label` | string | `"Carousel"` | Accessible name of the region. |
| `height` | number | none | Height in spacing units, so `height="56"` is `calc(var(--spacing) * 56)`. Needed for a vertical carousel. |

`ui-carousel-content`, `ui-carousel-item`, `ui-carousel-previous` and `ui-carousel-next` have no attributes.

### Events

| Event | Detail | Description |
| --- | --- | --- |
| `change` | `{ index: number }` | Fires on `ui-carousel` when scrolling of the track ends. `index` is the zero-based index of the slide in view. Bubbles. |

### CSS parts

| Part | Element | Description |
| --- | --- | --- |
| `track` | `ui-carousel-content` | The scroll container holding the slides. |

<a id="direction"></a>

## Direction

A wrapper that sets left-to-right or right-to-left text direction for everything inside it. [Docs and demos](https://realm.js.org/components/direction/).

- File: `direction.html`, import with `<import-element from="https://cdn.jsdelivr.net/npm/@realmorg/ui@alpha/components/direction.html"></import-element>`
- Tags: `ui-direction`

```html runnable
<import-element from="https://cdn.jsdelivr.net/npm/@realmorg/ui@alpha/components/breadcrumb.html"></import-element>
<import-element from="https://cdn.jsdelivr.net/npm/@realmorg/ui@alpha/components/button.html"></import-element>
<import-element from="https://cdn.jsdelivr.net/npm/@realmorg/ui@alpha/components/direction.html"></import-element>
<import-element from="https://cdn.jsdelivr.net/npm/@realmorg/ui@alpha/components/input.html"></import-element>

<ui-direction dir="rtl">
  <div>
    <ui-breadcrumb label="مسار التنقل">
      <ui-breadcrumb-list>
        <ui-breadcrumb-item><ui-breadcrumb-link href="#">الرئيسية</ui-breadcrumb-link></ui-breadcrumb-item>
        <ui-breadcrumb-separator></ui-breadcrumb-separator>
        <ui-breadcrumb-item><ui-breadcrumb-link href="#">المكونات</ui-breadcrumb-link></ui-breadcrumb-item>
        <ui-breadcrumb-separator></ui-breadcrumb-separator>
        <ui-breadcrumb-item><ui-breadcrumb-page>الاتجاه</ui-breadcrumb-page></ui-breadcrumb-item>
      </ui-breadcrumb-list>
    </ui-breadcrumb>
    <ui-input aria-label="بحث" placeholder="ابحث في الوثائق"></ui-input>
    <div>
      <ui-button>حفظ</ui-button>
      <ui-button variant="outline">إلغاء</ui-button>
    </div>
  </div>
</ui-direction>
```

### Attributes

| Attribute | Type | Default | Description |
| --- | --- | --- | --- |
| `dir` | `"ltr"` \| `"rtl"` | `"ltr"` | Text direction for the content. Written back to the native `dir` attribute, so it is always present on the element. |

### Slots

| Slot | Description |
| --- | --- |
| (default) | The content that takes the direction. |

<a id="resizable"></a>

## Resizable

Panel groups split by draggable handles that users resize with the pointer or the arrow keys. [Docs and demos](https://realm.js.org/components/resizable/).

- File: `resizable.html`, import with `<import-element from="https://cdn.jsdelivr.net/npm/@realmorg/ui@alpha/components/resizable.html"></import-element>`
- Tags: `ui-resizable`, `ui-resizable-panel`, `ui-resizable-handle`

```html runnable
<import-element from="https://cdn.jsdelivr.net/npm/@realmorg/ui@alpha/components/resizable.html"></import-element>

<ui-resizable variant="outline" height="48">
  <ui-resizable-panel size="50">
    <div>One</div>
  </ui-resizable-panel>
  <ui-resizable-handle></ui-resizable-handle>
  <ui-resizable-panel size="50">
    <div>Two</div>
  </ui-resizable-panel>
</ui-resizable>
```

### Attributes

#### ui-resizable

| Attribute | Type | Default | Description |
| --- | --- | --- | --- |
| `direction` | `"horizontal"` \| `"vertical"` | `"horizontal"` | Lays the panels out in a row or a column. Handles read it when they connect. |
| `height` | number | none | Height in spacing units, so `height="48"` is `calc(var(--spacing) * 48)`. Without it the group fills its parent's height. |
| `variant` | `"default"` \| `"outline"` | `"default"` | `outline` adds a 1px border and `var(--radius-lg)` corners. |

#### ui-resizable-panel

| Attribute | Type | Default | Description |
| --- | --- | --- | --- |
| `size` | number | `50` | The panel's share of the group, used as its `flex-grow`. Between 0 and 100. Make the sizes in a group add up to 100 so they read as percentages. Updated while resizing. |
| `min` | number | `10` | The smallest `size` a handle can shrink the panel to. At least 0. |

#### ui-resizable-handle

| Attribute | Type | Default | Description |
| --- | --- | --- | --- |
| `handle` | boolean | `false` | Shows a visible grip in the middle of the handle. |

### Properties and methods

`ui-resizable-panel` exposes `size` and `min` as properties. Set `size` from script to resize a panel. The handles
read both properties from the panels on either side.

### Parts

| Part | Slot | Renders |
| --- | --- | --- |
| `ui-resizable-panel` | (default) | One panel. Its content scrolls when it overflows. |
| `ui-resizable-handle` | (default) | A separator between two panels that resizes them. |

Panels and handles alternate in the default slot of `ui-resizable`. The default slot of a panel takes any content,
including another `ui-resizable`.

### CSS parts

| Part | Element | Description |
| --- | --- | --- |
| `grip` | `ui-resizable-handle` | The grip shown with the `handle` attribute. |

### CSS states and custom properties

- `ui-resizable-handle:state(vertical)` matches handles in a vertical group, which are horizontal lines.
- `ui-resizable-panel` applies its size through the reactive custom property `--attr-size`.

<a id="scroll-area"></a>

## Scroll Area

A box with its own thin, themed scrollbar for content that is taller or wider than the space it is given. [Docs and demos](https://realm.js.org/components/scroll-area/).

- File: `scroll-area.html`, import with `<import-element from="https://cdn.jsdelivr.net/npm/@realmorg/ui@alpha/components/scroll-area.html"></import-element>`
- Tags: `ui-scroll-area`

```html runnable
<import-element from="https://cdn.jsdelivr.net/npm/@realmorg/ui@alpha/components/item.html"></import-element>
<import-element from="https://cdn.jsdelivr.net/npm/@realmorg/ui@alpha/components/scroll-area.html"></import-element>
<import-element from="https://cdn.jsdelivr.net/npm/@realmorg/ui@alpha/components/separator.html"></import-element>

<ui-scroll-area variant="outline" width="48" height="72">
  <ui-item-group aria-label="Tags">
    <ui-item size="sm">v1.2.0-beta.50</ui-item>
    <ui-separator></ui-separator>
    <ui-item size="sm">v1.2.0-beta.49</ui-item>
    <ui-separator></ui-separator>
    <ui-item size="sm">v1.2.0-beta.48</ui-item>
    <ui-separator></ui-separator>
    <ui-item size="sm">v1.2.0-beta.47</ui-item>
    <ui-separator></ui-separator>
    <ui-item size="sm">v1.2.0-beta.46</ui-item>
    <ui-separator></ui-separator>
    <ui-item size="sm">v1.2.0-beta.45</ui-item>
    <ui-separator></ui-separator>
    <ui-item size="sm">v1.2.0-beta.44</ui-item>
    <ui-separator></ui-separator>
    <ui-item size="sm">v1.2.0-beta.43</ui-item>
    <ui-separator></ui-separator>
    <ui-item size="sm">v1.2.0-beta.42</ui-item>
    <ui-separator></ui-separator>
    <ui-item size="sm">v1.2.0-beta.41</ui-item>
    <ui-separator></ui-separator>
    <ui-item size="sm">v1.2.0-beta.40</ui-item>
    <ui-separator></ui-separator>
    <ui-item size="sm">v1.2.0-beta.39</ui-item>
  </ui-item-group>
</ui-scroll-area>
```

### Parts

| Part | Slot | Renders |
| --- | --- | --- |
| `ui-scroll-area` | (default) | A clipped box with a focusable scrolling viewport. Its default slot takes any content, which scrolls inside the viewport. |

### Attributes

| Attribute | Type | Default | Description |
| --- | --- | --- | --- |
| `orientation` | `"vertical"` \| `"horizontal"` \| `"both"` | `"vertical"` | Which directions scroll. Overflow in the other direction is clipped. |
| `width` | number | none | Width in spacing units (`calc(var(--spacing) * width)`). Without it the element fills its container. |
| `height` | number | none | Height in spacing units. Without it the element grows with its content and nothing scrolls vertically. |
| `variant` | `"default"` \| `"outline"` | `"default"` | `outline` adds a 1px border in `var(--border)` with `var(--radius-md)` corners. |

### CSS parts

| Part | Description |
| --- | --- |
| `viewport` | The scrolling container. Style `scrollbar-color` or `scrollbar-width` here to change the scrollbar. |

<a id="attachment"></a>

## Attachment

A compact file chip with a preview area, a downloadable file name, its size and optional actions. [Docs and demos](https://realm.js.org/components/attachment/).

- File: `attachment.html`, import with `<import-element from="https://cdn.jsdelivr.net/npm/@realmorg/ui@alpha/components/attachment.html"></import-element>`
- Tags: `ui-attachment`, `ui-attachment-media`, `ui-attachment-actions`

```html runnable
<import-element from="https://cdn.jsdelivr.net/npm/@realmorg/ui@alpha/components/attachment.html"></import-element>

<ui-attachment name="quarterly-report.pdf" size="1.2 MB" href="data:text/plain,Quarterly%20report">
  <ui-attachment-media>
    <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" aria-hidden="true"><path d="M14 3H6v18h12V7Z" /><path d="M14 3v4h4" /></svg>
  </ui-attachment-media>
</ui-attachment>
```

### Parts

| Part | Slot | Renders |
| --- | --- | --- |
| `ui-attachment` | (root) | The chip with the file name link and size. |
| `ui-attachment-media` | `media` | A muted rounded square before the name for a file icon (`<svg>`, 16px) or a thumbnail `<img>`. |
| `ui-attachment-actions` | `actions` | A row of buttons after the name, such as remove or open. |

The parts set their own `slot`, so they go directly inside `ui-attachment` with no attributes. The default slot of
each part takes any content.

### Attributes

| Attribute | Type | Default | Description |
| --- | --- | --- | --- |
| `name` | string | | File name. Shown as the link text and used as the `download` file name. |
| `size` | string | | Human-readable size, such as `1.2 MB`. Hidden when empty. |
| `href` | string | | URL of the file. Without it the name is not a link. |

### CSS parts

| Part | Description |
| --- | --- |
| `content` | The column with the name and size. |
| `title` | The file name link. |

<a id="bubble"></a>

## Bubble

A rounded chat bubble for message text, with colour variants, start or end alignment and a reactions row. [Docs and demos](https://realm.js.org/components/bubble/).

- File: `bubble.html`, import with `<import-element from="https://cdn.jsdelivr.net/npm/@realmorg/ui@alpha/components/bubble.html"></import-element>`
- Tags: `ui-bubble`, `ui-bubble-reactions`

```html runnable
<import-element from="https://cdn.jsdelivr.net/npm/@realmorg/ui@alpha/components/bubble.html"></import-element>
<import-element from="https://cdn.jsdelivr.net/npm/@realmorg/ui@alpha/components/message.html"></import-element>

<ui-message>
  <ui-bubble variant="muted">Did the deploy go out?</ui-bubble>
</ui-message>
<ui-message align="end">
  <ui-bubble align="end">Yes, about ten minutes ago. Everything is green.</ui-bubble>
</ui-message>
```

### Parts

| Part | Slot | Renders |
| --- | --- | --- |
| `ui-bubble` | (root) | The rounded box around the message content. |
| `ui-bubble-reactions` | `reactions` | A wrapping row below the bubble for reactions or actions. |

`ui-bubble-reactions` sets its own `slot`, so it goes directly inside `ui-bubble` with no attributes. Everything
else in `ui-bubble` is the message content. The default slot of each part takes any content.

### Attributes

| Attribute | Type | Default | Description |
| --- | --- | --- | --- |
| `variant` | `"default"` \| `"secondary"` \| `"muted"` \| `"outline"` \| `"ghost"` | `"default"` | Background and text colour. |
| `align` | `"start"` \| `"end"` | `"start"` | `end` sets `align-self: flex-end` on the bubble. |

### CSS parts

| Part | Description |
| --- | --- |
| `content` | The rounded box around the message content. |

<a id="code-block"></a>

## Code Block

A monospaced block for code with a language label and a copy-to-clipboard button. [Docs and demos](https://realm.js.org/components/code-block/).

- File: `code-block.html`, import with `<import-element from="https://cdn.jsdelivr.net/npm/@realmorg/ui@alpha/components/code-block.html"></import-element>`
- Tags: `ui-code-block`
- Imports for you: `button.html`

```html runnable
<import-element from="https://cdn.jsdelivr.net/npm/@realmorg/ui@alpha/components/code-block.html"></import-element>

<ui-code-block language="html"><pre>&lt;script type="module" src="https://cdn.jsdelivr.net/npm/@realmorg/realm@alpha/dist/realm.js"&gt;&lt;/script&gt;</pre></ui-code-block>
```

### Attributes

| Attribute | Type | Default | Description |
| --- | --- | --- | --- |
| `language` | string | | Label shown in the top corner, in upper case. Display only. |

### Parts

`ui-code-block` has no child parts. Its default slot takes the code, usually a `<pre>`. Its trimmed `textContent` is
what the copy button copies.

### CSS parts

| Part | Description |
| --- | --- |
| `code` | The scrolling area around the code (`white-space: pre`). |
| `bar` | The corner bar with the language label and the copy button. |

<a id="marker"></a>

## Marker

A small muted line of text with an optional icon, for day separators, system notices and section labels in a conversation. [Docs and demos](https://realm.js.org/components/marker/).

- File: `marker.html`, import with `<import-element from="https://cdn.jsdelivr.net/npm/@realmorg/ui@alpha/components/marker.html"></import-element>`
- Tags: `ui-marker`

```html runnable
<import-element from="https://cdn.jsdelivr.net/npm/@realmorg/ui@alpha/components/marker.html"></import-element>

<ui-marker variant="separator">Today</ui-marker>
<ui-marker>
  <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" aria-hidden="true"><path d="M16 21v-2a4 4 0 0 0-8 0v2" /><circle cx="12" cy="7" r="4" /></svg>
  Ana joined the channel
</ui-marker>
```

### Attributes

| Attribute | Type | Default | Description |
| --- | --- | --- | --- |
| `variant` | `"default"` \| `"separator"` \| `"border"` | `"default"` | `separator` draws lines on both sides. `border` draws a line below. |

### Parts

| Part | Slot | Renders |
| --- | --- | --- |
| `ui-marker` | (root) | A full-width row of small muted text. |

The default slot takes the text and an optional leading `<svg>` icon.

### CSS parts

None. The shadow root is a single slot. Style the host.

<a id="message"></a>

## Message

A chat message row that lays out an avatar, header, content and footer, aligned to the start or end. [Docs and demos](https://realm.js.org/components/message/).

- File: `message.html`, import with `<import-element from="https://cdn.jsdelivr.net/npm/@realmorg/ui@alpha/components/message.html"></import-element>`
- Tags: `ui-message`, `ui-message-avatar`, `ui-message-header`, `ui-message-footer`

```html runnable
<import-element from="https://cdn.jsdelivr.net/npm/@realmorg/ui@alpha/components/avatar.html"></import-element>
<import-element from="https://cdn.jsdelivr.net/npm/@realmorg/ui@alpha/components/bubble.html"></import-element>
<import-element from="https://cdn.jsdelivr.net/npm/@realmorg/ui@alpha/components/message.html"></import-element>

<ui-message>
  <ui-message-avatar>
    <ui-avatar><ui-avatar-fallback>OM</ui-avatar-fallback></ui-avatar>
  </ui-message-avatar>
  <ui-message-header>Olivia Martin</ui-message-header>
  <ui-bubble variant="muted">Hi, how can I help you today?</ui-bubble>
  <ui-message-footer>10:02</ui-message-footer>
</ui-message>
<ui-message align="end">
  <ui-message-avatar>
    <ui-avatar><ui-avatar-fallback>ME</ui-avatar-fallback></ui-avatar>
  </ui-message-avatar>
  <ui-bubble align="end">I cannot sign in to my account.</ui-bubble>
  <ui-message-footer>10:03 · Read</ui-message-footer>
</ui-message>
```

### Parts

| Part | Slot | Renders |
| --- | --- | --- |
| `ui-message` | (root) | The row, with the avatar beside a column with header, content and footer. |
| `ui-message-avatar` | `avatar` | Wraps a [`ui-avatar`](https://realm.js.org/components/avatar/), aligned to the bottom of the row. |
| `ui-message-header` | `header` | A small muted line above the content, such as the sender name and time. |
| `ui-message-footer` | `footer` | A small muted line below the content, such as the time or a read receipt. |

The parts set their own `slot`, so they go directly inside `ui-message` with no attributes. Any other child (bubbles,
attachments, code blocks) is the message content. The default slot of each part takes any content.

### Attributes

| Attribute | Type | Default | Description |
| --- | --- | --- | --- |
| `align` | `"start"` \| `"end"` | `"start"` | `end` reverses the row and aligns the content to the end. |

### CSS parts

| Part | Description |
| --- | --- |
| `content` | The column with the header, content and footer. |

<a id="message-scroller"></a>

## Message Scroller

A scrolling chat log that sticks to the newest message and offers a jump-to-latest button when the user scrolls up. [Docs and demos](https://realm.js.org/components/message-scroller/).

- File: `message-scroller.html`, import with `<import-element from="https://cdn.jsdelivr.net/npm/@realmorg/ui@alpha/components/message-scroller.html"></import-element>`
- Tags: `ui-message-scroller`
- Imports for you: `button.html`

```html runnable
<import-element from="https://cdn.jsdelivr.net/npm/@realmorg/ui@alpha/components/bubble.html"></import-element>
<import-element from="https://cdn.jsdelivr.net/npm/@realmorg/ui@alpha/components/button.html"></import-element>
<import-element from="https://cdn.jsdelivr.net/npm/@realmorg/ui@alpha/components/message-scroller.html"></import-element>

<custom-element name="message-scroller-demo">
  <script type="realm">
    const chat = useState({
      next: 6,
      messages: [
        { id: 1, align: "start", text: "Are we still on for the launch review?" },
        { id: 2, align: "end", text: "Yes, 3pm in the big room." },
        { id: 3, align: "start", text: "Great. Can you bring the metrics?" },
        { id: 4, align: "end", text: "Already in the deck." },
        { id: 5, align: "start", text: "Perfect, see you there." },
      ],
    });

    const REPLIES = ["On my way.", "Running five minutes late.", "Room changed to 4B.", "Starting now."];

    function send() {
      const text = REPLIES[chat.next % REPLIES.length];
      chat.messages.push({ id: chat.next, align: chat.next % 2 ? "start" : "end", text });
      chat.next++;
    }
  </script>

  <style type="realm">
    :host {
      display: grid;
      gap: calc(var(--spacing) * 3);
      width: 100%;
      max-width: calc(var(--spacing) * 112);
    }
    ui-message-scroller {
      border: 1px solid var(--border);
      border-radius: var(--radius-lg);
    }
    .messages {
      display: flex;
      flex-direction: column;
      gap: calc(var(--spacing) * 2);
      padding: calc(var(--spacing) * 4);
    }
  </style>

  <ui-message-scroller height="60">
    <div class="messages">
      <repeat-list of="#chat.messages" as="message" key="id">
        <ui-bubble variant="muted" _align="#message.align"><slot name="#message.text"></slot></ui-bubble>
      </repeat-list>
    </div>
  </ui-message-scroller>
  <ui-button variant="outline" onclick="send()">Receive a message</ui-button>
</custom-element>
<message-scroller-demo></message-scroller-demo>
```

### Attributes

| Attribute | Type | Default | Description |
| --- | --- | --- | --- |
| `threshold` | number | `48` | Distance from the bottom, in pixels, within which the scroller stays pinned to new content. Minimum `0`. |
| `height` | number | | Height in spacing units, so `height="60"` is `calc(var(--spacing) * 60)`. Without it, size the scroller from its layout. |

### Parts

| Part | Slot | Renders |
| --- | --- | --- |
| `ui-message-scroller` | (root) | The scrolling log and the "Jump to latest" button. |

The default slot takes the messages and any other content. Any change inside (added nodes, text changes) scrolls to
the bottom while pinned.

### CSS parts

| Part | Description |
| --- | --- |
| `viewport` | The scrolling element (`role="log"`, `aria-live="polite"`). |

### CSS states and custom properties

| Name | Description |
| --- | --- |
| `:state(attr-height)` | Matches while `height` is set. The height comes from `var(--attr-height)`. |
| `:state(state-scroll-pinned)` | Matches while the view is within `threshold` of the bottom. The "Jump to latest" button is hidden in this state. |
