Skip to content

@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>)

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)


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

  1. Props (error/success) — highest
  2. Rules (ValidationRule[] on input)
  3. 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)


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.