Component Library
Owlat includes a set of reusable Vue components in `packages/ui/components/ui/`.
Owlat includes a set of reusable Vue components in packages/ui/components/ui/.
Overview
All components follow these principles:
- Consistent styling - Uses design system tokens from main.css
- TypeScript - Full type support
- Accessible - ARIA attributes and keyboard navigation
- Auto-imported - Available without explicit imports (Nuxt feature)
Button (UiButton)
Versatile button component with variants and states.
Props
| Prop | Type | Default | Description |
|---|---|---|---|
variant | string | 'primary' | 'primary', 'secondary', 'ghost', 'danger', 'danger-ghost', 'danger-outline' |
size | string | 'md' | 'sm', 'md', 'lg' |
loading | boolean | false | Show spinner and disable |
disabled | boolean | false | Disable button |
fullWidth | boolean | false | Full width button |
Slots
default- Button texticonLeft- Icon before texticonRight- Icon after text
Examples
<!-- Primary button -->
<UiButton variant="primary">Save</UiButton>
<!-- With loading state -->
<UiButton variant="primary" :loading="isSubmitting">
Submit
</UiButton>
<!-- With icon -->
<UiButton variant="secondary">
<template #iconLeft>
<Plus class="w-4 h-4" />
</template>
Add Item
</UiButton>
<!-- Danger button -->
<UiButton variant="danger">Delete</UiButton>
Input (UiInput)
Text input with label, error state, and icons.
Props
| Prop | Type | Default | Description |
|---|---|---|---|
type | string | 'text' | 'text', 'email', 'password', 'number', 'date' |
modelValue | string/number | - | v-model value |
label | string | - | Label text |
placeholder | string | - | Placeholder text |
error | string | - | Error message |
helpText | string | - | Helper text (shown when no error) |
required | boolean | false | Show required asterisk |
disabled | boolean | false | Disable input |
size | string | 'md' | 'sm', 'md' |
Slots
iconLeft- Icon in left side of inputiconRight- Icon in right side of input
Examples
<!-- Basic input -->
<UiInput v-model="email" label="Email" type="email" placeholder="you@example.com" />
<!-- With error -->
<UiInput v-model="email" label="Email" :error="errors.email" required />
<!-- With search icon -->
<UiInput v-model="search" placeholder="Search...">
<template #iconLeft>
<Search class="w-4 h-4" />
</template>
</UiInput>
Select (UiSelect)
Native select with consistent styling.
Props
| Prop | Type | Default | Description |
|---|---|---|---|
options | array | [{ value, label }] | |
modelValue | string/number | - | v-model value |
label | string | - | Label text |
placeholder | string | - | Placeholder (first disabled option) |
error | string | - | Error message |
required | boolean | false | Show required asterisk |
disabled | boolean | false | Disable select |
size | string | 'md' | 'sm', 'md' |
Examples
<UiSelect
v-model="country"
label="Country"
placeholder="Select a country"
:options="[
{ value: 'us', label: 'United States' },
{ value: 'uk', label: 'United Kingdom' },
{ value: 'de', label: 'Germany' },
]"
/>
Textarea (UiTextarea)
Multi-line text input with character count.
Props
| Prop | Type | Default | Description |
|---|---|---|---|
modelValue | string | - | v-model value |
label | string | - | Label text |
placeholder | string | - | Placeholder text |
rows | number | 4 | Number of rows |
maxLength | number | - | Max characters (shows counter) |
resize | string | 'none' | 'none', 'vertical', 'both' |
error | string | - | Error message |
required | boolean | false | Show required asterisk |
Examples
<UiTextarea
v-model="description"
label="Description"
:rows="4"
:max-length="500"
resize="vertical"
/>
Checkbox (UiCheckbox)
Checkbox with label and optional description.
Props
| Prop | Type | Default | Description |
|---|---|---|---|
modelValue | boolean | false | v-model value |
label | string | - | Label text |
description | string | - | Helper text below label |
disabled | boolean | false | Disable checkbox |
Examples
<UiCheckbox v-model="acceptTerms" label="I accept the terms and conditions" />
<UiCheckbox
v-model="newsletter"
label="Subscribe to newsletter"
description="Receive weekly updates about new features"
/>
Radio Group (UiRadioGroup)
Radio button group with orientation options.
Props
| Prop | Type | Default | Description |
|---|---|---|---|
modelValue | string | - | v-model value |
options | array | [{ value, label, description? }] | |
name | string | required | Radio group name |
label | string | - | Group label (legend) |
orientation | string | 'vertical' | 'vertical', 'horizontal' |
disabled | boolean | false | Disable all options |
Examples
<UiRadioGroup
v-model="plan"
name="plan"
label="Select a plan"
:options="[
{ value: 'free', label: 'Free', description: 'Up to 1,000 contacts' },
{ value: 'pro', label: 'Pro', description: 'Up to 10,000 contacts' },
{ value: 'enterprise', label: 'Enterprise', description: 'Unlimited' },
]"
/>
Card (UiCard)
Container with variants and slots.
Props
| Prop | Type | Default | Description |
|---|---|---|---|
padding | string | 'md' | 'none', 'sm', 'md', 'lg' |
variant | string | 'default' | 'default', 'info', 'warning', 'error' |
hoverable | boolean | false | Add hover border effect |
clickable | boolean | false | Add cursor and emit click |
overflow | string | 'visible' | 'visible', 'hidden' |
Slots
default- Main contentheader- Header with bottom borderfooter- Footer with top border
Examples
<!-- Simple card -->
<UiCard padding="md">
Card content here
</UiCard>
<!-- Card with header and footer -->
<UiCard padding="none">
<template #header>
<div class="px-6 py-4">
<h2>Card Title</h2>
</div>
</template>
<div class="px-6 py-4">
Content here
</div>
<template #footer>
<div class="px-6 py-4 flex justify-end">
<UiButton>Action</UiButton>
</div>
</template>
</UiCard>
<!-- Info callout -->
<UiCard variant="info" padding="md">
<p>This is informational text.</p>
</UiCard>
Modal (UiModal)
Dialog overlay with backdrop.
Props
| Prop | Type | Default | Description |
|---|---|---|---|
open | boolean | false | v-model:open |
title | string | - | Modal title |
size | string | 'md' | 'sm', 'md', 'lg', 'xl' |
closable | boolean | true | Show close button |
persistent | boolean | false | Disable backdrop click close |
Slots
default- Modal bodyfooter- Modal footer (buttons)
Events
update:open- For v-model
Examples
<template>
<UiButton @click="showModal = true">Open Modal</UiButton>
<UiModal v-model:open="showModal" title="Confirm Action" size="sm">
<p>Are you sure you want to proceed?</p>
<template #footer>
<UiButton variant="ghost" @click="showModal = false"> Cancel </UiButton>
<UiButton variant="primary" @click="handleConfirm"> Confirm </UiButton>
</template>
</UiModal>
</template>
Modal Header (UiModalHeader)
Header component for modals with title, optional subtitle, icon, and close button.
Props
| Prop | Type | Default | Description |
|---|---|---|---|
title | string | required | Header title |
subtitle | string | - | Optional subtitle text |
icon | string | - | Lucide icon name |
iconClass | string | 'bg-bg-surface' | CSS classes for icon container |
iconTextClass | string | 'text-brand' | CSS classes for icon color |
closable | boolean | true | Show close button |
Slots
title-suffix- Content after title textactions- Actions area before close button
Events
close- Close button clicked
Modal Footer (UiModalFooter)
Footer component for modals with cancel and confirm buttons.
Props
| Prop | Type | Default | Description |
|---|---|---|---|
cancelText | string | 'Cancel' | Cancel button label |
confirmText | string | 'Confirm' | Confirm button label |
confirmVariant | string | 'primary' | Button variant: 'primary', 'secondary', 'ghost', 'danger', 'danger-ghost', etc. |
isLoading | boolean | false | Disables buttons and shows loading state |
isDisabled | boolean | false | Disables confirm button |
Slots
default- Custom footer content (overrides default buttons)
Events
cancel- Cancel button clickedconfirm- Confirm button clicked
Badge (UiBadge)
Status indicator with variants.
Props
| Prop | Type | Default | Description |
|---|---|---|---|
variant | string | 'default' | 'default', 'success', 'warning', 'error', 'neutral' |
size | string | 'sm' | 'sm', 'md' |
dot | boolean | false | Show dot instead of background |
Slots
default- Badge texticon- Icon before text
Examples
<UiBadge variant="success">Active</UiBadge>
<UiBadge variant="warning">Pending</UiBadge>
<UiBadge variant="error">Failed</UiBadge>
<!-- With dot -->
<UiBadge variant="success" dot>Online</UiBadge>
Tabs (UiTabs)
Tab navigation with keyboard support.
Props
| Prop | Type | Default | Description |
|---|---|---|---|
modelValue | string | - | v-model (active tab value) |
tabs | array | [{ value, label, count? }] |
Examples
<UiTabs
v-model="activeTab"
:tabs="[
{ value: 'all', label: 'All', count: 25 },
{ value: 'active', label: 'Active', count: 20 },
{ value: 'draft', label: 'Drafts', count: 5 },
]"
/>
Toggle (UiToggle)
Toggle switch using icons.
Props
| Prop | Type | Default | Description |
|---|---|---|---|
modelValue | boolean | false | v-model value |
label | string | - | Accessible label |
size | string | 'md' | 'sm', 'md' |
disabled | boolean | false | Disable toggle |
Examples
<UiToggle v-model="enabled" label="Enable feature" />
Empty State (UiEmptyState)
Placeholder for empty data.
Props
| Prop | Type | Default | Description |
|---|---|---|---|
icon | Component | - | Lucide icon component |
title | string | required | Main heading |
description | string | - | Secondary text |
Slots
action- CTA button
Examples
<UiEmptyState
:icon="Mail"
title="No emails yet"
description="Create your first email template to get started."
>
<template #action>
<UiButton variant="primary">
<template #iconLeft>
<Plus class="w-4 h-4" />
</template>
Create Template
</UiButton>
</template>
</UiEmptyState>
Error Boundary (UiErrorBoundary)
Catches errors from child components and displays a fallback UI with optional retry.
Props
| Prop | Type | Default | Description |
|---|---|---|---|
fallbackMessage | string | 'Something went wrong. Please try again.' | Custom fallback message |
showRetry | boolean | true | Whether to show a retry button |
Slots
default- Content to render (caught by error boundary)
Examples
<UiErrorBoundary fallback-message="Failed to load contacts.">
<ContactList />
</UiErrorBoundary>
Error Alert (UiErrorAlert)
Alert banner for displaying error, warning, info, or success messages.
Props
| Prop | Type | Default | Description |
|---|---|---|---|
message | string | required | Alert message text |
title | string | - | Custom title (defaults to variant-based title) |
variant | string | 'error' | 'error', 'warning', 'info', 'success' |
Examples
<UiErrorAlert message="Failed to save changes." />
<UiErrorAlert variant="warning" message="Your domain DNS is not yet verified." />
<UiErrorAlert variant="success" message="Campaign sent successfully!" />
Confirmation Dialog (UiConfirmationDialog)
Confirm/cancel modal with customizable title, description, and variant styles.
Props
| Prop | Type | Default | Description |
|---|---|---|---|
open | boolean | - | Controls dialog visibility |
title | string | 'Are you sure?' | Dialog title |
description | string | 'This action cannot be undone.' | Dialog description |
confirmText | string | 'Confirm' | Confirm button text |
cancelText | string | 'Cancel' | Cancel button text |
variant | string | 'default' | 'danger', 'warning', 'default' |
isLoading | boolean | false | Shows loading spinner on confirm button |
persistent | boolean | false | Prevents closing by clicking backdrop |
Slots
default- Custom content above action buttons
Events
update:open- Dialog visibility changedconfirm- Confirm button clickedcancel- Cancel button clicked
Examples
<UiConfirmationDialog
v-model:open="showDeleteDialog"
title="Delete Contact"
description="This will permanently remove the contact and all associated data."
variant="danger"
confirm-text="Delete"
:is-loading="isDeleting"
@confirm="handleDelete"
/>
Dropdown Menu (UiDropdownMenu)
Contextual menu that avoids overflow clipping.
Props
| Prop | Type | Default | Description |
|---|---|---|---|
open | boolean | false | v-model:open |
position | string | 'right' | 'left', 'right' |
Slots
trigger- Button that opens menudefault- Menu items
Related Components
UiDropdownMenuItem- Menu item with icon, disabled, danger propsUiDropdownDivider- Separator line
Examples
<UiDropdownMenu v-model:open="menuOpen">
<template #trigger>
<button class="btn btn-ghost p-2">
<MoreVertical class="w-4 h-4" />
</button>
</template>
<UiDropdownMenuItem :icon="Edit" @click="handleEdit">
Edit
</UiDropdownMenuItem>
<UiDropdownMenuItem :icon="Copy" @click="handleDuplicate">
Duplicate
</UiDropdownMenuItem>
<UiDropdownDivider />
<UiDropdownMenuItem :icon="Trash2" danger @click="handleDelete">
Delete
</UiDropdownMenuItem>
</UiDropdownMenu>
Dropdown Menu Item (UiDropdownMenuItem)
Menu item for use inside UiDropdownMenu.
Props
| Prop | Type | Default | Description |
|---|---|---|---|
icon | string | - | Lucide icon name |
disabled | boolean | false | Disables the menu item |
danger | boolean | false | Applies danger styling |
Slots
default- Menu item label
Events
click- Item clicked
Dropdown Divider (UiDropdownDivider)
Separator line for use inside UiDropdownMenu. No props.
Selectable List Item (UiSelectableListItem)
List item with radio or checkbox selection.
Props
| Prop | Type | Default | Description |
|---|---|---|---|
label | string | required | Item label text |
description | string | - | Optional description |
type | string | 'radio' | 'radio' or 'checkbox' |
modelValue | string / boolean / string | - | Current selected value |
value | string | - | Item value |
name | string | - | Input name attribute |
disabled | boolean | false | Disables the item |
Examples
<UiSelectableListItem
v-model="selectedPlan"
value="pro"
label="Pro Plan"
description="Up to 10,000 contacts"
name="plan"
/>
Segmented Control (UiSegmentedControl)
Tab-like segmented selector with animated indicator.
Props
| Prop | Type | Default | Description |
|---|---|---|---|
options | array | required | [{ value, label, disabled? }] |
modelValue | string | - | Currently selected value |
size | string | 'md' | 'sm', 'md' |
Slots
option-{value}- Custom rendering for a specific option (receivesoptionandactiveprops)
Examples
<UiSegmentedControl
v-model="view"
:options="[
{ value: 'grid', label: 'Grid' },
{ value: 'list', label: 'List' },
]"
/>
Stat Card (UiStatCard)
Statistics display card with value, label, and variant.
Props
| Prop | Type | Default | Description |
|---|---|---|---|
value | string / number | required | Statistic value |
label | string | required | Statistic label |
variant | string | 'default' | 'default', 'success', 'warning', 'error', 'secondary' |
Examples
<UiStatCard value="1,234" label="Total Contacts" />
<UiStatCard value="98.5%" label="Delivery Rate" variant="success" />
<UiStatCard value="12" label="Bounces" variant="error" />
Step Indicator (UiStepIndicator)
Progress indicator showing steps with completion status and connector lines.
Props
| Prop | Type | Default | Description |
|---|---|---|---|
steps | array | required | [{ id, label, number }] |
getStepStatus | function | required | (stepId: string) => 'completed' | 'current' | 'upcoming' |
isConnectorHighlighted | function | required | (index: number) => boolean — determines connector highlighting |
Examples
<UiStepIndicator
:steps="[
{ id: 'domain', label: 'Add Domain', number: 1 },
{ id: 'dns', label: 'Configure DNS', number: 2 },
{ id: 'verify', label: 'Verify', number: 3 },
]"
:get-step-status="getStatus"
:is-connector-highlighted="(i) => i < currentStep"
/>
Table (UiTable)
Data table with sorting and custom cells.
Props
| Prop | Type | Default | Description |
|---|---|---|---|
columns | array | Column definitions | |
data | array | Row data | |
loading | boolean | false | Show loading state |
emptyText | string | 'No data' | Empty state text |
hoverable | boolean | true | Row hover effect |
clickable | boolean | false | Clickable rows |
rowKey | string | '_id' | Key field for row identity |
Column Definition
interface Column {
key: string; // Field name in data
label: string; // Header text
sortable?: boolean; // Enable sorting
width?: string; // CSS width
align?: 'left' | 'center' | 'right';
format?: (value, row) => string; // Format function
}
Slots
header- Above table (filters, search)cell-{key}- Custom cell renderingempty- Custom empty state
Events
row-click- Row clicked (when clickable)sort- Column header clicked (when sortable)
Examples
<UiTable
:columns="[
{ key: 'name', label: 'Name', sortable: true },
{ key: 'email', label: 'Email' },
{ key: 'createdAt', label: 'Created', format: formatDate },
{ key: 'actions', label: '', width: '50px' },
]"
:data="contacts"
clickable
@row-click="handleRowClick"
>
<template #header>
<div class="px-4 py-3 flex items-center justify-between">
<h3>Contacts</h3>
<UiInput v-model="search" placeholder="Search..." size="sm" />
</div>
</template>
<template #cell-actions="{ row }">
<UiDropdownMenu>
<!-- ... -->
</UiDropdownMenu>
</template>
</UiTable>
Toast (useToast)
Global toast notifications via composable.
Usage
const { showToast } = useToast();
// Success (default)
showToast('Saved successfully');
// Error
showToast('Failed to save', 'error');
Toasts auto-dismiss after 3 seconds. Multiple toasts stack vertically.
Setup
UiToast component is included in app.vue for global availability:
<!-- app.vue -->
<template>
<NuxtLayout>
<NuxtPage />
</NuxtLayout>
<UiToast />
</template>