Skip to content

@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

TokenValueDescription
--spinner-xs0.75remInline text size
--spinner-sm1remAligns with btn-sm icon area
--spinner-md1.5remAligns with btn-md icon area
--spinner-lg2remAligns with btn-lg icon area
--spinner-xl3remPage/section loading
--spinner-width2pxBorder width (xs–md)
--spinner-width-lg3pxBorder width (lg)
--spinner-width-xl4pxBorder width (xl)
--spinner-color--color-accentSpinning arc color
--spinner-track-colortransparentTrack color
--spinner-speed0.6sRotation duration

Skeleton

TokenDefaultDescription
--skeleton-base--surface-elevatedPlaceholder background
--skeleton-highlight--surface-overlayShimmer highlight
--skeleton-radius--radius-smDefault border radius
--skeleton-speed1.5sShimmer 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 at lg/xl)
  • Colors: accent · white · muted · success · danger · current (inherits from parent)

Spinner Layout

  • spinner-wrap — inline-flex container with gap; stacked modifier for vertical layout
  • spinner-overlay — absolute positioned scrim overlay with centered spinner

Skeleton

  • skeleton — block placeholder with shimmer ::after animation
  • 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

FileDescription
index.cssBarrel — imports tokens + generated styles
spinners.cssToken definitions
spinners.g.cssGenerated component styles + keyframes
index.js / index.d.tsTypeScript types + runtime constants

Dependencies

Requires tokens from @vibe-labs/design (colors, surfaces, borders, spacing, transitions, overlays, z-index).

Build

bash
npm run build

Usage 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 / FlagValuesNotes
data-sizexs sm md lg xlDefault: md
data-coloraccent white muted success danger currentDefault: accent
data-stackedboolean flag on spinner-wrapVertical layout

Skeleton

Attribute / FlagValuesNotes
data-shapetext heading circle rectControls size + aspect
data-sizexs sm md lg xlExplicit height override
data-staticboolean flagDisables 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>

Vibe