AvesraAvesrabeta

Skeleton

Skeleton is a placeholder to show a loading state and the expected shape of a component.

Import

import { AvSkeletonComponent } from '@avesra/angular';

Usage

import { Component } from '@angular/core';
import { AvSkeletonComponent } from '@avesra/angular';

@Component({
  selector: 'app-skeleton-basic-demo',
  imports: [AvSkeletonComponent],
  host: { class: 'block' },
  template: `<div class="w-[250px] space-y-5 rounded-lg bg-transparent p-4 shadow-surface">
      <div av-skeleton class="h-32 rounded-lg"></div>
      <div class="space-y-3">
        <div av-skeleton class="h-3 w-3/5 rounded-lg"></div>
        <div av-skeleton class="h-3 w-4/5 rounded-lg"></div>
        <div av-skeleton class="h-3 w-2/5 rounded-lg"></div>
      </div>
    </div>`,
})
export class SkeletonBasicDemo {}

Anatomy

Use the div[av-skeleton] attribute selector and size or shape the placeholder with Tailwind utility classes. Optional animation-type overrides the theme default (--av-skeleton-animation).

<div av-skeleton class="h-20 w-32 rounded-full"></div>

<!-- Animation types -->
<div av-skeleton animation-type="shimmer" class="h-10 rounded-lg"></div>
<div av-skeleton animation-type="pulse" class="h-10 rounded-lg"></div>
<div av-skeleton animation-type="none" class="h-10 rounded-lg"></div>

<!-- Synchronized shimmer over a group -->
<div class="av-skeleton--shimmer relative grid grid-cols-3 gap-4 overflow-hidden rounded-xl">
  <div av-skeleton animation-type="none" class="h-24 rounded-xl"></div>
  <div av-skeleton animation-type="none" class="h-24 rounded-xl"></div>
  <div av-skeleton animation-type="none" class="h-24 rounded-xl"></div>
</div>

Text Content

import { Component } from '@angular/core';
import { AvSkeletonComponent } from '@avesra/angular';

@Component({
  selector: 'app-skeleton-text-content-demo',
  imports: [AvSkeletonComponent],
  host: { class: 'block w-full max-w-md' },
  template: `<div class="w-full space-y-3">
      <div av-skeleton class="h-4 w-full rounded"></div>
      <div av-skeleton class="h-4 w-5/6 rounded"></div>
      <div av-skeleton class="h-4 w-4/6 rounded"></div>
      <div av-skeleton class="h-4 w-full rounded"></div>
      <div av-skeleton class="h-4 w-3/6 rounded"></div>
    </div>`,
})
export class SkeletonTextContentDemo {}

User Profile

import { Component } from '@angular/core';
import { AvSkeletonComponent } from '@avesra/angular';

@Component({
  selector: 'app-skeleton-user-profile-demo',
  imports: [AvSkeletonComponent],
  host: { class: 'block w-full max-w-sm' },
  template: `<div class="flex w-full items-center gap-3">
      <div av-skeleton class="h-10 w-10 shrink-0 rounded-full"></div>
      <div class="flex-1 space-y-2">
        <div av-skeleton class="h-3 w-36 rounded-lg"></div>
        <div av-skeleton class="h-3 w-24 rounded-lg"></div>
      </div>
    </div>`,
})
export class SkeletonUserProfileDemo {}

List Items

import { Component } from '@angular/core';
import { AvSkeletonComponent } from '@avesra/angular';

@Component({
  selector: 'app-skeleton-list-demo',
  imports: [AvSkeletonComponent],
  host: { class: 'block w-full max-w-sm' },
  template: `<div class="w-full space-y-4">
      @for (item of items; track item) {
        <div class="flex items-center gap-3">
          <div av-skeleton class="h-10 w-10 shrink-0 rounded-lg"></div>
          <div class="flex-1 space-y-2">
            <div av-skeleton class="h-3 w-full rounded"></div>
            <div av-skeleton class="h-3 w-4/5 rounded"></div>
          </div>
        </div>
      }
    </div>`,
})
export class SkeletonListDemo {
  readonly items = [0, 1, 2];
}

