{
event.preventDefault()
setAt({ x: event.clientX, y: event.clientY })
}}
>
Right-click here
\| void` | | Runs the command. The dialog does not close itself: once this resolves, the kit unmounts it (unless `keepOpen`). A failure shows in the dialog and leaves it open, previewing again. |
| `onCancel` (required) | `() => void` | | Cancel, Escape, or a press outside (when modal). The kit unmounts the dialog. Not while OK runs. |
| `cancelLabel` | `string` | | The Cancel button's label ("Done" in upstream's Find, "Close" for information). |
| `onReset` | `() => void` | | Alt turns Cancel into Reset, which calls this (restores the dialog's defaults). |
| `keepOpen` | `boolean` | `false` | OK runs without closing, and the preview stays open (upstream's Find). |
| `modal` | `boolean` | `true` | Blocks the page (the default). With `false` there is no backdrop: a press outside does not cancel, Tab may leave the dialog, and the editor keeps its keys (to move the playhead under a picture preview). Escape still cancels. |
| `children` (required) | `ReactNode` | | |
| `uiId` | `string` | | The dialog's Kit UI id. |
| `ids` | `{ ok?: string; cancel?: string; preview?: string }` | | The buttons' Kit UI ids; Reset is the Cancel button. |
| `testId` | `string` | `'preview-dialog'` | |
| `position` | `{ x: number; y: number } \| null` | | Where the dialog sits (with `onMove`), as `Dialog`'s; absent: where the layout puts it. |
| `onMove` | `(position: { x: number; y: number }) => void` | | Makes the dialog movable by its title, as `Dialog`'s (a kit keeps the place); absent, it does not move. |
## Toast
Menus and overlays. A short notice at the top. Render Toaster once; call toast() or toast.success(), .error(), .loading(), .promise() anywhere, with an id to replace a notice and an action button.
```ts
import { Toaster } from '@kition/tessera-ui'
```
### Example: Levels
```tsx
import { Button, toast } from '@kition/tessera-ui'
// Render once near the root of the editor; toast() can then be called from anywhere.
export default function Demo() {
return (
toast('success', 'Saved', 'Budget 2027.xlsx')}>Success
toast('info', 'Agent is working', 'Rewriting slide 3')}>Info
toast('error', 'Could not save', 'The disk is full')}>Error
)
}
```
### Example: Actions, promises, sticky notices
```tsx
import { Button, toast } from '@kition/tessera-ui'
const wait = (ms: number) => new Promise((resolve) => setTimeout(resolve, ms))
export default function Demo() {
return (
toast.info('Row deleted', { action: { label: 'Undo', onClick: () => toast.success('Row restored') } })}>With an action
void toast.promise(wait(1500).then(() => 3), { loading: 'Exporting…', success: (count) => `Exported ${count} files`, error: 'Export failed' })}>A promise
toast.error('Offline', { id: 'network', duration: Number.POSITIVE_INFINITY, description: 'Changes are kept until you reconnect.' })}>Sticky
toast.dismiss()}>Dismiss all
)
}
```
### Toaster props
Where notices appear: centred at the top, `offset` px down (below a host's tab strip).
| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `offset` | `number` | `12` | |
| `closeButton` | `boolean` | `true` | |
## Modal
Menus and overlays. A composable modal dialog for host apps: header, content, footer; it traps focus and closes on Escape or an outside press.
```ts
import { Modal, ModalContent, ModalHeader, ModalTitle, ModalDescription, ModalBody, ModalFooter, ModalCloseButton } from '@kition/tessera-ui'
```
### When to use
- Confirming details or collecting a few values in an app (rename, share, create).
### When not to
- A kit tool’s settings: use Dialog.
- Editing beside the work: use Sheet.
### Example: Rename
```tsx
import { useState } from 'react'
import { Button, Field, Modal, ModalContent, ModalDescription, ModalFooter, ModalHeader, ModalTitle, TextInput } from '@kition/tessera-ui'
export default function Demo() {
const [open, setOpen] = useState(false)
const [name, setName] = useState('Quarterly plan')
return (
<>
setOpen(true)}>Rename…
Rename document
The name shows in the tab and the file tree.
{({ id }) => setName(event.target.value)} />}
setOpen(false)}>Cancel
setOpen(false)}>Rename
>
)
}
```
### Example: A long dialog
With `fixed` the header and footer stay put and ModalBody scrolls; a ModalCloseButton of your own can be disabled while work runs.
```tsx
import { useState } from 'react'
import { Button, Modal, ModalBody, ModalCloseButton, ModalContent, ModalFooter, ModalHeader, ModalTitle } from '@kition/tessera-ui'
export default function Demo() {
const [open, setOpen] = useState(false)
const [running, setRunning] = useState(false)
return (
<>
setOpen(true)}>Import file…
(running ? undefined : setOpen(next))}>
Import a file
{Array.from({ length: 24 }, (_, i) => (
Column {i + 1}: text
))}
setOpen(false)}>Cancel
{
setRunning(true)
setTimeout(() => {
setRunning(false)
setOpen(false)
}, 1200)
}}
>
Import
>
)
}
```
### Modal props
A modal dialog. Controlled with `open` and `onOpenChange`; Escape and an outside press close it.
Also takes the attributes of its HTML element (`className`, `aria-*`, events).
### ModalContent props
The dialog's panel, over a dimmed page: 12px radius, a hairline, the modal shadow.
| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `size` | `ModalSize`: `'sm'`, `'md'`, `'lg'`, `'none'`, `'xs'`, `'xl'`, `'2xl'`, `'3xl'`, `'4xl'` | `'lg'` | Its widest: `xs` 320px to `4xl` 896px; `none` leaves the width to `className`. |
| `hideClose` | `boolean` | | No close button in the corner (the footer has its own way out). |
| `dismissible` | `boolean` | `true` | When false, an outside press does not close it (Escape still does). |
| `disableAutoFocus` | `boolean` | | Keeps focus where it was instead of moving it into the dialog when it opens. |
| `fixed` | `boolean` | | A long dialog: the header and footer stay put with hairlines, and `ModalBody` scrolls between them. |
Also takes the attributes of its HTML element (`className`, `aria-*`, events).
### ModalHeader props
The title and description at the top of a modal.
Also takes the attributes of its HTML element (`className`, `aria-*`, events).
### ModalTitle props
Also takes the attributes of its HTML element (`className`, `aria-*`, events).
### ModalDescription props
Also takes the attributes of its HTML element (`className`, `aria-*`, events).
### ModalBody props
The content between a modal's header and footer; it scrolls in a `fixed` modal.
Also takes the attributes of its HTML element (`className`, `aria-*`, events).
### ModalFooter props
The actions at the bottom of a modal, right-aligned; full width on a narrow window.
Also takes the attributes of its HTML element (`className`, `aria-*`, events).
### ModalCloseButton props
The close button in a modal's corner. ModalContent draws one; with `hideClose`, place your own,
for one you can disable (`disabled` while an import runs).
Also takes the attributes of its HTML element (`className`, `aria-*`, events).
## Tooltip
Menus and overlays. A short label shown on hover or keyboard focus, for an icon or a status that needs a word.
```ts
import { Tooltip, TooltipProvider, TooltipContent } from '@kition/tessera-ui'
```
### When to use
- Naming an icon-only control.
- Explaining a status mark (an error in a cell).
### When not to
- Anything the person must read to act: put it on the page.
### Example: Icon and button
```tsx
import { Button, Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from '@kition/tessera-ui'
import { CircleAlertIcon } from '@kition/tessera-icons/react'
export default function Demo() {
return (
The value is not a date
Hover or focus me
Tooltips name a control; they never hold the only copy of something.
)
}
```
### Tooltip props
A short label shown on hover or keyboard focus. Works on its own; a TooltipProvider above it shares one delay among several.
Also takes the attributes of its HTML element (`className`, `aria-*`, events).
### TooltipProvider props
Shares one hover delay among the tooltips inside it (`delayDuration`, 200 ms by default), so moving between them skips the wait.
Also takes the attributes of its HTML element (`className`, `aria-*`, events).
### TooltipContent props
The tooltip's bubble: dark, small, above the page.
Also takes the attributes of its HTML element (`className`, `aria-*`, events).
## Popover
Menus and overlays. Floating content anchored to a trigger, positioned to stay on screen: filters, pickers, small forms.
```ts
import { Popover, PopoverContent } from '@kition/tessera-ui'
```
### When to use
- Filter, sort, and group controls of a table.
- A picker under a button.
### When not to
- A list of commands: use MenuButton.
### Example: A filter
```tsx
import { Button, Popover, PopoverContent, PopoverTrigger, Select, Switch } from '@kition/tessera-ui'
import { FunnelIcon } from '@kition/tessera-icons/react'
export default function Demo() {
return (
Filter
Show records where
Status is Open
Status is Done
Include archived
)
}
```
### Popover props
Floating content anchored to a trigger: filters, pickers, small forms.
Also takes the attributes of its HTML element (`className`, `aria-*`, events).
### PopoverContent props
The popover's panel: 12px radius, a hairline, the floating shadow; 288px wide unless `className` says otherwise.
Also takes the attributes of its HTML element (`className`, `aria-*`, events).
## Sheet
Menus and overlays. A panel that slides in from the right to edit one thing beside the work, guarding unsaved changes; from the left, it holds an app’s navigation on a phone.
```ts
import { Sheet } from '@kition/tessera-ui'
```
### When to use
- Editing a record’s field, a connection, a rule.
### When not to
- A quick question: use Modal or Confirm.
### Example: Edit a field
With unsaved changes, Escape, the backdrop, and the close button ask first.
```tsx
import { useState } from 'react'
import { Button, Field, Sheet, TextInput } from '@kition/tessera-ui'
export default function Demo() {
const [open, setOpen] = useState(false)
const [label, setLabel] = useState('Status')
const [saved, setSaved] = useState('Status')
const dirty = label !== saved
return (
<>
setOpen(true)}>Edit field…
{
if (window.confirm('Discard your changes?')) {
setLabel(saved)
setOpen(false)
}
}}
onClose={() => setOpen(false)}
footer={
<>
setOpen(false)}>Close
setSaved(label)}>Save
>
}
>
{({ id }) => setLabel(event.target.value)} />}
>
)
}
```
### Sheet props
A panel that slides in from the right over a dimmed page, for editing one thing beside the work.
| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `open` (required) | `boolean` | | |
| `onClose` (required) | `() => void` | | |
| `title` (required) | `ReactNode` | | |
| `children` (required) | `ReactNode` | | |
| `footer` | `ReactNode` | | Actions at the bottom, right-aligned. |
| `dirty` | `boolean` | `false` | With unsaved changes, closing (Escape, the backdrop, the close button) calls `onRequestClose` instead. |
| `onRequestClose` | `() => void` | | |
| `width` | `number` | `400` | Its width in pixels. |
| `label` | `string` | | Its accessible name, when `title` is not plain text (a logo and a name). |
| `side` | `'left' \| 'right'` | `'right'` | The edge it slides in from: `right` (default, editing beside the work) or `left` (an app's navigation on a phone). |
| `testId` | `string` | `'sheet'` | Test id of the sheet; its parts take `${testId}-backdrop`, `-header`, `-close`, `-footer`. |
## Confirm
Menus and overlays. `useConfirm()` asks a yes-or-no question and resolves a promise; asks queue one at a time.
```ts
import { ConfirmProvider } from '@kition/tessera-ui'
```
### When to use
- Before deleting or discarding.
### When not to
- Questions with more than two answers: use Modal.
### Example: Delete
A danger confirm puts focus on Cancel.
```tsx
import { useState } from 'react'
import { Button, ConfirmProvider, useConfirm } from '@kition/tessera-ui'
function DeleteButton() {
const confirm = useConfirm()
const [result, setResult] = useState('')
return (
{
const ok = await confirm({ title: 'Delete this table?', message: 'Its 240 records go to the trash for 30 days.', confirmLabel: 'Delete', variant: 'danger' })
setResult(ok ? 'Deleted' : 'Kept')
}}
>
Delete table
{result}
)
}
export default function Demo() {
return (
)
}
```
### ConfirmProvider props
Lets `useConfirm` ask from anywhere inside it; asks queue and show one at a time.
| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `children` | `ReactNode` | | |
## Command
Menus and overlays. A searchable list of commands or choices: type to filter, arrows and Enter to pick.
```ts
import { Command, CommandInput, CommandList, CommandEmpty, CommandGroup, CommandItem, CommandSeparator, CommandShortcut } from '@kition/tessera-ui'
```
### When to use
- A command palette, a searchable picker inside a popover.
### Example: A command palette
```tsx
import { Command, CommandEmpty, CommandGroup, CommandInput, CommandItem, CommandList, CommandSeparator, CommandShortcut, toast } from '@kition/tessera-ui'
import { FileTextIcon, SettingsIcon, TableIcon } from '@kition/tessera-icons/react'
export default function Demo() {
const run = (name: string) => toast('info', name)
return (
No matching command.
run('New document')}>
New document ⌘N
run('New table')}>
New table
run('Settings')}>
Settings ⌘,
)
}
```
### Command props
A searchable list of commands or choices (cmdk): type to filter, arrows and Enter to pick.
Also takes the attributes of its HTML element (`className`, `aria-*`, events).
### CommandInput props
| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `wrapClassName` | `string` | | |
Also takes the attributes of its HTML element (`className`, `aria-*`, events).
### CommandList props
Also takes the attributes of its HTML element (`className`, `aria-*`, events).
### CommandEmpty props
Also takes the attributes of its HTML element (`className`, `aria-*`, events).
### CommandGroup props
Also takes the attributes of its HTML element (`className`, `aria-*`, events).
### CommandItem props
Also takes the attributes of its HTML element (`className`, `aria-*`, events).
### CommandSeparator props
Also takes the attributes of its HTML element (`className`, `aria-*`, events).
### CommandShortcut props
A shortcut at the end of a command item.
Also takes the attributes of its HTML element (`className`, `aria-*`, events).
## DropPanel
Menus and overlays. A popover anchored to an element, closing on a press outside or Escape.
```ts
import { DropPanel } from '@kition/tessera-ui'
```
### Example: A setting under a button
```tsx
import { useRef, useState } from 'react'
import { Button, DropPanel, NumberField } from '@kition/tessera-ui'
export default function Demo() {
const anchor = useRef(null)
const [open, setOpen] = useState(false)
const [radius, setRadius] = useState(8)
return (
setOpen(!open)}>Corner radius ▾
setOpen(false)} label="Corner radius">
setRadius(value)} />
)
}
```
### DropPanel props
| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `open` (required) | `boolean` | | |
| `anchor` (required) | `RefObject` | | |
| `onClose` (required) | `() => void` | | |
| `label` (required) | `string` | | |
| `className` | `string` | `''` | |
| `testId` | `string` | | |
| `children` (required) | `ReactNode` | | |
## Scrollbar
Editor chrome. A persistent scrollbar for a virtual document viewport, with a proportional thumb, dragging, track clicks and keyboard navigation.
```ts
import { Scrollbar } from '@kition/tessera-ui'
```
### When to use
- A canvas pans through a bounded document instead of scrolling DOM content.
### When not to
- An ordinary DOM list: use native overflow scrolling.
### Example: Page sections
```tsx
import { useState } from 'react'
import { Scrollbar } from '@kition/tessera-ui'
export default function Demo() {
const [offset, setOffset] = useState(0)
return (
{Array.from({ length: 20 }, (_, index) =>
Page section {index + 1}
)}
)
}
```
### Scrollbar props
A persistent scrollbar for virtual viewports, with proportional thumb, pointer capture and keyboard navigation.
| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `label` (required) | `string` | | Accessible name supplied by the editor's locale. |
| `orientation` | `'vertical' \| 'horizontal'` | `'vertical'` | |
| `value` (required) | `number` | | Distance from the start of the content, in the caller's coordinates. |
| `max` (required) | `number` | | Maximum scroll distance (content length minus visible length). |
| `page` (required) | `number` | | Visible content length, in the same coordinates as value. |
| `onChange` (required) | `(value: number) => void` | | |
| `controls` | `string` | | |
## Ribbon
Editor chrome. Office-style command groups: large and small buttons, stacks, rows, and swatches; it collapses to Single line, and a ribbon too wide for its window gives way in steps (RibbonFit): small buttons, icons alone, then groups collapsed to one button each.
```ts
import { RibbonGroup, RibbonButton, RibbonStack, RibbonRow, RibbonItem, ColorGrid } from '@kition/tessera-ui'
```
### When to use
- A kit’s Home, Insert, Format tabs.
### Example: Home tab
Give each group an icon: it is the button the group collapses to when the ribbon has no room.
```tsx
import { useState } from 'react'
import { ColorGrid, MenuButton, RibbonButton, RibbonGroup, RibbonStack } from '@kition/tessera-ui'
import { BoldIcon, ClipboardPasteIcon, CopyIcon, ItalicIcon, LayersIcon, ScissorsIcon, SquareIcon } from '@kition/tessera-icons/react'
export default function Demo() {
const [bold, setBold] = useState(false)
const [fill, setFill] = useState('#5645d4')
return (
setBold(!bold)} />
}>
)
}
```
### RibbonGroup props
A ribbon group. With an `id` (`group:/`, or an engine menu path `menu:/`
for groups built from commands) it follows the UI configuration and ends with the host's
buttons for it. In Single line it shows its primary items and a popover with the rest; `icon`
marks the one button a group without primary items becomes there, and the button a Classic
group collapses to when the ribbon has no room for it (`RibbonFit`).
| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `id` | `string` | | |
| `label` (required) | `string` | | |
| `icon` | `IconComponent` | | |
| `children` (required) | `ReactNode` | | |
### RibbonButton props
| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `icon` | `IconComponent` | | |
| `label` (required) | `string` | | |
| `size` | `'large' \| 'small' \| 'icon'` | `'small'` | |
| `pressed` | `boolean` | | |
| `swatch` | `string` | | A colored mark under the icon (font color, fill). |
| `primary` | `boolean` | | Shown in Single line's row (small); the group's other items wait in its popover. |
| `lineLabel` | `boolean` | | In Single line's row, keep the label beside the icon; a primary item with an icon otherwise shows the icon alone there (its label stays its name and tooltip). |
Also takes the attributes of its HTML element (`className`, `aria-*`, events).
### RibbonStack props
Small buttons stacked in up to three rows; in a Single line group, its primary buttons in the row.
| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `children` (required) | `ReactNode` | | |
### RibbonRow props
| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `children` (required) | `ReactNode` | | |
### RibbonItem props
Any content in a ribbon group that is not a ribbon button (a field, a gallery): `primary` shows
it in Single line's row too; otherwise it waits in the group's popover there.
| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `primary` | `boolean` | | |
| `children` (required) | `ReactNode` | | |
### ColorGrid props
A grid of color swatches; `onPick` gets `#RRGGBB`.
| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `colors` | `readonly string[]` | `[...PALETTE_COLORS, ...STANDARD_COLORS]` | |
| `onPick` (required) | `(color: string) => void` | | |
| `testId` | `string` | | |
## OverflowStrip
Editor chrome. A row that scrolls sideways when it does not fit: the cut edge fades, and a button there scrolls.
```ts
import { OverflowStrip } from '@kition/tessera-ui'
```
### When to use
- A ribbon’s groups or a tool’s options bar in a narrow window.
### When not to
- Vertical lists: let the panel scroll.
### Example: Options bar
The row is wider than its box; use the arrows at the faded edges.
```tsx
import { Button, OverflowStrip } from '@kition/tessera-ui'
const TOOLS = ['Brush', 'Eraser', 'Clone', 'Heal', 'Dodge', 'Burn', 'Sponge', 'Smudge', 'Blur', 'Sharpen', 'Gradient', 'Fill', 'Text', 'Shape', 'Crop', 'Slice']
export default function Demo() {
return (
{TOOLS.map((tool) => (
{tool}
))}
)
}
```
### OverflowStrip props
A row that scrolls sideways when its content does not fit (the ribbon's groups, a tool's options
bar): the cut edge fades, and a button there scrolls by most of a width. `className` and
`panelClassName` add to the outer box and the scrolled row; `resetKey` (the shown tab, the active
tool) re-measures; the buttons' test ids are `${testId}-start` and `${testId}-end`.
| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `className` (required) | `string` | | |
| `panelClassName` (required) | `string` | | |
| `role` | `string` | | |
| `resetKey` | `unknown` | | |
| `labels` (required) | `{ start: string; end: string }` | | |
| `testId` (required) | `string` | | |
| `panelRef` | `RefObject` | | The scrolled row, for a caller that measures it (the frame's step-down). |
| `children` (required) | `ReactNode` | | |
## StatusBar
Editor chrome. The bottom bar: document facts on the left, zoom on the right.
```ts
import { StatusBar, StatusSpacer, ZoomControl } from '@kition/tessera-ui'
```
### Example: Zoom
```tsx
import { useState } from 'react'
import { StatusBar, StatusSpacer, ZoomControl } from '@kition/tessera-ui'
export default function Demo() {
const [zoom, setZoom] = useState(1)
return (
Slide 3 of 12
setZoom(1)} labels={{ out: 'Zoom out', in: 'Zoom in', fit: 'Fit' }} />
)
}
```
### StatusBar props
| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `children` (required) | `ReactNode` | | |
| `onClick` | `(event: ReactMouseEvent) => void` | | |
### StatusSpacer props
### ZoomControl props
Zoom out, a slider from `min` to `max`, zoom in, the value, and fit.
| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `zoom` (required) | `number` | | |
| `onZoom` (required) | `(zoom: number) => void` | | |
| `onFit` (required) | `() => void` | | |
| `min` | `number` | `0.1` | |
| `max` | `number` | `4` | |
| `labels` (required) | `{ out: string; in: string; fit: string }` | | |
## TableSizePicker
Editor chrome. Pick a table size by hovering a grid.
```ts
import { TableSizePicker } from '@kition/tessera-ui'
```
### Example: Insert a table
```tsx
import { useState } from 'react'
import { TableSizePicker } from '@kition/tessera-ui'
export default function Demo() {
const [picked, setPicked] = useState('')
return (
setPicked(`${rows} × ${columns}`)} label={(rows, columns) => `${columns} × ${rows} table`} />
{picked ? `Inserted a ${picked} table` : 'Pick a size'}
)
}
```
### TableSizePicker props
Choosing a table's size by pointing at a grid of cells (Insert > Table).
| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `rows` | `number` | `8` | |
| `columns` | `number` | `10` | |
| `onPick` (required) | `(rows: number, columns: number) => void` | | |
| `label` (required) | `(rows: number, columns: number) => string` | | |
## Panel parts
Editor chrome. Pieces of list panels: visibility eyes, in-place rename, footers, thumbnails.
```ts
import { EyeButton, RenameField, PanelFooter, Thumbnail, Panel } from '@kition/tessera-ui'
```
### When to use
- Layers, tracks, pages panels.
### Example: A layer row
```tsx
import { useState } from 'react'
import { EyeButton, PanelFooter, RenameField } from '@kition/tessera-ui'
export default function Demo() {
const [visible, setVisible] = useState(true)
const [name, setName] = useState('Background')
const [editing, setEditing] = useState(false)
return (
setVisible(!visible)} />
{editing ? (
setEditing(false)} />
) : (
setEditing(true)}>{name}
)}
1 layer} end={Double-click to rename } />
)
}
```
### EyeButton props
A row's eye: the eye icon while shown, an empty box while hidden (as Photoshop draws it).
A press toggles at once (`onPress` hears whether Alt was held); with `sweep`, dragging on from
the press down the eye column (mouse, pen, or touch) reaches every other eye of the group the
pointer crosses, through `sweep.onOver`, so the panel gives each the visibility the first press
set. A keyboard click toggles too. It is pressed while its row shows; `tabIndex` takes it out of
the tab order where a list moves the focus itself.
| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `visible` (required) | `boolean` | | |
| `label` (required) | `string` | | |
| `onPress` (required) | `(alt: boolean) => void` | | |
| `sweep` | `EyeSweep` | | |
| `tabIndex` | `number` | | |
| `testId` | `string` | | |
### RenameField props
A row name's in-place editor, focused with its text selected: Enter or leaving it commits,
Escape cancels (`onDone` once, with which); a composing input method keeps Enter for itself.
It keeps its presses and keys from the row (no selection change, no editor shortcut).
| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `value` (required) | `string` | | |
| `label` (required) | `string` | | |
| `onChange` (required) | `(text: string) => void` | | |
| `onDone` (required) | `(commit: boolean) => void` | | |
| `testId` | `string` | | |
### PanelFooter props
The buttons along a list panel's bottom, as Photoshop's: `start` on the left, `end` on the right.
| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `start` | `ReactNode` | | |
| `end` | `ReactNode` | | |
### Thumbnail props
A thumbnail: `image` fitted in a `size` × `size` cell at its own aspect, over a checkerboard
where it is transparent, with a thin frame; an empty cell while there is no image (not drawn
yet, or its source could not draw one).
| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `image` (required) | `ThumbnailImage \| null` | | |
| `size` (required) | `number` | | |
| `testId` | `string` | | |
### Panel props
| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `side` (required) | `'left' \| 'right'` | | |
| `width` (required) | `number` | | |
| `title` | `string` | | |
| `children` (required) | `ReactNode` | | |
| `testId` | `string` | | |
## WorkspaceLayout
Workspace. Split, tabbed, resizable panels in a tree, as video and photo editors lay out their workspaces.
```ts
import { WorkspaceLayout } from '@kition/tessera-ui'
```
### Example: Editing workspace
Drag the gutters; drag tabs between groups.
```tsx
import { useState } from 'react'
import { WorkspaceLayout, type WorkspaceNode } from '@kition/tessera-ui'
const START: WorkspaceNode = {
split: 'row',
size: { ratio: 0.32 },
a: { tabs: ['project', 'effects'], active: 0 },
b: { split: 'column', size: { ratio: 0.6 }, a: { tabs: ['program'], active: 0 }, b: { tabs: ['timeline'], active: 0 } },
}
export default function Demo() {
const [tree, setTree] = useState(START)
const [focused, setFocused] = useState('program')
const [maximized, setMaximized] = useState(null)
const panel = (id: string, label: string) => ({ id, label, render: () => {label}
})
return (
setTree(START)}
/>
)
}
```
### WorkspaceLayout props
| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `tree` (required) | `WorkspaceNode` | | |
| `onTree` (required) | `(tree: WorkspaceNode) => void` | | |
| `panels` (required) | `readonly WorkspacePanel[]` | | Keep this array, and each panel's `render`, stable across renders (see above). |
| `focused` (required) | `string \| null` | | |
| `onFocus` (required) | `(panel: string) => void` | | |
| `maximized` (required) | `string \| null` | | The maximized panel fills the area without changing the tree (upstream `keys.maximized`, the ` and Shift+` keys). The panel menu's Maximize Frame, as upstream, replaces the tree instead. |
| `onMaximized` (required) | `(panel: string \| null) => void` | | |
| `onRestore` (required) | `() => void` | | Restore Workspace in the panel menu. |
| `testId` | `string` | | |
## DockArea
Workspace. A column of collapsible, resizable panel groups, remembered per kit.
```ts
import { DockArea } from '@kition/tessera-ui'
```
### Example: Right dock
```tsx
import { useState } from 'react'
import { DockArea } from '@kition/tessera-ui'
export default function Demo() {
const [shown, setShown] = useState>(new Set(['color', 'layers']))
return (
Color
}, { id: 'swatches', label: 'Swatches', render: () => Swatches
}] },
{ id: 'layers', defaultHeight: 160, tabs: [{ id: 'layers', label: 'Layers', render: () => Layers
}] },
]}
/>
)
}
```
### DockArea props
| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `kit` (required) | `string` | | Remembers the layout per kit, in the kit's store (`kit::dock`, see `kitStore`). |
| `groups` (required) | `readonly DockGroup[]` | | |
| `shown` (required) | `ReadonlySet` | | Groups the Window menu shows; others are not drawn. |
| `onShownChange` | `(shown: ReadonlySet) => void` | | |
| `width` | `number` | | |
| `handle` | `Ref` | | Receives the dock's `DockAreaHandle`. |
| `testId` | `string` | `'dock'` | |
## FloatingPanels
Workspace. Movable, resizable windows over the workspace, remembered per kit.
```ts
import { FloatingPanels } from '@kition/tessera-ui'
```
### Example: A floating bin
```tsx
import { useState } from 'react'
import { Button, FloatingPanels } from '@kition/tessera-ui'
export default function Demo() {
const [open, setOpen] = useState(['bin'])
const [focused, setFocused] = useState('bin')
return (
setOpen(['bin'])}>Open the bin
({ id, title: 'Bin: Interviews', render: () => 12 clips
}))}
onClose={(id) => setOpen(open.filter((item) => item !== id))}
focused={focused}
onFocus={setFocused}
defaultSize={{ width: 280, height: 180 }}
/>
)
}
```
### FloatingPanels props
The floating windows layer (see the module's comment). Render it inside an element with a
non-static `position` (`relative`) that covers the area the windows may use: the layer fills
that element and keeps every window inside it.
| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `kit` (required) | `string` | | |
| `store` (required) | `string` | | Store name for positions and sizes (`useKitStore`). It holds the windows' rectangles only: anything else under it is dropped on read, so a kit keeps its own list of open windows under a different name. |
| `panels` (required) | `readonly FloatingPanel[]` | | The open windows, bottom to top as first opened. Keep the array and each `render` stable (`useMemo`, `useCallback`). |
| `onClose` (required) | `(id: string) => void` | | |
| `focused` (required) | `string \| null` | | The window the kit treats as focused (drawn as such and raised); null for none. |
| `onFocus` (required) | `(id: string) => void` | | A press or focus inside a window. |
| `defaultSize` | `FloatingSize` | | A new window's size (upstream's `default_size`, 520 × 360). |
| `minSize` | `FloatingSize` | | The smallest a window resizes to (upstream's `min_size`, 320 × 220). |
| `testId` | `string` | | |
## ColorPicker
Color. Saturation and brightness square, hue strip, hex and RGB fields.
```ts
import { ColorPicker } from '@kition/tessera-ui'
```
### Keyboard
| Keys | What they do |
| --- | --- |
| Arrows (on the square) | Change saturation (Left, Right) and brightness (Up, Down); with Shift, in larger steps. |
| Up, Down (on the hue strip) | Raise or lower the hue; with Shift, in larger steps. |
| Enter, Escape (in the hex field) | Commit the typed colour, or put the old one back. |
### Example: Pick a colour
```tsx
import { useState } from 'react'
import { ColorPicker, rgbToHex, type RGB } from '@kition/tessera-ui'
export default function Demo() {
const [rgb, setRgb] = useState([86, 69, 212])
return (
setRgb(value)} />
{rgbToHex(rgb)}
)
}
```
### ColorPicker props
| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `value` (required) | `RGB` | | |
| `onChange` (required) | `(rgb: RGB, final: boolean) => void` | | Called on every pointer move and arrow key (`final: false` mid-drag, one `final: true` on release or key). |
| `disabled` | `boolean` | | |
| `testId` | `string` | `'color-picker'` | |
| `uiId` | `string` | | A Kit UI id when the picker is a declared element. |
## ColorWheel
Color. A grading wheel with a lightness slider, as colour correction panels use.
```ts
import { ColorWheel } from '@kition/tessera-ui'
```
### Keyboard
| Keys | What they do |
| --- | --- |
| Arrows | Move the puck by 0.01 of the radius; with Shift, by 0.1. |
| Home | Put the puck back at the centre. |
### Example: Shadows wheel
```tsx
import { useState } from 'react'
import { ColorWheel } from '@kition/tessera-ui'
export default function Demo() {
const [shadows, setShadows] = useState<[number, number]>([0.2, -0.1])
const [lightness, setLightness] = useState(0)
return setShadows(value)} onLightness={(value) => setLightness(value)} />
}
```
### ColorWheel props
| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `value` (required) | `readonly [number, number]` | | The offset, `[x, y]`, y up, of length at most 1. |
| `lightness` (required) | `number` | | -100 to 100. |
| `onChange` (required) | `(value: [number, number], phase: DragPhase) => void` | | |
| `onLightness` (required) | `(value: number, phase: DragPhase) => void` | | |
| `label` (required) | `string` | | Shown above the wheel, and its accessible name. |
| `size` | `number` | `120` | The wheel's side in CSS pixels (120 by default, upstream's largest). The control is `size + 18` px wide: the lightness strip (16 px and a 2 px gap) sits outside the wheel, where upstream draws it inside the wheel's square. To fit upstream's row of three wheels (Film's Lumetri Color Wheels, Task 29), pass `size = min((available - 24) / 3, 120) - 18`. |
| `disabled` | `boolean` | | |
| `uiId` | `string` | | The wheel's Kit UI id. |
| `lightnessUiId` | `string` | | The lightness slider's Kit UI id. |
| `testId` | `string` | | |
## GradientSlider
Color. A slider drawn over its own gradient: temperature, tint, hue.
```ts
import { GradientSlider } from '@kition/tessera-ui'
```
### Keyboard
| Keys | What they do |
| --- | --- |
| Arrows | Change the value by a hundredth of the soft range (Right and Up raise it); with Shift, by ten of those. |
| Home, End | Go to the low or the high end of the soft range. |
### Example: White balance
```tsx
import { useState } from 'react'
import { GradientSlider, SLIDER_GRADIENTS } from '@kition/tessera-ui'
export default function Demo() {
const [temperature, setTemperature] = useState(10)
const [tint, setTint] = useState(-5)
return (
setTemperature(value)} />
setTint(value)} />
)
}
```
### GradientSlider props
| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `value` (required) | `number` | | |
| `min` (required) | `number` | | |
| `max` (required) | `number` | | |
| `softMin` | `number` | `min` | The track's range (upstream's `soft_min`/`soft_max`); `min` and `max` by default. The hot text reaches `min`..`max`. |
| `softMax` | `number` | `max` | |
| `decimals` | `number` | `0` | Decimals a value is rounded to (0 by default). |
| `default` | `number` | | Restored by a double-click on the track; no double-click without it. |
| `stops` | `readonly string[]` | | CSS gradient stops for the track (Temperature, Tint, Hue; see `SLIDER_GRADIENTS`); a plain track when absent. |
| `label` (required) | `string` | | |
| `onChange` (required) | `(value: number, phase: DragPhase) => void` | | |
| `disabled` | `boolean` | | |
| `uiId` | `string` | | |
| `testId` | `string` | | |
## CurveEditor
Color. Tone curves: drag points, double-click to add, with a histogram under them.
```ts
import { CurveEditor } from '@kition/tessera-ui'
```
### Keyboard
| Keys | What they do |
| --- | --- |
| Arrows (on a point) | Move the point by 0.01 of the range; with Shift, by 0.1. |
| Delete, Backspace | Remove the point. |
### Example: An RGB curve
```tsx
import { useState } from 'react'
import { CurveEditor, RGB_IDENTITY, type CurvePoint } from '@kition/tessera-ui'
export default function Demo() {
const [points, setPoints] = useState([RGB_IDENTITY[0], [0.3, 0.22], [0.7, 0.8], RGB_IDENTITY[1]])
return (
setPoints(next)} />
)
}
```
### CurveEditor props
| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `points` (required) | `readonly CurvePoint[]` | | |
| `kind` (required) | `CurveKind`: `'rgb'`, `'hue'` | | 'rgb': a square with protected end points; 'hue': periodic in x, a hue strip below, 0.45 aspect. |
| `samples` | `readonly number[]` | | The curve's samples to draw (0..1 over x), given by the caller; `sampleCurve`'s by default. |
| `color` | `string` | | The line and points' CSS colour; the theme's text colour by default. |
| `onChange` (required) | `(points: CurvePoint[], phase: DragPhase) => void` | | |
| `label` | `string` | | The plot's accessible name ("Curve" by default). |
| `disabled` | `boolean` | | |
| `uiId` | `string` | | |
| `testId` | `string` | | |
## Histogram
Color. A smoothed histogram of an image’s tones.
```ts
import { Histogram } from '@kition/tessera-ui'
```
### Example: Luminance
```tsx
import { Histogram } from '@kition/tessera-ui'
const bins = Array.from({ length: 256 }, (_, i) => Math.exp(-((i - 110) ** 2) / 1800) * 900 + Math.exp(-((i - 200) ** 2) / 600) * 500 + 40)
export default function Demo() {
return (
)
}
```
### Histogram props
| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `bins` (required) | `ArrayLike` | | Bin counts (256 for an 8-bit channel), left (shadows) to right (highlights). |
| `color` | `string` | | A CSS color for the bars; the element's own (inherited) color by default. |
| `height` | `number` | `100` | |
| `smooth` | `boolean` | `false` | Draws the bins through upstream's five-bin display kernel (`smoothHistogram`), as its Camera Raw histogram does. |
| `scale` | `'interior' \| 'rank'` | `'interior'` | What the bars are scaled to: `interior`, the interior bins' maximum (the default, see `scaleFor`), or `rank`, upstream's Histogram panel and tone editors (`tone.rs` `draw_histogram`): the 251st-smallest of 256 bins, so a handful of spikes (pure white, pure black) are clipped instead of flattening the rest. Either with 10% headroom. |
| `testId` | `string` | `'histogram'` | |
## Timecode and transport
Media and time. A timecode field you can type or scrub, and the transport buttons of a player.
```ts
import { TimecodeField, TransportBar } from '@kition/tessera-ui'
```
### Keyboard
| Keys | What they do |
| --- | --- |
| Enter, F2 | Start typing a time into the timecode field. |
| Enter (while typing) | Commit the typed time. |
| Escape (while typing) | Stop typing and keep the time. |
### Example: A player
```tsx
import { useEffect, useState } from 'react'
import { TimecodeField, TransportBar } from '@kition/tessera-ui'
import { PauseIcon, PlayIcon, SkipBackIcon, SkipForwardIcon } from '@kition/tessera-icons/react'
export default function Demo() {
const [time, setTime] = useState(83.4)
const [playing, setPlaying] = useState(false)
useEffect(() => {
if (!playing) return
const timer = setInterval(() => setTime((value) => value + 0.04), 40)
return () => clearInterval(timer)
}, [playing])
return (
setTime(seconds)} accent />
setTime(0) },
{ id: 'play', label: playing ? 'Pause' : 'Play', icon: playing ? PauseIcon : PlayIcon, large: true, onPress: () => setPlaying(!playing) },
{ id: 'end', label: 'Go to end', icon: SkipForwardIcon, onPress: () => setTime(600) },
]}
/>
)
}
```
### TimecodeField props
A timecode readout: `value` (seconds) shown as timecode at `fps`. With `onChange` it scrubs
(one frame per 2 px, reported with `final: false`, then once with `final: true` on release) and
a click turns it into a text field (`parseTimecode`'s forms; Enter or blur commit with
`final: true`, Escape or text that is not a time reverts). Without `onChange` it only shows.
| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `value` (required) | `number` | | Seconds. |
| `fps` (required) | `number` | | |
| `dropFrame` | `boolean` | | |
| `onChange` | `(seconds: number, final: boolean) => void` | | Called while scrubbing (`final: false`) and once on release, Enter, or blur. |
| `accent` | `boolean` | | Upstream's accent-coloured playhead timecode. |
| `label` (required) | `string` | | |
| `uiId` | `string` | | |
| `testId` | `string` | | |
### TransportBar props
The buttons in `buttons`' order, centred. A button's `testId` is `-`; one whose
Kit UI id the configuration hides is left out, and one it disables is disabled.
| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `buttons` (required) | `readonly TransportButton[]` | | |
| `testId` | `string` | | |
## LevelMeter
Media and time. Audio levels with peak hold, drawn on a canvas.
```ts
import { LevelMeter } from '@kition/tessera-ui'
```
### Example: Stereo
```tsx
import { useEffect, useState } from 'react'
import { LevelMeter } from '@kition/tessera-ui'
export default function Demo() {
const [peaks, setPeaks] = useState([-12, -9])
useEffect(() => {
const timer = setInterval(() => setPeaks([-6 - Math.random() * 30, -4 - Math.random() * 30]), 100)
return () => clearInterval(timer)
}, [])
return (
)
}
```
### LevelMeter props
| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `peaks` (required) | `readonly number[]` | | Current peak per channel, linear 0..1+ (above 1 clips). |
| `floor` | `number` | `-60` | dB at the bottom of the scale (upstream meters: -60). |
| `holds` | `readonly number[]` | | Peak hold per channel, linear. |
| `orientation` | `'vertical' \| 'horizontal'` | `'vertical'` | |
| `labels` | `readonly string[]` | | Channel captions under the bars ("S" "S" in upstream's solo buttons row, or "L" "R"). |
| `ref` | `Ref` | | Draws live levels without a React render. |
| `testId` | `string` | | |
## KeyframeLane
Media and time. Keyframes on a time lane: select, drag, and their interpolation shapes.
```ts
import { KeyframeLane } from '@kition/tessera-ui'
```
### Keyboard
| Keys | What they do |
| --- | --- |
| Left, Right | Move the selected keyframes by one frame. |
| Escape | While dragging: put the keyframes back where they were. |
### Example: Three keyframes
```tsx
import { useState } from 'react'
import { KeyframeLane, type Keyframe } from '@kition/tessera-ui'
export default function Demo() {
const [keyframes, setKeyframes] = useState([
{ id: 'a', time: 1 },
{ id: 'b', time: 3.5, interpolation: 'bezier' },
{ id: 'c', time: 6, interpolation: 'hold' },
])
return (
setKeyframes((list) => list.map((key) => ({ ...key, selected: ids.includes(key.id) })))}
onMove={(ids, delta) => setKeyframes((list) => list.map((key) => (ids.includes(key.id) ? { ...key, time: Math.max(0, key.time + delta) } : key)))}
/>
)
}
```
### KeyframeLane props
| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `keyframes` (required) | `readonly Keyframe[]` | | |
| `start` (required) | `number` | | Visible range in seconds and the lane's width map times to x. |
| `end` (required) | `number` | | |
| `onSelect` (required) | `(ids: string[], add: boolean) => void` | | |
| `onMove` (required) | `(ids: string[], delta: number) => void` | | Called once on release with the dragged ids and the time delta. |
| `snap` | `(time: number) => number` | | |
| `fps` | `number` | `30` | Frames per second: an arrow key moves the selected keyframes one frame (30 by default). |
| `onMenu` | `(id: string, at: ContextMenuPoint) => void` | | A keyframe's menu was asked for at a screen point (upstream's interpolation menu). |
| `uiId` | `string` | | The Kit UI id every keyframe carries (`control:effects.keyframe`). |
| `testId` | `string` | | |
## Strings and elements
Integration. What components ask of the app around them: strings (StringsProvider) and element states (ElementsProvider). Without them, components draw in English with every element shown.
```ts
import { StringsProvider, ElementsProvider, Element } from '@kition/tessera-ui'
```
### Example: Strings and element states of your own
zh-CN strings; one group disabled, one hidden by the host.
```tsx
import { ElementsProvider, StringsProvider, createStrings, TESSERA_CATALOGS, PasswordInput, RibbonButton, RibbonGroup, type Elements } from '@kition/tessera-ui'
import { ClipboardPasteIcon } from '@kition/tessera-icons/react'
// An app gives Tessera its strings and element states.
const elements: Elements = {
state: (id) => ({ hidden: id === 'group:home/hidden', disabled: id === 'group:home/clipboard' }),
renderExtras: () => null,
drawn: () => undefined,
}
export default function Demo() {
return (
)
}
```
### StringsProvider props
| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `strings` (required) | `Strings` | | |
| `children` (required) | `ReactNode` | | |
### ElementsProvider props
| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `elements` (required) | `Elements` | | |
| `children` (required) | `ReactNode` | | |
### Element props
Content that belongs to one element: not drawn when hidden; its form controls disabled when
disabled. A `fieldset` disables every control inside it without each control knowing.
| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `id` (required) | `string` | | |
| `children` (required) | `ReactNode` | | |
## Tokens and skins
Every value a Tessera component draws with is a CSS variable. The 15 theme tokens are the Kit Harness Protocol's (Kit UI protocol, section 4): a host sends them in `ui/initialize` and `ui/theme`, and every kit that follows the protocol takes the skin. The component tokens are Tessera's own layout values. A skin (Paper, Graphite) is one value per theme token.
Package: `@kition/tessera-tokens` (CSS variables, TypeScript) and the Rust crate `tessera-tokens` · [On the site](https://tessera.kition.ai/tokens/) · [Theming guide](guides/theming.md)
```ts
import '@kition/tessera-tokens/tokens.css' // the Paper values on :root
import { SKINS, skinTheme } from '@kition/tessera-tokens'
const theme = skinTheme('graphite') // { colorScheme, tokens }, ready to send as ui/theme
```
### Theme tokens (sent by hosts)
| Token | Paper | Graphite | Meaning |
| --- | --- | --- | --- |
| `--kit-font-sans` | `Inter, system-ui, -apple-system, 'SF Pro Text', 'Segoe UI', Roboto, 'PingFang SC', 'Hiragino Sans GB', 'Noto Sans SC', sans-serif` | `Inter, system-ui, -apple-system, 'SF Pro Text', 'Segoe UI', Roboto, 'PingFang SC', 'Hiragino Sans GB', 'Noto Sans SC', sans-serif` | Interface font stack |
| `--kit-font-mono` | `ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace` | `ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace` | Monospace font stack |
| `--kit-surface` | `#ffffff` | `#1b1e23` | Editor background |
| `--kit-surface-muted` | `#fafaf9` | `#22262c` | Panels, rails, ribbon background |
| `--kit-canvas` | `#f6f5f4` | `#15171b` | Behind the document (pasteboard) |
| `--kit-text` | `#1a1a1a` | `#eceff1` | Primary text |
| `--kit-text-muted` | `#5d5b54` | `#a9b0ba` | Secondary text |
| `--kit-border` | `#e5e3df` | `#33373e` | Hairlines and control borders |
| `--kit-primary` | `#5645d4` | `#8b7ff0` | Primary action, selection, focus |
| `--kit-primary-text` | `#ffffff` | `#0b0a1f` | Text on primary |
| `--kit-primary-soft` | `rgba(86, 69, 212, 0.1)` | `rgba(86, 69, 212, 0.1)` | Selected and hovered backgrounds |
| `--kit-danger` | `#e03131` | `#e03131` | Destructive and error |
| `--kit-radius-control` | `8px` | `8px` | Buttons, inputs |
| `--kit-radius-card` | `12px` | `12px` | Cards, panels, dialogs |
| `--kit-shadow-card` | `0 1px 2px rgba(15, 15, 15, 0.06), 0 4px 16px rgba(15, 15, 15, 0.06)` | `0 1px 2px rgba(15, 15, 15, 0.06), 0 4px 16px rgba(15, 15, 15, 0.06)` | Card and popover shadow |
### Component tokens (Tessera's own)
| Token | Paper | Graphite | Meaning |
| --- | --- | --- | --- |
| `--kit-meter-bg` | `#e9e8e4` | `#e9e8e4` | Level meter track |
| `--kit-meter-ok` | `#2f9e44` | `#2f9e44` | Level meter green gradient, at the floor |
| `--kit-meter-ok-high` | `#40c057` | `#40c057` | Level meter green gradient, at -12 dB |
| `--kit-meter-warn` | `#e8a200` | `#e8a200` | Level meter yellow band |
| `--kit-meter-clip` | `#e03131` | `#e03131` | Level meter red band |
| `--kui-bar-height` | `36px` | `36px` | The top strip of both shells |
| `--kui-ribbon-row` | `24px` | `24px` | A ribbon row |
| `--kui-tab-underline` | `2px` | `2px` | The selected tab's underline |
| `--kui-tab-selected-weight` | `600` | `600` | The selected tab's font weight |
| `--kui-hover` | `color-mix(in srgb, var(--kit-text) 7%, transparent)` | `color-mix(in srgb, var(--kit-text) 7%, transparent)` | A control under the pointer: neutral, unlike a pressed toggle's --kit-primary-soft |
| `--kui-select-chevron` | `url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%235d5b54' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='m6 9 6 6 6-6'/%3E%3C/svg%3E")` | `url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%235d5b54' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='m6 9 6 6 6-6'/%3E%3C/svg%3E")` | Chevron-down for drop-downs, in --kit-text-muted (a data URI cannot read a variable) |
| `--kui-success` | `#18794e` | `#18794e` | Success: done, active, paid (tags, alerts, results); text on its own soft tint |
| `--kui-warning` | `#ad5700` | `#ad5700` | Warning: pending, needs attention (tags, alerts, results); text on its own soft tint |
| `--kui-chart-1` | `#5645d4` | `#5645d4` | Chart series 1: lines and their legend swatches, in order |
| `--kui-chart-2` | `#0d74ce` | `#0d74ce` | Chart series 2: lines and their legend swatches, in order |
| `--kui-chart-3` | `#18794e` | `#18794e` | Chart series 3: lines and their legend swatches, in order |
| `--kui-chart-4` | `#ad5700` | `#ad5700` | Chart series 4: lines and their legend swatches, in order |
| `--kui-chart-5` | `#ce2c31` | `#ce2c31` | Chart series 5: lines and their legend swatches, in order |
| `--kui-chart-6` | `#8145b5` | `#8145b5` | Chart series 6: lines and their legend swatches, in order |
### Rules
- A component never hard-codes a colour, radius, font, or shadow: it reads a token.
- An app that is not a kit sets the same variables in its own stylesheet.
- Every skin is held to WCAG AA contrast by the token package’s tests.
- The source of every value is `packages/tokens/tokens/*.tokens.json`.