` counts without the token. An `aria-labelledby` you author always wins. |
| `description` | No | `[data-ui-part~='description'], header > p` | Supporting line under the title, wired with `aria-describedby` the same way. A `` in the panel `` counts without the token. |
## Attributes
Every value below is implemented by the stylesheets this component ships. Boolean attributes
are presence-based: author the attribute with no value, or omit it.
| Attribute | Values | Default | Description |
| --- | --- | --- | --- |
| `modal` | presence | absent | Present to open the sheet as a modal, trapping focus and blocking the page behind it. Omit for a non-modal sheet the user can interact around. |
| `open` | presence | absent | Present to render the sheet open on load. |
| `position` | `top` · `right` · `bottom` · `left` | `right` | Which viewport edge the sheet slides in from. |
## Element API
Attributes above are the authoring surface. These are the DOM properties and events the
registered element adds once it upgrades.
### ``
| Property | Type | Notes |
| --- | --- | --- |
| `modal` | `boolean` | Reflects the `modal` attribute |
| `open` | `boolean` | Reflects the `open` attribute |
| `position` | `'top' | 'right' | 'bottom' | 'left'` | Reflects the `position` attribute |
| Event | Detail | Description |
| --- | --- | --- |
| `ui-open` | `CustomEvent` | Dispatched after the sheet opens. |
| `ui-close` | `CustomEvent` | Dispatched after the sheet closes. |
| `ui-dismiss` | `CustomEvent` | Dispatched when the sheet closes through Escape, a backdrop click, or a swipe past the dismiss threshold, rather than through an explicit control. The detail names which. A swipe reports `swipe` and behaves exactly like a backdrop click, because that is what it is: a pointer gesture on the overlay rather than a command. |
## Styling
Root identity: `ui-sheet`.
Required stylesheets: `tokens.css`, `core/sheet.css`, `core/button.css`, `core/dialog.css`, `themes/atmosphere/tokens.css`, `themes/atmosphere/button.css`, `themes/atmosphere/sheet.css`, `themes/atmosphere/dialog.css`.
Set these custom properties to restyle the component:
| Custom property | Controls |
| --- | --- |
| `--ui-sheet-drag-offset` | How far the panel has been dragged along its own axis, written as a length while a swipe is in progress and cleared on release. The stylesheet turns it into a `translate`; setting it yourself only moves the panel. |
Design tokens this component reads (31), global and set at the theme level: `--ui-accent`, `--ui-bg-accent`, `--ui-bg-accent-active`, `--ui-bg-accent-hover`, `--ui-bg-control`, `--ui-bg-control-active`, `--ui-bg-control-hover`, `--ui-bg-control-muted`, `--ui-bg-danger`, `--ui-bg-danger-active`, `--ui-bg-danger-hover`, `--ui-bg-surface`, `--ui-bg-surface-subtle`, `--ui-duration-fast`, `--ui-ease-standard`, `--ui-fg`, `--ui-fg-muted`, `--ui-focus`, `--ui-line`, `--ui-radius-control`, `--ui-radius-lg`, `--ui-radius-pill`, `--ui-shadow-control-accent`, `--ui-shadow-control-active`, `--ui-shadow-control-danger`, `--ui-shadow-floating`, `--ui-shadow-outline-control`, `--ui-space-2`, `--ui-space-3`, `--ui-space-4`, `--ui-space-5`.
## Accessibility
Keep the native elements, roles, and relationships shown in the markup. Timeless adds only the
state and keyboard coordination the platform does not already provide, and it never supplies
your accessible names — those depend on your content.
Follows the [Modal Dialog pattern](https://www.w3.org/WAI/ARIA/apg/patterns/dialog-modal/) from the ARIA Authoring Practices Guide.
| Key | Action |
| --- | --- |
| `Escape` | Close the sheet. Handled by the native `` element. |
A `modal` sheet traps focus through `showModal()`; without `modal` the page stays interactive and focus is not trapped. Timeless restores focus to the trigger either way, including after a swipe. Naming comes from the `title` and `description` parts. Swipe-to-dismiss is an addition, never the only way out: Escape and a close control both stay, so the sheet is fully operable without a pointer.
## Before JavaScript runs
The markup above is complete and usable on its own: native controls submit, labels associate, and authored ARIA is already correct. Registration adds state synchronisation, focus management, and keyboard coordination on top of it.
---
# Skeleton
Reserve stable space while content loads. (CSS only.)
Reference: https://timeless.build/docs/components/skeleton/
## Markup
```html
```
## Install
```js
import '@timelessui/components/css/tokens.css'
import '@timelessui/components/css/core/skeleton.css'
import '@timelessui/components/css/themes/atmosphere/tokens.css'
import '@timelessui/components/css/themes/atmosphere/skeleton.css'
```
## Attributes
Every value below is implemented by the stylesheets this component ships. Boolean attributes
are presence-based: author the attribute with no value, or omit it.
| Attribute | Values | Default | Description |
| --- | --- | --- | --- |
| `data-ui-size` | `sm` · `md` · `lg` | `md` | Line height for the `text` shape, diameter for `circle`. |
| `data-ui-shape` | `text` · `circle` · `media` | `text` | Placeholder geometry. |
| `data-ui-width` | `full` · `medium` · `short` | `full` | Inline size, so a group of lines can look like real text. |
## Styling
Root identity: `ui-skeleton`.
Required stylesheets: `tokens.css`, `core/skeleton.css`, `themes/atmosphere/tokens.css`, `themes/atmosphere/skeleton.css`.
This component adds no custom properties of its own. Restyle it through the design tokens at https://timeless.build/docs/styling/theming/ or your own CSS.
Design tokens this component reads (6), global and set at the theme level: `--ui-bg-surface-raised`, `--ui-ease-standard`, `--ui-fg`, `--ui-radius-lg`, `--ui-radius-pill`, `--ui-radius-sm`.
## Accessibility
Keep the native elements, roles, and relationships shown in the markup. Timeless adds only the
state and keyboard coordination the platform does not already provide, and it never supplies
your accessible names — those depend on your content.
This component declares no keyboard contract, because it has none of its own: the markup above
is native elements and native ARIA, and everything a keyboard or a screen reader does with it
comes from the platform. Nothing here manages focus, and nothing here adds a role you did not
author. So the accessibility of this component is the accessibility of the markup — which is why
the markup is the part worth copying exactly.
## Before JavaScript runs
This primitive is CSS only. There is nothing to register and nothing to wait for.
---
# Spinner
Indicate a short operation without changing layout. (CSS only.)
Reference: https://timeless.build/docs/components/spinner/
## Markup
```html
Loading preview
```
## Install
```js
import '@timelessui/components/css/tokens.css'
import '@timelessui/components/css/core/spinner.css'
import '@timelessui/components/css/themes/atmosphere/tokens.css'
import '@timelessui/components/css/themes/atmosphere/spinner.css'
```
## Anatomy
Parts are authored in your own markup and identified by the selector below. Required parts
must be present for the component to work. Private `data-ui-internal-*` hooks are written by
the runtime and must never be authored.
| Part | Required | Selector | Purpose |
| --- | --- | --- | --- |
| `label` | No | `[data-ui-part~='label']` | Visible or screen-reader-only description of the wait. |
## Attributes
Every value below is implemented by the stylesheets this component ships. Boolean attributes
are presence-based: author the attribute with no value, or omit it.
| Attribute | Values | Default | Description |
| --- | --- | --- | --- |
| `data-ui-size` | `sm` · `md` · `lg` | `md` | Spinner diameter. |
| `data-ui-variant` | `neutral` · `accent` · `success` · `warning` · `danger` | `neutral` | Indicator color. |
## Styling
Root identity: `ui-spinner`.
Required stylesheets: `tokens.css`, `core/spinner.css`, `themes/atmosphere/tokens.css`, `themes/atmosphere/spinner.css`.
This component adds no custom properties of its own. Restyle it through the design tokens at https://timeless.build/docs/styling/theming/ or your own CSS.
Design tokens this component reads (8), global and set at the theme level: `--ui-accent`, `--ui-danger`, `--ui-fg`, `--ui-fg-muted`, `--ui-radius-pill`, `--ui-space-2`, `--ui-success`, `--ui-warning`.
## Accessibility
Keep the native elements, roles, and relationships shown in the markup. Timeless adds only the
state and keyboard coordination the platform does not already provide, and it never supplies
your accessible names — those depend on your content.
This component declares no keyboard contract, because it has none of its own: the markup above
is native elements and native ARIA, and everything a keyboard or a screen reader does with it
comes from the platform. Nothing here manages focus, and nothing here adds a role you did not
author. So the accessibility of this component is the accessibility of the markup — which is why
the markup is the part worth copying exactly.
## Before JavaScript runs
This primitive is CSS only. There is nothing to register and nothing to wait for.
---
# Switch
A native checkbox exposed as an immediate on or off setting. (CSS only.)
Reference: https://timeless.build/docs/components/switch/
> **Choosing between components.** Use Switch for a setting that applies immediately. Use [Toggle](/docs/components/toggle/) when the control is a button whose pressed state is part of a toolbar.
## Markup
```html
Dark mode
Follow the documentation theme.
```
## Install
```js
import '@timelessui/components/css/tokens.css'
import '@timelessui/components/css/core/forms.css'
import '@timelessui/components/css/themes/atmosphere/tokens.css'
import '@timelessui/components/css/themes/atmosphere/forms.css'
```
## Anatomy
Parts are authored in your own markup and identified by the selector below. Required parts
must be present for the component to work. Private `data-ui-internal-*` hooks are written by
the runtime and must never be authored.
**ui-choice**
| Part | Required | Selector | Purpose |
| --- | --- | --- | --- |
| `body` | No | `[data-ui-part~='body']` | Wrapper for the title and description beside the control. |
| `title` | No | `[data-ui-part~='title']` | The choice label text. |
| `description` | No | `[data-ui-part~='description']` | Supporting copy under the choice label. |
## Attributes
Every value below is implemented by the stylesheets this component ships. Boolean attributes
are presence-based: author the attribute with no value, or omit it.
**ui-choice**
| Attribute | Values | Default | Description |
| --- | --- | --- | --- |
| `data-ui-density` | `compact` · `normal` · `spacious` | `normal` | Gap between the control and its label. |
## State
Native attributes, ARIA, and platform pseudo-classes are authoritative. Style state through
the selectors below rather than adding your own state classes.
| State | Source | How to express it |
| --- | --- | --- |
| `invalid` | native | Native `:invalid`, or `aria-invalid="true"`. |
| `disabled` | native | Native `disabled`. |
## Styling
Root identity: `ui-switch`, `ui-choice`.
Required stylesheets: `tokens.css`, `core/forms.css`, `themes/atmosphere/tokens.css`, `themes/atmosphere/forms.css`.
This component adds no custom properties of its own. Restyle it through the design tokens at https://timeless.build/docs/styling/theming/ or your own CSS.
Design tokens this component reads (26), global and set at the theme level: `--ui-accent`, `--ui-bg-control`, `--ui-bg-control-muted`, `--ui-bg-surface`, `--ui-bg-surface-subtle`, `--ui-danger`, `--ui-danger-soft`, `--ui-disabled-opacity`, `--ui-duration-fast`, `--ui-ease-standard`, `--ui-fg`, `--ui-fg-muted`, `--ui-fg-subtle`, `--ui-focus`, `--ui-line`, `--ui-line-strong`, `--ui-radius-md`, `--ui-radius-pill`, `--ui-radius-sm`, `--ui-shadow-control`, `--ui-shadow-inset`, `--ui-space-1`, `--ui-space-2`, `--ui-space-3`, `--ui-space-4`, `--ui-space-5`.
## Accessibility
Keep the native elements, roles, and relationships shown in the markup. Timeless adds only the
state and keyboard coordination the platform does not already provide, and it never supplies
your accessible names — those depend on your content.
Follows the [Switch pattern](https://www.w3.org/WAI/ARIA/apg/patterns/switch/) from the ARIA Authoring Practices Guide.
A native `input[type="checkbox"]` with `role="switch"`, which is the whole component: Tab reaches it, Space toggles it, the checked state is `:checked`, and it submits and resets with the form. `role="switch"` changes only how it is announced — on or off rather than checked or unchecked — so use it when the control takes effect immediately and leave it off when the change needs saving. Label it as you would any checkbox, and do not add `aria-checked`; the native state is authoritative and the two can disagree.
## Before JavaScript runs
This primitive is CSS only. There is nothing to register and nothing to wait for.
---
# Table
Responsive native tables with readable density. (CSS only.)
Reference: https://timeless.build/docs/components/table/
## Markup
```html
Primitive coverage
CSS-only component contracts in this milestone batch.
Primitive
Contract
Depth
Badge
.ui-badge
Flat
Card
.ui-card
Flat
```
## Install
```js
import '@timelessui/components/css/tokens.css'
import '@timelessui/components/css/core/table.css'
import '@timelessui/components/css/core/badge.css'
import '@timelessui/components/css/themes/atmosphere/tokens.css'
import '@timelessui/components/css/themes/atmosphere/badge.css'
import '@timelessui/components/css/themes/atmosphere/table.css'
```
## Anatomy
Parts are authored in your own markup and identified by the selector below. Required parts
must be present for the component to work. Private `data-ui-internal-*` hooks are written by
the runtime and must never be authored.
| Part | Required | Selector | Purpose |
| --- | --- | --- | --- |
| `caption` | No | `[data-ui-part~='caption']` | Table name. Use ``. |
| `description` | No | `[data-ui-part~='description']` | Supporting copy inside the caption. |
| `empty` | No | `[data-ui-part~='empty']` | Row shown in place of data when the table has none. |
## Attributes
Every value below is implemented by the stylesheets this component ships. Boolean attributes
are presence-based: author the attribute with no value, or omit it.
| Attribute | Values | Default | Description |
| --- | --- | --- | --- |
| `data-ui-density` | `compact` · `normal` | `normal` | Cell padding. |
| `data-ui-align` | `start` · `end` | `start` | Cell text alignment. Set it on a `` or ` `, not on the table. `end` also enables tabular numerals. |
## Styling
Root identity: `ui-table`.
Required stylesheets: `tokens.css`, `core/table.css`, `core/badge.css`, `themes/atmosphere/tokens.css`, `themes/atmosphere/badge.css`, `themes/atmosphere/table.css`.
This component adds no custom properties of its own. Restyle it through the design tokens at https://timeless.build/docs/styling/theming/ or your own CSS.
Design tokens this component reads (21), global and set at the theme level: `--ui-accent-soft`, `--ui-bg-control-muted`, `--ui-bg-surface`, `--ui-bg-surface-raised`, `--ui-danger`, `--ui-danger-soft`, `--ui-fg`, `--ui-fg-muted`, `--ui-focus`, `--ui-line`, `--ui-radius-lg`, `--ui-radius-pill`, `--ui-space-1`, `--ui-space-2`, `--ui-space-3`, `--ui-space-4`, `--ui-space-5`, `--ui-success`, `--ui-success-soft`, `--ui-warning`, `--ui-warning-soft`.
## Accessibility
Keep the native elements, roles, and relationships shown in the markup. Timeless adds only the
state and keyboard coordination the platform does not already provide, and it never supplies
your accessible names — those depend on your content.
This component declares no keyboard contract, because it has none of its own: the markup above
is native elements and native ARIA, and everything a keyboard or a screen reader does with it
comes from the platform. Nothing here manages focus, and nothing here adds a role you did not
author. So the accessibility of this component is the accessibility of the markup — which is why
the markup is the part worth copying exactly.
## Before JavaScript runs
This primitive is CSS only. There is nothing to register and nothing to wait for.
---
# Tabs
Associate tabs and panels with native keyboard behavior. (Custom element.)
Reference: https://timeless.build/docs/components/tabs/
## Markup
```html
Install
Usage
Install @timelessui/components from npm.
Author native Light DOM anatomy.
```
## Install
```js
import '@timelessui/components/css/tokens.css'
import '@timelessui/components/css/core/tabs.css'
import '@timelessui/components/css/themes/atmosphere/tokens.css'
import '@timelessui/components/css/themes/atmosphere/tabs.css'
import '@timelessui/components/register/ui-tabs'
```
A `register/` import is a side effect: it defines the element as the module evaluates, so it
has to run in code the browser loads. To register explicitly instead — to control the timing,
or to define into another window — call the function from the matching `define/` entry point,
which registers nothing on its own:
```js
import { defineTabsElement } from '@timelessui/components/define/ui-tabs'
defineTabsElement()
```
## Anatomy
Parts are authored in your own markup and identified by the selector below. Required parts
must be present for the component to work. Private `data-ui-internal-*` hooks are written by
the runtime and must never be authored.
| Part | Required | Selector | Purpose |
| --- | --- | --- | --- |
| `tablist` | Yes | `[role='tablist']` | Container for the tabs. |
| `tab` | Yes | `[role='tab']` | One tab. Use `` and give it a `value`; Timeless wires `id`, `aria-controls`, `aria-selected`, and `tabindex`. |
| `tabpanel` | Yes | `[role='tabpanel']` | One panel per tab, in the same order. Timeless wires `id`, `aria-labelledby`, and `hidden`. |
These parts also accept per-item input of their own. No stylesheet selects these
attributes — the component reads them.
| Part | Attribute | Description |
| --- | --- | --- |
| `tab` | `data-ui-value` | The value this tab selects, for a tab that cannot carry a bare `value` attribute. `value` wins over it, and the tab `id` is the last fallback. |
## Attributes
Every value below is implemented by the stylesheets this component ships. Boolean attributes
are presence-based: author the attribute with no value, or omit it.
| Attribute | Values | Default | Description |
| --- | --- | --- | --- |
| `activation` | `automatic` · `manual` | `automatic` | Whether moving focus with the arrow keys selects the tab immediately (`automatic`) or waits for Enter or Space (`manual`). Use `manual` when selecting a tab is expensive. |
| `orientation` | `horizontal` · `vertical` | `horizontal` | Arrow-key axis. Mirrored onto `aria-orientation` on the tablist during enhancement. |
| `value` | any `string` | — | The tab selected on load and after a form reset. Match a tab’s `value` attribute. Assign the `value` property for live changes. |
## Element API
Attributes above are the authoring surface. These are the DOM properties and events the
registered element adds once it upgrades.
### ``
| Property | Type | Notes |
| --- | --- | --- |
| `activation` | `'automatic' | 'manual'` | Reflects the `activation` attribute |
| `orientation` | `'horizontal' | 'vertical'` | Reflects the `orientation` attribute |
| `defaultValue` | `string` | Reflects the `value` attribute |
| `value` | `string` | Live value. Assigning it does not rewrite the authored default and does not dispatch transition events. |
| Event | Detail | Description |
| --- | --- | --- |
| `ui-before-change` | `CustomEvent` | Cancelable proposal dispatched before the selected tab changes. Call `preventDefault()` to reject the transition and keep the current value. |
| `ui-change` | `CustomEvent` | Dispatched after the selected tab has changed. Bubbles and is composed. |
## State
Native attributes, ARIA, and platform pseudo-classes are authoritative. Style state through
the selectors below rather than adding your own state classes.
| State | Source | How to express it |
| --- | --- | --- |
| `selected` | aria | `aria-selected="true"` on the active tab. |
## Styling
Root identity: `ui-tabs`.
Required stylesheets: `tokens.css`, `core/tabs.css`, `themes/atmosphere/tokens.css`, `themes/atmosphere/tabs.css`.
This component adds no custom properties of its own. Restyle it through the design tokens at https://timeless.build/docs/styling/theming/ or your own CSS.
Design tokens this component reads (12), global and set at the theme level: `--ui-accent`, `--ui-accent-soft`, `--ui-duration-fast`, `--ui-ease-standard`, `--ui-fg`, `--ui-fg-muted`, `--ui-focus`, `--ui-line`, `--ui-radius-md`, `--ui-space-2`, `--ui-space-4`, `--ui-space-5`.
## Accessibility
Keep the native elements, roles, and relationships shown in the markup. Timeless adds only the
state and keyboard coordination the platform does not already provide, and it never supplies
your accessible names — those depend on your content.
Follows the [Tabs pattern](https://www.w3.org/WAI/ARIA/apg/patterns/tabs/) from the ARIA Authoring Practices Guide.
| Key | Action |
| --- | --- |
| `Arrow keys` | Move focus between tabs along the `orientation` axis. |
| `Home / End` | Move focus to the first or last tab. |
| `Enter / Space` | Select the focused tab. Only needed when `activation` is `manual`; with `automatic` the arrow keys select as they move. |
The tablist is one tab stop: Tab moves into the selected tab, then out to the panel. Timeless manages roving `tabindex`, `aria-selected`, and panel `hidden`.
## Before JavaScript runs
The markup above is complete and usable on its own: native controls submit, labels associate, and authored ARIA is already correct. Registration adds state synchronisation, focus management, and keyboard coordination on top of it.
---
# Text and Code
Readable links, keyboard hints, and code treatments. (CSS only.)
Reference: https://timeless.build/docs/components/text-and-code/
## Markup
```html
Use links , inline code, and Cmd K in dense interface text.
import '@timelessui/components/css/themes/atmosphere.css'
```
## Install
```js
import '@timelessui/components/css/tokens.css'
import '@timelessui/components/css/core/code.css'
import '@timelessui/components/css/core/kbd.css'
import '@timelessui/components/css/themes/atmosphere/tokens.css'
import '@timelessui/components/css/themes/atmosphere/link.css'
import '@timelessui/components/css/themes/atmosphere/kbd.css'
import '@timelessui/components/css/themes/atmosphere/code.css'
```
## Attributes
Every value below is implemented by the stylesheets this component ships. Boolean attributes
are presence-based: author the attribute with no value, or omit it.
**ui-link**
| Attribute | Values | Default | Description |
| --- | --- | --- | --- |
| `data-ui-variant` | `default` · `muted` · `danger` | `default` | Link color intent. |
## Styling
Root identity: `ui-link`, `ui-kbd`, `ui-code`.
Required stylesheets: `tokens.css`, `core/code.css`, `core/kbd.css`, `themes/atmosphere/tokens.css`, `themes/atmosphere/link.css`, `themes/atmosphere/kbd.css`, `themes/atmosphere/code.css`.
This component adds no custom properties of its own. Restyle it through the design tokens at https://timeless.build/docs/styling/theming/ or your own CSS.
Design tokens this component reads (16), global and set at the theme level: `--ui-accent`, `--ui-accent-hover`, `--ui-bg-control-muted`, `--ui-bg-surface`, `--ui-bg-surface-raised`, `--ui-danger`, `--ui-duration-fast`, `--ui-ease-standard`, `--ui-fg`, `--ui-fg-muted`, `--ui-focus`, `--ui-font-mono`, `--ui-line`, `--ui-radius-sm`, `--ui-radius-xs`, `--ui-space-4`.
## Accessibility
Keep the native elements, roles, and relationships shown in the markup. Timeless adds only the
state and keyboard coordination the platform does not already provide, and it never supplies
your accessible names — those depend on your content.
This component declares no keyboard contract, because it has none of its own: the markup above
is native elements and native ARIA, and everything a keyboard or a screen reader does with it
comes from the platform. Nothing here manages focus, and nothing here adds a role you did not
author. So the accessibility of this component is the accessibility of the markup — which is why
the markup is the part worth copying exactly.
## Before JavaScript runs
This primitive is CSS only. There is nothing to register and nothing to wait for.
---
# Toast
Announce transient status from an explicit toaster. (Custom element.)
Reference: https://timeless.build/docs/components/toast/
## Markup
```html
Preview ready
The static example finished building.
```
## Install
```js
import '@timelessui/components/css/tokens.css'
import '@timelessui/components/css/core/toast.css'
import '@timelessui/components/css/themes/atmosphere/tokens.css'
import '@timelessui/components/css/themes/atmosphere/toast.css'
import '@timelessui/components/register/ui-toaster'
import '@timelessui/components/register/ui-toast'
```
A `register/` import is a side effect: it defines the element as the module evaluates, so it
has to run in code the browser loads. To register explicitly instead — to control the timing,
or to define into another window — call the function from the matching `define/` entry point,
which registers nothing on its own:
```js
import { defineToasterElement } from '@timelessui/components/define/ui-toaster'
defineToasterElement()
import { defineToastElement } from '@timelessui/components/define/ui-toast'
defineToastElement()
```
## Anatomy
Parts are authored in your own markup and identified by the selector below. Required parts
must be present for the component to work. Private `data-ui-internal-*` hooks are written by
the runtime and must never be authored.
****
| Part | Required | Selector | Purpose |
| --- | --- | --- | --- |
| `content` | No | `[data-ui-part~='content']` | Wrapper for the title and description. |
| `title` | No | `[data-ui-part~='title']` | Short summary line. |
| `description` | No | `[data-ui-part~='description']` | Supporting detail. |
| `close` | No | `[data-ui-part~='close']` | Dismiss button. Give it an accessible name. |
## Attributes
Every value below is implemented by the stylesheets this component ships. Boolean attributes
are presence-based: author the attribute with no value, or omit it.
****
| Attribute | Values | Default | Description |
| --- | --- | --- | --- |
| `placement` | `top-start` · `top-center` · `top-end` · `bottom-start` · `bottom-center` · `bottom-end` | `bottom-end` | Corner or edge of the viewport the toasts stack against. |
| `stack` | `overlap` · `list` | `overlap` | Whether queued toasts overlap into a deck (`overlap`) or lay out as a full list (`list`). |
****
| Attribute | Values | Default | Description |
| --- | --- | --- | --- |
| `duration` | any `number` | `5000` | Milliseconds before the toast dismisses itself. `0` keeps it open until dismissed. |
| `persistent` | presence | absent | Present to disable auto-dismiss regardless of `duration`. |
## Element API
Attributes above are the authoring surface. These are the DOM properties and events the
registered element adds once it upgrades.
### ``
| Property | Type | Notes |
| --- | --- | --- |
| `placement` | `'top-start' | 'top-center' | 'top-end' | 'bottom-start' | 'bottom-center' | 'bottom-end'` | Reflects the `placement` attribute |
| `stack` | `'overlap' | 'list'` | Reflects the `stack` attribute |
This element dispatches no component events.
### ``
| Property | Type | Notes |
| --- | --- | --- |
| `duration` | `string` | Reflects the `duration` attribute |
| `persistent` | `boolean` | Reflects the `persistent` attribute |
| Event | Detail | Description |
| --- | --- | --- |
| `ui-dismiss` | `CustomEvent` | Dispatched when the toast is dismissed. The detail names the reason: a timeout, the close control, or the imperative API. |
## Styling
Root identity: `ui-toaster`, `ui-toast`.
Required stylesheets: `tokens.css`, `core/toast.css`, `themes/atmosphere/tokens.css`, `themes/atmosphere/toast.css`.
Set these custom properties to restyle the component:
| Custom property | Controls |
| --- | --- |
| `--ui-toaster-gap` | Gap between toasts in `list` mode. |
| `--ui-toaster-overlap` | Offset between stacked toasts in `overlap` mode. |
Design tokens this component reads (13), global and set at the theme level: `--ui-bg-control`, `--ui-bg-surface`, `--ui-fg`, `--ui-fg-muted`, `--ui-focus`, `--ui-line`, `--ui-radius-lg`, `--ui-radius-md`, `--ui-shadow-floating`, `--ui-space-1`, `--ui-space-3`, `--ui-space-4`, `--ui-space-5`.
## Accessibility
Keep the native elements, roles, and relationships shown in the markup. Timeless adds only the
state and keyboard coordination the platform does not already provide, and it never supplies
your accessible names — those depend on your content.
****
Follows the [Alert pattern](https://www.w3.org/WAI/ARIA/apg/patterns/alert/) from the ARIA Authoring Practices Guide.
The toaster is the live region, and it is yours to name — author `role="region"` with an `aria-label`, or `role="status"` when the whole surface should announce. Timeless supplies both as a default and neither as a replacement for your own. Nothing here traps focus or steals it: a toast appears without moving the user, and is reached with Tab like anything else. Choose the role by urgency, not by appearance: `role="status"` on a toast is polite and waits its turn, `role="alert"` interrupts and should be rare.
****
Follows the [Alert pattern](https://www.w3.org/WAI/ARIA/apg/patterns/alert/) from the ARIA Authoring Practices Guide.
Give the toast the role its urgency deserves — `role="status"` for the ordinary case, `role="alert"` only when it must interrupt — and give the close control an accessible name. SC 2.2.1 is the criterion to hold in mind: `duration` defaults to 5000ms, and the timer stops while the toast is hovered or contains focus, so nobody loses the message they are reading or the control they are reaching for. `persistent`, or `duration="0"`, removes the limit outright, which is the right choice for anything the user must act on. Dismissing a toast that holds focus hands focus to a surviving toast, or back to wherever it came from, rather than dropping it on ``.
## Before JavaScript runs
The markup above is complete and usable on its own: native controls submit, labels associate, and authored ARIA is already correct. Registration adds state synchronisation, focus management, and keyboard coordination on top of it.
---
# Toggle
A pressed-state button using native button behavior. (CSS only.)
Reference: https://timeless.build/docs/components/toggle/
> **Choosing between components.** Use Toggle for a button that stays pressed, such as bold in a toolbar. Use [Switch](/docs/components/switch/) for a setting that applies immediately, which is a checkbox rather than a button.
## Markup
```html
Bold
```
## Install
```js
import '@timelessui/components/css/tokens.css'
import '@timelessui/components/css/core/toggle.css'
import '@timelessui/components/css/core/button.css'
import '@timelessui/components/css/themes/atmosphere/tokens.css'
import '@timelessui/components/css/themes/atmosphere/button.css'
import '@timelessui/components/css/themes/atmosphere/toggle.css'
```
## Attributes
Every value below is implemented by the stylesheets this component ships. Boolean attributes
are presence-based: author the attribute with no value, or omit it.
| Attribute | Values | Default | Description |
| --- | --- | --- | --- |
| `data-ui-variant` | `primary` · `secondary` · `outline` · `ghost` · `danger` · `danger-outline` · `link` | `primary` | Visual intent, resolved by `themes/atmosphere/button.css`. Author `class="ui-button ui-toggle"` so the shared button styling applies. |
| `data-ui-size` | `sm` · `md` · `lg` | `md` | Control height, padding, and font size. Resolved by `themes/atmosphere/button.css`. |
## State
Native attributes, ARIA, and platform pseudo-classes are authoritative. Style state through
the selectors below rather than adding your own state classes.
| State | Source | How to express it |
| --- | --- | --- |
| `pressed` | aria | Author `aria-pressed="true"` or `"false"`; never omit it. |
| `disabled` | native | Native `disabled`. |
## Styling
Root identity: `ui-toggle`.
Required stylesheets: `tokens.css`, `core/toggle.css`, `core/button.css`, `themes/atmosphere/tokens.css`, `themes/atmosphere/button.css`, `themes/atmosphere/toggle.css`.
This component adds no custom properties of its own. Restyle it through the design tokens at https://timeless.build/docs/styling/theming/ or your own CSS.
Design tokens this component reads (24), global and set at the theme level: `--ui-accent`, `--ui-accent-soft`, `--ui-bg-accent`, `--ui-bg-accent-active`, `--ui-bg-accent-hover`, `--ui-bg-control`, `--ui-bg-control-active`, `--ui-bg-control-hover`, `--ui-bg-control-muted`, `--ui-bg-danger`, `--ui-bg-danger-active`, `--ui-bg-danger-hover`, `--ui-duration-fast`, `--ui-ease-standard`, `--ui-fg`, `--ui-focus`, `--ui-line`, `--ui-radius-control`, `--ui-shadow-control-accent`, `--ui-shadow-control-active`, `--ui-shadow-control-danger`, `--ui-shadow-control-hover`, `--ui-shadow-outline-control`, `--ui-space-2`.
## Accessibility
Keep the native elements, roles, and relationships shown in the markup. Timeless adds only the
state and keyboard coordination the platform does not already provide, and it never supplies
your accessible names — those depend on your content.
This component declares no keyboard contract, because it has none of its own: the markup above
is native elements and native ARIA, and everything a keyboard or a screen reader does with it
comes from the platform. Nothing here manages focus, and nothing here adds a role you did not
author. So the accessibility of this component is the accessibility of the markup — which is why
the markup is the part worth copying exactly.
## Before JavaScript runs
This primitive is CSS only. There is nothing to register and nothing to wait for.
---
# Toggle Group
Coordinate single or multiple pressed controls. (Custom element.)
Reference: https://timeless.build/docs/components/toggle-group/
## Markup
```html
Left
Center
Right
```
## Install
```js
import '@timelessui/components/css/tokens.css'
import '@timelessui/components/css/core/toggle.css'
import '@timelessui/components/css/core/button.css'
import '@timelessui/components/css/themes/atmosphere/tokens.css'
import '@timelessui/components/css/themes/atmosphere/button.css'
import '@timelessui/components/css/themes/atmosphere/toggle.css'
import '@timelessui/components/register/ui-toggle-group'
```
A `register/` import is a side effect: it defines the element as the module evaluates, so it
has to run in code the browser loads. To register explicitly instead — to control the timing,
or to define into another window — call the function from the matching `define/` entry point,
which registers nothing on its own:
```js
import { defineToggleGroupElement } from '@timelessui/components/define/ui-toggle-group'
defineToggleGroupElement()
```
## Anatomy
Parts are authored in your own markup and identified by the selector below. Required parts
must be present for the component to work. Private `data-ui-internal-*` hooks are written by
the runtime and must never be authored.
****
| Part | Required | Selector | Purpose |
| --- | --- | --- | --- |
| `item` | Yes | `button[aria-pressed]` | One toggle button. Author `value` and `aria-pressed`; Timeless keeps the pressed set in sync. |
## Attributes
Every value below is implemented by the stylesheets this component ships. Boolean attributes
are presence-based: author the attribute with no value, or omit it.
****
| Attribute | Values | Default | Description |
| --- | --- | --- | --- |
| `attached` | presence | absent | Present to join the buttons into one segmented control. Styling only, resolved by `themes/atmosphere/toggle.css`. |
| `orientation` | `horizontal` · `vertical` | `horizontal` | Layout and arrow-key axis. |
| `selection` | `single` · `multiple` | `single` | Whether pressing one button releases the others (`single`) or toggles independently (`multiple`). |
**ui-toggle**
| Attribute | Values | Default | Description |
| --- | --- | --- | --- |
| `data-ui-variant` | `primary` · `secondary` · `outline` · `ghost` · `danger` · `danger-outline` · `link` | `primary` | Visual intent, resolved by `themes/atmosphere/button.css`. Author `class="ui-button ui-toggle"` so the shared button styling applies. |
| `data-ui-size` | `sm` · `md` · `lg` | `md` | Control height, padding, and font size. Resolved by `themes/atmosphere/button.css`. |
## Element API
Attributes above are the authoring surface. These are the DOM properties and events the
registered element adds once it upgrades.
### ``
| Property | Type | Notes |
| --- | --- | --- |
| `orientation` | `'horizontal' | 'vertical'` | Reflects the `orientation` attribute |
| `selection` | `'single' | 'multiple'` | Reflects the `selection` attribute |
| Event | Detail | Description |
| --- | --- | --- |
| `ui-before-change` | `CustomEvent` | Cancelable proposal dispatched before the pressed set changes. Call `preventDefault()` to reject the transition and keep the current value. |
| `ui-change` | `CustomEvent` | Dispatched after the pressed set has changed. Bubbles and is composed. |
## State
Native attributes, ARIA, and platform pseudo-classes are authoritative. Style state through
the selectors below rather than adding your own state classes.
| State | Source | How to express it |
| --- | --- | --- |
| `pressed` | aria | Author `aria-pressed="true"` or `"false"`; never omit it. |
| `disabled` | native | Native `disabled`. |
## Styling
Root identity: `ui-toggle-group`, `ui-toggle`.
Required stylesheets: `tokens.css`, `core/toggle.css`, `core/button.css`, `themes/atmosphere/tokens.css`, `themes/atmosphere/button.css`, `themes/atmosphere/toggle.css`.
This component adds no custom properties of its own. Restyle it through the design tokens at https://timeless.build/docs/styling/theming/ or your own CSS.
Design tokens this component reads (24), global and set at the theme level: `--ui-accent`, `--ui-accent-soft`, `--ui-bg-accent`, `--ui-bg-accent-active`, `--ui-bg-accent-hover`, `--ui-bg-control`, `--ui-bg-control-active`, `--ui-bg-control-hover`, `--ui-bg-control-muted`, `--ui-bg-danger`, `--ui-bg-danger-active`, `--ui-bg-danger-hover`, `--ui-duration-fast`, `--ui-ease-standard`, `--ui-fg`, `--ui-focus`, `--ui-line`, `--ui-radius-control`, `--ui-shadow-control-accent`, `--ui-shadow-control-active`, `--ui-shadow-control-danger`, `--ui-shadow-control-hover`, `--ui-shadow-outline-control`, `--ui-space-2`.
## Accessibility
Keep the native elements, roles, and relationships shown in the markup. Timeless adds only the
state and keyboard coordination the platform does not already provide, and it never supplies
your accessible names — those depend on your content.
Follows the [Toolbar pattern](https://www.w3.org/WAI/ARIA/apg/patterns/toolbar/) from the ARIA Authoring Practices Guide.
| Key | Action |
| --- | --- |
| `Arrow keys` | Move focus to the previous or next button, following the orientation and skipping disabled ones. |
| `Home / End` | Move focus to the first or last enabled button. |
The Toolbar pattern rather than the Button pattern, which is what the host renders: `role="toolbar"`, roving `tabindex`, and the whole group as one tab stop. Each button keeps native activation, and `aria-pressed` carries the state — so an individual control is still a button, which is why the pattern link used to point there. With `selection="single"` pressing one button releases the others. Give the host an accessible name; a toolbar without one is indistinguishable from any other.
## Before JavaScript runs
The markup above is complete and usable on its own: native controls submit, labels associate, and authored ARIA is already correct. Registration adds state synchronisation, focus management, and keyboard coordination on top of it.
---
# Toolbar
Arrow-key navigation across a group of commands. (Custom element.)
Reference: https://timeless.build/docs/components/toolbar/
## Markup
```html
Bold
Italic
Underline
Comment
```
## Install
```js
import '@timelessui/components/css/tokens.css'
import '@timelessui/components/css/core/toolbar.css'
import '@timelessui/components/css/core/button.css'
import '@timelessui/components/css/themes/atmosphere/tokens.css'
import '@timelessui/components/css/themes/atmosphere/button.css'
import '@timelessui/components/css/themes/atmosphere/toolbar.css'
import '@timelessui/components/register/ui-toolbar'
```
A `register/` import is a side effect: it defines the element as the module evaluates, so it
has to run in code the browser loads. To register explicitly instead — to control the timing,
or to define into another window — call the function from the matching `define/` entry point,
which registers nothing on its own:
```js
import { defineToolbarElement } from '@timelessui/components/define/ui-toolbar'
defineToolbarElement()
```
## Anatomy
Parts are authored in your own markup and identified by the selector below. Required parts
must be present for the component to work. Private `data-ui-internal-*` hooks are written by
the runtime and must never be authored.
| Part | Required | Selector | Purpose |
| --- | --- | --- | --- |
| `item` | Yes | `button, a, input, select, textarea` | Any focusable control in the toolbar. Timeless makes the group one tab stop and moves focus with the arrow keys. |
## Attributes
Every value below is implemented by the stylesheets this component ships. Boolean attributes
are presence-based: author the attribute with no value, or omit it.
| Attribute | Values | Default | Description |
| --- | --- | --- | --- |
| `orientation` | `horizontal` · `vertical` | `horizontal` | Arrow-key axis across the toolbar controls. |
## Element API
Attributes above are the authoring surface. These are the DOM properties and events the
registered element adds once it upgrades.
### ``
| Property | Type | Notes |
| --- | --- | --- |
| `orientation` | `'horizontal' | 'vertical'` | Reflects the `orientation` attribute |
This element dispatches no component events.
## Styling
Root identity: `ui-toolbar`.
Required stylesheets: `tokens.css`, `core/toolbar.css`, `core/button.css`, `themes/atmosphere/tokens.css`, `themes/atmosphere/button.css`, `themes/atmosphere/toolbar.css`.
This component adds no custom properties of its own. Restyle it through the design tokens at https://timeless.build/docs/styling/theming/ or your own CSS.
Design tokens this component reads (22), global and set at the theme level: `--ui-accent`, `--ui-bg-accent`, `--ui-bg-accent-active`, `--ui-bg-accent-hover`, `--ui-bg-control`, `--ui-bg-control-active`, `--ui-bg-control-hover`, `--ui-bg-control-muted`, `--ui-bg-danger`, `--ui-bg-danger-active`, `--ui-bg-danger-hover`, `--ui-duration-fast`, `--ui-ease-standard`, `--ui-fg`, `--ui-focus`, `--ui-radius-control`, `--ui-radius-md`, `--ui-shadow-control-accent`, `--ui-shadow-control-active`, `--ui-shadow-control-danger`, `--ui-shadow-outline-control`, `--ui-space-1`.
## Accessibility
Keep the native elements, roles, and relationships shown in the markup. Timeless adds only the
state and keyboard coordination the platform does not already provide, and it never supplies
your accessible names — those depend on your content.
Follows the [Toolbar pattern](https://www.w3.org/WAI/ARIA/apg/patterns/toolbar/) from the ARIA Authoring Practices Guide.
| Key | Action |
| --- | --- |
| `Arrow keys` | Move focus to the previous or next control, following the orientation and skipping disabled ones. |
| `Home / End` | Move focus to the first or last enabled control. |
The whole toolbar is one tab stop. Timeless manages roving `tabindex` so Tab moves past the group rather than through every control. A toolbar control is a real control rather than a command, so `disabled` is the right spelling here and the arrows skip it — unlike [Menu](/docs/components/menu/), where `aria-disabled` keeps an unavailable command discoverable. Give the host an accessible name; a toolbar without one is indistinguishable from any other.
## Before JavaScript runs
The markup above is complete and usable on its own: native controls submit, labels associate, and authored ARIA is already correct. Registration adds state synchronisation, focus management, and keyboard coordination on top of it.
---
# Tooltip
A concise tooltip built on the hover-card controller. (Custom element.)
Reference: https://timeless.build/docs/components/tooltip/
> **Choosing between components.** Tooltip and [Hover Card](/docs/components/hover-card/) are one custom element with two boxes. `variant="tooltip"` gives a short non-interactive label that sizes to its content and never scrolls; omit it for a roomier surface holding content worth reading or clicking. Because both are ``, the host attributes — `anchor`, `placement`, `open-delay`, `close-delay` — are the same for each and are documented once, on [Hover Card](/docs/components/hover-card/#attributes).
## Markup
```html
Copy
Copy canonical markup
```
## Install
```js
import '@timelessui/components/css/tokens.css'
import '@timelessui/components/css/core/floating.css'
import '@timelessui/components/css/core/popover.css'
import '@timelessui/components/css/core/button.css'
import '@timelessui/components/css/themes/atmosphere/tokens.css'
import '@timelessui/components/css/themes/atmosphere/button.css'
import '@timelessui/components/css/themes/atmosphere/popover.css'
import '@timelessui/components/register/ui-hover-card'
```
A `register/` import is a side effect: it defines the element as the module evaluates, so it
has to run in code the browser loads. To register explicitly instead — to control the timing,
or to define into another window — call the function from the matching `define/` entry point,
which registers nothing on its own:
```js
import { defineHoverCardElement } from '@timelessui/components/define/ui-hover-card'
defineHoverCardElement()
```
## Anatomy
Parts are authored in your own markup and identified by the selector below. Required parts
must be present for the component to work. Private `data-ui-internal-*` hooks are written by
the runtime and must never be authored.
| Part | Required | Selector | Purpose |
| --- | --- | --- | --- |
| `trigger` | Yes | `[data-ui-part~='trigger']` | Control the label describes. Point its `aria-describedby` at the surface. |
| `content` | Yes | `[popover]` | The label. One short, non-interactive line; give it `role="tooltip"`. |
## Element API
Attributes above are the authoring surface. These are the DOM properties and events the
registered element adds once it upgrades.
### ``
| Property | Type | Notes |
| --- | --- | --- |
| `anchor` | `string` | Reflects the `anchor` attribute |
| `variant` | `'tooltip'` | Reflects the `variant` attribute |
| `placement` | `'bottom' | 'top' | 'right' | 'left'` | Reflects the `placement` attribute |
| `openDelayValue` | `string` | Reflects the `open-delay` attribute |
| `closeDelayValue` | `string` | Reflects the `close-delay` attribute |
This element dispatches no component events.
## Styling
Root identity: `ui-hover-card[variant='tooltip']`.
Required stylesheets: `tokens.css`, `core/floating.css`, `core/popover.css`, `core/button.css`, `themes/atmosphere/tokens.css`, `themes/atmosphere/button.css`, `themes/atmosphere/popover.css`.
Set these custom properties to restyle the component:
| Custom property | Controls |
| --- | --- |
| `--ui-tooltip-bg` | Surface background. Inverted against the page by default. |
| `--ui-tooltip-fg` | Label color, and the border tint is mixed from it. |
Design tokens this component reads (30), global and set at the theme level: `--ui-accent`, `--ui-bg-accent`, `--ui-bg-accent-active`, `--ui-bg-accent-hover`, `--ui-bg-control`, `--ui-bg-control-active`, `--ui-bg-control-hover`, `--ui-bg-control-muted`, `--ui-bg-danger`, `--ui-bg-danger-active`, `--ui-bg-danger-hover`, `--ui-bg-surface`, `--ui-duration-fast`, `--ui-ease-standard`, `--ui-fg`, `--ui-fg-muted`, `--ui-focus`, `--ui-line`, `--ui-radius-control`, `--ui-radius-lg`, `--ui-radius-md`, `--ui-shadow-control-accent`, `--ui-shadow-control-active`, `--ui-shadow-control-danger`, `--ui-shadow-floating`, `--ui-shadow-outline-control`, `--ui-shadow-tooltip`, `--ui-space-2`, `--ui-space-3`, `--ui-space-4`.
## Accessibility
Keep the native elements, roles, and relationships shown in the markup. Timeless adds only the
state and keyboard coordination the platform does not already provide, and it never supplies
your accessible names — those depend on your content.
Follows the [Tooltip pattern](https://www.w3.org/WAI/ARIA/apg/patterns/tooltip/) from the ARIA Authoring Practices Guide.
| Key | Action |
| --- | --- |
| `Escape` | Close the label while the trigger has focus. |
A tooltip names or describes its trigger and nothing else. Point the trigger at it with `aria-describedby` and give the surface `role="tooltip"`; Timeless wires relationships, never content. It opens on hover and on keyboard focus, so it is reachable without a mouse, and closes when either leaves. It is deliberately not a disclosure: clicking the trigger does not toggle the label, so a trigger that is also a button keeps its own job on click. The pointer can still be moved onto the label without it disappearing, because WCAG 2.2 SC 1.4.13 requires hover-triggered content to be hoverable — reading a label is not interacting with it. Never put the only copy of anything here — for content the user may want to read at length or click, use Hover Card instead. Both variants share one `open-delay` / `close-delay` pair, documented once on Hover Card; a tooltip that should appear faster sets the attribute rather than getting a different default, because one attribute cannot have two.
## Before JavaScript runs
The markup above is complete and usable on its own: native controls submit, labels associate, and authored ARIA is already correct. Registration adds state synchronisation, focus management, and keyboard coordination on top of it.