a11y-fixes
Resolve axe-core accessibility violations reported by Vitest (test/a11y.ts), Playwright (.playwright/a11y.ts), or the code-audit-frontend agent's a11y bucket.…
For building skeleton loading states that are pixel-perfect matches of real content. Use this skill whenever adding loading states to components, building skeletons for async data, handling pending loader states in route transitions, or implementing the shimmer animation
$ npx -y skills add gaia-react/gaia --skill skeleton-loaders --agent claude-codeHow it fires
How this skill gets triggered: by you, by Claude, or both.
/skeleton-loadersContext preview
The summary Claude sees to decide when to auto-load this skill.
For building skeleton loading states that are pixel-perfect matches of real content. Use this skill whenever adding loading states to components, building skeletons for async data, handling pending loader states in route transitions, or implementing the shimmer animation
name: skeleton-loaders description: For building skeleton loading states that are pixel-perfect matches of real content. Use this skill whenever adding loading states to components, building skeletons for async data, handling pending loader states in route transitions, or implementing the shimmer animation pattern. Also trigger when the user asks about preventing layout shift during data fetching. model: haiku
Build skeleton loading states that are pixel-perfect matches of real content.
Use real HTML elements (`<p>`, `<span>`, `<h2>`, `<button>`) with the same font classes as the real component, plus shimmer + transparency classes. This makes skeletons inherit exact line-height, font-size, and weight, producing pixel-perfect dimensions without hardcoded `h-*`/`w-*` values.
Define a shared class string at the top of the skeleton component:
const shimmer = 'animate-shimmer rounded-sm bg-linear-to-r from-slate-950 via-slate-900 to-slate-950 bg-size-[200%_100%] text-transparent select-none';
Copy the real component's element type and font classes, add `shimmer`. How you fill the text depends on whether the real text is static or dynamic:
import {twJoin} from 'tailwind-merge';
// Real component
<h2 className="text-lg font-bold text-white">{t('profile.heading')}</h2> // static
<p className="truncate text-sm font-semibold text-white">{data.name}</p> // dynamic
<p className="text-xs text-slate-400">{data.value}</p> // dynamic
// Skeleton
<h2 className={twJoin('text-lg font-bold', shimmer)}>{t('profile.heading')}</h2> // same t(), exact width
<p className={twJoin('truncate text-sm font-semibold', shimmer)}>Name</p> // approximate
<p className={twJoin('text-xs', shimmer)}>Example value</p> // approximateKeep as empty divs with the shimmer class, no text needed:
<div className={twJoin('size-14 shrink-0', shimmer)} />Use the real element type with `tabIndex={-1}` to prevent focus. Button labels are static text, so use the real `t()` value (exact width on reveal):
<button
className={twJoin('w-full py-2 text-xs font-medium', shimmer)}
tabIndex={-1}
type="button"
>
{t('common.submit')}
</button>Avoid skeleton flash on fast loads. Show stale/empty content for 200ms before revealing the skeleton:
const [showSkeleton, setShowSkeleton] = useState(false);
// Valid Effect: synchronizing component state with a timer (external system).
// The cleanup prevents a state update after unmount.
useEffect(() => {
const timer = setTimeout(() => setShowSkeleton(true), 200);
return () => clearTimeout(timer);
}, []);
if (!showSkeleton) return null; // or return stale content
return <MySkeleton />;Skeleton text is transparent, but screen readers still announce it (placeholder text and any `t()` labels). Hide the skeleton from assistive tech and announce loading instead:
import {twJoin} from 'tailwind-merge';
const shimmer =
'animate-shimmer rounded-sm bg-linear-to-r from-slate-950 via-slate-900 to-slate-950 bg-size-[200%_100%] text-transparent select-none';
const ExampleSkeleton = () => (
<div aria-hidden className="border border-slate-700 bg-slate-900">
<div className="flex items-center gap-3 p-3">
<div className={twJoin('size-14 shrink-0', shimmer)} />
<div className="min-w-0 flex-1">
<p className={twJoin('truncate text-sm font-semibold', shimmer)}>
Name
</p>
<p className={twJoin('truncate text-xs', shimmer)}>Example value</p>
</div>
</div>
</div>
);
export default ExampleSkeleton;Claude is raw power. GAIA is order and focus. The foundation that keeps Claude-shipped code production-grade as your team scales. The React frontend is handled. You build the rest of your app on top. Every convention enforced in code.
Repo: gaia-react/gaia
Resolve axe-core accessibility violations reported by Vitest (test/a11y.ts), Playwright (.playwright/a11y.ts), or the code-audit-frontend agent's a11y bucket.…
Resolve specific ESLint errors and warnings that appear in this project. Use when fixing lint failures, ESLint reported issues, or autofix conflicts (e.g.…
File a new tech-debt GitHub issue for an out-of-scope code-review finding, building the dedup key, checking for an existing open or declined-closed match, and…
Generate a comprehensive GAIA session handoff document, accomplishments, decisions, current state, open questions, so context can be cleared or compacted…
Restore context from the most recent GAIA session handoff and suggest the next action. Trigger on `/gaia-pickup` or natural-language asks like "pick up where…
Diagnose React render performance by driving a micro-interaction, capturing real renders, and surfacing memo-defeating reference instability with a recommended…