`s; `cell` is the class a footer cell takes, `rows` what the search and filters left, `span` the column count. |
| `onrowclick?` | `(row: T, index: number) => void` | Called when a row is pressed. Makes rows interactive. |
| `loading?` | `boolean` | Draw skeletons while the data is on its way. Default `false`. |
| `loadingRows?` | `number` | Default `5`. |
| `emptyText?` | `string` | Message when there are no rows at all. Default `'Nothing here yet'`. |
| `noResultsText?` | `string` | Message when the filters or the search excluded everything. Default `'No rows match your filters'`. |
| `label?` | `string` | Accessible name for the table. |
| `caption?` | `string` | Visible caption under the table. |
| `class?` | `string` | |
| `classes?` | `TableClasses` | Per-part Tailwind overrides. `class` still covers the root. |
| `actions?` | `Snippet` | Extra controls on the trailing edge of the toolbar. |
| `selectionActions?` | `Snippet<[Key[]]>` | Replaces the toolbar's trailing edge while rows are selected. |
| `empty?` | `Snippet` | Rich empty state, in place of `emptyText`. |
### TableFilterControl
`import { TableFilterControl } from 'omaris'`
One filter in a table's toolbar.
| Prop | Type | Notes |
| --- | --- | --- |
| `label` | `string` | |
| `options` | `TableFilterOption[]` | |
| `selected` | `string[]` | Bindable. Default `[]`. |
| `single?` | `boolean` | Default `false`. |
| `inline?` | `boolean` | Lay the options out as toggle chips rather than behind a menu. |
| `format?` | `(value: string) => string` | |
| `onchange?` | `(selected: string[]) => void` | |
### TabList
`import { TabList } from 'omaris'`
The row of tabs, and the indicator that slides between them.
| Prop | Type | Notes |
| --- | --- | --- |
| `label?` | `string` | Accessible name for the tab set. |
| `divider?` | `boolean` | Draw the hairline the indicator rides on. Default `true`. |
| `scrollable?` | `boolean` | One line that scrolls sideways, for a set too wide to fit. Default `false`. |
| `class?` | `string` | |
| `children?` | `Snippet` | |
### TabPanel
`import { TabPanel } from 'omaris'`
The content behind one tab. Its `value` must match that tab's.
| Prop | Type | Notes |
| --- | --- | --- |
| `value` | `string` | Must match the `value` of the tab that opens it. |
| `keepMounted?` | `boolean` | Keep the panel mounted while hidden. Costs memory but preserves scroll position, form state and any in-flight work in the panel. Default `false`. |
| `class?` | `string` | |
| `children?` | `Snippet` | |
### TabPanels
`import { TabPanels } from 'omaris'`
The viewport the panels live in, and the thing a swipe moves.
| Prop | Type | Notes |
| --- | --- | --- |
| `threshold?` | `number` | Fraction of the width a drag has to cover before it counts as a move. A fast flick moves regardless. Default `0.25`. |
| `disabled?` | `boolean` | Turns the gesture off for this viewport, leaving the layout alone. Default `false`. |
| `class?` | `string` | |
| `children?` | `Snippet` | |
### Tabs
`import { Tabs } from 'omaris'`
MD3 tabs — a `TabList` of `Tab`s over one `TabPanel` per tab.
| Prop | Type | Notes |
| --- | --- | --- |
| `value?` | `string` | The selected tab's value. Bindable. Default `undefined`. |
| `variant?` | `TabsVariant` | MD3's two kinds. `primary` is the top-level one — bigger, with the indicator hugging the label. `secondary` sits inside a primary tab's content and runs its indicator the full width of the tab. Default `'primary'`. |
| `size?` | `TabsSize` | Default `'md'`. |
| `fitted?` | `boolean` | Share the width evenly rather than hugging the labels. Default `false`. |
| `swipeable?` | `boolean` | Let the panels be dragged sideways. Needs the panels wrapped in `TabPanels`, and mounts them all at once so there is something to drag to — so leave it off for a set whose panels are expensive. Default `false`. |
| `orientation?` | `TabsOrientation` | `vertical` stacks the tabs down the start edge beside the panels — a settings screen's sections. ↑ and ↓ move between them. Default `'horizontal'`. |
| `activation?` | `TabsActivation` | `automatic` shows a tab's panel as soon as the arrow keys reach it. `manual` only moves focus, and Enter or Space opens it — for panels that are expensive to show, or that fetch when they open. Default `'automatic'`. |
| `class?` | `string` | |
| `children?` | `Snippet` | |
### TagsInput
`import { TagsInput } from 'omaris'`
Free-form tags, typed into one field.
#### Use it for
- Free-form values the person types — keywords, labels, email recipients, allowed
domains — bound as a `string[]` with `bind:value`.
- A list that is vetted as it grows: `validate` returns the reason a tag was
refused and leaves the text in the box to fix; `unique` (on by default)
refuses a repeat; `max` caps it and shows a count.
- Pasting a list from somewhere else — a comma- or newline-separated paste
becomes one tag per piece, each vetted on its own.
- A form field like any other: inside a [Field](/docs/components/field), or
with its own `label` and `supportingText`, in `Input`'s variants and sizes.
#### Not for
- Picking several from a known list → [Multi Select](/docs/components/multi-select);
its `creatable` covers the occasional value that is not on the list.
- One free-text value → [Input](/docs/components/input); one choice from a
list → [Combobox](/docs/components/combobox).
- Filters the person switches on and off → filter
[Chips](/docs/components/chip) in a `ChipGroup`.
- Showing tags nobody edits → [Chips](/docs/components/chip) or
[Badges](/docs/components/badge).
#### Do
- Say what separates tags in the `placeholder` or `supportingText` — "Separate
with commas" — the first time it matters.
- Return a specific reason from `validate` — "Not an email address", not
"Invalid" — and keep it short; it shares a line with the count.
- Pass `name` in a plain form: every tag posts as its own value under it.
- Localise `removeLabel`, `duplicateMessage` and `maxMessage` with the rest of
the page's strings.
#### Don't
- Use it for values that must come from a fixed set; a typed tag that the
server then rejects is worse than a list that never offered it.
- Put more than a line or two of tags in it — past about fifteen, the
field is a list and wants to be a [List](/docs/components/list) with an
add row.
- Set `required` expecting the browser to enforce it; the box is empty
whenever a tag has been added. Check `value.length` yourself.
#### Pairs with
- [Field](/docs/components/field), [Chip](/docs/components/chip),
[Multi Select](/docs/components/multi-select), [Input](/docs/components/input)
Parts (`classes`): `root` `field` `control` `label` `adornment` `affix` `action` `support` `counter` `list` `chip` `input`
| Prop | Type | Notes |
| --- | --- | --- |
| `value?` | `string[]` | The tags, in the order they were added. Bindable. Default `[]`. |
| `query?` | `string` | What is typed but not yet a tag. Bindable — handy for suggestions. Default `''`. Bindable. |
| `variant?` | `InputVariant` | Default `'outline'`. |
| `size?` | `InputSize` | One of `sm`, `default`, `lg`. Default `'default'`. |
| `label?` | `string` | Label above the field. Wired to the text box, so clicking it focuses. |
| `supportingText?` | `string` | Helper line under the field. A rejected tag's reason replaces it. |
| `invalid?` | `boolean` | Marks the field invalid. Inside a `Field`, defaults to the field's. |
| `required?` | `boolean` | Stars the label. Inside a `Field`, defaults to the field's. |
| `max?` | `number` | Most tags it takes. Adds an `n/max` count under the field. |
| `unique?` | `boolean` | Refuse a tag that is already there, ignoring case. Default `true`. |
| `validate?` | `(tag: string, value: string[]) => string \| true` | Vets a tag before it goes in. Return `true` to accept it, or the reason it was refused — that text shows under the field and the typed text stays put for fixing. |
| `duplicateMessage?` | `string` | Shown when `unique` refuses a tag. Default `'Already added.'`. |
| `maxMessage?` | `string` | Shown when a tag would go past `max`. Defaults to "Up to {max}." |
| `removeLabel?` | `(tag: string) => string` | Accessible name of a tag's ×. Default `(tag: string) => `Remove ${tag}``. |
| `onchange?` | `(value: string[]) => void` | Fires with the new list after every add and remove. |
| `class?` | `string` | |
| `classes?` | `TagsInputClasses` | Per-part Tailwind overrides. `class` still covers the root. |
| `ref?` | `HTMLInputElement \| null` | Default `null`. Bindable. |
### Text
`import { Text } from 'omaris'`
Text — the one component every string in the app goes through, instead of a ``, a `` or a `` with a size class on it.
#### Use it for
- Every string in the app: a heading, a paragraph, a cell, a caption.
`variant` is how it looks, `as` is what it means.
`
` is a card title that is also a heading.
- Five roles, three sizes each. `display-*` for a stat card's hero number,
`headline-*` for page and section titles, `title-*` for cards and dialogs,
`body-*` for prose and cells, `label-*` for chips, headers and timestamps.
- Text with its own meaning: `tone="muted"` for secondary text,
`tone="destructive"` for an error. Everything else inherits, so text on a
coloured surface stays legible.
- Figures. `tabular` lines up a column and stops a live counter twitching;
`font="mono"` for ids, hashes and anything compared character by character.
- Clipping. `lines={1}` for a filename in a row, `lines={2}` for a card
description.
#### Not for
- A label inside a component that already sets its type, like a
[Button](/docs/components/button), [Chip](/docs/components/chip),
[Badge](/docs/components/badge) or [Label](/docs/components/label) → pass the
string. The component owns the `label-*` role.
- A block of code → [Code Block](/docs/components/code-block). `as="code"` is
for one identifier inside a sentence.
- Long prose with its own headings, lists and links → the
[Typography](/docs/typography) guide covers that scale.
- A `sr-only` span, a single glyph or a layout-only wrapper → a raw utility
class. An emoji → `Emoji` from [Emoji Picker](/docs/components/emoji-picker).
#### Do
- Let `as` carry the semantics and `variant` the look. Set both when they
differ; leave one out and it is inferred. `as="h2"` is `headline-md`; a
variant alone is a `` (a `` for `label-*`).
- Pick the variant by the job of the text, not the size you want. Each variant
sets size, line height, letter spacing and weight together. Needing
`font-semibold` after `variant="title-md"` means the wrong variant.
- Leave `wrap` alone. Headings balance and body avoids orphans already. Set
`wrap="nowrap"` with `lines={1}` only for a cell that must stay one line.
- On a dashboard card: `display-sm` is the metric, `title-md` the card's
name, `body-sm` with `tone="muted"` the comparison under it.
#### Don't
- Write ``. That is
``, and it moves with the theme.
- Use `weight` to make a label bold. `label-lg` is already the weight buttons
and tabs use.
- Put `tone="default"` on text inside a tinted card or a filled button. It
goes illegible in dark mode. Let it inherit.
- Use `display-lg` twice on a screen. It is the one number the screen is
about.
#### Pairs with
- [Card](/docs/components/card), [Table](/docs/components/table),
[Label](/docs/components/label), [Badge](/docs/components/badge),
[Typography](/docs/typography)
| Prop | Type | Notes |
| --- | --- | --- |
| `variant?` | `TextVariant` | How it looks — the type token minus `text-`. Pick by the job the text does: `display` is the one hero number on a screen, `headline` titles a page or section, `title` titles a card or dialog, `body` is prose and cell contents, `label` is buttons, chips, tabs and column headers. Each one carries size, line height, letter spacing and weight together. Defaults to `body-md`, or to the role that matches `as` when that is a heading. One of `display-lg`, `display-md`, `display-sm`, `headline-lg`, `headline-md`, `headline-sm`, `title-lg`, `title-md`, `title-sm`, `body-lg`, `body-md`, `body-sm`, `label-lg`, `label-md`, `label-sm`. |
| `as?` | `TextElement` | What it means. Defaults to `span` for `label-*` and `p` for everything else. Use a heading tag for anything that is one — the look does not make it a heading, this does. |
| `tone?` | `TextTone` | Colour role. Inherits from the surface by default, so a Text inside a coloured container stays legible; set it when the colour itself means something — `muted` for secondary copy, `destructive` for an error. One of `inherit`, `default`, `muted`, `primary`, `secondary`, `tertiary`, `destructive`, `success`, `warning`, `info`, `inverse`. Default `'inherit'`. |
| `weight?` | `TextWeight` | Overrides the weight the variant carries. If you want it, the role is probably wrong. One of `normal`, `medium`, `semibold`, `bold`. |
| `align?` | `TextAlign` | Logical, so it follows the writing direction. One of `start`, `center`, `end`, `justify`. |
| `wrap?` | `TextWrap` | Overrides the wrapping the variant sets — headings balance, body avoids orphans. One of `normal`, `balance`, `pretty`, `nowrap`. |
| `lines?` | `TextLines` | Clamps to N lines with an ellipsis. `lines={1}` is a single line. One of `1`, `2`, `3`, `4`, `5`, `6`. |
| `font?` | `TextFont` | Family. Inherits unless set; `mono` for ids, hashes and code. One of `sans`, `mono`. |
| `tabular?` | `boolean` | Fixed-width digits, for numbers that sit in a column. Default `false`. |
| `for?` | `string` | Only with `as="label"`: the id of the control it labels. |
| `datetime?` | `string` | Only with `as="time"`: the machine-readable date. |
| `class?` | `string` | |
| `children?` | `Snippet` | |
### Textarea
`import { Textarea } from 'omaris'`
Multi-line text field.
#### Use it for
- Text that runs past a line: delivery notes, a description, a reply, an
address.
- A composer. `autosize` with `rows={1}` and a `maxRows` grows one line at a
time. `bind:lines` swaps in a send button or shows a "shift+enter for a new
line" hint.
- A hard limit the person should see coming. Pass `counter` with `maxlength`.
#### Not for
- One line, like a title, a name or a URL → [Text Field](/docs/components/input).
- Bold, a list, a link → [Rich Text Editor](/docs/components/rich-text-editor).
- Code, JSON, a query → [Code Editor](/docs/components/code-editor) with
`lang`. It indents, closes brackets and colours.
- Chat text with emoji → still this, with an [Emoji Button](/docs/components/emoji-picker)
beside it.
#### Do
- Match the Input next to it: same `variant`, same `size`. They share a frame
and line up without adjustment.
- Set `rows` to the length you expect: `2` for a note, `6` for a description.
The default `3` is a guess.
- Give `autosize` a `maxRows`, or a pasted essay pushes the submit button off
the screen.
- Put the reason in `supportingText` and set `invalid`. The helper line
becomes the error line.
#### Don't
- Autosize inside a grid of cards. The row below moves every time a line is
added. Fix `rows` there and let it scroll.
- Expect the resize grip with `autosize`. The box sizes itself, so the grip
is removed. Without `autosize` the grip is on, vertical only.
- Use the placeholder for instructions needed after typing starts. That is
`supportingText`.
#### Pairs with
- [Text Field](/docs/components/input), [Label](/docs/components/label),
[Emoji Picker](/docs/components/emoji-picker), [Button](/docs/components/button),
[Chip](/docs/components/chip)
Parts (`classes`): `root` `field` `well` `control` `mirror` `label` `support` `counter`
| Prop | Type | Notes |
| --- | --- | --- |
| `variant?` | `TextareaVariant` | One of `outline`, `filled`, `ghost`. Default `'outline'`. |
| `size?` | `TextareaSize` | One of `sm`, `default`, `lg`. Default `'default'`. |
| `label?` | `string` | |
| `required?` | `boolean` | Inside a `Field`, defaults to the field's. |
| `supportingText?` | `string` | |
| `invalid?` | `boolean` | Inside a `Field`, defaults to the field's. |
| `value?` | `string` | Default `''`. Bindable. |
| `counter?` | `boolean` | Live `n/maxlength` count beside the supporting text. Default `false`. |
| `autosize?` | `boolean` | Grow with the text. The box starts at `rows`, gains a line as the text takes one, and scrolls once it reaches `maxRows`. Default `false`. |
| `rows?` | `number` | Starting height, in lines — and the floor when `autosize`. Default `3`. |
| `maxRows?` | `number` | Ceiling for `autosize`, in lines. Past this it scrolls. |
| `lines?` | `number` | Lines the text currently occupies. Bindable, and read-only in practice — useful for a "shift+enter for a new line" hint or for swapping a send button in once the composer has something in it. Default `1`. Bindable. |
| `class?` | `string` | |
| `classes?` | `TextareaClasses` | Per-part Tailwind overrides. `class` still covers the root. |
| `supporting?` | `Snippet` | Richer supporting text — a link, a hint. |
| `ref?` | `HTMLTextAreaElement \| null` | Default `null`. Bindable. |
### ThemeSettings
`import { ThemeSettings } from 'omaris'`
A ready-made appearance screen for the theme store.
#### Use it for
- The appearance screen of a product built on omaris. Put ` `
behind a "Customise" button, in a [Dialog](/docs/components/dialog) or a
[Sheet](/docs/components/sheet).
- Every knob of the theme store: colour, shape, density, type, motion. Each
changes the whole product live. The store and its tokens are explained in
[Theming](/docs/theming).
- A settings page that already has tabs. `layout="stack"` drops the tabs and
gives each section a heading.
- Only what the product honours: `sections={['color']}`, with
`customHue={false}` to keep people on the presets.
- A product in English, Arabic, Turkish or Sorani Kurdish. It follows the
page's `lang`, or `locale="ar" | "tr" | "ckb" | "en"` picks one; Arabic and
Kurdish mirror the screen. `labels` swaps any word.
#### Not for
- A dark-mode toggle in the app bar → a [Switch](/docs/components/switch) or
an [Icon Button](/docs/components/icon-button) bound to the theme store
(see [Theming](/docs/theming)). This is the whole screen, not one row.
- Setting the brand for everyone → tokens in `app.css` and the provider's
defaults ([Tokens](/docs/tokens)). This screen is one person's preference.
- A colour for a record, like a tag or a label → [Color Picker](/docs/components/color-picker).
- An app without `OmarisProvider` → nothing works. The provider applies the
theme and remembers it.
#### Do
- Wrap the app in `OmarisProvider` so the choice applies live and survives a
reload.
- Put it where people look for it, under Settings or Appearance, and give it
a measure like `max-w-2xl`.
- Trim `sections` to what the product honours. If the typeface is pinned,
drop `text`; if the density is fixed, drop `layout`.
- Keep `resettable` on. Someone who picked the wrong hue needs a way back.
- Set `lang` on `` and let the screen follow it, rather than passing
`locale` in one place and forgetting it in another.
#### Don't
- Ship all five sections in a one-screen tool. Two sections in `stack` is
enough.
- Restyle the swatches through `classes` to match the brand. The swatches are
the presets; their colours are the point.
- Put it in onboarding. People choose a theme after they have seen the
product.
- Mount two on one page. Both write the same store.
- Translate it by wrapping it in your own strings. Pass `labels` for the
words you want to change; the rest stay in the chosen language.
#### Pairs with
- [Dialog](/docs/components/dialog), [Sheet](/docs/components/sheet),
[Switch](/docs/components/switch), [Tabs](/docs/components/tabs),
[Color Picker](/docs/components/color-picker)
Parts (`classes`): `root` `section` `heading` `row` `label` `hint` `choices` `choice` `choiceArt` `choiceLabel` `swatch` `swatchDot` `swatchLabel` `footer`
| Prop | Type | Notes |
| --- | --- | --- |
| `layout?` | `ThemeSettingsLayout` | One of `tabs`, `stack`. Default `'tabs'`. |
| `sections?` | `ThemeSettingsSection[]` | Which groups to show, in order. Default `ALL`. |
| `resettable?` | `boolean` | Show the "Reset" row at the bottom. Default `true`. |
| `customHue?` | `boolean` | Offer the custom hue slider alongside the presets. Default `true`. |
| `locale?` | `ThemeSettingsLocale` | The language — `en`, `ar`, `tr` or `ckb` (Sorani Kurdish). Arabic and Kurdish lay the screen out right to left. Leave it out to follow the nearest `lang` attribute on the page, falling back to English. |
| `labels?` | `Partial` | Replace any of the words, over whichever language is showing. |
| `class?` | `string` | |
| `classes?` | `ThemeSettingsClasses` | Per-part Tailwind overrides. `class` still covers the root. |
### Timeline
`import { Timeline } from 'omaris'`
An activity feed or an order's history: events in order, each on a rail with a dot or an icon, joined by the line that makes them read as one sequence rather than a list of unrelated rows.
#### Use it for
- An order's or a ticket's history — placed, paid, shipped, delivered — with
the step still to come marked `pending`.
- An activity feed: who did what and when, `variant="compact"`, one
`Timeline` per day with a `heading` ("Today", "Yesterday").
- An audit log with exact dates: `relative={false}` and a `dateFormat`.
- Events that carry more than a line — a comment, a set of labels, a diff —
as the item's children.
#### Not for
- Steps the person is moving through right now — a checkout, an onboarding →
[Stepper](/docs/components/stepper).
- Records to sort, filter or compare → [Table](/docs/components/table).
- Live, high-volume output — a build log, a server stream →
[Log Viewer](/docs/components/log-viewer).
- A list of things that are not in time order → [List](/docs/components/list).
#### Do
- Pass `time` as a `Date`, a timestamp or an ISO string. It is written relative
to now in the page's language, marked up as ``, and kept
current from one shared clock.
- Use `tone="neutral"` for routine events and save the colours for the ones
that need attention — a failure, a refund, a delivery.
- Set `maxAge` on a long feed so old events read as dates rather than "47
weeks ago".
- Give the list a name: a `heading`, or a `label` when there is none.
#### Don't
- Mark more than one item `pending`; it is the step that has not happened
yet, and there is one.
- Put the time in the title as text; `time` is what gives it the tooltip and
the machine-readable date.
- Give every item an icon. Icons pick out the events worth finding at a
glance; a column of them is as flat as a column of dots.
#### Pairs with
- [Data List](/docs/components/data-list) for the details above the history,
[Avatar](/docs/components/avatar) as an item's icon,
[Chip](/docs/components/chip), [Card](/docs/components/card),
[Sheet](/docs/components/sheet)
Parts (`classes`): `root` `heading` `list`
| Prop | Type | Notes |
| --- | --- | --- |
| `variant?` | `TimelineVariant` | `default` gives each event room and icon-sized markers; `compact` is a dense feed. Default `'default'`. |
| `heading?` | `string` | A heading over the list, which also names it for a screen reader — "Today", "Yesterday". Use one `Timeline` per group. |
| `level?` | `2 \| 3 \| 4 \| 5 \| 6` | Heading level, so the group fits the page outline. Default `3`. |
| `label?` | `string` | Accessible name for the list when there is no `heading`. |
| `relative?` | `boolean` | Write times relative to now — "3 minutes ago". Off, they are written as dates with `dateFormat`. Either way the exact moment is in the `` and the tooltip. Default `true`. |
| `maxAge?` | `number` | Past this age, in ms, a relative time switches to the date: a week is `7 * 24 * 3600_000`. "47 weeks ago" is not information. |
| `dateFormat?` | `Intl.DateTimeFormatOptions` | How absolute dates are written. Defaults to a long date and a short time. |
| `locale?` | `string` | Language of the times. English, Arabic, Turkish and Sorani Kurdish are built in; defaults to ``. Digits stay Western. |
| `class?` | `string` | |
| `classes?` | `TimelineClasses` | Per-part Tailwind overrides. `class` still covers the root. |
| `header?` | `Snippet` | Rich heading, in place of `heading`. |
| `children?` | `Snippet` | |
### TimelineItem
`import { TimelineItem } from 'omaris'`
One event on a `Timeline`: a marker on the rail, a title, when it happened, and whatever else it needs underneath.
Parts (`classes`): `root` `rail` `marker` `dot` `icon` `line` `content` `header` `title` `time` `description` `body`
| Prop | Type | Notes |
| --- | --- | --- |
| `title?` | `string` | What happened — "Order shipped". |
| `description?` | `string` | A supporting line under the title. |
| `time?` | `Date \| string \| number` | When it happened. A `Date`, a timestamp or an ISO string is written relative to now and marked up as ``; a string that is not a date ("Tomorrow, 9–12") is shown as given. |
| `tone?` | `TimelineItemTone` | The marker's colour role. `neutral` is a quiet grey dot for events that need no attention; save the tones for the ones that do. One of `neutral`, `primary`, `secondary`, `tertiary`, `destructive`, `success`, `warning`, `info`. Default `'primary'`. |
| `pending?` | `boolean` | The event that has not happened yet — the courier on the way, the review awaiting. Its marker pulses (and holds still under reduced motion), and the line into it is dashed. Usually the last item. Default `false`. |
| `pendingLabel?` | `string` | Spoken after the title of a pending item. Default `'In progress'`. |
| `class?` | `string` | |
| `classes?` | `TimelineItemClasses` | Per-part Tailwind overrides. `class` still covers the root. |
| `icon?` | `Snippet` | An icon in a tonal circle, in place of the dot. |
| `children?` | `Snippet` | Rich content under the description. |
### TimePicker
`import { TimePicker } from 'omaris'`
Hour, minute and AM/PM.
Parts (`classes`): `root` `header` `stage` `band` `wheels` `actions` `action`
| Prop | Type | Notes |
| --- | --- | --- |
| `value?` | `string` | `"HH:mm"`, stored 24-hour; the wheels always show 1–12 and AM/PM. Bindable. Default `'09:00'`. |
| `minuteStep?` | `number` | Minutes between rows — 5 or 15 for an opening time, 1 for an alarm. Default `1`. |
| `loop?` | `boolean` | Spin past midnight and noon instead of stopping. On by default. Default `true`. |
| `labels?` | `{ hour?: string; minute?: string; meridiem?: string; am?: string; pm?: string }` | Accessible names for the wheels, and the AM/PM row text. |
| `onchange?` | `(value: string) => void` | Fires once a wheel settles, with the 24-hour string. |
| `columnClasses?` | `PickerColumnClasses` | Per-part overrides for the wheels themselves. |
| `classes?` | `PickerClasses` | |
| `size?` | `PickerSize` | Row height and type scale. |
| `surface?` | `PickerSurface` | How the frame is drawn. |
| `visible?` | `number` | Rows on screen at once. Rounded up to the next odd number. |
| `title?` | `string` | A title above the wheels. |
| `disabled?` | `boolean` | Disables every wheel inside. |
| `haptics?` | `boolean` | Play a tick as each row passes the centre, once haptics are on. |
| `onconfirm?` | `() => void` | Renders the action row. Called with the value already committed. |
| `oncancel?` | `() => void` | Renders a cancel button beside it. |
| `confirmLabel?` | `string` | |
| `cancelLabel?` | `string` | |
| `footer?` | `Snippet` | Replaces the built-in action row. |
| `class?` | `string` | |
### Toast
`import { Toast } from 'omaris'`
One toast card.
#### Use it for
- Confirming something just happened: "Saved", "Invitation sent", "Copied".
Call `toast('Saved')` from anywhere. `OmarisProvider` already renders the
` `.
- Undo. Put `action: { label: 'Undo', onclick }` on the toast that reports a
delete or archive, instead of a confirm dialog before it.
- A job the person can walk away from. `toast.promise()` with `loading`,
`success` and `error` turns one card from spinner to result.
- A failure that needs no decision: "Could not sync, retrying". `toast.error`
stays six seconds instead of four.
- A message that must stand out while the page is in use: `variant="rich"`
tints the whole card.
#### Not for
- A message that stays until the problem clears → [Alert](/docs/components/alert).
- A decision, like "Delete 12 files?" → [Dialog](/docs/components/dialog). A
toast never blocks and leaves on its own.
- Progress of the button just pressed → the [Button](/docs/components/button)'s
`loading`.
- A count or a "new" mark → [Badge](/docs/components/badge).
- Field validation → the field's `supportingText`. A toast is gone before it
is read.
#### Do
- Write one line with a verb: "Invoice sent", not "Success!". Put detail in
`description`.
- Keep to one `action`. Add `cancel` only for a destructive pair. Three
buttons means you want a dialog.
- Reuse an `id` to update a toast in place, for a changing count or a retry.
- Let it close on its own. Use `duration: 0` with `closeButton` only for a
message that needs acknowledging, like a lost connection.
- Set `keepOpen` on an action whose press does not finish the job.
#### Don't
- Toast a result that is already on screen: the row is gone, the panel
closed, the page moved. Most actions get no toast.
- Toast every row in a bulk edit. Batch it: "12 rows updated".
- Put the only way to reach something in a toast. A swipe throws it away.
- Fire one on page load.
- Mount a second ` `. There is one stack; a message that belongs to
a region is an [Alert](/docs/components/alert).
#### Pairs with
- [Alert](/docs/components/alert), [Dialog](/docs/components/dialog),
[Button](/docs/components/button), [Progress](/docs/components/progress),
[Badge](/docs/components/badge)
Parts (`classes`): `root` `icon` `body` `title` `description` `actions` `action` `cancel` `close`
| Prop | Type | Notes |
| --- | --- | --- |
| `toast` | `ToastItem` | |
| `variant?` | `ToastVariant` | One of `plain`, `rich`. Default `'plain'`. |
| `swipe?` | `ToastSwipe` | Default `{ x: 0, y: 1 }`. |
| `closeButton?` | `boolean` | Stack-wide default; the toast's own `closeButton` wins. Default `false`. |
| `ondismiss?` | `() => void` | |
| `onhold?` | `(held: boolean) => void` | Held while a finger is on the card, so the timer can pause. |
| `ontap?` | `() => void` | A press that turned out to be a press: it ended without becoming a swipe and did not land on a button. The stack uses it as "expand" on a touchscreen, where there is no hover to fan the deck with. |
| `class?` | `string` | |
| `classes?` | `ToastClasses` | Per-part Tailwind overrides. `class` still covers the root. |
### Toaster
`import { Toaster } from 'omaris'`
Where toasts appear. `OmarisProvider` renders one, so you only need this if you want them somewhere else or stacked differently.
| Prop | Type | Notes |
| --- | --- | --- |
| `position?` | `ToastPosition` | Default `'bottom-end'`. |
| `visible?` | `number` | How many are on screen at once. The rest wait in the deck. Default `3`. |
| `variant?` | `ToastVariant` | Fill style for every toast the stack renders. Default `'plain'`. |
| `closeButton?` | `boolean` | Show the × on every toast. Default `false`. |
| `expand?` | `boolean` | Keep the deck fanned out instead of expanding on hover. Default `false`. |
| `gap?` | `number` | Gap between toasts once expanded, in px. Default `14`. |
| `class?` | `string` | |
### Toc
`import { Toc } from 'omaris'`
Toc — the "on this page" rail.
#### Use it for
- The "on this page" rail beside a long document, a report, or a settings
page with a dozen sections.
- Content that scrolls inside an [App Shell](/docs/components/app-shell)
column, a panel or a dialog. It follows whatever scrolls, not only the
window.
- A page that knows its own position. `spy={false}` and `bind:active` move
the marker from your own stepper or form.
#### Not for
- Moving between pages → [Navigation Drawer](/docs/components/navigation-drawer).
- Sections that swap rather than scroll → [Tabs](/docs/components/tabs).
- Where this page sits among others → [Breadcrumb](/docs/components/breadcrumb).
- A page with two headings → drop the rail. The marker needs enough sections
to move between.
#### Do
- Set `offset` to the height of whatever sticks to the top, like a `sticky`
[Top App Bar](/docs/components/top-app-bar), plus a little. Otherwise the
marker changes section early.
- Give every heading a stable `id`, and pass `level` so nesting indents.
Indentation is relative to the shallowest level in the list.
- Hide it below a wide breakpoint (`hidden xl:block`). It needs a column of
its own, and a phone has none.
- Use `size="sm"` when it shares a narrow column with something else.
#### Don't
- List every `h4`. Two levels is enough; deeper is noise.
- Drop `label` when hiding the heading with `showLabel={false}`. The
accessible name comes from it.
- Leave headings without ids. The links are anchors, and nothing scrolls to a
heading it cannot find.
#### Pairs with
- [App Shell](/docs/components/app-shell), [Top App Bar](/docs/components/top-app-bar),
[Text](/docs/components/text), [Accordion](/docs/components/accordion),
[Breadcrumb](/docs/components/breadcrumb)
Parts (`classes`): `root` `title` `rail` `marker` `list` `item` `link`
| Prop | Type | Notes |
| --- | --- | --- |
| `items?` | `TocItem[]` | The headings, in the order they appear on the page. Default `[]`. |
| `label?` | `string` | Heading above the list, and the nav's accessible name. Default `'On this page'`. |
| `showLabel?` | `boolean` | Draw the heading. The accessible name survives either way. Default `true`. |
| `active?` | `string` | The id of the highlighted heading. Bindable. Default `undefined`. |
| `spy?` | `boolean` | Follow the scroll position. Off leaves `active` to the consumer. Default `true`. |
| `offset?` | `number` | How far below the top of whatever scrolls — the viewport, or the panel the headings are in — a heading counts as read, in pixels: the height of whatever sticks to the top, plus a little. Default `96`. |
| `size?` | `TocSize` | One of `sm`, `md`. Default `'md'`. |
| `marker?` | `boolean` | Draw the sliding marker. Off leaves the hairline and the colour. Default `true`. |
| `onnavigate?` | `(id: string) => void` | Runs when a link is clicked, after `active` has moved. |
| `class?` | `string` | |
| `classes?` | `TocClasses` | Per-part Tailwind overrides. `class` still covers the root. |
### Tooltip
`import { Tooltip } from 'omaris'`
MD3 tooltip — plain and rich.
#### Use it for
- Naming an [Icon Button](/docs/components/icon-button). Wrap it and pass
`text` with the same words as its `aria-label`. On a toolbar the first
waits; the rest open as fast as the pointer moves.
- A line about a truncated cell, a chart point or an abbreviation. Plain
`text`, a phrase long.
- Context worth a paragraph and a link, like what a retention window means.
`title` turns on rich mode, `content` is the body, `actions` the buttons.
It stays open while the pointer is inside.
- `open` to pin one for a screenshot or a story.
#### Not for
- Anything needed to finish the task → put it on the page, as `supportingText`
or an [Alert](/docs/components/alert). On a phone a tooltip only appears on
long press.
- The only name a control has → a visible label or an `aria-label`. A tooltip
describes; it does not name.
- Content people interact with, like a form or a list → [Menu](/docs/components/menu),
[Sheet](/docs/components/sheet) or [Dialog](/docs/components/dialog). Only a
rich tooltip's `actions` are reachable.
- A sequence of hints → [Tour](/docs/components/tour).
- Something that just happened → [Toast](/docs/components/toast).
#### Do
- Wrap the thing it describes. The wrapper is the anchor; hover, focus and
long press all open it, so keyboard users get it too.
- Keep a plain tooltip to a phrase. It caps at 16rem and wraps into a block
after that.
- Set `side` to where there is usually room. It flips when there is none, but
one that always flips looks wrong.
- Leave `delay` alone across an app. `0` makes every hover flash.
#### Don't
- Put one on a disabled button. It gets no hover and no focus, so it never
opens. Say why in text beside it.
- Attach one to plain text. A `` never takes focus, so it becomes
pointer-only. Give the trigger `tabindex="0"` or make it a button.
- Repeat the visible label. "Save" under a button that says Save is noise.
- Use rich when plain would do. A rich tooltip needs a title and a reason to
exist.
#### Pairs with
- [Icon Button](/docs/components/icon-button), [Button](/docs/components/button),
[Tour](/docs/components/tour), [Menu](/docs/components/menu),
[Text](/docs/components/text)
Parts (`classes`): `root` `surface` `title` `body` `actions`
| Prop | Type | Notes |
| --- | --- | --- |
| `text?` | `string` | Plain-tooltip text. |
| `title?` | `string` | Heading of a rich tooltip. Its presence turns rich mode on. |
| `side?` | `AnchorSide` | Which side to prefer. Flips when there isn't room. Default `'top'`. |
| `align?` | `AnchorAlign` | Default `'center'`. |
| `offset?` | `number` | Gap between the trigger and the tooltip, in px. Default `8`. |
| `delay?` | `number` | Delay before opening, in ms. Suppressed once one is already open. Default `500`. |
| `open?` | `boolean` | Keep it open. Useful for a story or a walkthrough. Default `false`. Bindable. |
| `disabled?` | `boolean` | Default `false`. |
| `class?` | `string` | |
| `classes?` | `TooltipClasses` | Per-part Tailwind overrides. `class` still covers the root. |
| `children?` | `Snippet` | The trigger. |
| `content?` | `Snippet` | Rich body, in place of `text`. |
| `actions?` | `Snippet` | Buttons along the bottom of a rich tooltip. |
### TopAppBar
`import { TopAppBar } from 'omaris'`
MD3 top app bar — small, medium, large and center-aligned.
#### Use it for
- The top of every screen in an [App Shell](/docs/components/app-shell): the
screen's `title`, a `leading` back arrow or menu button, and up to three
`actions`.
- A long read, like a report or a document. `size="large"` with `flexible`
collapses the headline into a small bar as the page scrolls.
- The dashboard's top bar: a [Search Bar](/docs/components/search-bar) in the
`search` slot, navigation on the left, the account on the right.
- A map or a canvas. `floating` detaches it into a rounded, blurred capsule
over the content.
- A tab row under the bar, passed as `children`. It moves with the bar.
#### Not for
- The app's navigation → [Navigation Drawer](/docs/components/navigation-drawer)
or [Navigation Bar](/docs/components/navigation-bar). The bar carries one
screen's title and actions.
- A bar plus a filter row that stick together → wrap both in an
`AppShellHeader` from [App Shell](/docs/components/app-shell).
- A headline with no actions, halfway down a page → [Text](/docs/components/text)
with `variant="headline-md"`.
- A selection bar like "3 selected, delete" → a sticky `AppShellFooter` from
[App Shell](/docs/components/app-shell), at the bottom.
#### Do
- Set `sticky`. It does not stick on its own. Inside a shell it knows which
column scrolls; `scrollContainer` is only for other layouts.
- Use one `leading` control: back on a detail screen, the menu button when the
drawer is modal, nothing on a top-level screen.
- Keep to three icon `actions`. The rest go in a [Menu](/docs/components/menu)
behind the last one.
- Pick `size` by the screen: `small` for a dense dashboard page, `medium` or
`large` where the headline is the point, `center` on a top-level screen
with one action.
#### Don't
- Stack two bars. The tab row is `children`; the search is `search`.
- Set `hideOnScroll` on a screen whose actions people use while they work. It
is for reading.
- Use `variant="surface"` on a flat page. `flat` is flat at rest and lifts
once content scrolls under it.
- Expect `title` and `search` together on a small bar. `search` replaces the
middle; only `medium` and `large` keep the headline on the row below.
#### Pairs with
- [App Shell](/docs/components/app-shell), [Navigation Drawer](/docs/components/navigation-drawer),
[Search Bar](/docs/components/search-bar), [Tabs](/docs/components/tabs),
[Icon Button](/docs/components/icon-button), [Menu](/docs/components/menu)
Parts (`classes`): `root` `bar` `leading` `title` `actions` `headlineRow` `headline` `search`
| Prop | Type | Notes |
| --- | --- | --- |
| `size?` | `TopAppBarSize` | One of `small`, `center`, `medium`, `large`. Default `'small'`. |
| `variant?` | `TopAppBarVariant` | One of `flat`, `surface`, `transparent`. Default `'flat'`. |
| `title?` | `string` | Headline text. |
| `sticky?` | `boolean` | Stick to the top of the scroll container. Default `false`. |
| `floating?` | `boolean` | Rounded, blurred and inset from the edges. Default `false`. |
| `elevateOnScroll?` | `boolean` | Lift onto a raised surface once the page is scrolled. Give it a scroll container element to watch something other than the window. Default `true`. |
| `flexible?` | `boolean` | Collapse a medium or large bar into a small one as the page scrolls — MD3's flexible bar. Default `false`. |
| `collapseDistance?` | `number` | Scroll distance the collapse runs over, in px. Defaults to the headline's own height, which is the distance it has to travel. |
| `hideOnScroll?` | `boolean` | Slide the bar away while scrolling down; bring it back on the way up. Default `false`. |
| `scrollContainer?` | `HTMLElement \| null` | What to watch for scrolling. Inside an `AppShell` this is already known — the shell hands over its scrolling column — so it's only needed for a layout that scrolls something else. `null` is the window, explicitly. |
| `threshold?` | `number` | Scroll distance, in px, before the bar lifts. Default `4`. |
| `progress?` | `number` | How far the collapse has run, 0…1. Bindable, read-only in practice. Default `0`. Bindable. |
| `class?` | `string` | |
| `classes?` | `TopAppBarClasses` | Per-part Tailwind overrides. `class` still covers the root. |
| `leading?` | `Snippet` | Leading control — a back arrow or menu button. |
| `headline?` | `Snippet` | Rich headline, in place of `title`. |
| `actions?` | `Snippet` | Trailing controls. |
| `search?` | `Snippet` | Takes over the middle of the action row, full width — a `SearchBar`, usually. It *replaces* what sits there rather than joining it, so on a small or centred bar `title` is not drawn at all while this is set. A medium or large bar still draws its headline on the row below, so those keep both. |
| `children?` | `Snippet` | Anything below the bar — a tab row. |
### Tour
`import { Tour } from 'omaris'`
A guided tour: a spotlight on one thing at a time, with a card explaining it.
#### Use it for
- A first run through a dashboard: three to six steps pointing at real things
like the new-order button, the filter bar, the settings menu.
`createTour(steps, { id, once: true })` runs it once per browser and
resumes after a reload.
- A "what's new" walk started from a button or `?tour=1` in the URL. Call
`tour.start()` from anywhere, `restart()` to run it again.
- A flow across pages. Put a `path` on the steps that live elsewhere, pass
`navigate: goto`, and mount ` ` in `+layout.svelte` so
navigation does not unmount it.
- Steps whose target is not on screen yet. Open a menu, switch a tab or await
a fetch in the step's `before`.
#### Not for
- Explaining one control on hover → [Tooltip](/docs/components/tooltip). A
tour is a sequence.
- Documentation, more than two sentences a step → a docs page, linked from a
rich [Tooltip](/docs/components/tooltip) or an [Alert](/docs/components/alert)
with an action.
- An announcement with nothing to point at, like "Welcome to v2" →
[Dialog](/docs/components/dialog). One centred, targetless step can open a
tour but should not be the whole tour.
- Walking someone through a form → the form itself, with `required` fields
and `supportingText`.
- A screen with nothing on it → [Empty](/docs/components/empty). With no
targets every step is skipped and the tour finishes at once.
#### Do
- Select targets by an id or a `data-tour` attribute you own. A step that
names a design-system class breaks on the next refactor.
- Keep a step to a title and a sentence or two. The card fits the room beside
the target and scrolls; on a phone that room is small.
- Let a step ask for a real click when it helps. The page stays live inside
the spotlight; use the next step's `before` to put things back.
- Set `round` on a step pointing at an avatar or icon button, and `padding`
where the target's own padding is thin.
- Leave `dismissible` on, and watch `onskip` to learn where people leave.
#### Don't
- Run it on every visit. Give it an `id` and `once`, and a "Take the tour"
button for the rest.
- Put more than about eight steps in one tour. Split it by page and start the
second from the first's `onfinish`.
- Mount ` ` inside the page a step navigates away from. It goes with
the page.
- Point at a target inside an `overflow: hidden` box that clips it. The hole
is clipped to what is visible.
#### Pairs with
- [Tooltip](/docs/components/tooltip), [Dialog](/docs/components/dialog),
[Empty](/docs/components/empty), [App Shell](/docs/components/app-shell),
[Button](/docs/components/button)
Parts (`classes`): `scrim` `spotlight` `card` `scroller` `body` `header` `title` `counter` `progress` `progressBar` `content` `footer` `dots` `dot` `actions` `ghost` `next`
| Prop | Type | Notes |
| --- | --- | --- |
| `steps?` | `TourStep[]` | The steps. Omit it when `tour` is given — the controller holds them. |
| `tour?` | `TourController` | A controller from `createTour`, holding `steps`, `open` and `index` outside the component. Use one when the tour crosses pages: the state lives in the controller (and, with an `id`, in storage), so a real navigation resumes the tour where it was instead of ending it. |
| `open?` | `boolean` | Whether the tour is running. Bindable. Ignored when `tour` is given. Default `false`. Bindable. |
| `index?` | `number` | Which step, from 0. Bindable. Setting it to a step whose target is missing lands on the next one that resolves. Ignored when `tour` is given. Default `0`. Bindable. |
| `scrim?` | `TourScrim` | One of `soft`, `strong`. Default `'soft'`. |
| `padding?` | `number` | Default breathing room around a target, in px. Default `8`. |
| `width?` | `number \| string` | Card width. A number is px, a string any CSS length. Default `320`. |
| `dismissible?` | `boolean` | Let `Escape` and a click on the scrim end the tour. Default `true`. |
| `dots?` | `boolean` | Show the progress dots. Default `true`. |
| `progress?` | `boolean` | Show the slim progress bar under the header. Default `true`. |
| `labels?` | `{ next?: string; back?: string; done?: string; skip?: string }` | Button text. |
| `scrollIntoView?` | `boolean` | Bring each target into view before pointing at it. Default `true`. |
| `wait?` | `number` | How long to wait, in ms, for a step's target to appear before the step is treated as missing and skipped. `0` looks once. Raise it for targets that arrive with a fetch; a step with a `path` waits anyway. Default `0`. |
| `navigate?` | `(path: string) => void \| Promise` | How a step's `path` is followed. `goto` from `$app/navigation` in a SvelteKit app; a full page load otherwise, which a tour with an `id` survives. |
| `onfinish?` | `() => void` | |
| `onskip?` | `(index: number) => void` | |
| `onstep?` | `(index: number, step: TourStep) => void` | Fires once per step shown, after its target has been found. |
| `class?` | `string` | |
| `classes?` | `TourClasses` | |
### TreemapChart
`import { TreemapChart } from 'omaris/chart'`
Treemap — a part-of-a-whole where the parts are too many, or too lopsided, for a pie.
| Prop | Type | Notes |
| --- | --- | --- |
| `data` | `T[]` | |
| `label?` | `Field` | Field naming each tile. Defaults to `row.label`. Default `'label'`. |
| `value?` | `Field` | Field holding each tile's size. Defaults to `row.value`. Default `'value'`. |
| `group?` | `Field` | Field gathering rows into blocks. |
| `height?` | `number \| string` | Plot height. A number is px; a string is any CSS length. Default `300`. |
| `gap?` | `number` | Gap between tiles, in px. Default `2`. |
| `radius?` | `number` | Corner radius on the tiles, in px. Default `4`. |
| `values?` | `boolean` | Print each tile's value under its name. Default `false`. |
| `tooltip?` | `boolean` | Default `true`. |
| `format?` | `(value: number) => string` | How each value reads. |
| `colors?` | `readonly string[]` | Palette override, cycled across the tiles or the groups. Default `CHART_COLORS`. |
| `title?` | `string` | Accessible description of what the chart shows. |
| `class?` | `string` | |
| `empty?` | `Snippet` | |
| `emptyText?` | `string` | |
| `error?` | `boolean \| string \| Error` | Something went wrong. Truthy puts the chart into its error state; a string or an `Error` supplies the message. |
| `errorText?` | `string` | The message, when `error` is just `true`. |
| `errorState?` | `Snippet<[{ message: string }]>` | Replaces the whole error state. |
| `onRetry?` | `() => void` | Offers a "Try again" button in the error state. |
| `csv?` | `boolean \| string` | Offer the plotted rows as a CSV download, from a button in the corner. A string names the file. Default `false`. |
| `csvColumns?` | `(string \| { key: string; label?: string; value?: (row: T) => unknown })[]` | Which fields the download writes, in order. Defaults to every field. |
### TreeView
`import { TreeView } from 'omaris'`
A tree of rows you can open, close and walk with the keyboard.
#### Use it for
- Files and folders: a repository browser, an attachment picker, a media
library.
- Any nesting a person navigates rather than reads: a category tree, an org
chart, nested filters, a JSON path, a chart of accounts.
- A sidebar of sections whose depth varies from page to page, where a flat
list would lose the relationships.
- Picking a set out of a hierarchy, with `checkable`. Ticking a branch ticks
everything under it; a partly ticked branch shows a dash.
#### Not for
- One level of rows → [List](/docs/components/list).
- Sections of content on one page → [Accordion](/docs/components/accordion).
An accordion opens content; a tree opens more rows.
- The app's own navigation → [Navigation Drawer](/docs/components/navigation-drawer),
which knows the current route.
- Rows with several columns of data → [Table](/docs/components/table).
- A data structure for reading rather than walking → [JSON Viewer](/docs/components/json-viewer).
#### Do
- Keep `expanded` yourself when open branches should survive a navigation or
come from a URL. `bind:expanded` is a plain array of ids.
- Use `expandable: true` on a branch whose children are fetched, load them in
`onexpand`, and set `loading` while they are on the way. The chevron
becomes a spinner.
- Put a size, a count or a status in `meta`, not in the label. It is
right-aligned and stays out of the truncation.
- Give the tree a `label`. It is the accessible name.
- Turn `guides` off for a shallow tree and keep them past about three levels.
Use `size="sm"` in a sidebar, where the tree is not the subject.
#### Don't
- Render thousands of rows at once. A tree is for a hierarchy someone walks.
Page or search instead.
- Use `checkable` and `onselect` for two jobs at once. With checkboxes,
clicking a row ticks it; a row that also navigates is ambiguous.
- Nest more than about five levels. The indent runs out of screen first.
- Reuse an `id` anywhere in the tree. `expanded`, `selected` and `checked`
all key off it.
#### Pairs with
- [List](/docs/components/list), [Resizable](/docs/components/resizable),
[Code Block](/docs/components/code-block),
[Context Menu](/docs/components/context-menu),
[Empty](/docs/components/empty)
Parts (`classes`): `root` `item` `toggle` `icon` `label` `meta` `check` `group` `guide`
| Prop | Type | Notes |
| --- | --- | --- |
| `items?` | `TreeNode[]` | The tree. Default `[]`. |
| `expanded?` | `string[]` | Ids of the open branches. Bindable. Default `[]`. |
| `selected?` | `string \| null` | Id of the selected row, or `null`. Bindable. Default `null`. |
| `checked?` | `string[]` | Ids of the ticked **leaves** — rows with no children. A branch's own state is worked out from them, so a half-ticked folder is a state you never have to store. Bindable. Default `[]`. |
| `checkable?` | `boolean` | Give every row a checkbox and tick branches through their leaves. Default `false`. |
| `size?` | `TreeViewSize` | One of `sm`, `md`. Default `'md'`. |
| `guides?` | `boolean` | Draw the vertical rules down each open branch. Default `true`. |
| `icons?` | `boolean` | Use the built-in folder and file icons for rows with no `icon`. Default `true`. |
| `defaultExpanded?` | `boolean` | Open every branch on first render. Default `false`. |
| `label?` | `string` | Accessible name for the tree. |
| `onselect?` | `(node: TreeNode) => void` | Fires when a row is picked — clicked, or Enter on the keyboard. |
| `onexpand?` | `(node: TreeNode) => void` | Fires when a branch opens. The place to fetch its children. |
| `oncollapse?` | `(node: TreeNode) => void` | Fires when a branch closes. |
| `row?` | `Snippet<[TreeNode, { depth: number; expanded: boolean; selected: boolean }]>` | Replaces a row's contents, after the chevron and the checkbox. |
| `empty?` | `Snippet` | Shown when `items` is empty. |
| `class?` | `string` | |
| `classes?` | `TreeViewClasses` | Per-part Tailwind overrides. `class` still covers the root. |
### VideoPlayer
`import { VideoPlayer } from 'omaris'`
A video player with its own chrome.
#### Use it for
- A clip that belongs to the app, like an onboarding video, a lesson or a
recording, with chrome that matches the UI. Pass `src`, `poster`, `title`.
- Long content. `chapters` mark the scrubber and name the hover bubble;
`tracks` add captions, and with one track the CC button is a switch.
- Handing the file over: `download`, and `pip` where the browser has it.
- A hero: `aspect="21/9"`, `fit="cover"`, `rounded="xl"`. `rounded="none"` for
a player that fills the page.
- A page that follows along. `paused`, `currentTime`, `volume` and `muted`
are bindable, and `ref` is the `` itself.
#### Not for
- Pictures → [Image Viewer](/docs/components/image-viewer).
- A muted background loop with no controls → a plain
``.
- A YouTube or Vimeo embed → their `