From a51aa3cf7eee7c89cc1c16ee90dd92cf2453cf8e Mon Sep 17 00:00:00 2001 From: sdarbinyan Date: Wed, 15 Jul 2026 03:42:18 +0400 Subject: [PATCH] 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. --- .../ui/skeleton/skeleton.component.html | 1 + .../ui/skeleton/skeleton.component.scss | 40 +++++++++++++++++++ .../shared/ui/skeleton/skeleton.component.ts | 24 +++++++++++ 3 files changed, 65 insertions(+) create mode 100644 src/app/shared/ui/skeleton/skeleton.component.html create mode 100644 src/app/shared/ui/skeleton/skeleton.component.scss create mode 100644 src/app/shared/ui/skeleton/skeleton.component.ts diff --git a/src/app/shared/ui/skeleton/skeleton.component.html b/src/app/shared/ui/skeleton/skeleton.component.html new file mode 100644 index 0000000..2b725da --- /dev/null +++ b/src/app/shared/ui/skeleton/skeleton.component.html @@ -0,0 +1 @@ + diff --git a/src/app/shared/ui/skeleton/skeleton.component.scss b/src/app/shared/ui/skeleton/skeleton.component.scss new file mode 100644 index 0000000..8482dc2 --- /dev/null +++ b/src/app/shared/ui/skeleton/skeleton.component.scss @@ -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); + } +} diff --git a/src/app/shared/ui/skeleton/skeleton.component.ts b/src/app/shared/ui/skeleton/skeleton.component.ts new file mode 100644 index 0000000..5466880 --- /dev/null +++ b/src/app/shared/ui/skeleton/skeleton.component.ts @@ -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('text'); + readonly width = input('100%'); + readonly height = input(null); +}