Appearance
@vibe-labs/design-vue — Contents Reference
Quick-reference for all Vue components, props, events, slots, and composables. For component authoring — not consumer docs.
All components require their corresponding @vibe-labs/design-components-* CSS package loaded.
Core (@vibe-labs/design-vue-core)
VibePanel
Content container with variant styles and padding.
Props: variant (default|outlined|raised|ghost, default: "default") · size (sm|md|lg|xl, default: "md") · flush (boolean) · as (string|Component, default: "div") · section (boolean → renders <section>)
VibeLink
Polymorphic link — auto-resolves <a>, <router-link>, or custom. External URLs auto-detected.
Props: href (string) · to (string|object → router-link) · as (string|Component) · variant (accent|muted|inherit, default: "accent") · external (boolean, auto) · active (boolean) · disabled (boolean → renders <span>)
VibeLabel
Styled label with required indicator.
Props: for (string) · size (sm|md|lg) · required (boolean) · disabled (boolean)
VibeDivider
Horizontal/vertical separator with optional label slot.
Props: orientation (horizontal|vertical) · ariaLabel (string) Slots: default — label text (creates labelled divider with lines on either side)
VibeHidden
Screen-reader-only content. Visually hidden, accessible to assistive tech.
Props: focusable (boolean — visible on focus, for skip links) · as (string, default: "span")
Avatars (@vibe-labs/design-vue-avatars)
VibeAvatar
Circular avatar with auto initials, deterministic color from name, LQIP image crossfade.
Props: name (string, required) · initials (string, auto) · src (string) · thumbSrc (string — LQIP) · alt (string, default: name) · size (AvatarSize, default: "md") · status (online|offline|busy|away) · bgColor (string, auto) · fgColor (string, auto) · loading (lazy|eager, default: "lazy") · ring (boolean) · clickable (boolean) Events: imageLoad (src) · imageError (src|undefined) Slots: default — custom overlays/badges
VibeAvatarGroup
Horizontal stack with overlap and "+N" overflow.
Props: max (number) · label (string — aria-label)
Utilities
getInitials(name, max?) — extract initials from name string
Deps: @vibe-labs/core (ColorFromString), @vibe-labs/design-vue-images (VibeImage)
Badges (@vibe-labs/design-vue-badges)
VibeBadge
Label badge with variant styling, auto-color, dismiss.
Props: label (string, required) · variant (BadgeVariant, default: "accent-subtle") · size (sm|md|lg) · pill (boolean, default: true) · square (boolean) · interactive (boolean) · dismissible (boolean) · autoColor (boolean — deterministic from label) · bgColor (string) · fgColor (string) Events: dismissSlots: left · right (hidden when dismissible) · dismiss-icon
VibeBadgeCount
Notification count — inline or anchored to child element.
Props: count (number, required) · max (number, default: 99 → shows "99+") · variant (BadgeVariant, default: "danger") · size (BadgeSize, default: "sm") · hideZero (boolean, default: true) · pulse (boolean) · bgColor (string) · fgColor (string) Slots: default — anchor element (count positions as superscript)
VibeDot
Minimal status dot.
Props: variant (BadgeVariant, default: "accent") · color (string) · pulse (boolean) · ariaLabel (string → sets role="img")
Deps: @vibe-labs/core (ColorFromString)
Buttons (@vibe-labs/design-vue-buttons)
VibeButton
Polymorphic button with variant styling, loading state, custom color cascade.
Props: variant (primary|secondary|ghost|danger|link, default: "primary") · size (sm|md|lg) · loading (boolean) · full (boolean) · icon (boolean — square aspect) · disabled (boolean) · color (string — sets --color-accent locally) · as (string|Component, default: "button") · type (button|submit|reset, default: "button") Events: click (MouseEvent — prevented when disabled/loading) Slots: default
Custom color cascade: color prop sets --color-accent on element. Variant tokens resolve to it automatically. Hover/active derived via color-mix().
VibeButtonGroup
Groups buttons with shared radius handling and overlap.
Props: role (group|toolbar, default: "group") · label (string — aria-label)
Cards (@vibe-labs/design-vue-cards)
VibeCard
Polymorphic card container.
Props: variant (default|elevated|outlined|ghost|gradient|gradient-subtle|gradient-elevated) · size (sm|md|lg) · interactive (boolean) · horizontal (boolean — row layout, media 40%) · seamless (boolean) · flush (boolean) · as (string|Component, default: "div") · href (string — auto-enables interactive + <a>)
VibeCardHeader / VibeCardBody / VibeCardFooter
Section components. Header = flex row + bottom border. Footer = flex row + top border + subtle bg.
VibeCardMedia
Overflow-hidden container for images/video. Child <img>/<video> auto-fill.
Props: position (top|bottom)
Dropdowns (@vibe-labs/design-vue-dropdowns)
VibeDropdown
Root compound component. Manages open state via provide/inject.
Props: open (boolean, v-model:open) · closeOnSelect (boolean, default: true) · closeOnClickOutside (boolean, default: true) · closeOnEscape (boolean, default: true) Events: update:open · select (unknown) Scoped slot: { isOpen, toggle, close }
VibeDropdownTrigger
Wraps trigger element. Handles click-to-toggle, aria-haspopup, aria-expanded, keyboard open.
VibeDropdownMenu
Popup panel. Auto-focuses first item on open. Full keyboard nav.
Props: align (bottom-start|bottom-end|bottom-center|top-start|top-end, default: "bottom-start") · full (boolean — match trigger width)
VibeDropdownItem
Polymorphic interactive item (<button> or <a> when href).
Props: value (unknown) · variant (danger) · href (string) · disabled (boolean) · selected (boolean) Events: select (unknown) Slots: default · icon · description · trail
VibeDropdownGroup
Grouped items with heading.
Props: label (string)
VibeDropdownDivider
Visual separator.
Composables
useClickOutside(targets, handler, enabled?) — composedPath() for shadow DOM compat useMenuKeyboard(menuEl, opts?) — ArrowUp/Down, Home/End, Escape, Tab, typeahead
Keyboard
ArrowDown/Enter/Space (trigger) → open. ArrowDown/Up → navigate. Home/End → first/last. Enter/Space (item) → select. Escape → close + focus trigger. Tab → close. Any letter → typeahead.
Forms (@vibe-labs/design-vue-forms)
Pure logic — no CSS. Renderless <VibeForm> + composables.
VibeForm
Props: initial-values (T) · validate ((values: T) → FormErrors<T>) · validate-on (change|blur|submit, default: "submit") · no-form-error (boolean) · id (string) · autocomplete (string) Events: submit ((values, helpers)) · resetScoped slot: FormReturn<T> Slots: form-error ({ error })
Scoped Slot API (FormReturn<T>)
Field binding: form.field('name') → spreads modelValue, onUpdate:modelValue, name, error, onBlur State: values (T) · errors (Partial<Record<keyof T, string>>) · formError (Ref<string|null>) · touched · dirty (computed) · submitting (Ref) · submitted (Ref) · submitCount (Ref) · valid (computed) Methods: field(name) · handleSubmit(e?) · setErrors(errors) · setFieldError(name, msg) · clearFieldError(name) · clearErrors() · setFormError(msg) · reset(nextValues?) · touchField(name) · isFieldDirty(name)
Rules
required(msg?) · minLength(n, msg?) · maxLength(n, msg?) · pattern(regex, msg?) · email(msg?) · url(msg?) · min(n, msg?) · max(n, msg?) · matches(getFn, msg?) · custom(fn, trigger?)createFormValidator({ field: rules[] }) — builds form-level validate fn from per-field rules
Composables
useVibeForm(config) — core composable, use without <VibeForm> wrapper useFormField(options) — auto-register with parent VibeForm via provide/inject
Utilities
toFormData(values) — converts to FormData for multipart dirtyValues(values, initial) — returns only changed fields for PATCH
Graphs (@vibe-labs/design-vue-graphs)
VibeGraphBar
Multi-series bar chart with grouped/stacked, horizontal/vertical, annotations.
Props: series (GraphSeries[], required) · horizontal (boolean) · stacked (boolean) · barRadius (number) + GraphBaseProps + GraphAxisProps Slots: tooltip ({ datum, series })
VibeGraphLine
Multi-series line chart with curves, area fills, point markers, draw animation.
Props: series (GraphSeries[], required) · curve (linear|monotoneX|step|natural|cardinal, default: "monotoneX") · points (boolean, default: true) · area (boolean) + GraphBaseProps + GraphAxisProps
VibeGraphPie
Pie/donut chart.
Props: data (GraphSlice[], required) · innerRadius (number, 0=pie, 0.6=donut) · padAngle (number) · cornerRadius (number) · labels (boolean) + GraphBaseProps
VibeGraphSparkline
Minimal inline chart.
Props: data (number[], required) · width (string, default: "100%") · height (string, default: "2rem") · color (string, default: "1") · area (boolean) · curve (linear|monotoneX|natural) · showExtremes (boolean) · animate (boolean)
GraphBaseProps (shared)
width · height · ratio (16x9|4x3|1x1|21x9) · legend (boolean) · legendPosition (top|bottom|left|right) · tooltip (boolean) · animate (boolean) · animationDuration (number, default: 600) · emptyText (string) · ariaLabel (string)
GraphAxisProps (bar + line)
xLabel · yLabel · grid (boolean) · yMin · yMax · yFormat · xFormat · annotations (GraphAnnotation[])
Shared Components
VibeGraphLegend — interactive legend with toggle VibeGraphTooltip — positioned tooltip, auto-clamps within container VibeGraphEmpty — no-data placeholder
Events (all charts)
hover (GraphHoverEvent) · leave · click (GraphClickEvent)
Composables
useSeriesColors(items) — resolves series color props to CSS colors resolveSeriesColor(color, fallbackIndex) — standalone resolution useGraphDimensions(containerEl, margins?) — ResizeObserver reactive dimensions useTooltip(containerEl) — tooltip state + smart clamping
Data Types
ts
interface GraphDatum {
label: string;
value: number;
meta?: Record<string, unknown>;
}
interface GraphSlice extends GraphDatum {
color?: string;
}
interface GraphSeries {
name: string;
data: GraphDatum[];
color?: string;
}
interface GraphAnnotation {
value: number;
label?: string;
variant?: GraphAnnotationVariant;
}Series Color Resolution
"1"–"8" → var(--graph-series-N) · "positive"|"negative"|"warning"|"neutral" → status tokens · "#hex" → passthrough · undefined → auto-assigned by index
Deps: d3-scale, d3-shape
Images (@vibe-labs/design-vue-images)
VibeImage
Standard image with LQIP blur-up crossfade, lazy loading, auto aspect-ratio.
Props: src (string) · thumbSrc (string — LQIP) · alt (string) · srcset · sizes · width · height · fit (ImageFit, default: "cover") · position (ImagePosition) · loading (lazy|eager, default: "lazy") · fetchpriority · decoding (default: "async") · crossfade (boolean, default: true) · duration (number, default: 300ms) · radius (string — token name or CSS) · aspect (string, auto) · draggable (boolean) · as (string|Component) Events: load (src, dimensions) · error (src|undefined) · statusChange (status, src|null) Slots: placeholder · error
LQIP flow: thumbSrc loads immediately → blur(20px) + scale(1.1) → src loads in background → crossfade to sharp.
VibeBgImage
Background image container with LQIP, overlay, slot for content.
Props: src · thumbSrc · fit (BgFit, default: "cover") · position (BgPosition, default: "center") · loading · fixed (boolean) · overlay (string|boolean — true = 40% black) · width · height · radius · aspect · ariaLabel (→ role="img")
VibePicture
Responsive <picture> with multiple sources, LQIP, crossfade.
Props: All VibeImage props except as/srcset/sizes, plus sources (PictureSource[])
Composables
useImageLoad(src) — reactive loader with generation tracking. Returns { status, currentSrc, dimensions }useLazyLoad(target, options?) — IntersectionObserver visibility detection useImageQueue() / imageQueue — global singleton queue, configurable concurrency (default: 3) useRadius(radius) — maps token shorthand to CSS ("lg" → "var(--radius-lg)")
Inputs (@vibe-labs/design-vue-inputs)
VibeInput
Text input with validation, debounce, password reveal, search clear, character count.
Props: modelValue (string) · type (text|email|password|url|tel|search) · size (sm|md|lg) · label · placeholder · helperText · error (string) · success (string) · disabled · readonly · required · maxlength · showCount (boolean) · debounce (number, ms) · rules (ValidationRule[]) · validateOn (change|blur|submit, default: "blur") · pattern · autocomplete · id (auto) · nameEvents: update:modelValue · validate · focus · blur · clearSlots: leading · trailing · clearIcon · revealIcon ({ visible }) Exposed: focus() · blur() · select() · validate() · clearValidation() · el
VibeInputNumber
Numeric input with +/- controls, precision, clamping, hold-to-repeat.
Props: modelValue (number|null) · min · max · step (default: 1) · precision · controls (boolean, default: true) · nullable (boolean, default: true) + InputBaseProps Keyboard: ArrowUp/Down increment/decrement. Hold-to-repeat on buttons (120ms).
VibeInputGroup
Groups related inputs in fieldset with shared label/error/layout.
Props: label · error · helperText · direction (vertical|horizontal) · size · seamless (boolean — collapse borders)
VibeTextArea
Multi-line textarea with auto-resize, character count, validation.
Props: modelValue (string) · rows (default: 3) · autoResize (boolean) · maxRows · maxlength · showCount · resizable (boolean, default: true) + InputBaseProps
VibeCheckbox
Single or group-compatible checkbox with indeterminate.
Props: modelValue (boolean|string[]) · value (string — group mode) · label · size · indeterminate (boolean) · disabled · error (boolean) · nameSlots: icon ({ checked })
VibeCheckboxGroup
Props: label · horizontal (boolean) · error (string)
VibeRadio
Single radio, standalone or within VibeRadioGroup.
Props: modelValue (string) · value (string, required) · label · size · disabled · error (boolean) · name
VibeRadioGroup
Shared name/value context via provide/inject.
Props: modelValue (string) · label · name · horizontal (boolean) · error (string)
Composables
useInputField(options) — ID gen, validation state, ARIA bindings, CSS data-attrs useAutoResize(textareaEl, value, options) — auto-resize textarea to content
Validation Priority
- Props (
error/success) — highest - Rules (ValidationRule[] on input)
- Form-level (from VibeForm via
form.field())
Lists (@vibe-labs/design-vue-lists)
VibeList
Root container with selection, drag-and-drop reorder, and keyboard navigation. Provides shared context to children via inject.
Props: variant (ListVariant, default: "default") · size (ListSize, default: "md") · divided (boolean) · striped (boolean) · hoverable (boolean) · interactive (boolean) · flush (boolean) · reorderable (boolean) · selectionMode (ListSelectionMode, default: "none") · selected (string|string[], v-model:selected) · ariaLabel (string) · tag (string, default: "div") Events: update:selected (string|string[]) · item-click (string, MouseEvent) · reorder (ListReorderEvent)
ARIA: Auto-switches role="list" (no selection) ↔ role="listbox" (selection enabled), with aria-multiselectable for multi mode.
VibeListItem
Interactive row with structured slots, selection state, and drag support.
Props: value (string — required for selection/reorder) · label (string) · description (string) · selected (boolean — override) · disabled (boolean) Events: click (MouseEvent) Slots: default (label) · icon · description · trail · handle
Selection: Injects parent context — role="option" + aria-selected when selection enabled. Explicit selected prop overrides parent state. Drag: When parent is reorderable and item has value, renders handle and sets draggable="true". CSS states via data-dragging / data-drag-over.
VibeListGroup
Section wrapper with labelled heading.
Props: label (string) Slots: default · label
VibeListDivider
Visual separator. role="separator".
VibeListEmpty
No-data placeholder. role="status".
Props: message (string) Slots: default
Keyboard
ArrowDown/Up → focus next/prev item (wraps). Home/End → first/last. Space/Enter → toggle selection. Active when interactive or selectionMode !== "none".
Reorder
HTML5 Drag and Drop. Items with value become draggable when reorderable is set. Default 6-dot grip handle (overridable via handle slot). Emits reorder({ from, to }) with source/target values — consumer handles array splice.
Selection Modes
"none" — no selection. "single" — one at a time, re-click deselects. "multi" — toggle independently.
Deps: @vibe-labs/design-components-lists (CSS tokens + generated styles)
Menus (@vibe-labs/design-vue-menus)
VibeMenu
Root menu container. Provides context (horizontal/compact/depth) via inject.
Props: nav (boolean → wraps in <nav>) · ariaLabel · horizontal (boolean) · compact (boolean — icons only) · tag (ul|ol) Exposed: focusFirst() · focusLast() · el
VibeMenuItem
Polymorphic item — <button>, <a>, <router-link>, or custom.
Props: label · active (boolean → aria-current="page") · disabled · href · to (string|object) · as · target · trail (string) · chevron (boolean) · expanded (boolean → aria-expanded) Slots: default · icon · trail · chevron · submenu
VibeMenuGroup
Labelled group with optional collapse.
Props: label · collapsible (boolean) · expanded (boolean, v-model:expanded) · defaultExpanded (boolean, default: true) · id (auto)
VibeMenuFlyout
Nested submenu on hover or click.
Props: align (right|bottom, default: "right") · open (boolean, v-model:open) · trigger (hover|click, default: "hover") · label · disabledTrigger slot: { open, toggle, attrs }
VibeMenuDivider
Visual separator.
Composable
useMenuKeyboard(options) — arrow-key nav, horizontal-aware, wrap-around, Home/End
Nesting
Auto-tracks depth via provide/inject. Each VibeMenu increments counter (0=root, 1=first nested).
Modals (@vibe-labs/design-vue-modals)
VibeModal
Root modal with backdrop, animation lifecycle, focus trap, scroll lock.
Props: open (boolean, v-model:open) · size (sm|md|lg|xl|full) · variant (danger) · seamless · centered · drawer (boolean — right-side slide-in) · closeOnBackdrop (boolean, default: true) · closeOnEscape (boolean, default: true) · trapFocus (boolean, default: true) · lockScroll (boolean, default: true) · teleport (string|false, default: "body") · ariaLabel · initialFocus (string|"none") · noClose (boolean) Events: update:open · opened · closedScoped slot: { close }
VibeModalHeader
Props: srOnly (boolean — visually hidden) Slots: default (title) · description · actions ({ close }) · close-icon
VibeModalBody
Scrollable content area.
VibeModalFooter
Props: split (boolean — space-between layout)
Composables
useModal(initialOpen?) — returns { isOpen, open, close, toggle }useFocusTrap(options) — Tab/Shift+Tab wrapping, restores previous focus useScrollLock(enabled) — reference-counted body scroll lock, scrollbar width compensation
Animation
Four-state machine: hidden → entering → open → exiting → hidden. 500ms fallback timeout for prefers-reduced-motion.
Nested Modals
Scroll lock reference-counted. Escape uses stopPropagation — only topmost closes.
Pagination (@vibe-labs/design-vue-pagination)
VibePagination
Windowed page range with ellipsis.
Props: page (number, v-model:page, 1-based) · totalPages (number, required) · siblings (number, default: 7 — max visible buttons) · showPrevNext (boolean, default: true) · prevLabel (string, default: "←") · nextLabel (string, default: "→") · disabled (boolean) · ariaLabel (string, default: "Pagination") Events: update:pageSlots: prev · next · page ({ page, active }) · ellipsis
Composable
usePagination(options) — headless windowing algorithm. Returns { items, hasPrev, hasNext, hasPages }PageItem = { type: "page", page } | { type: "ellipsis", key }
Progress (@vibe-labs/design-vue-progress)
VibeProgressBar
Linear progress bar with label positions, shimmer, stripes, auto-color.
Props: value (number) · min · max (default: 100) · size (xs|sm|md|lg) · variant (accent|success|warning|danger|auto) · color (string) · autoThresholds (AutoColorThreshold[]) · label (none|above|inside|right) · labelText · formatLabel ((v, min, max, pct) → string) · title · indeterminate (boolean) · striped (boolean) · animated (boolean) · shimmer (boolean) · chromeless (boolean) · ariaLabel
VibeProgressRing
Radial/circular progress with centre label.
Props: value · min · max · size (sm|md|lg|xl) · variant · color · autoThresholds · strokeWidth · showLabel (boolean, default: true) · labelText · formatLabel · indeterminate · ariaLabelScoped slot: { value, percentage, formatted }
Auto-Color Thresholds (default)
0–33% → danger · 34–66% → warning · 67–100% → success. Override via autoThresholds.
Composables
useProgress(options) — percentage, clamping, auto-color resolution useProgressTimer(options) — ETA estimation from progress samples. Returns { eta, etaFormatted, rate, reliable, completionTime }
Responsive (@vibe-labs/design-vue-responsive)
VibeResponsiveContainer
Establishes a container query context. Wrap any area that needs responsive children.
Props: tag (string, default: "div") · type (ContainerType, default: "inline") · name (ContainerName) Slots: default
VibeResponsiveGrid
12-column grid with per-breakpoint column counts via container queries. Must be inside a VibeResponsiveContainer.
Props: tag (string, default: "div") · cols (GridColumn, default: 1) · colsXs (GridColumn) · colsSm (GridColumn) · colsMd (GridColumn) · colsLg (GridColumn) · colsXl (GridColumn) · cols2xl (GridColumn) Slots: default
VibeResponsiveStack
Flex column that switches to row at a container-query breakpoint. Must be inside a VibeResponsiveContainer.
Props: tag (string, default: "div") · breakpoint (Breakpoint, default: "md") Slots: default
Composables
useContainerBreakpoint(target) — observes container element inline size via ResizeObserver. Returns { current, above, below, width }. Use for conditional rendering / prop switching that CSS container queries can't handle.
Types
VibeResponsiveContainerProps · VibeResponsiveGridProps · VibeResponsiveStackProps · ContainerBreakpointReturn Re-exports: Breakpoint · Breakpoints · ContainerType · ContainerTypes · ContainerName · ContainerNames · GridColumn · GridColumns
Deps: @vibe-labs/design-components-responsive (CSS + types)
Sliders (@vibe-labs/design-vue-sliders)
VibeSlider
Single value or dual-thumb range slider with pointer/touch/keyboard interaction, step snapping, marks, tooltips, vertical orientation, and multiple thumb shapes.
Props: modelValue (number|[number, number]) · min (default: 0) · max (default: 100) · step (default: 1) · range (boolean) · size (sm|md|lg) · orientation (horizontal|vertical) · thumbShape (circle|bar|pill|none) · color (accent|success|warning|danger) · tooltip (none|hover|always) · marks (boolean|number[]|SliderMark[]) · formatValue ((v: number) → string) · disabled · ariaLabel · ariaLabelEnd · id (auto) · nameEvents: update:modelValue (number|[number, number]) · dragStart (SliderDragEvent) · dragEnd (SliderDragEvent) Exposed: focus() · blur() · value (computed)
Keyboard
ArrowRight/Up → +step · ArrowLeft/Down → −step · PageUp → +10×step or 10% of range · PageDown → −10×step · Home → min · End → max
Range Mode
Dual thumbs with range prop. modelValue becomes [number, number]. Thumbs can't cross — clamped at each other's position. Track fill uses --slider-fill-start/--slider-fill-end. ARIA aria-valuemin/aria-valuemax constrained per thumb.
Marks
true → auto-generate from step (capped at 50) · number[] → marks at specific values · SliderMark[] → value + optional label. Marks below current value get data-active.
CSS Custom Properties
Container sets --slider-fill (single) or --slider-fill-start/--slider-fill-end (range) as percentages. Each thumb/tooltip positions via --slider-thumb-position inline style.
Composable
useSliderDrag(options) — pointer capture drag, click-to-jump on track, keyboard navigation, step snapping with float precision, range thumb clamping, vertical orientation support.
Options: trackEl · min · max · step · orientation · disabled · values (Ref<[number, number]>) · range · onUpdate · onDragStart? · onDragEnd?Returns: isDragging · activeThumb · thumbPercents · onThumbDown · onTrackDown · onKeyDown · valueToPercent · percentToValue
Data Types
ts
interface SliderMark {
value: number;
label?: string;
}
type SliderMarkProp = boolean | number[] | SliderMark[];
type SliderFormatFn = (value: number) => string;
interface SliderDragEvent {
thumb: 0 | 1;
value: number;
}Spinners (@vibe-labs/design-vue-spinners)
VibeSpinner
Animated loading spinner.
Props: size (xs|sm|md|lg|xl) · color (accent|muted|white) · label (string) · stacked (boolean — label below) · ariaLabel (default: "Loading")
VibeSpinnerOverlay
Full-container overlay with centred spinner. Parent needs position: relative.
Props: visible (boolean) · size (default: "lg") · color · label · stacked (default: true) Slots: default — custom content
VibeSkeleton
Placeholder with shimmer.
Props: shape (text|circle|rect) · size (SkeletonSize) · static (boolean — no shimmer) · width · height · lines (number, text shape only) · ariaLabel (default: "Loading")
All use role="status" for live region announcements.
Tabs (@vibe-labs/design-vue-tabs)
VibeTabs
Root container, provides shared context.
Props: modelValue (string, v-model) · defaultValue (string) · mountStrategy (lazy|eager|unmount, default: "lazy") Events: update:modelValue · change
VibeTabList
Tab bar with keyboard nav.
Props: variant (pill) · scrollable (boolean) · full (boolean — stretch) · ariaLabel
VibeTab
Individual tab button. Auto-registers on mount.
Props: name (string, required) · disabled · badge (string) Slots: default · icon · badge
VibeTabPanels
Thin wrapper container for panels.
VibeTabPanel
Content panel, mount/show based on active tab + strategy.
Props: name (string, required — must match a VibeTab)
Mount Strategies
lazy (default) — mount on first activation, stay mounted eager — all mounted immediately, v-show toggle unmount — mount on activation, unmount on deactivation
Keyboard
ArrowRight/Down → next (skips disabled, wraps). ArrowLeft/Up → previous. Home/End → first/last enabled.
Tables (@vibe-labs/design-vue-tables)
VibeTable
Data-driven table rendering from column definitions and items array. Flex-based layout, ARIA table roles, slot-based cell customisation.
Props: columns (TableColumn<T>[], required) · items (T[], required) · rowKey (TableRowKey<T>, default: "id"/index) · sort (TableSortState|null, v-model:sort) · variant (default|outlined|elevated|ghost, default: "default") · size (sm|md|lg, default: "md") · striped (boolean) · hoverable (boolean, default: true) · interactive (boolean) · fixedHeader (boolean) · full (boolean, default: true) · emptyText (string, default: "No data") · rowSelected ((item: T) → boolean) · rowDisabled ((item: T) → boolean) · ariaLabel (string) Events: update:sort (TableSortState|null) · row-click (item, index) Slots: toolbar · header-{key} ({ column }) · cell-{key} ({ item, value, column, index }) · empty · footer ({ itemCount })
Composables
useTableSort(options) — client-side sorting with three-state toggle (asc → desc → clear). Default comparator: numeric comparison, locale-aware string collation, nulls sort last.
Options: items (MaybeRefOrGetter<T[]>) · initial (TableSortState|null) · comparator ((a, b, key, direction) → number) Returns: sort (Ref) · sortedItems (ComputedRef) · toggleSort (key → void) · clearSort (→ void)
useTableSelection(options) — single/multi row selection state management.
Options: rowKey (TableRowKey<T>, required) · mode (single|multi, default: "multi") · initial (Iterable<string|number>) Returns: selected (Ref<Set>) · isSelected (item → boolean) · toggle (item → void) · select (item → void) · deselect (item → void) · selectAll (items → void) · clear (→ void) · selectedCount (ComputedRef)
Column Definition
ts
interface TableColumn<T> {
key: keyof T & string;
label: string;
width?: string;
minWidth?: string;
align?: "left" | "center" | "right";
sortable?: boolean;
truncate?: boolean;
mono?: boolean;
hidden?: boolean;
cellClass?: string;
headerClass?: string;
}Design Decisions
Table doesn't sort data — it tracks sort state via v-model:sort and renders indicators. Use useTableSort for client-side, or listen to @update:sort for server-side. Selection is external via predicates (rowSelected/rowDisabled), paired with useTableSelection composable.
Toasts (@vibe-labs/design-vue-toasts)
Setup
Add <VibeToastContainer /> once at app root. Fire from anywhere — singleton reactive store, no provide/inject.
Imperative API
ts
toast("message");
toast({ title, description, variant, duration, closable, actions, render });
toast.success("msg") / toast.warning() / toast.danger() / toast.info();
toast.promise(promise, { loading, success, error });
toast.dismiss(id) / toast.dismissAll();
toast.update(id, options);Duration 0 = persistent. Default 5000ms.
VibeToastContainer
Props: position (top-right|top-left|top-center|bottom-right|bottom-left|bottom-center, default: "top-right") · limit (number, default: 5) · mode (stack|queue, default: "stack") · pauseOnHover (boolean, default: true) · defaultDuration (number, default: 5000) Slots: icon ({ variant })
Actions
ts
toast({
title: "Archived",
actions: [
{
label: "Undo",
onClick: (dismiss) => {
undo();
dismiss();
},
},
],
});Composable
useToast() — returns { toast, toasts, dismiss, dismissAll, update }
Toggles (@vibe-labs/design-vue-toggles)
VibeToggle
Toggle switch with role="switch" + aria-checked.
Props: modelValue (boolean, v-model) · size (sm|md|lg) · disabled · label · labelPosition (left|right, default: "right") · name · value (string, default: "on") · required · id (auto) · ariaLabel · ariaLabelledby · ariaDescribedbyEvents: update:modelValue · change
Hidden <input type="checkbox"> for native form semantics + keyboard activation (Space).
Uploads (@vibe-labs/design-vue-uploads)
VibeUploadZone
Drop zone with drag-and-drop, click-to-browse, directory support.
Props: accept (string) · multiple (boolean, default: true) · disabled · compact (boolean) · directory (boolean) Events: files (FileList) Slots: default ({ isDragOver, open }) · icon · label · hintExposed: openFilePicker()
VibeUploadList
Scrollable container.
Props: unbounded (boolean — no max-height)
VibeUploadItem
Individual file row with thumb, progress, status, actions.
Props: file (UploadFile, required) · showPreview (boolean, default: true) · removable (boolean, default: true) Events: remove (id) · retry (id) · cancel (id) Slots: thumb ({ file }) · file-icon ({ file }) · status ({ status })
VibeUploadIndicator
Compact toolbar/header status button.
Props: progress (number, required) · active (number, required) · total (number, required) · complete (boolean) · error (boolean) · label (string, auto)
useUploadQueue(options)
Core upload engine — queue, concurrency, validation, retry, cancellation.
Options: upload (async (file, { onProgress, signal }) → meta, required) · concurrency (default: 3) · autoUpload (boolean, default: true) · maxRetries (default: 0) · previews (boolean, default: true) · validation ({ accept, maxFileSize, maxFiles }) · onValidationError · onFileComplete · onFileError · onQueueComplete
Returns: files · addFiles · removeFile · retryFile · cancelFile · startAll · cancelAll · clearCompleted · clearAll · progress (computed 0–100) · isUploading · isComplete · hasErrors · stats
File Lifecycle
queued → pending → uploading → complete | error → (retry) → queued | cancelled (via abort)
Validation
Runs before queue entry: count (maxFiles) → type (MIME wildcards, exact, extensions) → size (maxFileSize). Invalid files reported via onValidationError, never enter queue.