# `@proj-airi/ui` Component Reference > **Auto-maintained**: When adding or updating components in `packages/ui`, update this document accordingly. Standardized primitives built on [reka-ui](https://reka-ui.com/). Minimal business logic — use these instead of raw DOM elements. Source: `packages/ui/src/components/` --- ## Animations ### AnimatedContent Behavior-only primitive that animates height, opacity, vertical offset, and an inner content blur from an external `data-state="open|closed"` lifecycle. The lifecycle owner must keep the primitive mounted until the closing animation finishes. It can compose with Reka UI content primitives through `as-child`, but does not depend on a specific menu, popover, or presence implementation. Visual styling remains caller-owned. | Prop | Type | Default | Description | |------|------|---------|-------------| | `as` | `PrimitiveProps['as']?` | `'div'` | Element or component rendered as the animated outer container | **Slots**: `default` ### TransitionBidirectional Bidirectional Vue `` wrapper with customizable CSS classes. | Prop | Type | Default | Description | |------|------|---------|-------------| | `fromClass` | `string?` | — | CSS class for initial state | | `activeClass` | `string?` | — | CSS class during transition | | `toClass` | `string?` | — | CSS class for final state | **Slots**: `default` ### TransitionHorizontal Horizontal slide/fade transition (0.5s hardcoded). **Props**: None | **Slots**: `default` ### TransitionVertical Smooth vertical expand/collapse with height animation and opacity control. | Prop | Type | Default | Description | |------|------|---------|-------------| | `duration` | `number?` | `250` | Animation duration (ms) | | `easingEnter` | `string?` | `'ease-in-out'` | Enter easing function | | `easingLeave` | `string?` | `'ease-in-out'` | Leave easing function | | `opacityClosed` | `number?` | `0` | Opacity when closed | | `opacityOpened` | `number?` | `1` | Opacity when opened | **Slots**: `default` --- ## Layout ### Collapsible Expandable/collapsible container with trigger button and vertical animation. | Prop | Type | Default | Description | |------|------|---------|-------------| | `default` | `boolean?` | — | Initial visibility | | `label` | `string?` | — | Trigger button label | **v-model**: `visible: boolean` **Slots**: `trigger({ visible, setVisible })`, `default({ visible, setVisible })` ### Screen Responsive screen component that calculates canvas dimensions based on breakpoints. **Props**: None | **Slots**: `default({ width, height })` ### ScrollableArea Reka UI scroll area with shared light-mode and dark-mode scrollbar styles. The component forwards HTML attributes to the scroll-area root. | Prop | Type | Default | Description | |------|------|---------|-------------| | `contentAsChild` | `boolean?` | `false` | Render the viewport content wrapper through the single default slot child | | `orientation` | `'vertical' \| 'horizontal' \| 'both'` | `'vertical'` | Scrollbar orientations to render | | `type` | `ScrollAreaRootProps['type']?` | `'auto'` | Reka UI scrollbar visibility behavior | | `viewportClass` | `string \| string[]?` | — | Classes for the Reka UI viewport | **Slots**: `default` **Exposed**: `viewport` (the native scroll owner. Reka UI hides its native scrollbar and renders the configured custom track.) ### Skeleton Loading placeholder with animation. | Prop | Type | Default | Description | |------|------|---------|-------------| | `animation` | `'pulse' \| 'wave' \| 'none'` | `'pulse'` | Animation style | **Slots**: `default` ### Truncatable Line-clamped content container that expands and collapses when the overflowing content area is clicked. | Prop | Type | Default | Description | |------|------|---------|-------------| | `lineClamp` | `number?` | `3` | Maximum visible lines while collapsed | **Slots**: `default` --- ## Misc ### BottomDrawer Mobile modal surface built on Vaul Vue. It owns the drag handle, overlay, focus boundary, scroll region, and bottom safe area. Dragging starts only on the handle, so action buttons and scrolling do not dismiss it. | Prop | Type | Default | Description | |------|------|---------|-------------| | `title` | `string` | required | Visible and accessible title | | `minimumHeight` | `'content' \| 'half'` | `'content'` | Uses content height or at least half of the viewport height | Dismiss with the handle, overlay, or Escape. There is no close button. **v-model**: `boolean`, defaults to `false`. **Slots**: `trigger` (one button), `default` (drawer content). **Emits**: `afterClose()` after the dismissal animation; `closeAutoFocus(event)` to prevent focus restoration when another modal opens. Use for mobile action menus and settings panels. Desktop dialogs and panels that need snap points use their own surface. ### Avatar Shared user-avatar primitive built on Reka UI. It retries when `src` changes and renders the fallback when the URL is missing, loading, or fails. | Prop | Type | Default | Description | |------|------|---------|-------------| | `src` | `string \| null \| undefined` | `null` | Avatar image URL | | `alt` | `string \| null \| undefined` | `null` | Accessible image/fallback description; omit for decorative avatars | | `referrerPolicy` | `ImgHTMLAttributes['referrerpolicy']?` | — | Image referrer policy | | `crossOrigin` | `ImgHTMLAttributes['crossorigin']?` | — | Image cross-origin mode | **Slots**: `fallback` (optional override for the built-in user icon) ### BasicButton Behavioral foundation for custom buttons. It owns disabled/loading behavior, press animation, sizing, icon/label rendering, and block layout, but supplies no surface, border, shape, or color styling. | Prop | Type | Default | Description | |------|------|---------|-------------| | `icon` | `string?` | — | UnoCSS/Iconify icon class | | `label` | `string?` | — | Button text | | `disabled` | `boolean?` | `false` | Disabled state | | `loading` | `boolean?` | `false` | Loading state | | `size` | `'sm' \| 'md' \| 'lg' \| 'unset'` | `'md'` | Preset size; `unset` leaves sizing to the caller | | `block` | `boolean?` | `false` | Full width | **Slots**: `default` (fallback when no `label`) `size="unset"` omits the preset padding and text-size classes in `BasicButton`, `Button`, and `GhostButton`. Callers must supply any required sizing, including fixed dimensions for circular buttons. ### Button Solid general-purpose action. It has no border and shows offset outlines on hover and keyboard focus. Choose a Wind3 color family independently from its primary or secondary visual emphasis. Includes all `BasicButton` props, plus: | Prop | Type | Default | Description | |------|------|---------|-------------| | `shape` | `'rect' \| 'rounded' \| 'circle' \| 'parallelogram'` | `'rect'` | Rectangular, pill-like, circular, or rounded angled geometry | | `color` | `'neutral' \| 'primary' \| 'cyan' \| 'blue' \| 'green' \| 'lime' \| 'amber' \| 'red' \| 'orange' \| 'purple' \| 'pink'` | `'neutral'` | Wind3 color family; `primary` follows the configured theme hue | | `variant` | `'primary' \| 'secondary'` | `'secondary'` | Solid high-emphasis or subtle low-emphasis treatment | | `outline` | `boolean?` | `true` | Show the offset outline on hover and keyboard focus | ### GhostButton Low-emphasis contextual action with a transparent default surface and compact spacing. Hover, pressed, and selected states use a subtle primary surface and text treatment without an outline. The offset outline is reserved for keyboard focus. Includes all `BasicButton` props. | Prop | Type | Default | Description | |------|------|---------|-------------| | `active` | `boolean?` | `false` | Persist the subtle selected surface for toggle controls | ### IconButton Minimal icon-only action for controls such as favorite, like, copy, or retry. It directly uses `BasicButton`, removes its padding, and does not add a background, outline, shape, or forced square dimensions. Supply an accessible `aria-label`. Props: `icon`, `disabled`, and `loading`. ### OverlayButton Translucent, backdrop-blurred action without an outline, intended for floating controls such as the Tamagotchi Controls Island. Includes all `BasicButton` props. ### Callout Alert/callout box with themed accent bar. | Prop | Type | Default | Description | |------|------|---------|-------------| | `theme` | `'primary' \| 'violet' \| 'lime' \| 'orange'` | `'primary'` | Color theme | | `label` | `string?` | — | Title | **Slots**: `label`, `default` ### ContainerError Error display with copy/feedback buttons and scrollable stack trace. | Prop | Type | Default | Description | |------|------|---------|-------------| | `error` | `unknown?` | — | Error object | | `message` | `string?` | — | Custom message | | `stack` | `string?` | — | Stack trace | | `includeStack` | `boolean?` | `true` | Show stack | | `showCopyButton` | `boolean?` | `true` | Show copy button | | `showFeedbackButton` | `boolean?` | `true` | Show feedback button | | `copyButtonLabel` | `string?` | `'Copy'` | Copy button text | | `copiedButtonLabel` | `string?` | `'Copied'` | Copied state text | | `feedbackButtonLabel` | `string?` | `'Feedback'` | Feedback button text | | `heightPreset` | `'sm' \| 'md' \| 'lg' \| 'xl' \| 'auto'` | `'md'` | Container height | **Emits**: `copy(content: string)`, `feedback()` ### ErrorBoundary Catches synchronous render/setup errors in descendants via `onErrorCaptured` and renders a fallback (built-in `ContainerError` + retry button) instead of letting the error propagate. Use to wrap `` or any subtree where partial failure should not blank the host layout. Async/unhandled rejections are NOT captured — use `app.config.errorHandler` for those. | Prop | Type | Default | Description | |------|------|---------|-------------| | `title` | `string?` | — | Optional title shown above error details | | `retryable` | `boolean?` | `true` | Show built-in retry button | | `retryLabel` | `string?` | `'Try again'` | Retry button label | **Slots**: `default`, `fallback({ error, info, retry })` **Emits**: `error(err, instance, info)`, `retry()` **Exposed**: `retry()`, `hasError()` ### DoubleCheckButton Two-stage confirmation button — click once to reveal confirm/cancel. | Prop | Type | Default | Description | |------|------|---------|-------------| | `color` | `ButtonColor` | `'red'` | Confirm button color family | | `variant` | `ButtonVariant` | `'primary'` | Confirm button visual emphasis | | `size` | `'sm' \| 'md' \| 'lg'` | `'md'` | Size | | `block` | `boolean?` | `false` | Full width | | `disabled` | `boolean?` | `false` | Disabled | | `loading` | `boolean?` | `false` | Loading | **Emits**: `confirm()`, `cancel()` **Slots**: `default` (initial text), `confirm` (confirm text), `cancel` (cancel text) ### Progress Linear progress bar with animated shine. | Prop | Type | Default | Description | |------|------|---------|-------------| | `progress` | `number` | *(required)* | Percentage 0–100 | | `barClass` | `string?` | — | Custom bar color class | --- ## Form — Input ### Input Basic text/number input. | Prop | Type | Default | Description | |------|------|---------|-------------| | `type` | `InputType?` | — | HTML input type | | `variant` | `'primary' \| 'secondary' \| 'primary-dimmed'` | `'primary'` | Visual variant | | `size` | `'sm' \| 'md' \| 'lg'` | `'md'` | Size | | `disabled` | `boolean?` | — | Disable editing and focus | **v-model**: `modelValue: string | number` ### BasicInputFile Low-level file input with drag-drop support. | Prop | Type | Default | Description | |------|------|---------|-------------| | `accept` | `string?` | — | Accepted MIME types | | `multiple` | `boolean?` | — | Allow multiple files | | `isDraggingClasses` | `string \| string[]?` | — | Classes when dragging | | `isNotDraggingClasses` | `string \| string[]?` | — | Classes when not dragging | **v-model**: `modelValue: File[]` **Slots**: `default({ isDragging, firstFile, files })` ### InputFile File input with preview and drag-drop UI. | Prop | Type | Default | Description | |------|------|---------|-------------| | `accept` | `string?` | — | Accepted file types | | `multiple` | `boolean?` | — | Allow multiple | | `placeholder` | `string?` | `'Choose file'` | Placeholder text | **v-model**: `modelValue: File[] | undefined` ### InputFileCard Styled file upload card with drag-and-drop zone. | Prop | Type | Default | Description | |------|------|---------|-------------| | `accept` | `string?` | — | Accepted file types | | `multiple` | `boolean?` | — | Allow multiple | **v-model**: inherits from `BasicInputFile` **Slots**: `default` (custom upload UI) ### InputKeyValue Two-column input for key-value pairs. | Prop | Type | Default | Description | |------|------|---------|-------------| | `name` | `string?` | — | Input name attribute | | `keyPlaceholder` | `string?` | — | Key placeholder | | `valuePlaceholder` | `string?` | — | Value placeholder | **v-model**: `propertyKey: string`, `propertyValue: string` --- ## Form — Textarea ### BasicTextarea Auto-resizing textarea with submit and paste-file events. The native row count defaults to one, so typing does not introduce a second row. Content grows when it wraps. Native `rows` attributes can override this minimum. When set, `defaultHeight` also provides the baseline for content measurement, so flex layouts do not stretch the empty measurement box. | Prop | Type | Default | Description | |------|------|---------|-------------| | `defaultHeight` | `string?` | — | Initial height when empty | | `submitOnEnter` | `boolean?` | `true` | Submit on Enter (Shift+Enter for newline) | **v-model**: `input: string` **Emits**: `submit(message: string)`, `pasteFile(files: File[])` ### Textarea Styled textarea wrapping `BasicTextarea`. **v-model**: `modelValue: string` --- ## Form — Checkbox / Radio ### Checkbox Toggle switch using reka-ui `SwitchRoot`. | Prop | Type | Default | Description | |------|------|---------|-------------| | `disabled` | `boolean?` | — | Disabled state | **v-model**: `modelValue: boolean` ### Radio Single radio button for radio groups. | Prop | Type | Default | Description | |------|------|---------|-------------| | `id` | `string` | *(required)* | Unique ID | | `name` | `string` | *(required)* | Radio group name | | `value` | `string` | *(required)* | Option value | | `title` | `string` | *(required)* | Display label | | `deprecated` | `boolean?` | `false` | Deprecation indicator | **v-model**: `modelValue: string` --- ## Form — Range ### Range Horizontal slider with progress visualization. | Prop | Type | Default | Description | |------|------|---------|-------------| | `min` | `number?` | `0` | Minimum value | | `max` | `number?` | `100` | Maximum value | | `step` | `number?` | `1` | Step increment | | `disabled` | `boolean?` | `false` | Disabled | | `thumbColor` | `string?` | `'#9090906e'` | Thumb color | | `trackColor` | `string?` | `'gray'` | Track color | | `trackValueColor` | `string?` | `'red'` | Filled track color | **v-model**: `modelValue: number` ### ColorHueRange HSL hue selector (0–360) with rainbow gradient. | Prop | Type | Default | Description | |------|------|---------|-------------| | `disabled` | `boolean?` | — | Disabled | **v-model**: `modelValue: number` ### RoundRange Rounded-style slider. | Prop | Type | Default | Description | |------|------|---------|-------------| | `min` | `number?` | `0` | Minimum | | `max` | `number?` | `100` | Maximum | | `step` | `number?` | `1` | Step | | `disabled` | `boolean?` | `false` | Disabled | **v-model**: `modelValue: number` --- ## Form — Select ### Select Dropdown select using reka-ui with grouping and custom rendering. | Prop | Type | Default | Description | |------|------|---------|-------------| | `options` | `SelectOptionItem[] \| SelectOptionGroupItem[]` | *(required)* | Options | | `placeholder` | `string?` | `'Select an option'` | Placeholder | | `disabled` | `boolean?` | `false` | Disabled | | `by` | `string \| ((a: T, b: T) => boolean)?` | — | Custom comparison | | `contentMinWidth` | `string \| number?` | `160` | Dropdown min width | | `contentWidth` | `string \| number?` | — | Dropdown width | | `contentSide` | `'top' \| 'right' \| 'bottom' \| 'left'` | `'bottom'` | Preferred dropdown side before collision handling | | `contentAlign` | `'start' \| 'center' \| 'end'` | `'start'` | Preferred dropdown alignment before collision handling | | `shape` | `'rounded' \| 'default'` | `'default'` | Shape | | `variant` | `'blurry' \| 'default'` | `'default'` | Variant | **v-model**: `modelValue: T` **Slots**: `value({ option, value, placeholder })`, `option({ option })` ### SelectOption Individual option item within `Select`. | Prop | Type | Default | Description | |------|------|---------|-------------| | `option` | `SelectOptionItem` | *(required)* | Option data | **Slots**: `default` --- ## Form — Combobox ### Combobox Searchable dropdown/autocomplete using reka-ui with grouping. | Prop | Type | Default | Description | |------|------|---------|-------------| | `options` | `ComboboxOptionItem[] \| ComboboxOptionGroupItem[]` | *(required)* | Options | | `placeholder` | `string?` | — | Placeholder | | `disabled` | `boolean?` | `false` | Disabled | | `openOnClick` | `boolean?` | `true` | Auto-open dropdown on click | | `contentMinWidth` | `string \| number?` | — | Dropdown min width | | `contentWidth` | `string \| number?` | — | Dropdown width | **v-model**: `modelValue: T` **Slots**: `option({ option })`, `empty` ### ComboboxSelect Simplified Combobox wrapper for string/number options. | Prop | Type | Default | Description | |------|------|---------|-------------| | `options` | `{ label, value, description?, disabled?, icon? }[]?` | — | Options | | `placeholder` | `string?` | — | Placeholder | | `disabled` | `boolean?` | `false` | Disabled | | `openOnClick` | `boolean?` | `true` | Auto-open dropdown on click | | `title` | `string?` | — | Title | | `layout` | `'horizontal' \| 'vertical'?` | — | Layout direction | | `contentMinWidth` | `string \| number?` | — | Dropdown min width | | `contentWidth` | `string \| number?` | — | Dropdown width | **v-model**: `modelValue: string | number` **Slots**: `option({ option })`, `empty` ### ComboboxOption Option item within combobox (uses provide/inject). | Prop | Type | Default | Description | |------|------|---------|-------------| | `value` | `string \| number` | *(required)* | Value | | `label` | `string?` | — | Display text | | `active` | `boolean?` | — | Active state | **Slots**: `default` --- ## Form — SelectTab ### SelectTab Tab-like selection using radio buttons with animated indicator. | Prop | Type | Default | Description | |------|------|---------|-------------| | `options` | `SelectTabOption[]` | *(required)* | Tab options `{ label, value, description?, icon? }` | | `disabled` | `boolean?` | `false` | Disabled | | `readonly` | `boolean?` | `false` | Read-only | | `size` | `'sm' \| 'md'` | `'md'` | Size | **v-model**: `modelValue: T` --- ## Form — Field (Labeled wrappers) All Field components wrap a base input with `label`, `description`, and consistent layout. Common slots: `label`, `description`. ### FieldButton Displays field information on the left and a compact action button on the right. | Prop | Type | Default | Description | |------|------|---------|-------------| | `label` | `string` | *(required)* | Field label | | `description` | `string?` | — | Helper text | | `buttonLabel` | `string` | *(required)* | Action button label | | `buttonIcon` | `string?` | — | UnoCSS/Iconify class for the action button | | `disabled` | `boolean?` | — | Prevents the action | | `loading` | `boolean?` | — | Shows a spinner and prevents the action | **Slots**: `label`, `description` **Emits**: `click(event: MouseEvent)` ### FieldInput | Prop | Type | Default | Description | |------|------|---------|-------------| | `label` | `string?` | — | Label | | `description` | `string?` | — | Helper text | | `placeholder` | `string?` | — | Placeholder | | `required` | `boolean?` | — | Required indicator | | `disabled` | `boolean?` | — | Disable editing and focus | | `type` | `InputType?` | — | Input type | | `autocomplete` | `string?` | — | Native autocomplete hint | | `inputClass` | `string?` | — | Custom input class | | `singleLine` | `boolean?` | `true` | `true` = input, `false` = textarea | **v-model**: `modelValue: T` ### FieldCheckbox | Prop | Type | Default | Description | |------|------|---------|-------------| | `label` | `string?` | — | Label | | `description` | `string?` | — | Helper text | | `disabled` | `boolean?` | — | Disabled | | `placement` | `'left' \| 'right'` | `'right'` | Switch position | **v-model**: `modelValue: boolean` ### FieldTextArea | Prop | Type | Default | Description | |------|------|---------|-------------| | `label` | `string?` | — | Label | | `description` | `string?` | — | Helper text | | `placeholder` | `string?` | — | Placeholder | | `required` | `boolean?` | — | Required indicator | | `textareaClass` | `string?` | — | Custom textarea class | | `rows` | `number?` | `6` | Rows | **v-model**: `modelValue: string` ### FieldRange | Prop | Type | Default | Description | |------|------|---------|-------------| | `min` | `number?` | — | Min | | `max` | `number?` | — | Max | | `step` | `number?` | — | Step | | `label` | `string?` | — | Label | | `description` | `string?` | — | Helper text | | `formatValue` | `(value: number) => string?` | — | Value formatter | | `as` | `'label' \| 'div'` | `'label'` | Wrapper element | | `defaultValue` | `number?` | — | When set, shows a reset button next to the label that restores this value. Use with `as="div"`. | **v-model**: `modelValue: number` ### FieldInputFile | Prop | Type | Default | Description | |------|------|---------|-------------| | `label` | `string?` | — | Label | | `description` | `string?` | — | Helper text | | `accept` | `string?` | — | Accepted types | | `multiple` | `boolean?` | — | Multiple | | `placeholder` | `string?` | — | Placeholder | **v-model**: `modelValue: File[] | undefined` ### FieldSelect | Prop | Type | Default | Description | |------|------|---------|-------------| | `label` | `string` | *(required)* | Label | | `description` | `string?` | — | Helper text | | `options` | `SelectOptionItem[] \| SelectOptionGroupItem[]?` | — | Options | | `placeholder` | `string?` | — | Placeholder | | `disabled` | `boolean?` | — | Disabled | | `layout` | `'horizontal' \| 'vertical'` | `'horizontal'` | Layout | | `by` | `string \| ((a, b) => boolean)?` | — | Comparison | | `shape` | `'rounded' \| 'default'?` | — | Shape | | `variant` | `'blurry' \| 'default'?` | — | Variant | **v-model**: `modelValue: T` **Slots**: `label`, `description`, `value`, `option` ### FieldCombobox | Prop | Type | Default | Description | |------|------|---------|-------------| | `label` | `string` | *(required)* | Label | | `description` | `string?` | — | Helper text | | `options` | `{ label, value, description?, disabled?, icon? }[]?` | — | Options | | `placeholder` | `string?` | — | Placeholder | | `disabled` | `boolean?` | `false` | Disabled | | `openOnClick` | `boolean?` | `true` | Auto-open dropdown on click | | `layout` | `'horizontal' \| 'vertical'` | `'horizontal'` | Layout | **v-model**: `modelValue: string` **Slots**: `label`, `description`, `option`, `empty` ### FieldKeyValues Dynamic key-value pair list with add/remove. | Prop | Type | Default | Description | |------|------|---------|-------------| | `label` | `string?` | — | Label | | `description` | `string?` | — | Helper text | | `name` | `string?` | — | Input name | | `keyPlaceholder` | `string?` | — | Key placeholder | | `valuePlaceholder` | `string?` | — | Value placeholder | | `required` | `boolean?` | — | Required | | `inputClass` | `string?` | — | Custom input class | **v-model**: `keyValues: { key: string, value: string }[]` **Emits**: `remove(index: number)`, `add(key: string, value: string)` ### FieldValues Dynamic string list with add/remove. | Prop | Type | Default | Description | |------|------|---------|-------------| | `label` | `string?` | — | Label | | `description` | `string?` | — | Helper text | | `name` | `string?` | — | Input name | | `valuePlaceholder` | `string?` | — | Value placeholder | | `required` | `boolean?` | — | Required | | `inputClass` | `string?` | — | Custom input class | **v-model**: `items: string[]` **Emits**: `remove(index: number)`, `add()` --- ## Composables Exported from `packages/ui/src/composables/`: - **`useDeferredMount()`** — Defers component mounting (useful for heavy components). - **`useTheme()`** — Theme management composable.