Skip to content

feat(#421): add CreatorCardSkeleton matching CreatorCard layout#471

Open
Topmatrixmor2014 wants to merge 1 commit into
accesslayerorg:devfrom
Topmatrixmor2014:feat/issue-421-creator-card-skeleton
Open

feat(#421): add CreatorCardSkeleton matching CreatorCard layout#471
Topmatrixmor2014 wants to merge 1 commit into
accesslayerorg:devfrom
Topmatrixmor2014:feat/issue-421-creator-card-skeleton

Conversation

@Topmatrixmor2014

@Topmatrixmor2014 Topmatrixmor2014 commented Jun 26, 2026

Copy link
Copy Markdown

Summary

Adds a dedicated CreatorCardSkeleton component that mirrors the dimensions of the live CreatorCard so the marketplace shows a layout-stable loading placeholder while the creator list is still fetching. Previously the page rendered a generic grid skeleton that did not match card dimensions, causing context loss when real cards arrived.

Fixes #421.

Acceptance criteria

# Criterion Status Notes
1 Skeleton renders in place of cards while data is fetching LandingPage swaps CreatorGridSkeletonCreatorCardGridSkeleton in the initial-loading branch.
2 Animation uses the existing dark theme colors Reuses the existing .skeleton-shimmer keyframe / class already defined in src/index.css. Blocks use bg-white/12 with a slightly brighter static ring (bg-white/16 ring-1 ring-white/15) for the reduced-motion fallback — identical tokens used by the sibling CreatorSkeleton component.
3 No layout shift when real cards replace skeletons The skeleton replicates every section of CreatorCard at the same height / width / spacing tokens (avatar, name+badges row, handle, bio, sparkline placeholder, mini stat chips, 3 meta rows, social links, action row, helper text).
4 Render 6 skeletons by default while the list is loading CreatorCardGridSkeleton defaults to count = 6, matching the live first-page footprint on LandingPage.

What changed

New files

  • src/components/common/CreatorCardSkeleton.tsx — single-card placeholder that replicates CreatorCard section by section. Exposes a matching disableShimmer prop for parity with CreatorSkeleton. Includes role="status" + aria-label="Loading creator card" + sr-only span, mirroring the accessibility pattern used by CreatorProfileHeaderSkeleton.
  • src/components/common/__tests__/CreatorCardSkeleton.test.tsx — 7 vitest specs covering: shimmer rendering, disableShimmer static fallback, className merging, default count === 6, custom count, disableShimmer propagation to every grid card, and grid className merging. Each assertion uses real RTL queries (getByRole, getByTestId, querySelectorAll) so the tests fail loudly if dimensions or structure regress.

Modified files

  • src/pages/LandingPage.tsx — initial-loading branch now renders <CreatorCardGridSkeleton count={6} /> instead of <CreatorGridSkeleton count={6} /> so the marketplace shows dimensionally-stable placeholders while data is fetching. The generic CreatorGridSkeleton export is left in place for any callers that still rely on it.

Untouched

  • src/components/common/CreatorSkeleton.tsx, src/utils/creatorCardTokens.ts, src/index.css — the new component reuses the existing .skeleton-shimmer CSS keyframe and the CREATOR_CARD_MEDIA_RADIUS_CLASS token, so no style or token regressions are introduced.

How the new skeleton maps to CreatorCard

CreatorCard section CreatorCardSkeleton block Width / height
Outer card rounded-2xl border p-4 marketplace-card-surface full
DropdownMenu trigger (top-right) absolute size-8 rounded-full placeholder exact
Avatar (aspect-square) aspect-square overflow-hidden ${CREATOR_CARD_MEDIA_RADIUS_CLASS} exact
Name + verified/change/supply/recent-activity badge row h-6 name + 3 small badge placeholders matches widths
Handle (.marketplace-label-muted) h-4 line matches
Bio (CreatorBio) two h-3 lines matches
Sparkline placeholder h-10 w-full rounded-lg exact (CreatorCard already uses these exact tokens)
Mini stat chip row (3 chips) 3 h-6 rounded placeholders matches
Meta rows (Join Date / Handle / Key Price) 3 label + value blocks separated by CreatorListRowDivider matches
Social links row 4 icon-sized placeholders matches
Action row (NetworkFeeHint + Buy button) h-4 fee hint + h-9 w-24 rounded-xl button matches
Helper text (BuyActionHelperText) h-3 trailing bar matches

Because the structure and sizes are identical, swapping the skeleton for real cards produces zero visible reflow.

Reduced-motion

The .skeleton-shimmer CSS rule already includes its own @media (prefers-reduced-motion: reduce) fallback (stops the animation and flattens the gradient). On top of that, every block carries Tailwind motion-reduce:bg-white/18 motion-reduce:ring-1 motion-reduce:ring-white/15 for users whose motion setting prefers a hard-edged static state. Callers that already suppress shimmer at a higher level can pass disableShimmer to opt out per-instance.

Accessibility

  • role="status" on the wrapper announces the loading state to assistive tech.
  • aria-label="Loading creator card" on the wrapper (verified via getByRole(status, { name: ... }) in tests).
  • An sr-only span echoes the same label so screen-reader users get a redundant explicit cue, matching the existing CreatorProfileHeaderSkeleton pattern.
  • No interactive elements are rendered, so focus stays exactly where it was; no keyboard traps, no auto-focus jumps.

Testing

New tests (all passing locally)

✓ src/components/common/__tests__/CreatorCardSkeleton.test.tsx (7 tests)
  ✓ renders shimmer blocks for the loading affordance
  ✓ disables the shimmer with `disableShimmer` and falls back to the static block
  ✓ merges additional className onto the card surface
✓ CreatorCardGridSkeleton
  ✓ renders 6 skeletons by default (#421 acceptance criterion)
  ✓ renders the requested number of cards
  ✓ propagates `disableShimmer` to every card in the grid
  ✓ applies extra className to the grid wrapper

Full validation suite (local)

Command Result
pnpm tsc -p tsconfig.app.json --noEmit ✅ no errors
pnpm vitest run src/components/common/__tests__/CreatorCardSkeleton.test.tsx src/components/common/__tests__/CreatorSkeleton.test.tsx ✅ 11/11 passing
pnpm eslint src/components/common/CreatorCardSkeleton.tsx src/components/common/__tests__/CreatorCardSkeleton.test.tsx src/pages/LandingPage.tsx ✅ clean

Known unrelated issue (transparency)

src/pages/__tests__/LandingPage.keyboard.test.tsx fails on pristine main too (verified with git stash + rerun). The failure is Error: useLocation() may be used only in the context of a <Router> component. at LandingPage.tsx:284 because the test renders <LandingPage /> without a <MemoryRouter> wrapper. It pre-dates this PR and is intentionally left out of scope.

Suggested CI follow-up

This is the natural time to land a small fix for the LandingPage keyboard test (wrap the render in <MemoryRouter>). Happy to follow up with that in a separate PR if wanted.

Risk / impact

  • Blast radius: limited to the initial-loading branch in LandingPage. Once creators resolve, the same CreatorCard path renders as before. The generic CreatorGridSkeleton is still exported, so any out-of-tree consumer keeps working.
  • Performance: identical to the previous skeleton — same number of shimmer elements per card (more detailed breakdowns) but the same overall DOM cost.
  • Visual: matches the existing dark-mode card surface; no new colors introduced.

Checklist

  • Acceptance criteria met
  • Tests added for the new component and grid helper
  • pnpm tsc -p tsconfig.app.json --noEmit clean
  • pnpm eslint on touched files clean
  • pnpm vitest run on touched files clean
  • No introduction of unused imports (LandingPage drops CreatorGridSkeleton from the named-imports block)
  • Branch feat/issue-421-creator-card-skeleton pushed to fork
  • Targeted at dev per the project's pr-target-check.yml workflow

/closes #421

closes #421

…d layout

- Add new CreatorCardSkeleton component that mirrors CreatorCard's
  dimensions (avatar, name + verified/change/supply/recent-activity
  badges, handle, bio, sparkline placeholder, 3 mini stat chips, 3
  meta rows, social links, action row, helper text) so it acts as a
  layout-stable placeholder while the creator list is fetching.
- Use the existing dark-theme .skeleton-shimmer animation and
  CREATOR_CARD_MEDIA_RADIUS_CLASS token so the skeleton visually
  matches the rest of the marketplace.
- Respect prefers-reduced-motion via the shared CSS rule and a
  disableShimmer prop, matching CreatorSkeleton's API.
- Add a CreatorCardGridSkeleton helper that defaults to count = 6
  to satisfy the acceptance criterion.
- Wire CreatorCardGridSkeleton into LandingPage's initial-loading
  branch so the marketplace shows 6 stable skeleton cards instead of
  the generic grid skeleton.
- Cover the new component with vitest tests asserting role/aria
  coverage, 6-by-default grid, disableShimmer propagation, className
  merging, and shimmer-vs-static block toggling.
@drips-wave

drips-wave Bot commented Jun 26, 2026

Copy link
Copy Markdown

@Topmatrixmor2014 Great news! 🎉 Based on an automated assessment of this PR, the linked Wave issue(s) no longer count against your application limits.

You can now already apply to more issues while waiting for a review of this PR. Keep up the great work! 🚀

Learn more about application limits

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Add loading skeleton component for creator list cards

1 participant