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:
1
src/app/shared/ui/skeleton/skeleton.component.html
Normal file
1
src/app/shared/ui/skeleton/skeleton.component.html
Normal file
@@ -0,0 +1 @@
|
||||
<!-- purely visual placeholder, no projected content -->
|
||||
40
src/app/shared/ui/skeleton/skeleton.component.scss
Normal file
40
src/app/shared/ui/skeleton/skeleton.component.scss
Normal 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);
|
||||
}
|
||||
}
|
||||
24
src/app/shared/ui/skeleton/skeleton.component.ts
Normal file
24
src/app/shared/ui/skeleton/skeleton.component.ts
Normal 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);
|
||||
}
|
||||
Reference in New Issue
Block a user