Grid

import { Component } from '@angular/core';
import { AvSkeletonComponent } from '@avesra/angular';

@Component({
  selector: 'app-skeleton-grid-demo',
  imports: [AvSkeletonComponent],
  host: { class: 'block w-full max-w-xl' },
  template: `<div class="grid w-full grid-cols-3 gap-4">
      <div av-skeleton class="h-24 rounded-xl"></div>
      <div av-skeleton class="h-24 rounded-xl"></div>
      <div av-skeleton class="h-24 rounded-xl"></div>
    </div>`,
})
export class SkeletonGridDemo {}

Single Shimmer

A synchronized shimmer effect that passes over all skeleton elements at once. Apply the av-skeleton--shimmer class to a parent container and set animation-type="none" on child skeletons.

import { Component } from '@angular/core';
import { AvSkeletonComponent } from '@avesra/angular';

@Component({
  selector: 'app-skeleton-single-shimmer-demo',
  imports: [AvSkeletonComponent],
  host: { class: 'block w-full max-w-xl' },
  template: `<div
      class="av-skeleton--shimmer relative grid w-full grid-cols-3 gap-4 overflow-hidden rounded-xl"
    >
      <div av-skeleton animation-type="none" class="h-24 rounded-xl"></div>
      <div av-skeleton animation-type="none" class="h-24 rounded-xl"></div>
      <div av-skeleton animation-type="none" class="h-24 rounded-xl"></div>
    </div>`,
})
export class SkeletonSingleShimmerDemo {}

Animation Types

Shimmer

Pulse

None

import { Component } from '@angular/core';
import { AvSkeletonComponent } from '@avesra/angular';

@Component({
  selector: 'app-skeleton-animation-types-demo',
  imports: [AvSkeletonComponent],
  host: { class: 'block w-full max-w-xl' },
  template: `<div class="grid w-full grid-cols-1 gap-6 sm:grid-cols-2 lg:grid-cols-3">
      <div class="space-y-2">
        <p class="truncate text-xs text-muted">Shimmer</p>
        <div class="space-y-3 rounded-lg bg-transparent p-4 shadow-surface">
          <div av-skeleton animation-type="shimmer" class="h-20 rounded-lg"></div>
          <div av-skeleton animation-type="shimmer" class="h-3 w-3/5 rounded-lg"></div>
          <div av-skeleton animation-type="shimmer" class="h-3 w-4/5 rounded-lg"></div>
        </div>
      </div>

      <div class="space-y-2">
        <p class="truncate text-xs text-muted">Pulse</p>
        <div class="space-y-3 rounded-lg bg-transparent p-4 shadow-surface">
          <div av-skeleton animation-type="pulse" class="h-20 rounded-lg"></div>
          <div av-skeleton animation-type="pulse" class="h-3 w-3/5 rounded-lg"></div>
          <div av-skeleton animation-type="pulse" class="h-3 w-4/5 rounded-lg"></div>
        </div>
      </div>

      <div class="space-y-2">
        <p class="truncate text-xs text-muted">None</p>
        <div class="space-y-3 rounded-lg bg-transparent p-4 shadow-surface">
          <div av-skeleton animation-type="none" class="h-20 rounded-lg"></div>
          <div av-skeleton animation-type="none" class="h-3 w-3/5 rounded-lg"></div>
          <div av-skeleton animation-type="none" class="h-3 w-4/5 rounded-lg"></div>
        </div>
      </div>
    </div>`,
})
export class SkeletonAnimationTypesDemo {}

Styling

Global Animation Configuration

You can set a default animation type for all Skeleton components by defining the --av-skeleton-animation CSS variable:

/* In your global CSS / theme file */
:root {
  /* Possible values: shimmer, pulse, none */
  --av-skeleton-animation: pulse;
}

