Appearance
@vibe-labs/design-components-spinners
Spinner and skeleton loading component tokens, styles, and TypeScript types for the Vibe Design System.
Installation
css
@import "@vibe-labs/design-components-spinners";ts
import { SpinnerSizes, SpinnerColors, SkeletonShapes } from "@vibe-labs/design-components-spinners/types";
import type { SpinnerStyleProps, SkeletonStyleProps } from "@vibe-labs/design-components-spinners/types";Contents
Tokens (spinners.css)
CSS custom properties defined in @layer vibe.tokens:
Spinner
| Token | Value | Description |
|---|---|---|
--spinner-xs | 0.75rem | Inline text size |
--spinner-sm | 1rem | Aligns with btn-sm icon area |
--spinner-md | 1.5rem | Aligns with btn-md icon area |
--spinner-lg | 2rem | Aligns with btn-lg icon area |
--spinner-xl | 3rem | Page/section loading |
--spinner-width | 2px | Border width (xs–md) |
--spinner-width-lg | 3px | Border width (lg) |
--spinner-width-xl | 4px | Border width (xl) |
--spinner-color | --color-accent | Spinning arc color |
--spinner-track-color | transparent | Track color |
--spinner-speed | 0.6s | Rotation duration |
Skeleton
| Token | Default | Description |
|---|---|---|
--skeleton-base | --surface-elevated | Placeholder background |
--skeleton-highlight | --surface-overlay | Shimmer highlight |
--skeleton-radius | --radius-sm | Default border radius |
--skeleton-speed | 1.5s | Shimmer animation duration |
Generated Styles (spinners.g.css)
Component classes generated into @layer vibe.components.
Spinner
- spinner — border-based rotating circle (border-top colored)
- Sizes:
xs·sm·md·lg·xl(default:md, border width scales atlg/xl) - Colors:
accent·white·muted·success·danger·current(inherits from parent)
Spinner Layout
- spinner-wrap — inline-flex container with gap;
stackedmodifier for vertical layout - spinner-overlay — absolute positioned scrim overlay with centered spinner
Skeleton
- skeleton — block placeholder with shimmer
::afteranimation - Shapes:
text(0.75em, full width) ·heading(1.5em, 60% width) ·circle(aspect-ratio 1) ·rect(16:9 aspect) - Sizes:
xs(1rem) ·sm(1.5rem) ·md(2.5rem) ·lg(4rem) ·xl(8rem) - static modifier — disables shimmer animation
Keyframes
vibe-shimmer— translateX sweep for skeleton highlight
TypeScript Types (types/)
ts
SpinnerSizes; // ["xs", "sm", "md", "lg", "xl"]
SpinnerColors; // ["accent", "white", "muted", "success", "danger", "current"]
SkeletonShapes; // ["text", "heading", "circle", "rect"]
SkeletonSizes; // ["xs", "sm", "md", "lg", "xl"]
interface SpinnerStyleProps {
size?: SpinnerSize;
color?: SpinnerColor;
}
interface SpinnerWrapStyleProps {
stacked?: boolean;
}
interface SpinnerOverlayStyleProps {}
interface SkeletonStyleProps {
shape?: SkeletonShape;
size?: SkeletonSize;
static?: boolean;
}Dist Structure
| File | Description |
|---|---|
index.css | Barrel — imports tokens + generated styles |
spinners.css | Token definitions |
spinners.g.css | Generated component styles + keyframes |
index.js / index.d.ts | TypeScript types + runtime constants |
Dependencies
Requires tokens from @vibe-labs/design (colors, surfaces, borders, spacing, transitions, overlays, z-index).
Build
bash
npm run buildUsage Guide
Import
css
@import "@vibe-labs/design-components-spinners";ts
import { SpinnerSizes, SpinnerColors, SkeletonShapes } from "@vibe-labs/design-components-spinners/types";
import type { SpinnerStyleProps, SkeletonStyleProps } from "@vibe-labs/design-components-spinners/types";Variants
Spinner
| Attribute / Flag | Values | Notes |
|---|---|---|
data-size | xs sm md lg xl | Default: md |
data-color | accent white muted success danger current | Default: accent |
data-stacked | boolean flag on spinner-wrap | Vertical layout |
Skeleton
| Attribute / Flag | Values | Notes |
|---|---|---|
data-shape | text heading circle rect | Controls size + aspect |
data-size | xs sm md lg xl | Explicit height override |
data-static | boolean flag | Disables shimmer animation |
Examples
Basic spinner
html
<div class="spinner" role="status" aria-label="Loading"></div>Spinner sizes
html
<div class="spinner" data-size="xs"></div>
<div class="spinner" data-size="sm"></div>
<div class="spinner" data-size="md"></div>
<div class="spinner" data-size="lg"></div>
<div class="spinner" data-size="xl"></div>Spinner colors
html
<div class="spinner" data-color="accent"></div>
<div class="spinner" data-color="success"></div>
<div class="spinner" data-color="danger"></div>
<div class="spinner" data-color="muted"></div>
<div style="background: #333; padding: 8px;">
<div class="spinner" data-color="white"></div>
</div>Spinner with label (horizontal)
html
<div class="spinner-wrap">
<div class="spinner" data-size="sm"></div>
<span>Loading…</span>
</div>Spinner with label (stacked/vertical)
html
<div class="spinner-wrap" data-stacked>
<div class="spinner" data-size="lg"></div>
<span>Please wait</span>
</div>Spinner overlay (over a section)
html
<div style="position: relative;">
<p>Content that is loading…</p>
<div class="spinner-overlay">
<div class="spinner" data-size="xl"></div>
</div>
</div>Skeleton placeholders
html
<!-- Text line -->
<div class="skeleton" data-shape="text"></div>
<!-- Heading -->
<div class="skeleton" data-shape="heading"></div>
<!-- Avatar circle -->
<div class="skeleton" data-shape="circle" data-size="md"></div>
<!-- Image rect (16:9) -->
<div class="skeleton" data-shape="rect"></div>
<!-- Static (no shimmer) -->
<div class="skeleton" data-shape="text" data-static></div>Card skeleton pattern
html
<div style="display: flex; gap: 12px; padding: 16px;">
<div class="skeleton" data-shape="circle" data-size="md"></div>
<div style="flex: 1; display: flex; flex-direction: column; gap: 8px;">
<div class="skeleton" data-shape="heading"></div>
<div class="skeleton" data-shape="text"></div>
<div class="skeleton" data-shape="text"></div>
</div>
</div>With Vue
vue
<template>
<template v-if="loading">
<div class="spinner-wrap" data-stacked>
<div class="spinner" :data-size="size" :data-color="color" role="status" :aria-label="label"></div>
<span v-if="label">{{ label }}</span>
</div>
</template>
<slot v-else />
</template>