# omaris — everything The complete API followed by every guide from the documentation site. For an index that links to one page per component, read llms.txt instead; the pages it links to also ship in the package, under docs/. --- # omaris A Material 3 Expressive component library for SvelteKit. This is the complete public API in one place, generated from the source — every prop, its type, and whatever the source says about it. For an index that links to one page per component instead, read llms.txt. ## Conventions that apply everywhere - Import from `omaris`. Charts are `omaris/chart` and maps are `omaris/map`, separate so their heavy dependencies stay opt-in. - `class` styles the root element and is merged last, so it always wins. - Multi-part components also take `classes`, one Tailwind string per part: ``. The parts each component has are listed under it below. - Classes merge through tailwind-merge, which only resolves conflicts within a group. `aspect-video` does not cancel a height; where a component ships one it gives you a prop instead (see `Carousel.height`). - Every component spreads the rest of its props onto its root element, so native attributes and `data-*` work as usual. - Variants are exported as `Variants` for reuse on your own elements. - Icons: hand a component an ``; it sets the size and the colour. omaris bundles no icon set. Lucide matches its built-in glyphs. - Each component below opens with "Use it for" and "Not for". A "Not for" line names the component to use instead; follow it. ## Components ### Accordion `import { Accordion } from 'omaris'` MD3 expansion panels. #### Use it for - Stacked sections people scan by heading and open one at a time: an FAQ, a settings page's groups, the stages of a build. `type="single"` opens one and closes the rest. - Sections read together, like a filter panel or a checklist, where several stay open. Use `type="multiple"`, usually with `variant="separated"` so each panel is its own card. - Open state that lives elsewhere. `value` is the whole open set and is bindable, so it can come from the URL or storage. - A header that carries status. `supportingText` for "Passed in 42s", `end` for a badge or a count, `disabled` for a stage that cannot open yet. #### Not for - Peers of equal weight where exactly one is visible → [Tabs](/docs/components/tabs). - One block behind a "Show more" → [Collapsible](/docs/components/collapsible). - Sections of a site's navigation → [Navigation Drawer](/docs/components/navigation-drawer). - Rows the person picks from → [List](/docs/components/list). #### Do - Match the variant to the surface: `plain` flush on a page, `outlined` among other things, `separated` when each panel is its own item, like a pipeline stage or an order. - Keep the trigger to one line and a `supportingText`. Put content in the body. - Use `disabled` with a reason in `supportingText` ("Blocked by the failing tests") instead of hiding the panel. - Leave `collapsible` on. Turn it off only when an empty accordion makes no sense, like a wizard that must always show the current step. #### Don't - Put a form's required fields in a closed panel. A hidden validation error blocks the submit. - Open every panel of a `multiple` accordion by default. That is a page with headings; use [Text](/docs/components/text) headings. - Put a button or a link in `heading`. The trigger is already a button. - Nest one accordion inside another. #### Pairs with - [Badge](/docs/components/badge), [Card](/docs/components/card), [List](/docs/components/list), [Divider](/docs/components/divider), [Tabs](/docs/components/tabs) | Prop | Type | Notes | | --- | --- | --- | | `value?` | `string[]` | Open item values. Bindable. Default `[]`. | | `type?` | `'single' \| 'multiple'` | `single` closes the others on open; `multiple` doesn't. Default `'single'`. | | `variant?` | `AccordionVariant` | One of `plain`, `separated`, `outlined`. Default `'plain'`. | | `collapsible?` | `boolean` | Stop the last open item from being closed. Default `true`. | | `class?` | `string` | | | `children?` | `Snippet` | | ### AccordionItem `import { AccordionItem } from 'omaris'` One panel of an `Accordion`: a header you press and the content it reveals. Parts (`classes`): `root` `trigger` `headline` `supporting` `wrapper` `content` `inner` `chevron` | Prop | Type | Notes | | --- | --- | --- | | `value` | `string` | Identifies the panel in the accordion's `value`. | | `title?` | `string` | Trigger text. | | `supportingText?` | `string` | Second line in the trigger. | | `disabled?` | `boolean` | Default `false`. | | `class?` | `string` | | | `classes?` | `AccordionItemClasses` | Per-part Tailwind overrides. `class` still covers the root. | | `icon?` | `Snippet` | Leading icon in the trigger. | | `heading?` | `Snippet` | Rich trigger content, in place of `title`. | | `end?` | `Snippet` | Trailing content before the chevron — a badge, a count. | | `children?` | `Snippet` | The panel body. | ### AiChat `import { AiChat } from 'omaris'` A conversation with a model — the thread, the streaming reply, the composer, the stop button — wired to whichever model you have. #### Use it for - A conversation with a model, anywhere one belongs: a help desk in a drawer, an assistant in a dashboard's sidebar, a whole chat page. One `send` prop and it streams, renders markdown, scrolls, stops and retries. - An assistant with a job. `system` says what it is for, `title` and `hint` greet the person, `suggestions` are the three things people ask it. - A conversation that comes back. `bind:messages` is the whole state; save it in `onchange`, load it on the way in. - A reply that shows its work. Reasoning a model streams is folded above its answer, with nothing to turn on. - A composer of your own. An empty `composer` snippet takes the bar away, and `send()`, `stop()`, `regenerate()` and `clear()` come through `bind:this`. #### Not for - Chat between people → [List](/docs/components/list) with your own transport. This one has a person and a model, and the tools on a message (regenerate, edit) assume it. - One question with one answer, with no thread → a [Text Field](/docs/components/input) and a [Card](/docs/components/card) for the reply. - Making a picture → [AI Image](/docs/components/ai-image). A model that replies with one can still be shown here through the `message` snippet. - Long-form output to be edited → [Rich Text Editor](/docs/components/rich-text-editor), with the model writing into it. #### Do - Keep the key on the server. `chatEndpoint('/api/chat')` here and `chatRoute()` from `omaris/ai` in the route, or any route that streams. The component reads the AI SDK's stream, plain streamed text, or a JSON `{ text }`. - Return the `fetch` `Response` as is from a `send` of your own. There is nothing to parse on your side. - Give it a height it can scroll in. `height` or `class="h-full"` inside a flex column; a chat that grows with its thread pushes the composer off the screen. - Put a `disclaimer` under it when the answers matter. - Leave the reading column alone in a wide frame. It stops at 48rem and centres, which is what keeps a reply readable on a full page; set `--chat-measure` on the root only for a different measure. - Use `mockChat()` while the route is not there yet. It streams markdown a word at a time after a pause, and can fail on demand. #### Don't - Fight the scroll from outside. The thread follows the reply until the person scrolls up, and the jump button brings them back; a `scrollTo` of your own on every change drags them down mid-read. - Put the model's reply in a bubble through `classes.bubble`. That slot is the person's message; the reply is plain text on purpose, so long answers and code blocks read like a document. - Store secrets in `body` of `chatEndpoint()`. It is the request body, in the network tab. - Drop `status: 'error'` messages from `messages` before resending them. `chatEndpoint()` already leaves them out. #### Pairs with - [AI Image](/docs/components/ai-image), [Sheet](/docs/components/sheet), [Drawer](/docs/components/navigation-drawer), [Card](/docs/components/card), [Chip](/docs/components/chip), [Code Block](/docs/components/code-block) Parts (`classes`): `root` `header` `thread` `content` `user` `bubble` `assistant` `avatar` `reply` `prose` `cursor` `typing` `tools` `reasoning` `alert` `empty` `mark` `title` `description` `suggestions` `footer` `jump` `composer` `input` `disclaimer` | Prop | Type | Notes | | --- | --- | --- | | `send?` | `SendFn` | Answers the conversation. `chatEndpoint('/api/chat')`, `mockChat()`, or any `(messages, ctx) => reply` of your own — a `fetch` whose `Response` you return as is will do. | | `messages?` | `AiChatMessage[]` | The conversation. Bindable; give it a history to resume one. Default `[]`. Bindable. | | `prompt?` | `string` | The text in the composer. Bindable. Default `''`. | | `system?` | `string` | A system prompt, carried in the request by `chatEndpoint()`. | | `status?` | `AiChatStatus` | Where it is. Bindable, read-only in practice. Default `'idle'`. Bindable. | | `tone?` | `AiChatTone` | Colours the send button, the focus ring, links and the assistant's mark. One of `primary`, `secondary`, `tertiary`, `destructive`, `success`, `warning`, `info`. Default `'primary'`. | | `height?` | `number \| string` | Height of the whole thing, in px or any CSS length. `class="h-full"` also works. Default `'32rem'`. | | `avatar?` | `boolean` | A small mark beside each reply. The `icon` snippet replaces what is in it. Default `false`. | | `suggestions?` | `string[]` | Prompts offered as chips before the first message. Default `[]`. | | `assistantName?` | `string` | The name the assistant goes by, for screen readers. Default `'Assistant'`. | | `title?` | `string` | The headline before the first message. Default `'How can I help?'`. | | `hint?` | `string` | The line under it. | | `placeholder?` | `string` | Default `'Message…'`. | | `disclaimer?` | `string` | The line under the composer. | | `disabled?` | `boolean` | Default `false`. | | `onsend?` | `(message: AiChatMessage) => void` | Called with the message the person sent. | | `onfinish?` | `(message: AiChatMessage) => void` | Called with the finished reply. | | `onerror?` | `(error: Error) => void` | | | `onchange?` | `(messages: AiChatMessage[]) => void` | Called after any change to the conversation. | | `class?` | `string` | | | `classes?` | `AiChatClasses` | Per-part Tailwind overrides. `class` still covers the root. | | `header?` | `Snippet` | Above the thread. | | `icon?` | `Snippet` | The assistant's mark, in the empty state and the `avatar`. Defaults to sparkles. | | `empty?` | `Snippet<[AiChatEmptyContext]>` | Replaces the empty thread. | | `message?` | `Snippet<[AiChatMessageContext]>` | Replaces what a message shows — the bubble's text, or the reply's. | | `actions?` | `Snippet<[AiChatMessageContext]>` | Extra buttons under a message, after copy, regenerate and edit. | | `composer?` | `Snippet<[AiChatComposerContext]>` | Replaces the composer. An empty snippet takes it away. | ### AiImage `import { AiImage } from 'omaris'` A picture made from a sentence — the frame, the prompt bar, the wait and the picture, wired to whichever model you have. #### Use it for - Any place a picture is made from words: a cover for a playlist, a hero for a landing page, a product placeholder, a logo sketch, an avatar. One `generate` prop and it has the bar, the spinner, the fade-in and the tools. - Editing a picture rather than starting over. `attach` takes one from the person; the edit tool feeds a result back in. Either way the generator is handed `reference`, and `gemini()` and `openaiImages()` both use it. - A picture with nothing to press. `prompt` and `auto` make it on mount and again when the prompt changes; `composer="none"` and `tools={[]}` leave only the frame. - Several at once. `count` tiles them in the frame; tap one to keep it. `history` keeps every result on a strip under it. - Your own controls around the frame. `composer="none"`, then `generate()`, `cancel()`, `clear()` and `download()` through `bind:this`. #### Not for - Choosing a picture that already exists → [File Upload](/docs/components/file-upload) or [Image Viewer](/docs/components/image-viewer). - Cropping or rotating a picture → [Image Cropper](/docs/components/image-cropper). - A conversation about the picture → [AI Chat](/docs/components/ai-chat), with this in a message snippet if the reply is a picture. - Loading a picture you already have the URL for. A plain `` with a [Skeleton](/docs/components/skeleton) is enough; the wait here is for a picture that does not exist yet. #### Do - Keep the key on the server. `endpoint('/api/image')` here, and `gemini()`, `openaiImages()` or `imageRoute()` from `omaris/ai` in the route. - Set `aspect`. It is both the shape of the frame and what the model is asked for, so the picture lands in a frame that already fits it. - Give `suggestions`. An empty frame with three good prompts on it is used; an empty frame with a blank field is stared at. - Bind `images` if the session matters — it is every result, newest first, and the strip and the viewer both read from it. - Return `text` from your generator when the model says something about the picture, and set `caption` to show it. - Use `mockGenerate()` while the backend is not there yet. It makes an abstract picture from the prompt after a believable wait, so the screen is designed against the real states. - Reach for the `loading` snippet when the wait should say more than a spinner — it is handed the provider's `progress`, `message` and `partial`. #### Don't - Put an API key in `gemini({ apiKey })` in a browser. It ships to everyone who opens the page. - Set `auto` without a `prompt` you control. Every keystroke into a bound prompt is a request. - Hand it a `src` you have not waited on and expect the fade. A picture it makes itself is decoded behind the spinner before it fades in; one passed in from outside just shows, so preload it or wrap it in a [Skeleton](/docs/components/skeleton). - Raise `count` past what the model makes in one call. Gemini makes one per call, so `count={4}` is four calls, billed as four. - Leave out a stop with `composer="none"`. The frame shows only a spinner, so your own controls call `cancel()`, or a slow model has no way out. #### Pairs with - [AI Chat](/docs/components/ai-chat), [Drawing Board](/docs/components/drawing-board) (its `ai` prop takes the same `generate`), [File Upload](/docs/components/file-upload), [Image Viewer](/docs/components/image-viewer), [Chip](/docs/components/chip), [Card](/docs/components/card), [Dialog](/docs/components/dialog) Parts (`classes`): `root` `frame` `canvas` `grid` `cell` `image` `loader` `spinner` `empty` `emptyIcon` `title` `description` `suggestions` `alert` `tools` `caption` `composer` `attachment` `input` `controls` `history` `thumb` | Prop | Type | Notes | | --- | --- | --- | | `generate?` | `GenerateFn` | Makes the pictures. `gemini({ apiKey })`, `openaiImages({ apiKey })`, `endpoint('/api/image')`, `mockGenerate()`, or any `(request, ctx) => results` of your own. | | `prompt?` | `string` | The prompt in the bar. Bindable. Default `''`. | | `auto?` | `boolean` | Generate as soon as there is a prompt — on mount, and again when it changes. Default `false`. | | `aspect?` | `AiImageAspect` | The shape of the picture. Also the shape of the frame. Default `'1:1'`. | | `height?` | `number \| string` | Frame height, in px or any CSS length. Replaces the aspect. | | `count?` | `number` | How many pictures per prompt. Above one they tile in the frame. Default `1`. | | `reference?` | `string \| Blob` | A picture to edit or draw from — a URL, data URL or `Blob`. Bindable: the attach button and the edit tool set it. Default `undefined`. Bindable. | | `params?` | `Record void` | | | `onresult?` | `(images: AiImageItem[]) => void` | | | `onerror?` | `(error: Error) => void` | | | `oncancel?` | `() => void` | | | `onchange?` | `(image: AiImageItem \| undefined) => void` | Called when the picture on show changes — a new result, a history pick, a clear. | | `ondownload?` | `(image: AiImageItem) => void` | | | `class?` | `string` | | | `classes?` | `AiImageClasses` | Per-part Tailwind overrides. `class` still covers the root. | | `empty?` | `Snippet<[AiImageEmptyContext]>` | Replaces the empty frame. | | `loading?` | `Snippet<[AiImageLoadingContext]>` | Replaces the spinner. Drawn on the loader's surface. | | `image?` | `Snippet<[AiImageResultContext]>` | Replaces one finished picture. | | `error?` | `Snippet<[AiImageErrorContext]>` | Replaces the error. | | `bar?` | `Snippet<[AiImageComposerContext]>` | Replaces the prompt bar. | | `actions?` | `Snippet<[AiImageResultContext]>` | Extra buttons in the corner toolbar. | | `children?` | `Snippet` | Content under the frame, before the bar — a caption, a legend. | ### Alert `import { Alert } from 'omaris'` Alert — MD3's banner, for a message that belongs to the page rather than floating over it. #### Use it for - A message that belongs to the page and stays until the problem clears: a failed payment method, a maintenance window, an unverified email. `tonal` (the default) at the top of the content, under the [Top App Bar](/docs/components/top-app-bar). - A form-level error listing what to fix. `tone="destructive"` announces itself at once; `variant="plain"` is an inline one with no container. - A quieter note inside a form, like "Applies to every member". Use `outlined` or `bar`. - A message with a way out. `actions` holds one or two buttons ("Fix", "Learn more"); `dismissible` lets the person close it. #### Not for - Confirming something just happened, like "Saved" or "Sent" → [Toast](/docs/components/toast). - A decision that must be made before continuing → [Dialog](/docs/components/dialog). An alert never blocks. - A one-word status on an item, like "Overdue" → [Badge](/docs/components/badge). - A hint about one field → that field's `supportingText` on the [Text Field](/docs/components/input). - A list with nothing in it → [Empty](/docs/components/empty). #### Do - Keep one alert at the top of a page. Two stacked banners cancel each other out. - Lead with a `title` that says what is wrong and a `description` that says what to do. Drop the title for a one-line note. - Match `tone` to the meaning: `destructive` for broken, `warning` for about to break, `info` (the default) for worth knowing, `success` for a state. A success event is a toast. - Keep `dismissible` off while the problem is still there. #### Don't - Fire one from an event handler. A message that answers a press is a [Toast](/docs/components/toast). - Put a third button in `actions`. It belongs on the page the first two point to. - Use `size="lg"` in a dashboard column. It is for a landing page or a full-width notice. - Swap the icon to decorate. The tone's own icon makes it readable without colour. #### Pairs with - [Toast](/docs/components/toast), [Dialog](/docs/components/dialog), [Button](/docs/components/button), [Empty](/docs/components/empty), [Text Field](/docs/components/input) Parts (`classes`): `root` `icon` `body` `title` `description` `actions` `close` | Prop | Type | Notes | | --- | --- | --- | | `tone?` | `AlertTone` | One of `primary`, `secondary`, `tertiary`, `destructive`, `success`, `warning`, `info`. Default `'info'`. | | `variant?` | `AlertVariant` | One of `tonal`, `outlined`, `bar`, `plain`. Default `'tonal'`. | | `size?` | `AlertSize` | One of `sm`, `md`, `lg`. Default `'md'`. | | `title?` | `string` | Heading line. | | `description?` | `string` | Body text. Use children for rich content. | | `showIcon?` | `boolean` | Draw the tone's default icon. Default `true`. | | `dismissible?` | `boolean` | Adds the trailing ×. Default `false`. | | `ondismiss?` | `() => void` | | | `class?` | `string` | | | `classes?` | `AlertClasses` | Per-part Tailwind overrides. `class` still covers the root. | | `icon?` | `Snippet` | Replaces the tone's default icon. | | `children?` | `Snippet` | Body content, in place of `description`. | | `actions?` | `Snippet` | Buttons under the body. | ### AppShell `import { AppShell } from 'omaris'` The frame a dashboard is built in. #### Use it for - The frame of a dashboard. `AppShellSidebar` holds a `NavigationDrawer`, `AppShellMain` holds a `TopAppBar` and `AppShellContent`. Only the content column scrolls; the sidebar and bar stay put with no `sticky` anywhere. - An inspector on the trailing edge, like the selected row's details. Add a second `AppShellSidebar side="end"`. - A bar a selection needs, or a form's unsaved-changes strip. `AppShellFooter sticky` holds the bottom of the column. - A page's reading measure. `AppShellContent width` defaults to `full`, which a table wants; `md` for prose, `sm` for a lone form. #### Not for - A marketing or landing page → a plain layout. The document should scroll. - A sign-in screen → a centred [Card](/docs/components/card) on the page. - Panes the person drags → [Resizable](/docs/components/resizable) inside `AppShellMain`. The shell's sidebar is a fixed column. - Long articles where the address bar should collapse on a phone → `scroll="page"`, which scrolls the document and sticks the sidebar instead. #### Do - Put it in `+layout.svelte` once. Pages render inside `AppShellContent`. - Plan the phone. The sidebar column is gone below `breakpoint` (`md` by default), so a phone needs a modal [Navigation Drawer](/docs/components/navigation-drawer) rendered outside the sidebar, or a [Navigation Bar](/docs/components/navigation-bar) in the main column. - Use `padded={false}` when a page brings its own edges, like a full-bleed map or a kanban board. - Use `AppShellHeader` for a bar plus a filter row that must stay up together. A `TopAppBar` on its own sticks by itself and finds the scroller from the shell. #### Don't - Put `h-screen` or `overflow-auto` on your own wrappers inside it. The shell already picked the scrolling element; a second one brings back the double scrollbar. - Pass `scrollContainer` to a `TopAppBar` inside the shell. It already knows. - Nest an `AppShell` per route. - Leave `width="full"` on a page of prose. Lines a screen wide do not read. #### Pairs with - [Navigation Drawer](/docs/components/navigation-drawer), [Top App Bar](/docs/components/top-app-bar), [Navigation Bar](/docs/components/navigation-bar), [Resizable](/docs/components/resizable), [Breadcrumb](/docs/components/breadcrumb) | Prop | Type | Notes | | --- | --- | --- | | `scroll?` | `NonNullable` | Which element scrolls: the content column, or the document. Default `'main'`. | | `class?` | `string` | | | `children?` | `Snippet` | | ### AppShellContent `import { AppShellContent } from 'omaris'` The padded well a page's content sits in. | Prop | Type | Notes | | --- | --- | --- | | `width?` | `NonNullable` | One of `sm`, `md`, `lg`, `xl`, `full`. Default `'full'`. | | `padded?` | `boolean` | Gutter and the rhythm between sections. Off when the page brings its own. Default `true`. | | `class?` | `string` | | | `children?` | `Snippet` | | ### AppShellFooter `import { AppShellFooter } from 'omaris'` The bottom band of the content column. | Prop | Type | Notes | | --- | --- | --- | | `sticky?` | `boolean` | Hold it at the bottom of the column rather than after the content. Default `false`. | | `class?` | `string` | | | `children?` | `Snippet` | | ### AppShellHeader `import { AppShellHeader } from 'omaris'` A band that stays at the top of the content column while it scrolls. | Prop | Type | Notes | | --- | --- | --- | | `sticky?` | `boolean` | Ride up with the content instead of staying put. Default `true`. | | `class?` | `string` | | | `children?` | `Snippet` | | ### AppShellMain `import { AppShellMain } from 'omaris'` The content column of an `AppShell` — and, in the default scroll mode, the element that scrolls. | Prop | Type | Notes | | --- | --- | --- | | `class?` | `string` | | | `children?` | `Snippet` | | ### AppShellSidebar `import { AppShellSidebar } from 'omaris'` The column an `AppShell` puts beside its main area — a navigation drawer, usually. | Prop | Type | Notes | | --- | --- | --- | | `side?` | `'start' \| 'end'` | Which edge it sits on. `end` gives you an inspector panel instead. Default `'start'`. | | `breakpoint?` | `AppShellSidebarBreakpoint` | Viewport width at which the column appears. `false` keeps it at every size — what you want when the thing inside is already responsive. Default `'md'`. | | `label?` | `string` | Accessible name, when the column isn't a single labelled drawer. | | `class?` | `string` | | | `children?` | `Snippet` | | ### AreaChart `import { AreaChart } from 'omaris/chart'` Area chart. #### Use it for - A value over time → `LineChart`. The same with the space under it filled, which reads as "amount" → `AreaChart`, `stacked` when the series add up to a total. - Categories → `BarChart`, `horizontal` when the names are long, `grouped` or `stacked` when several series share a band. - Parts of a small whole → `PieChart`, `donut` with the total in `centre`, for up to about five slices. Too many parts, or one that dwarfs the rest → `TreemapChart`, with `group` for one level of nesting. - Stages → `FunnelChart`, `drop` for stage over stage. Correlation → `ScatterChart`, `size` for a bubble. Open, high, low, close → `CandlestickChart`. A magnitude and a rate on two scales → `ComboChart` with `axis: 'right'`. Several measures on one shape → `RadarChart`. - A few progress-shaped values → `RadialBarChart`. One number against a range → `Gauge`. A trend inside a stat tile or a table cell → `Sparkline`. #### Not for - More than about six rings → a `BarChart`. Inner rings of a `RadialBarChart` get shorter for the same value, so the comparison is off. - A pie of twelve slices, or one slice at 90% → `TreemapChart`, or a sorted `horizontal` `BarChart`. - One number with no range → a `display-sm` [Text](/docs/components/text) in a [Card](/docs/components/card). With a range → [Gauge](/docs/components/gauge) from the core entry, which needs no peer dependency. - Exact values people read off and compare → [Table](/docs/components/table). - Places → [Map](/docs/components/map). #### Do - Import from `omaris/chart` and add `layerchart` and `d3-shape` as peers. It is opt-in so a page with only a Button pays nothing for it; see [Charts and maps](/docs/charts-and-maps). - Give every chart a `label`. The SVG is `role="img"` and that is its name. - Pass `error` (and `onRetry`) when the request failed. A failed fetch must not draw a clean empty pair of axes; `emptyText` is for a real nothing. - Keep the `series` shape. Swapping a line for an area or a bar is a one-word change, and `references` (a target, an SLO), `annotations`, `brush`, `zoom` and `csv` work the same on all of them. - Switch to `legend="isolate"` once there are more than two series. #### Don't - Set the height from a class. `height` is a prop, and the frame is what layerchart measures. - Put anything hover-dependent in a `Sparkline`. It is inert on purpose so it cannot swallow a tap inside a row. - Ask a `ComboChart` for grouped bars beside a line. Bars stack there; use a `BarChart` and a second chart. - Hand-pick a `color` per series unless it means something, like a team's colour or a status. The palette already follows the theme. #### Pairs with - [Card](/docs/components/card), [Gauge](/docs/components/gauge), [Table](/docs/components/table), [Text](/docs/components/text), [Charts and maps](/docs/charts-and-maps) | Prop | Type | Notes | | --- | --- | --- | | `stacked?` | `boolean` | Pile the series on top of each other rather than overlapping them. Default `false`. | | `curve?` | `ChartCurve` | Default `'smooth'`. | | `fillOpacity?` | `number` | Opacity of the fill under each line. Default `0.18`. | | `points?` | `boolean` | Mark each data point. Default `false`. | | `data` | `T[]` | | | `x?` | `Field` | The category or time field. | | `y?` | `Field` | The value field. Ignored once `series` names its own. | | `series?` | `ChartSeries[]` | Several values per row. Each gets a colour and a legend entry. | | `height?` | `number \| string` | Plot height. A number is px; a string is any CSS length. Default `260`. | | `axis?` | `boolean \| 'x' \| 'y'` | `true` for both, or just the one you want. Default `true`. | | `grid?` | `boolean` | Default `true`. | | `legend?` | `ChartLegend` | `true` shows the legend and lets a click toggle a series off. `'isolate'` makes a click show that series alone, and a second one bring the rest back — which is the move you actually want on a chart with six lines on it. Default `false`. | | `tooltip?` | `boolean` | Default `true`. | | `formatX?` | `(value: never) => string` | Tick labels along the x axis. | | `formatY?` | `(value: never) => string` | Tick labels along the y axis, and the tooltip's values. | | `colors?` | `readonly string[]` | Palette override, cycled across the series. Default `CHART_COLORS`. | | `padding?` | `number \| { top?: number; right?: number; bottom?: number; left?: number }` | Space around the plot, in px — room for long axis labels. | | `label?` | `string` | Accessible description of what the chart shows. | | `class?` | `string` | | | `empty?` | `Snippet` | Shown in place of the plot when `data` is empty. | | `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. | | `references?` | `ChartReference[]` | Lines and bands at fixed values — targets, thresholds, windows. | | `annotations?` | `ChartAnnotation[]` | Notes pinned to a point. | | `brush?` | `ChartBrush` | Drag across the plot to select a range, and zoom the chart to it. | | `zoom?` | `ChartZoom` | Wheel or pinch to zoom, drag to pan. | ### Avatar `import { Avatar } from 'omaris'` Avatar — image, initials, or icon, in that order of preference. #### Use it for - A person in a table row, a comment, an assignee on a card. `src` when there is a photo, `name` for initials when there is not, an icon in `children` when there is neither. The initials show until the image has decoded. - A team, an organisation, a product. `shape="square"` is the rounded square for things that are not people. - Presence. `status="online"`, `away`, `busy` or `offline` puts a dot on the edge of the shape, at every size, without clipping it. - The people on a thread or a project, stacked. `AvatarGroup` with `overflow` for the trailing "+n". #### Not for - A picture that is content, not identity, like a product photo → an ``, or [Image Viewer](/docs/components/image-viewer) when it opens full screen. - A count or a status word beside a name → [Badge](/docs/components/badge). The presence dot is the only state an avatar carries. - Choosing a person from a list → [Combobox](/docs/components/combobox) with an Avatar in each row and the picked one as a [Chip](/docs/components/chip). - A decorative icon in a tinted circle on an empty screen → [Empty](/docs/components/empty), which draws that circle itself. #### Do - Pass `name` even when `src` is set. It shows while the photo loads and stays if the URL 404s, so a broken image never leaves a hole. - Turn on `colorize` everywhere or nowhere. The hue comes from the name, so the same person is the same colour on every screen only if every avatar opts in. - Give `AvatarGroup` the same `size` as the avatars inside it. The overlap is per size and a mismatch shows at once. - Use `xs` and `sm` in a table cell or a chip, `md` in a list row, `lg` and `xl` in a profile header. #### Don't - Add `overflow-hidden` on the root to round a photo. The `media` slot already clips, and the presence dot sits outside the root. - Write `initials` by hand unless the derived pair is wrong, like a mononym or a company name. "Ada L." already becomes "AL". - Stack more than about five in a group. That is what `overflow` is for. #### Pairs with - [Badge](/docs/components/badge), [List](/docs/components/list), [Table](/docs/components/table), [Chip](/docs/components/chip), [Tooltip](/docs/components/tooltip) Parts (`classes`): `root` `media` `image` `status` | Prop | Type | Notes | | --- | --- | --- | | `size?` | `AvatarSize` | One of `sm`, `md`, `lg`, `xl`. Default `'md'`. | | `shape?` | `AvatarShape` | One of `circle`, `square`. Default `'circle'`. | | `status?` | `AvatarStatus` | Presence dot in the bottom corner. One of `none`, `online`, `away`, `busy`, `offline`. Default `'none'`. | | `src?` | `string` | | | `alt?` | `string` | | | `name?` | `string` | Full name. Initials and the fallback tint are derived from it. | | `initials?` | `string` | Override the derived initials. | | `colorize?` | `boolean` | Paint the initials fallback with the name-derived tint. Default `false`. | | `class?` | `string` | | | `classes?` | `AvatarClasses` | Per-part Tailwind overrides. `class` still covers the root. | | `children?` | `Snippet` | Shown when there's no image and no name — an icon, usually. | ### AvatarGroup `import { AvatarGroup } from 'omaris'` A stack of overlapping avatars — the people on a thread, the members of a team. | Prop | Type | Notes | | --- | --- | --- | | `size?` | `AvatarSize` | Overlap amount. Must match the avatars' own `size` to look right. One of `xs`, `sm`, `md`, `lg`, `xl`. Default `'md'`. | | `overflow?` | `number` | Renders a "+n" chip after the children. | | `class?` | `string` | | | `classes?` | `ClassesOf` | Tailwind per part. | | `children?` | `Snippet` | | ### Badge `import { Badge } from 'omaris'` Badge — a status pill, or the count that rides on an icon. #### Use it for - A count on an icon: unread on a bell, items in a cart. `value` with `anchor="top-end"` on a `relative` parent, and `max` so 100 reads "99+". - "Something new" with no number. `dot` on a [Navigation Bar](/docs/components/navigation-bar) item or an [Icon Button](/docs/components/icon-button). - A read-only status on a row or a card: "Paid", "Draft", "Beta". A labelled pill, `tonal` or `outlined`, with `tone` carrying the meaning. - A coloured word in a table cell. `variant="text"` when a pill in every row is too loud. #### Not for - Anything pressable, like a filter or a removable tag → [Chip](/docs/components/chip). A badge has no state layer and no focus ring. - A sentence → [Alert](/docs/components/alert). - A number people compare across rows → [Text](/docs/components/text) with `tabular-nums`, or a [Gauge](/docs/components/gauge). - A message that should be noticed now → [Toast](/docs/components/toast). A badge waits to be seen. #### Do - Give a count or a dot a `label`: "3 unread", not "3", so a screen reader says what the number is. - Put `relative` on the parent before you `anchor`. The badge is positioned absolutely and needs a box to pin to. - Keep one tone per meaning across the app: `success` paid, `warning` pending, `destructive` failed. - Clear the dot as soon as the thing has been seen. A dot that never goes away means nothing. #### Don't - Use `filled` for status pills in a table. A column of solid capsules fights the data; use `tonal` or `outlined`. - Show a zero. No `value`, no badge. - Anchor a count to something nobody will act on. "99+" on a log tab is noise. #### Pairs with - [Icon Button](/docs/components/icon-button), [Navigation Bar](/docs/components/navigation-bar), [Avatar](/docs/components/avatar), [Table](/docs/components/table), [Chip](/docs/components/chip) | Prop | Type | Notes | | --- | --- | --- | | `variant?` | `BadgeVariant` | One of `filled`, `tonal`, `outlined`, `text`. Default `'filled'`. | | `tone?` | `BadgeTone` | One of `primary`, `secondary`, `tertiary`, `destructive`, `success`, `warning`, `info`. Default `'primary'`. | | `size?` | `BadgeSize` | One of `sm`, `md`, `lg`. Default `'md'`. | | `dot?` | `boolean` | Renders the 6dp dot instead of a label. Default `false`. | | `value?` | `number` | A number badge. Values above `max` render as "99+", and the pill stays a circle until two digits force it wider. | | `max?` | `number` | Cap for `value`. Default `99`. | | `anchor?` | `BadgeAnchor` | Corner of the `relative` parent to pin to. One of `none`, `top-end`, `top-start`. Default `'none'`. | | `label?` | `string` | Screen-reader text — "3 unread", not "3". | | `class?` | `string` | | | `children?` | `Snippet` | | ### BarChart `import { BarChart } from 'omaris/chart'` Bar chart. | Prop | Type | Notes | | --- | --- | --- | | `horizontal?` | `boolean` | Bars run along the x axis, categories down the y one. Default `false`. | | `stacked?` | `boolean` | Pile several series into one bar. Default `false`. | | `grouped?` | `boolean` | Sit several series side by side within the band. Default `false`. | | `radius?` | `number` | Corner radius on the bars, in px. Default `4`. | | `labels?` | `boolean` | Print the value on each bar. Default `false`. | | `data` | `T[]` | | | `x?` | `Field` | The category or time field. | | `y?` | `Field` | The value field. Ignored once `series` names its own. | | `series?` | `ChartSeries[]` | Several values per row. Each gets a colour and a legend entry. | | `height?` | `number \| string` | Plot height. A number is px; a string is any CSS length. Default `260`. | | `axis?` | `boolean \| 'x' \| 'y'` | `true` for both, or just the one you want. Default `true`. | | `grid?` | `boolean` | Default `true`. | | `legend?` | `ChartLegend` | `true` shows the legend and lets a click toggle a series off. `'isolate'` makes a click show that series alone, and a second one bring the rest back — which is the move you actually want on a chart with six lines on it. Default `false`. | | `tooltip?` | `boolean` | Default `true`. | | `formatX?` | `(value: never) => string` | Tick labels along the x axis. | | `formatY?` | `(value: never) => string` | Tick labels along the y axis, and the tooltip's values. | | `colors?` | `readonly string[]` | Palette override, cycled across the series. Default `CHART_COLORS`. | | `padding?` | `number \| { top?: number; right?: number; bottom?: number; left?: number }` | Space around the plot, in px — room for long axis labels. | | `label?` | `string` | Accessible description of what the chart shows. | | `class?` | `string` | | | `empty?` | `Snippet` | Shown in place of the plot when `data` is empty. | | `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. | | `references?` | `ChartReference[]` | Lines and bands at fixed values — targets, thresholds, windows. | | `annotations?` | `ChartAnnotation[]` | Notes pinned to a point. | | `brush?` | `ChartBrush` | Drag across the plot to select a range, and zoom the chart to it. | | `zoom?` | `ChartZoom` | Wheel or pinch to zoom, drag to pan. | ### Breadcrumb `import { Breadcrumb } from 'omaris'` Breadcrumbs. #### Use it for - Where a page sits in a hierarchy three or more levels deep: region, customer, order. Give every crumb but the last an `href`. - Client-side routing with no URL to give. A crumb with `onclick` and no `href` renders as a button. - A trail that gets long. Past `maxItems` it collapses in the middle, and the "…" expands in place instead of navigating. #### Not for - The app's top-level sections → [Navigation Drawer](/docs/components/navigation-drawer) or [Navigation Bar](/docs/components/navigation-bar). - Views within one page → [Tabs](/docs/components/tabs). - A page one level deep → a back arrow in the `leading` slot of the [Top App Bar](/docs/components/top-app-bar). - Steps in a flow that happen in order → [Progress](/docs/components/progress) for how far, a [Button](/docs/components/button) for next. Crumbs are places, not stages. #### Do - Mirror the URL: one crumb per segment a person could land on, in the order they nest. - Use the entity's name as the label: "Acme Ltd", not "Customer: Acme Ltd". The position implies the level. - Put it above the page title in the content column, not in the app bar. - Raise `itemsAfterCollapse` when the deep levels matter most, which in a dashboard they usually do. #### Don't - Link the last crumb to itself. It is marked current and rendered as text. - Make it the only way off a phone screen. It is small and wraps badly, so give the phone a back arrow too. - Truncate labels with a class. Let the trail collapse in the middle; the ends carry the meaning. #### Pairs with - [Top App Bar](/docs/components/top-app-bar), [Navigation Drawer](/docs/components/navigation-drawer), [App Shell](/docs/components/app-shell), [Text](/docs/components/text) Parts (`classes`): `root` `list` `item` `link` `current` `separator` `expand` | Prop | Type | Notes | | --- | --- | --- | | `items` | `BreadcrumbItem[]` | | | `size?` | `BreadcrumbSize` | One of `sm`, `md`. Default `'md'`. | | `maxItems?` | `number` | Collapse the middle once the trail is longer than this. `0` never collapses. Default `5`. | | `itemsBeforeCollapse?` | `number` | Crumbs kept at the start of a collapsed trail. Default `1`. | | `itemsAfterCollapse?` | `number` | Crumbs kept at the end of a collapsed trail. Default `2`. | | `label?` | `string` | Accessible name. Default `'Breadcrumb'`. | | `class?` | `string` | | | `classes?` | `BreadcrumbClasses` | Per-part Tailwind overrides. `class` still covers the root. | | `separator?` | `Snippet` | Custom separator, in place of the chevron. | ### Button `import { Button } from 'omaris'` MD3 buttons, the Expressive way. #### Use it for - The main action on a screen or dialog. Use `filled`, once per view. - The actions next to it. `tonal` for the second most important, `outlined` or `text` for the rest. - An action that takes a moment. Pass `loading`; the label stays and a spinner appears. #### Not for - A link that only changes the URL → give it `href` and it renders an ``. `variant="link"` is for a link inside a sentence. - An icon with no label → [Icon Button](/docs/components/icon-button). - The floating main action on a phone → [FAB](/docs/components/fab). - One action plus a menu of alternatives → [Split Button](/docs/components/split-button). - Picking between options → [Segmented Button](/docs/components/segmented-button) or filter [Chips](/docs/components/chip). #### Do - Keep one `filled` button per view. Everything else is `tonal`, `outlined` or `text`. - Write labels in sentence case, verb first: "Save changes", not "SAVE" or "OK". - Give destructive actions `tone="destructive"` and confirm them in a [Dialog](/docs/components/dialog). #### Don't - Wrap a label onto two lines, or put two icons in one button. - Disable a button to explain why it cannot be pressed. Keep it enabled and say why in the supporting text or a toast. - Use `size="lg"` or `xl` in a dashboard. They are for landing screens. #### Pairs with - [Icon Button](/docs/components/icon-button), [Button Group](/docs/components/button-group), [Split Button](/docs/components/split-button), [Dialog](/docs/components/dialog), [Toast](/docs/components/toast) | Prop | Type | Notes | | --- | --- | --- | | `variant?` | `ButtonVariant` | Fill style. MD3's five, plus `link` for inline navigation. One of `filled`, `tonal`, `elevated`, `outlined`, `text`, `link`. Default `'filled'`. | | `tone?` | `ButtonTone` | Color role the fill style paints with. One of `primary`, `secondary`, `tertiary`, `destructive`, `success`, `warning`, `info`. Default `'primary'`. | | `size?` | `ButtonSize` | MD3 Expressive heights: 32 / 40 / 56 / 96 / 136dp. One of `xs`, `sm`, `md`, `lg`, `xl`. Default `'sm'`. | | `shape?` | `ButtonShape` | Pill (`round`) or MD3 rounded rectangle (`square`). One of `round`, `square`. Default `'round'`. | | `icon?` | `boolean` | Square, label-less button. Requires an `aria-label`. Default `false`. | | `ripple?` | `boolean` | Material ripple on press. Set false for a flat, instant button. Default `true`. | | `confetti?` | `boolean \| ConfettiOptions` | Confetti on press. `true` for the default burst, or any `ConfettiOptions` — `confetti={{ preset: 'fireworks' }}`. Default `false`. | | `loading?` | `boolean` | Grows a spinner in at the start of the button and blocks interaction. The label stays put; only an `icon` button swaps its icon out. Default `false`. | | `toggle?` | `boolean` | Turns the button into a two-state toggle driven by `pressed`. Ignored when `href` is set — a link has no pressed state. Default `false`. | | `pressed?` | `boolean` | Selected state of a toggle button. Bindable. Default `false`. | | `class?` | `string` | | | `children?` | `Snippet` | | ### ButtonGroup `import { ButtonGroup } from 'omaris'` MD3 button group — a row of buttons that reads as one control. #### Use it for - A row of actions that reads as one control: Undo/Redo, Bold/Italic/Underline, Zoom in/out. `connected` is the default: pill ends outside, squared joins inside, a hairline gap between. - A segmented control built from full `Button`s. `toggle` buttons with `pressed` inside a `connected` group, when a segment needs an `href`, a `loading` state or its own tone. - `spaced` when the buttons stay separate but should sit on the 8dp rhythm: a card's action row, a dialog footer. - `orientation="vertical"` for a stacked control in a narrow side panel; `full` for a group that fills a phone width or a sheet edge to edge. #### Not for - One choice out of two to five that you bind a value to → [Segmented Button](/docs/components/segmented-button). It owns the selection and draws the tick; a group only lays buttons out. - One default action plus a menu of the rest → [Split Button](/docs/components/split-button). - Filters that can wrap or scroll and can all be off → [Chips](/docs/components/chip) in a `ChipGroup`. - Page numbers → [Pagination](/docs/components/pagination). - Two unrelated buttons that happen to be neighbours → a flex container with `gap-2`. #### Do - Give every child the same `variant` and `size`. The group reshapes the corners; it does not unify the fills. - Pass `label`. It becomes the `aria-label` of the `role="group"`, so a screen reader can say what the row is for. - Use [Icon Buttons](/docs/components/icon-button) as the children of a toolbar group, each with its own `aria-label`. #### Don't - Mix `filled` and `outlined` children in a `connected` group. The joins only read as one control when the fills match. - Go past five in a row. Beyond that it is a toolbar: `spaced` clusters with a [Divider](/docs/components/divider) between them. - Use `full` at desktop width. The buttons stretch into bars; it is for a phone or a sheet. #### Pairs with - [Button](/docs/components/button), [Icon Button](/docs/components/icon-button), [Segmented Button](/docs/components/segmented-button), [Split Button](/docs/components/split-button), [Divider](/docs/components/divider) | Prop | Type | Notes | | --- | --- | --- | | `variant?` | `ButtonGroupVariant` | One of `connected`, `spaced`. Default `'connected'`. | | `orientation?` | `ButtonGroupOrientation` | One of `horizontal`, `vertical`. Default `'horizontal'`. | | `full?` | `boolean` | Default `false`. | | `label?` | `string` | Accessible name for the group. | | `class?` | `string` | | | `children?` | `Snippet` | | ### Calendar `import { Calendar } from 'omaris'` Month grid — one date, a range, or a handful of dates. #### Use it for - A date picked by looking at the month it falls in — a delivery day, a booking, a shift — where seeing the weekday and the week around it is the point. - A span of days with `mode="range"`: a stay, a leave request, a report period. `months={2}` shows a range that crosses a month end without paging. - Several separate days with `mode="multiple"` — rota shifts, class dates, blackout days. - A calendar that stays on the page: a booking sidebar, a scheduling panel, the body of a card. For one that opens from a field, reach for the field. #### Not for - A date inside a form, shown as text until it is edited → [Date Field](/docs/components/date-field). It wraps this calendar in a popover on a desktop and a sheet on a phone. - A period filter on a dashboard, with shortcuts like "Last 30 days" → [Date Range Field](/docs/components/date-range-field). - A date far from today — a birthday, an expiry year → the `DatePicker` wheels in [Picker](/docs/components/picker). Paging back forty years a month at a time is the wrong tool, even with the year grid. - A time of day → `TimePicker` in [Picker](/docs/components/picker). - Showing events across a month or a week. This picks dates; it does not lay out a schedule. #### Do - Keep values at local midnight — `new Date(2026, 2, 3)`, or `startOfDay` on anything else. The calendar hands back midnights, and compares by day. - Set `min` and `max` whenever the valid window is known. The arrows stop at its months and the out-of-range days grey out, which is kinder than an error after the fact. - Use `isDateDisabled` for the rules a window cannot express — weekends, holidays, sold-out nights. - Leave `locale` and `weekStartsOn` alone unless the page's language is wrong for the calendar. Both follow ``, and a week that starts on the wrong day is the first thing people notice. #### Don't - Build dates from ISO strings — `new Date('2026-03-03')` is UTC midnight, which is the 2nd of March west of Greenwich. - Expect a range's `value` to change on the first press. It is set once the range is whole; the half-drawn state is the calendar's own. - Set `showOutsideDays` with two months — the neighbouring month is already on screen, so it is ignored. - Squeeze it much under 280px. The cells shrink to fit, but below about 36px a day is hard to hit with a finger. #### Pairs with - [Date Field](/docs/components/date-field), [Date Range Field](/docs/components/date-range-field), [Popover](/docs/components/popover), [Sheet](/docs/components/sheet), [Card](/docs/components/card), [Picker](/docs/components/picker) Parts (`classes`): `root` `months` `month` `header` `caption` `nav` `grid` `weeks` `row` `weekday` `weekNumber` `cell` `day` `track` `band` `face` `picker` `pickerHeader` `pickerGrid` `pickerItem` | Prop | Type | Notes | | --- | --- | --- | | `mode?` | `M` | `'single'` (the default), `'range'` or `'multiple'`. Decides what `value` holds. | | `value?` | `CalendarValue` | The selection, at local midnight. A `Date` or `null` in `single`, `{ start, end }` or `null` in `range` — set on the second press, never half-filled — and a sorted `Date[]` in `multiple`. Bindable. | | `month?` | `Date` | The first month on screen. Defaults to the selection's month, or today's. Bindable. | | `min?` | `Date` | Earliest selectable day. The previous button stops at its month. | | `max?` | `Date` | Latest selectable day. The next button stops at its month. | | `isDateDisabled?` | `(date: Date) => boolean` | Days that cannot be picked — weekends, holidays, sold-out dates. | | `locale?` | `string` | BCP-47 tag for names and the first weekday. Defaults to ``, then the browser. | | `weekStartsOn?` | `Weekday` | 0 is Sunday. Defaults to the locale's own first day. | | `months?` | `1 \| 2` | Months side by side. They stack below the `sm` breakpoint. Default `1`. | | `showOutsideDays?` | `boolean` | Fill the first and last rows with the neighbouring months' days. Ignored with two months, where the neighbour is already on screen. Default `true`. | | `showWeekNumbers?` | `boolean` | ISO week numbers at the start of each row. Default `false`. | | `weekdayFormat?` | `'narrow' \| 'short'` | Weekday headings: `narrow` ("M") or `short` ("Mon"). Default `'narrow'`. | | `autofocus?` | `boolean` | Focus the active day as soon as the calendar mounts — for one that opens in a popup. Default `false`. | | `swipe?` | `boolean` | Swipe sideways on a touch screen to change month. Default `true`. | | `haptics?` | `boolean` | Vibrate on a pick, where the haptics store is on. Default `true`. | | `labels?` | `CalendarLabels` | Accessible names for the arrows, the caption and the week column — for translation. | | `onchange?` | `(value: CalendarValue) => void` | Fires with the new value after every pick — for `range`, once the range is whole. | | `class?` | `string` | Merged onto the root, so `class="p-0"` removes the padding. | | `classes?` | `CalendarClasses` | Per-part Tailwind overrides. `class` still covers the root. | ### CandlestickChart `import { CandlestickChart } from 'omaris/chart'` Candlestick chart — open, high, low and close in one mark per period. | Prop | Type | Notes | | --- | --- | --- | | `data` | `T[]` | | | `x?` | `Field` | The period field. Defaults to `row.date`. Default `'date'`. | | `open?` | `Field` | Defaults to `row.open`. Default `'open'`. | | `high?` | `Field` | Defaults to `row.high`. Default `'high'`. | | `low?` | `Field` | Defaults to `row.low`. Default `'low'`. | | `close?` | `Field` | Defaults to `row.close`. Default `'close'`. | | `height?` | `number \| string` | Plot height. A number is px; a string is any CSS length. Default `300`. | | `axis?` | `boolean \| 'x' \| 'y'` | `true` for both, or just the one you want. Default `true`. | | `grid?` | `boolean` | Default `true`. | | `tooltip?` | `boolean` | Default `true`. | | `up?` | `string` | Any CSS colour for a period that closed up. Default `'var(--success)'`. | | `down?` | `string` | Any CSS colour for a period that closed down. Default `'var(--destructive)'`. | | `hollow?` | `boolean` | Draw rising candles as outlines rather than fills. Default `false`. | | `width?` | `number` | Share of the band each candle body takes. Default `0.62`. | | `formatX?` | `(value: never) => string` | Tick labels along the x axis. | | `formatY?` | `(value: never) => string` | Tick labels along the y axis, and the tooltip's values. | | `padding?` | `number \| { top?: number; right?: number; bottom?: number; left?: number }` | Space around the plot, in px. | | `label?` | `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. | | `references?` | `ChartReference[]` | Lines and bands at fixed values — targets, thresholds, windows. | | `annotations?` | `ChartAnnotation[]` | Notes pinned to a point. | | `brush?` | `ChartBrush` | Drag across the plot to select a range, and zoom the chart to it. | | `zoom?` | `ChartZoom` | Wheel or pinch to zoom, drag to pan. | ### Card `import { Card } from 'omaris'` MD3 card — elevated, filled, or outlined. #### Use it for - One thing with a heading, a body and its actions: an invoice, an order, a project. `CardHeader`, `CardContent`, `CardFooter`. The body grows, so a row of cards with different amounts of content still lines its footers up. - A grid of pressable tiles. Give the card `href` or `onclick` and it becomes a real target with a ripple and a focus ring, with no wrapper ``. - A picker, like plans or delivery options. `interactive` and `selected`, with `aria-pressed` on each so the choice is read out. - An image-led card. `CardMedia` with a `ratio`; `bleed` is on by default so the picture reaches the edges and rounds only the corners it touches. #### Not for - Rows of the same shape you read down → [List](/docs/components/list). Several columns you compare or sort → [Table](/docs/components/table). - Grouping a form's fields → whitespace and a [Text](/docs/components/text) heading. A card around every fieldset is a page of boxes. - Pictures of unequal height in a wall → [Masonry](/docs/components/masonry) around the cards, not a grid of cards with cropped images. #### Do - Pick the variant by what is underneath. `outlined` (the default) reads well on a plain page. A dashboard grid usually wants `elevated`, so cards read as separate objects without a hairline each. `filled` is for a card inside another surface, like a sheet or a dialog. - Set `level` on `CardHeader` to fit the page outline. The default `3` assumes the page has an `h2` above it. - Leave `CardFooter` actions at the end. `align="between"` is only for keeping a destructive action apart from the safe one. - Put a menu or a badge in the header's `end` slot, an avatar or icon in `start`. #### Don't - Nest a card in a card. Use `CardContent` with a `Divider` or a `List` inside it. - Put a button, a link or a menu inside an `interactive` card. Make the whole card the target, or none of it. - Give a card more than one `filled` button. One action commits; the rest are `text`. - Add a `border` class to make an `elevated` card visible on a busy surface. Switch to `outlined` instead. #### Pairs with - [Button](/docs/components/button), [Icon Button](/docs/components/icon-button), [Avatar](/docs/components/avatar), [Badge](/docs/components/badge), [Masonry](/docs/components/masonry), [Skeleton](/docs/components/skeleton) | Prop | Type | Notes | | --- | --- | --- | | `variant?` | `CardVariant` | One of `elevated`, `filled`, `outlined`. Default `'outlined'`. | | `size?` | `CardSize` | One of `md`, `lg`. Default `'md'`. | | `interactive?` | `boolean` | Force the interactive treatment on a card that handles its own press. | | `selected?` | `boolean` | Chosen state — border and ring in the primary color. Default `false`. | | `ripple?` | `boolean` | Ripple on press. Interactive cards only. Default `true`. | | `class?` | `string` | | | `children?` | `Snippet` | | | `tone?` | `ButtonTone` | Color role the fill style paints with. | | `shape?` | `ButtonShape` | Pill (`round`) or MD3 rounded rectangle (`square`). | | `icon?` | `boolean` | Square, label-less button. Requires an `aria-label`. | | `confetti?` | `boolean \| ConfettiOptions` | Confetti on press. `true` for the default burst, or any `ConfettiOptions` — `confetti={{ preset: 'fireworks' }}`. | | `loading?` | `boolean` | Grows a spinner in at the start of the button and blocks interaction. The label stays put; only an `icon` button swaps its icon out. | | `toggle?` | `boolean` | Turns the button into a two-state toggle driven by `pressed`. Ignored when `href` is set — a link has no pressed state. | | `pressed?` | `boolean` | Selected state of a toggle button. Bindable. | ### CardContent `import { CardContent } from 'omaris'` A card's body — the text and controls between its header and its footer. | Prop | Type | Notes | | --- | --- | --- | | `class?` | `string` | | | `children?` | `Snippet` | | ### CardFooter `import { CardFooter } from 'omaris'` A card's actions, laid out in a row that wraps. | Prop | Type | Notes | | --- | --- | --- | | `align?` | `'start' \| 'end' \| 'between'` | Where the actions sit. MD3 puts card actions at the end. Default `'end'`. | | `class?` | `string` | | | `children?` | `Snippet` | | ### CardHeader `import { CardHeader } from 'omaris'` The heading row of a card: a leading slot, a title block, a trailing slot. Parts (`classes`): `root` `start` `body` `title` `description` `end` | Prop | Type | Notes | | --- | --- | --- | | `heading?` | `string` | Heading text. Use the `title` snippet instead for rich markup. | | `description?` | `string` | Supporting line under the heading. | | `level?` | `2 \| 3 \| 4 \| 5 \| 6` | Heading level, so cards nest correctly in a page outline. Default `3`. | | `class?` | `string` | | | `classes?` | `CardHeaderClasses` | Per-part Tailwind overrides. `class` still covers the root. | | `start?` | `Snippet` | Leading slot — an avatar or icon. | | `end?` | `Snippet` | Trailing slot — an overflow menu, a badge. | | `title?` | `Snippet` | Rich heading, in place of `heading`. | | `supporting?` | `Snippet` | Rich supporting line, in place of `description`. | ### CardMedia `import { CardMedia } from 'omaris'` The image at the top of a card. | Prop | Type | Notes | | --- | --- | --- | | `src?` | `string` | | | `alt?` | `string` | Default `''`. | | `ratio?` | `'16/9' \| '4/3' \| '1/1' \| '3/2' \| 'auto'` | Common MD3 media ratios. Default `'16/9'`. | | `bleed?` | `boolean` | Cancels the card's padding so the media reaches the edges, and keeps only the corners it actually touches rounded. Default `true`. | | `class?` | `string` | | | `children?` | `Snippet` | Overlaid content — a play button, a gradient caption. | ### Carousel `import { Carousel } from 'omaris'` MD3 carousel. #### Use it for - A shelf of visual things where one is in front and the rest peek: product photos, places, featured content. The default `hero` layout, or `centered` when the front item should sit in the middle. - A browsing row of many equal cards: "more like this", a team, a set of templates. `variant="uncontained"` with an `itemWidth`. - A highlights banner that runs itself. `variant="full"`, `autoplay` and `loop`. It pauses under the pointer, on focus, and off screen. - A story: full-bleed slides on a timer, tapped through. `variant="story"` with `autoplay`, `indicators` for the segmented bar, and `header`/`footer` for the name and the actions over it. Onboarding, a release tour, a daily recap. - A front item that something else follows, like a caption, a thumbnail strip or a map pin. `bind:index`. #### Not for - Pictures the person wants to see all at once → [Masonry](/docs/components/masonry) or a plain grid. A carousel hides everything but the front. - Looking closely at one picture → [Image Viewer](/docs/components/image-viewer). Wrap each item in an `ImageViewerItem` and the carousel becomes the gallery. - Steps that are not visual, like a wizard → a morphing [Dialog](/docs/components/dialog) or [Tabs](/docs/components/tabs). - Rows of text → [List](/docs/components/list). #### Do - Give it a `label` ("Product photos"), and an `itemLabel` when "3 of 6" is not a useful name for an item. - Pin a caption with `w-(--carousel-card)` and fade it with `opacity-(--carousel-reveal)`, so it is clipped instead of re-wrapped as the card folds into a strip. - Fill the card: images `object-cover` at `size-full`. Size the viewport with the `height` prop, or use `height="auto"` plus an `aspect-*` on `classes.viewport` to size by ratio. - Add `controls` where a mouse is likely, like a dashboard or a desktop shelf. A finger swipes without them. - Hold a story open with `bind:paused` while something else has the screen: a menu, a dialog, a buffering video. Use `oncomplete` to move to the next story or close the screen. - Put only controls in a story's `header` and `footer`. Links, buttons and fields there stay clickable; everything else taps through, so a wide label between two buttons does not eat the tap that advances the story. #### Don't - `autoplay` content people need to read or act on. It is for ambient highlights, not a form or a list of offers. - Put twenty items in a `hero` carousel. Nobody pages that far; use `uncontained` or a wall. - Put text that has to wrap, or a form, inside an item. Strips fold and clip whatever is in them. - Give a story slides that have to be read. A person gets one hold and one tap-back, not a second reading. - Write `h-56` in `class` to size it. The base sets its own height; the `height` prop replaces it. #### Pairs with - [Image Viewer](/docs/components/image-viewer), [Card](/docs/components/card), [Masonry](/docs/components/masonry), [Icon Button](/docs/components/icon-button), [Segmented Button](/docs/components/segmented-button) Parts (`classes`): `root` `frame` `viewport` `track` `item` `strip` `control` `indicators` `dot` `segments` `segment` `segmentFill` `header` `footer` | Prop | Type | Notes | | --- | --- | --- | | `items` | `T[]` | | | `item` | `Snippet<[T, CarouselItemContext]>` | How to draw one item. It fills the card; images should `object-cover`. | | `variant?` | `CarouselVariant` | One of `hero`, `centered`, `multi-browse`, `full`, `story`, `uncontained`. Default `'hero'`. | | `index?` | `number` | The item in front. Bindable. Default `0`. | | `gap?` | `number` | Gap between items, in px. Defaults to 8, and to 0 for a story. | | `stripWidth?` | `number` | Width of a peeking strip, in px. Default `56`. | | `itemWidth?` | `number` | Item width for `uncontained`, in px. Default `240`. | | `loop?` | `boolean` | Wrap around at the ends. With enough items to fill the row it is a true loop — the last folds into the first in either direction; with fewer, the ends still join but by a cut. Default `false`. | | `autoplay?` | `number` | Advance every this many ms. Pauses under the pointer, on focus, and offscreen — a story instead pauses while held, while a key has focused it, and whenever `paused` is set. Default `0`. | | `controls?` | `boolean` | Draw prev/next buttons. Default `false`. | | `indicators?` | `boolean` | Draw the dots. Default `false`. | | `label?` | `string` | Accessible name — "Product photos". | | `itemLabel?` | `(item: T, index: number) => string` | Names each item for assistive tech. Defaults to "n of total". | | `onchange?` | `(index: number) => void` | | | `oncomplete?` | `() => void` | The last slide's timer ran out with `loop` off — the story is over. | | `header?` | `Snippet<[T, CarouselItemContext]>` | Drawn across the top of the frame, over the slide: a name, a time, a close button. Direct children of it are clickable; everything around them still taps through to the story. | | `footer?` | `Snippet<[T, CarouselItemContext]>` | The same, across the bottom: actions, a reply box. | | `paused?` | `boolean` | Held open by the caller — a menu is up, a video is buffering. Bindable. A press-and-hold is not written back here — it is its own state, read from the root's `data-held` attribute. Default `false`. Bindable. | | `height?` | `number \| string` | How tall the viewport is. A number is px, a string is any CSS length. Defaults to `14rem`, and to `30rem` for a story — `100dvh` for one that fills the screen. | | `morph?` | `boolean \| CarouselMorph` | Dissolve between slides with a warp instead of sliding them, for `full` and `story`. `true` is `melt`; see `CarouselMorph` for the others. Ignored by the morphing layouts, which have their own motion. Default `false`. | | `morphStrength?` | `number` | How far the warp displaces at its peak, in px. Default `48`. | | `class?` | `string` | | | `classes?` | `CarouselClasses` | | ### ChartFrame `import { ChartFrame } from 'omaris/chart'` The box every chart draws into. | Prop | Type | Notes | | --- | --- | --- | | `height` | `number \| string` | | | `label?` | `string` | Accessible description of what the chart shows. | | `role?` | `'img' \| 'group'` | `'group'` — the default — for a plot with anything in it a person can reach: a legend that filters, a tooltip, the CSV button, the "Try again" of the error state. `'img'` for a plot that really is just a picture, which hides the SVG's innards from a screen reader — and which promises there is nothing inside to tab to, so it is wrong the moment there is. Default `'group'`. | | `blank?` | `boolean` | True when there's nothing to draw. Default `false`. | | `emptyText?` | `string` | Default `'No data'`. | | `empty?` | `Snippet` | | | `error?` | `boolean \| string \| Error` | Something went wrong. Truthy replaces the plot with the error state; a string or an `Error` supplies the message. | | `errorText?` | `string` | The message, when `error` is just `true`. Default `'Could not load this chart'`. | | `errorState?` | `Snippet<[{ message: string }]>` | Replaces the whole error state. | | `onRetry?` | `() => void` | Offers a "Try again" button in the error state. | | `csv?` | `boolean \| string` | Offer `rows` as a CSV download. A string names the file. Default `false`. | | `rows?` | `readonly T[]` | The rows the download writes. | | `csvColumns?` | `(string \| CsvColumn)[]` | Which fields the download writes, in order. Defaults to every field. | | `class?` | `string` | | | `children?` | `Snippet` | | ### Checkbox `import { Checkbox } from 'omaris'` MD3 checkbox. #### Use it for - Any number of items from a set: permissions, columns to show, rows to act on. - A yes/no submitted with the form rather than applied at once: "Accept the terms", "Send me the digest". Bind `checked` and give it a `name`. - The parent row over a set. `indeterminate` while some but not all children are checked. The parent computes it; it is not a third value. - An option that needs explaining. `description` is the second line that says what the first commits you to. - A run of rows ticked in one go. Dragging across a column of checkboxes paints them all to the state the first one was heading to, the way a phone's photo grid selects. It is on by default and needs no wiring. #### Not for - A setting that takes effect the moment it flips → [Switch](/docs/components/switch). - Exactly one of several → [Radio](/docs/components/radio). - A filter above a list that shows its result as you tick → filter [Chips](/docs/components/chip). - A single boolean in a toolbar → a `toggle` [Icon Button](/docs/components/icon-button). - More than about seven options → a searchable [Combobox](/docs/components/combobox). #### Do - Use `label` (or `children` for rich text) rather than a separate `