feat(design-system): add reusable Skeleton loading primitive

Standalone, OnPush, aria-hidden. Shapes text/circle/rect, configurable
width/height. Shimmer respects prefers-reduced-motion. Colors via
existing tenant CSS vars with fallbacks.
This commit is contained in:
sdarbinyan
2026-07-15 03:42:18 +04:00
parent efdb03f23e
commit a51aa3cf7e
3 changed files with 65 additions and 0 deletions

View File

@@ -0,0 +1 @@
<!-- purely visual placeholder, no projected content -->

View File

@@ -0,0 +1,40 @@
:host {
display: block;
background: linear-gradient(
90deg,
var(--bg-secondary, #f4f4f5) 25%,
var(--border-color, #e4e4e7) 50%,
var(--bg-secondary, #f4f4f5) 75%
);
background-size: 200% 100%;
animation: app-skeleton-shimmer 1.4s ease-in-out infinite;
}
:host.app-skeleton--text {
height: 1em;
border-radius: var(--radius-sm, 4px);
}
:host.app-skeleton--circle {
border-radius: 50%;
}
:host.app-skeleton--rect {
border-radius: var(--radius-md, 6px);
}
@keyframes app-skeleton-shimmer {
0% {
background-position: 200% 0;
}
100% {
background-position: -200% 0;
}
}
@media (prefers-reduced-motion: reduce) {
:host {
animation: none;
background: var(--bg-secondary, #f4f4f5);
}
}

View File

@@ -0,0 +1,24 @@
import { ChangeDetectionStrategy, Component, input } from '@angular/core';
export type SkeletonShape = 'text' | 'circle' | 'rect';
@Component({
selector: 'app-skeleton',
standalone: true,
templateUrl: './skeleton.component.html',
styleUrl: './skeleton.component.scss',
changeDetection: ChangeDetectionStrategy.OnPush,
host: {
'[class.app-skeleton--text]': "shape() === 'text'",
'[class.app-skeleton--circle]': "shape() === 'circle'",
'[class.app-skeleton--rect]': "shape() === 'rect'",
'[style.width]': 'width()',
'[style.height]': 'height()',
'aria-hidden': 'true'
}
})
export class SkeletonComponent {
readonly shape = input<SkeletonShape>('text');
readonly width = input<string>('100%');
readonly height = input<string | null>(null);
}