/* You can also set different values for light/dark themes */
[data-av-theme='light'],
.av-light {
  --av-skeleton-animation: shimmer;
}

[data-av-theme='dark'],
.dark,
.av-dark {
  --av-skeleton-animation: pulse;
}

This global setting is overridden by the animation-type input when specified on an individual div[av-skeleton].

Passing Tailwind CSS classes

Pass utility classes on div[av-skeleton] to control size, shape, and layout.

import { Component } from '@angular/core';
import { AvSkeletonComponent } from '@avesra/angular';

@Component({
  selector: 'app-skeleton-custom-styling-demo',
  imports: [AvSkeletonComponent],
  host: { class: 'block' },
  template: `<div
  class="w-[250px] space-y-5 rounded-xl border border-border/80 bg-surface p-4 shadow-sm ring-1 ring-black/5 dark:ring-white/10"
>
  <div av-skeleton class="h-32 rounded-lg bg-neutral-200/90 dark:bg-neutral-800/90"></div>
  <div class="space-y-3">
    <div av-skeleton class="h-3 w-3/5 rounded-lg bg-neutral-200/90 dark:bg-neutral-800/90"></div>
    <div av-skeleton class="h-3 w-4/5 rounded-lg bg-neutral-200/90 dark:bg-neutral-800/90"></div>
    <div av-skeleton class="h-3 w-2/5 rounded-lg bg-neutral-200/90 dark:bg-neutral-800/90"></div>
  </div>
</div>`,
})
export class SkeletonCustomStylingDemo {}

Customizing the component classes

To customize the Skeleton classes, use the @layer components directive. Learn more.

@layer components {
  /* Base skeleton styles */
  .av-skeleton {
    @apply bg-surface-secondary/50; /* Change base background */
  }

  /* Shimmer animation gradient */
  .av-skeleton--shimmer::after {
    @apply via-surface; /* Change shimmer gradient color */
  }

  /* Pulse animation */
  .av-skeleton--pulse {
    @apply animate-pulse opacity-75; /* Customize pulse animation */
  }

  /* No animation variant */
  .av-skeleton--none {
    @apply opacity-50; /* Style for static skeleton */
  }
}

Avesra follows the BEM methodology so component variants and states stay reusable and easy to customize.

CSS Classes

The Skeleton component uses these CSS classes:

Base Class

  • .av-skeleton — Base skeleton styles with background and rounded corners

Animation Variant Classes

  • .av-skeleton--shimmer — Shimmer animation with gradient effect (default)
  • .av-skeleton--pulse — Pulse animation using Tailwind's animate-pulse
  • .av-skeleton--none — No animation, static skeleton

Animation

The Skeleton component supports three animation types, each with different visual effects:

Shimmer Animation

The shimmer effect creates a gradient that moves across the skeleton element:

.av-skeleton--shimmer::after {
  @apply absolute inset-0 -translate-x-full animate-skeleton
         bg-linear-to-r from-transparent via-surface-tertiary to-transparent
         content-[''];
}

The shimmer animation is defined in the theme using:

@theme inline {
  --animate-skeleton: skeleton 2s linear infinite;

  @keyframes skeleton {
    100% {
      transform: translateX(200%);
    }
  }
}

Pulse Animation

The pulse animation uses Tailwind's built-in animate-pulse utility:

.av-skeleton--pulse {
  @apply animate-pulse;
}

No Animation

For static skeletons without any animation:

.av-skeleton--none {
  /* No animation styles applied */
}

API

Props for div[av-skeleton]. Size and shape come from host utility classes rather than dedicated inputs.

PropTypeDefaultDescription
animation-type'shimmer' | 'pulse' | 'none''shimmer' or --av-skeleton-animationAnimation type for the skeleton. When omitted, reads --av-skeleton-animation from computed styles, then falls back to shimmer.

Made with ❤ by SyntaxHertz. Open source and available on GitHub.