Skip to content

@vibe-labs/design-vue-spinners

Vue 3 loading indicators for the Vibe Design System. Spinners, spinner overlays, and skeleton placeholders.

Installation

ts
import { VibeSpinner, VibeSpinnerOverlay, VibeSkeleton } from "@vibe-labs/design-vue-spinners";

Requires the CSS layer from @vibe-labs/design-components-spinners.

Philosophy: These components provide base primitives and shape presets for loading states. Most teams will compose their own skeleton screens and loading patterns from these building blocks rather than using them directly — treat them as the foundation, not the finished article.

Components

VibeSpinner

Animated loading spinner with optional label.

vue
<!-- Basic -->
<VibeSpinner />

<!-- Sizes -->
<VibeSpinner size="xs" />
<VibeSpinner size="sm" />
<VibeSpinner size="md" />
<VibeSpinner size="lg" />
<VibeSpinner size="xl" />

<!-- Colors -->
<VibeSpinner color="accent" />
<VibeSpinner color="muted" />
<VibeSpinner color="white" />

<!-- With label -->
<VibeSpinner label="Loading results…" />

<!-- Stacked label (below spinner) -->
<VibeSpinner label="Please wait" stacked />

<!-- Custom aria-label -->
<VibeSpinner aria-label="Fetching data" />

Props

PropTypeDefaultDescription
sizeSpinnerSize"md"xs · sm · md · lg · xl
colorSpinnerColor"accent"accent · muted · white
labelstringVisible label text
stackedbooleanfalseStack label below spinner
ariaLabelstring"Loading"Screen reader label (when no visible label)

VibeSpinnerOverlay

Full-container overlay with a centred spinner. Fades in/out with <Transition>.

vue
<!-- Basic (over a parent container) -->
<div style="position: relative">
  <DataTable :rows="rows" />
  <VibeSpinnerOverlay :visible="isLoading" />
</div>

<!-- With label -->
<VibeSpinnerOverlay :visible="isLoading" label="Saving changes…" />

<!-- Custom size/color -->
<VibeSpinnerOverlay :visible="isLoading" size="xl" color="white" />

<!-- Custom content via slot -->
<VibeSpinnerOverlay :visible="isLoading">
  <VibeProgressRing indeterminate />
  <p>Processing your request…</p>
</VibeSpinnerOverlay>

Props

PropTypeDefaultDescription
visiblebooleantrueShow the overlay
sizeSpinnerSize"lg"Spinner size
colorSpinnerColor"accent"Spinner color
labelstringLabel text
stackedbooleantrueStack label below spinner

The parent element should have position: relative for the overlay to fill it correctly.

VibeSkeleton

Placeholder skeleton with shimmer animation for loading states.

vue
<!-- Single text line -->
<VibeSkeleton />

<!-- Multiple text lines (last line 75% width for natural look) -->
<VibeSkeleton :lines="3" />

<!-- Shapes -->
<VibeSkeleton shape="text" />
<VibeSkeleton shape="circle" />
<VibeSkeleton shape="rect" />

<!-- Sizes -->
<VibeSkeleton shape="rect" size="sm" />
<VibeSkeleton shape="rect" size="md" />
<VibeSkeleton shape="rect" size="lg" />

<!-- Custom dimensions -->
<VibeSkeleton shape="rect" width="200px" height="120px" />

<!-- Static (no shimmer animation) -->
<VibeSkeleton static />

<!-- Card skeleton example -->
<div style="display: flex; gap: 1rem; align-items: center">
  <VibeSkeleton shape="circle" size="lg" />
  <div style="flex: 1">
    <VibeSkeleton width="60%" />
    <VibeSkeleton :lines="2" />
  </div>
</div>

Props

PropTypeDefaultDescription
shapeSkeletonShape"text"text · circle · rect
sizeSkeletonSizeHeight preset
staticbooleanfalseDisable shimmer animation
widthstringCustom CSS width
heightstringCustom CSS height
linesnumber1Number of text lines (text shape only)
ariaLabelstring"Loading"Accessible label

Accessibility

All components use role="status" for live region announcements. VibeSpinnerOverlay additionally uses aria-live="polite". Screen-reader-only text is included via .sr-only spans when no visible label is present.

Dependencies

PackagePurpose
@vibe-labs/design-components-spinnersCSS tokens + generated styles

Build

bash
npm run build

Built with Vite + vite-plugin-dts. Outputs ES module with TypeScript declarations.