codeburn/app/renderer/components/Skeleton.tsx
iamtoruk daf944e714 feat(app): motion layer (gsap) — count-ups, chart grow-in, section fade, skeletons, toasts
- one gate (motionEnabled): off under reduced-motion, missing matchMedia,
  and vitest; every path checks it first
- mount/filter-change only: count-up and bar grow-in key off the
  period|provider|range key, so 30s poll refreshes snap values silently
  instead of re-animating
- first-load skeleton shimmer replaces bare scanning text (kept sr-only
  for screen readers); slide-in toast host for Settings/export feedback
  (validation errors stay inline); CSS hover-lift + press micro-
  interactions, all with a reduced-motion escape hatch
- gsap 3.15.0 + @gsap/react 2.1.2 (+74KB raw JS; G4 flame work shares it)

244/244, typecheck + build green.
2026-07-16 04:39:09 -07:00

21 lines
910 B
TypeScript

/**
* First-load placeholder: CSS shimmer blocks shown while a section has no data
* and no error. The visible blocks are aria-hidden; the real loading text is
* kept for screen readers via a visually-hidden status node.
*/
export function SectionSkeleton({ label, rows = 4, chart = false }: { label: string; rows?: number; chart?: boolean }) {
return (
<div className="panel skel-card">
<span className="sr-only" role="status">{label}</span>
<div className="phead skel-head" aria-hidden="true">
<span className="skel skel-line" style={{ width: '38%' }} />
</div>
<div className="pbody skel-body" aria-hidden="true">
{chart && <span className="skel skel-chart" />}
{Array.from({ length: rows }, (_, index) => (
<span key={index} className="skel skel-line" style={{ width: `${88 - index * 13}%` }} />
))}
</div>
</div>
)
}