Skip to content
Velocity X Docs
← All components

Loading Skeleton Set

Free · MIT

Shimmer skeleton placeholders for text lines, an avatar row, a card, and a table row, with the shimmer disabled under reduced motion.

Category
Core UI
Framework
astro
Version
1.0.0
Peer deps
none

Install

bash
velocityx add loading-skeleton

Free and MIT licensed — no account needed. The file lands under src/components/velocity/loading-skeleton/ by default (override with --dest), and it's yours to edit from the moment it's written.

Demo

View the live demo on the showcase site ↗

Source

Pulled live from the registry — this is the exact file velocityx add loading-skeleton writes into your project right now.

components/velocity/loading-skeleton/LoadingSkeleton.astro

astro
---
//    VELOCITY COMPONENT · theming
// 
//    Colours here are Tailwind `primary-*` / `accent-*`, which resolve through
//    the Velocity preset. Out of the box they are a neutral indigo — NOT the
//    Aidxn brand — so this drops into your project without dragging our look in.
// 
//    ⚡ NOT USING VELOCITY? Run this once, in your project root:
// 
//          velocityx theme
// 
//    It writes velocity-preset.cjs + velocity-theme.css. Add the preset to your
//    tailwind.config, import the css, then set your whole brand in ONE place:
// 
//          :root {
//            --vx-brand: custom;
//            --vx-primary-600: 220 38 38;   <- your action colour
//            --vx-primary-700: 185 28 28;
//          }
// 
//    ⚠️  Raw RGB channels, space separated — NOT hex. `#DC2626` here silently
//        breaks every /opacity variant in the library (bg-primary-600/20 etc).
// 
//    Prefer not to install anything? Find-and-replace `primary-` with your own
//    scale name. Nothing else in this file depends on Velocity.
/**
 * LoadingSkeleton — shimmer placeholder set: text lines, avatar, card, table
 * row. Pick a `shape`, or compose your own from `.v-skel` directly. Shimmer
 * is a CSS gradient sweep (no JS), disabled under prefers-reduced-motion in
 * favour of a static tint.
 *
 * Usage:
 *   <LoadingSkeleton shape="text" lines={3} />
 *   <LoadingSkeleton shape="card" />
 *   <LoadingSkeleton shape="avatar-line" />
 *   <LoadingSkeleton shape="table-row" columns={4} />
 */
export interface Props {
  shape?: 'text' | 'avatar-line' | 'card' | 'table-row';
  lines?: number;
  columns?: number;
}
const { shape = 'text', lines = 3, columns = 4 } = Astro.props;
---

<style>
  .v-skel {
    position: relative;
    overflow: hidden;
    background-color: theme('colors.secondary.150', theme('colors.secondary.100'));
  }
  :global(.dark) .v-skel { background-color: theme('colors.secondary.800'); }
  .v-skel::after {
    content: '';
    position: absolute;
    inset: 0;
    transform: translateX(-100%);
    background-image: linear-gradient(90deg, transparent, rgba(255,255,255,0.45), transparent);
    animation: v-skel-sweep 1.6s ease-in-out infinite;
  }
  :global(.dark) .v-skel::after {
    background-image: linear-gradient(90deg, transparent, rgba(255,255,255,0.08), transparent);
  }
  @media (prefers-reduced-motion: reduce) {
    .v-skel::after { animation: none; display: none; }
  }
  @keyframes v-skel-sweep {
    100% { transform: translateX(100%); }
  }
</style>

<div role="status" aria-label="Loading" class="animate-none">
  {shape === 'text' && (
    <div class="flex flex-col gap-2.5">
      {Array.from({ length: lines }).map((_, i) => (
        <div class:list={['v-skel h-3 rounded-full', i === lines - 1 ? 'w-2/3' : 'w-full']} />
      ))}
    </div>
  )}

  {shape === 'avatar-line' && (
    <div class="flex items-center gap-3">
      <div class="v-skel h-10 w-10 rounded-full shrink-0" />
      <div class="flex-1 flex flex-col gap-2">
        <div class="v-skel h-3 w-1/3 rounded-full" />
        <div class="v-skel h-2.5 w-1/2 rounded-full" />
      </div>
    </div>
  )}

  {shape === 'card' && (
    <div class="rounded-2xl border border-secondary-200 dark:border-secondary-800 p-4">
      <div class="v-skel aspect-video w-full rounded-xl mb-4" />
      <div class="v-skel h-3 w-3/4 rounded-full mb-2" />
      <div class="v-skel h-2.5 w-1/2 rounded-full" />
    </div>
  )}

  {shape === 'table-row' && (
    <div class="flex items-center gap-4 py-3">
      {Array.from({ length: columns }).map(() => (
        <div class="v-skel h-3 flex-1 rounded-full" />
      ))}
    </div>
  )}
  <span class="sr-only">Loading…</span>
</div>

Theming

Uses the shared primary-* / accent-* scale — see Theming if colours look unstyled after install.