From b73b93850c385f8f33adeb34e15f3dfa8ab1845d Mon Sep 17 00:00:00 2001 From: Ettore Di Giacinto Date: Thu, 18 Jun 2026 10:32:07 +0000 Subject: [PATCH] feat(ui): Skeleton shimmer primitive Assisted-by: Claude:claude-opus-4-8 [Claude Code] Signed-off-by: Ettore Di Giacinto --- core/http/react-ui/src/App.css | 20 +++++++++++++++++++ .../http/react-ui/src/components/Skeleton.jsx | 16 +++++++++++++++ 2 files changed, 36 insertions(+) create mode 100644 core/http/react-ui/src/components/Skeleton.jsx diff --git a/core/http/react-ui/src/App.css b/core/http/react-ui/src/App.css index c7cdfa24f..9f2a7ca48 100644 --- a/core/http/react-ui/src/App.css +++ b/core/http/react-ui/src/App.css @@ -975,6 +975,26 @@ .spinner-md .spinner-ring { width: 24px; height: 24px; } .spinner-lg .spinner-ring { width: 40px; height: 40px; } +/* Skeleton shimmer placeholders */ +.skeleton { + display: block; + border-radius: var(--radius-sm); + background: linear-gradient( + 100deg, + var(--color-surface-sunken) 30%, + var(--color-surface-hover) 50%, + var(--color-surface-sunken) 70% + ); + background-size: 200% 100%; + animation: skeletonShimmer 1.4s ease-in-out infinite; +} +.skeleton--line { height: 0.85em; margin: 0.35em 0; width: 100%; } +.skeleton--block { height: 100%; width: 100%; border-radius: var(--radius-md); } +@keyframes skeletonShimmer { + from { background-position: 200% 0; } + to { background-position: -200% 0; } +} + /* Model selector */ .model-selector { background: var(--color-bg-tertiary); diff --git a/core/http/react-ui/src/components/Skeleton.jsx b/core/http/react-ui/src/components/Skeleton.jsx new file mode 100644 index 000000000..4ee0f86d8 --- /dev/null +++ b/core/http/react-ui/src/components/Skeleton.jsx @@ -0,0 +1,16 @@ +// Content-shaped shimmer placeholders. Render `count` rows. +export default function Skeleton({ variant = 'line', width, height, count = 1, className = '' }) { + const items = Array.from({ length: count }) + return ( + <> + {items.map((_, i) => ( +