@merfor/angular-skeleton
v1.0.4
Published
Production-ready, signals-first skeleton loader library for Angular 17+
Downloads
46
Maintainers
Readme
@merfor/angular-skeleton
Production-ready, signals-first skeleton loader library for Angular 17+.
- Zero dependencies
- Standalone components (no NgModule)
- Signals-first (
input(),computed()) OnPusheverywhere — no unnecessary re-renders- SSR / hydration safe
- Accessible (
role="status",aria-busy,prefers-reduced-motion) - Three built-in themes:
default,glass,minimal - Four animations:
shimmer,pulse,wave,none - Tree-shakable — pay only for what you import
- Angular Package Format (APF) — works with Ivy partial compilation
Installation
npm install @merfor/angular-skeletonQuick Start
// app.component.ts
import { SkeletonComponent } from '@merfor/angular-skeleton';
@Component({
standalone: true,
imports: [SkeletonComponent],
template: `<merfor-skeleton width="300px" height="20px" />`,
})
export class AppComponent {}Variants
<!-- Text line (default) -->
<merfor-skeleton />
<!-- Rectangle block -->
<merfor-skeleton width="100%" height="200px" variant="rect" />
<!-- Circle avatar -->
<merfor-skeleton width="48px" height="48px" variant="circle" />
Themes
<merfor-skeleton theme="default" />
<merfor-skeleton theme="glass" />
<merfor-skeleton theme="minimal" />Dark mode is detected automatically via prefers-color-scheme: dark. Override CSS variables for custom colours:
:root {
--merfor-sk-bg-light: #ebebeb;
--merfor-sk-bg-dark: #1a1a1a;
}Animations
<merfor-skeleton animation="shimmer" /> <!-- default -->
<merfor-skeleton animation="pulse" />
<merfor-skeleton animation="wave" />
<merfor-skeleton animation="none" /> <!-- disable -->
<merfor-skeleton [animated]="false" /> <!-- also disables -->prefers-reduced-motion: reduce automatically strips all animations at the CSS layer — no JavaScript required.
API — <merfor-skeleton>
| Input | Type | Default | Description |
|-------------|-----------------------------------------|--------------|-------------------------------------|
| width | string | '100%' | CSS width |
| height | string | '16px' | CSS height |
| variant | 'text' \| 'rect' \| 'circle' | 'text' | Shape preset |
| radius | string \| null | null | Explicit border-radius override |
| animated | boolean | true | Master animation toggle |
| theme | 'default' \| 'glass' \| 'minimal' \| null | null | Visual theme (null = global default)|
| animation | 'shimmer' \| 'pulse' \| 'wave' \| 'none' \| null | null | Animation style |
Preset Components
<merfor-skeleton-card>
Avatar + title + subtitle + two body lines.
<merfor-skeleton-card />
<merfor-skeleton-card theme="glass" animation="pulse" />
| Input | Type | Default |
|-------------|-----------------------|---------|
| theme | SkeletonTheme\|null | null |
| animation | SkeletonAnimation\|null | null |
<merfor-skeleton-list>
Repeating list rows with avatar + two text lines each.
<merfor-skeleton-list [count]="10" />
| Input | Type | Default |
|-------------|----------|---------|
| count | number | 5 |
| theme | SkeletonTheme\|null | null |
| animation | SkeletonAnimation\|null | null |
<merfor-skeleton-table>
Full <table> with configurable rows and columns.
<merfor-skeleton-table [rows]="8" [columns]="5" />
| Input | Type | Default |
|-------------|----------|---------|
| rows | number | 5 |
| columns | number | 4 |
| theme | SkeletonTheme\|null | null |
| animation | SkeletonAnimation\|null | null |
<merfor-skeleton-avatar>
Single circular or square avatar placeholder.
<merfor-skeleton-avatar size="64px" shape="circle" />
<merfor-skeleton-avatar size="48px" shape="square" />| Input | Type | Default |
|-------------|---------------------------|------------|
| size | string | '48px' |
| shape | 'circle' \| 'square' | 'circle' |
| theme | SkeletonTheme\|null | null |
| animation | SkeletonAnimation\|null | null |
<merfor-skeleton-button>
Button-shaped placeholder in three size tiers.
<merfor-skeleton-button size="small" />
<merfor-skeleton-button size="medium" />
<merfor-skeleton-button size="large" />| Input | Type | Default |
|-------------|-------------------------------|------------|
| size | 'small' \| 'medium' \| 'large' | 'medium' |
| theme | SkeletonTheme\|null | null |
| animation | SkeletonAnimation\|null | null |
<merfor-skeleton-paragraph>
Multi-line text block (last line renders at 60% to mimic real paragraphs).
<merfor-skeleton-paragraph [count]="5" />| Input | Type | Default |
|-------------|----------|---------|
| count | number | 4 |
| theme | SkeletonTheme\|null | null |
| animation | SkeletonAnimation\|null | null |
Structural Directive *merforSkeleton
Conditionally renders a skeleton placeholder or the real content. Pass the placeholder template via the placeholder: microsyntax binding.
<!-- Define the skeleton placeholder as a named template ref -->
<ng-template #skeletonTpl>
<merfor-skeleton-card />
</ng-template>
<!-- The directive swaps between real content and the placeholder -->
<div *merforSkeleton="isLoading(); placeholder: skeletonTpl">
<!-- real content shown when isLoading() === false -->
<app-user-profile />
</div>import { SkeletonDirective, SkeletonCardComponent } from '@merfor/angular-skeleton';
@Component({
standalone: true,
imports: [SkeletonDirective, SkeletonCardComponent],
})
export class ProfilePageComponent {
readonly isLoading = signal(true);
}Global Configuration
Override library-wide defaults at the application root — or any provider scope.
import { provideMerforSkeletonConfig } from '@merfor/angular-skeleton';
bootstrapApplication(AppComponent, {
providers: [
provideMerforSkeletonConfig({
animationDuration: '1.8s',
lightBackground: '#ebebeb',
darkBackground: '#1a1a1a',
defaultAnimation: 'pulse',
defaultTheme: 'default',
}),
],
});| Option | Type | Default |
|---------------------|--------------------|----------------|
| animationDuration | string | '1.4s' |
| lightBackground | string | '#f2f2f2' |
| darkBackground | string | '#262626' |
| defaultAnimation | SkeletonAnimation| 'shimmer' |
| defaultTheme | SkeletonTheme | 'default' |
Dark Mode
Dark mode is CSS-native — prefers-color-scheme: dark flips the background automatically. There is no JavaScript involvement, which means:
- It works in SSR before hydration.
- It works even if JavaScript is disabled.
- It respects the OS preference immediately without a flash.
To force dark mode unconditionally, override the CSS variable:
:root {
--merfor-sk-bg-light: #262626; /* force dark everywhere */
}Best Practices
Match skeleton dimensions to real content The skeleton should closely match the layout of the content it replaces to prevent layout shift.
Use the directive for data-fetching patterns
*merforSkeleton is the idiomatic way to use the library — it guarantees the skeleton and its content never coexist in the DOM.
Prefer preset components
merfor-skeleton-card, merfor-skeleton-list, etc. give consistent layouts with a single line.
Don't nest role="status" elements
Preset components already have role="status" on the host. Avoid wrapping them in another role="status" container.
Disable animation for static / print contexts
<merfor-skeleton [animated]="false" />Or globally:
provideMerforSkeletonConfig({ defaultAnimation: 'none' })License
MIT © Madhivanan R
