Skeleton tokens + diagonal shimmer (WEB-889)
Version: 0.2.0 · Type: ✨ Feature
WEB-889 ([Skeleton] tokens for radius/color + card-level shimmer)
Problem
The old Skeleton used animate-pulse + rounded-md + a hardcoded light gray; its radius, base color, and shimmer color were not tied to tokens, it did not adapt to dark mode, and the shimmer was "one separate light per element", which did not match the design's requirement of a "card-level continuous shimmer".
Changed Files
src/components/ui/skeleton.tsxsrc/components/Skeleton/Skeleton.tsxsrc/components/Skeleton/SkeletonGroup.tsx(new)src/components/Skeleton/skeleton-shimmer.ts(new)src/components/Skeleton/skeleton-group-context.ts(new)src/components/Skeleton/index.tssrc/components/index.tssrc/components/Skeleton/__tests__/Skeleton.test.tsxpackages/design-site/docs/components/atomic/progress-indicators/skeleton.mdxpackages/design-site/i18n/zh-CN/.../skeleton.mdxpackages/design-site/src/theme/ReactLiveScope/index.tsx
Changes
- Radius
rounded-md(.375rem) →rounded-sm(--radius-sm= .25rem). - Base color tied to
--Grays-Gray-1, shimmer color tied to--Grays-Gray-2, both referencing tokens instead of hardcoded values; dark mode switches automatically via tokens. - Changed the shimmer to a 115° diagonal, looping at
2.4s cubic-bezier(0.65, 0, 0.35, 1); the gradient uses gray1 at both ends and gray2 at the center (it does not fade toward transparent, to avoid dirty edges). - The shimmer is disabled under
prefers-reduced-motion: reduce(staying plain gray); the shimmer only starts once measurement is ready, so no light is exposed on the first frame. - Added
SkeletonGroup: it treats the whole card as a single canvas, measuring each block's offset within the card and the card's dimensions (ResizeObserver+MutationObserver), so one light sweeps continuously across blocks; the gaps between blocks stay plain gray; different groups are not chained together. A standaloneSkeletonis treated as a "single-block canvas" and shimmers by its own dimensions.
Notes
- The shimmer gradient and keyframes depend on geometric CSS variables on each block (
--plaud-skeleton-gw/--plaud-skeleton-gh/--plaud-skeleton-sx/--plaud-skeleton-sy), which cannot be expressed with pure Tailwind utility classes and need to be consistent across multiple consumers, so they are injected via a singleton<style>(ensureSkeletonShimmerStyles); the base color / radius still use Tailwind classes (rounded-sm bg-(--Grays-Gray-1)). - Approach selection: a pure CSS overlay would let the light sweep across the gaps between blocks, which does not satisfy "gaps unaffected", so JS measurement was used (the design issue's option 3) to achieve "continuous within a group, independent across groups, plain gray in the gaps".