Components · Feedback
Skeleton
A loading placeholder with a slow sheen that stands still under reduced motion.
Loading the team
Loading the team
Both previews render the same markup; the HTML tab below is that markup without a framework.
Install
- React
- npx shadcn@
latest add https:// www.themes.dev/ caseroom/ r/ skeleton.json - Astro
- npx shadcn@
latest add https:// www.themes.dev/ caseroom/ r/ astro/ skeleton.json
Or copy the files below into your project. They need clsx, tailwind-merge, and the Caseroom theme. The HTML needs only the compiled CSS.
import type { HTMLAttributes } from 'react';
import { cn } from '@/lib/utils';
import { skeletonClasses } from './skeleton.variants';
export type SkeletonProps = HTMLAttributes<HTMLSpanElement>;
/** A loading placeholder. Size it with classes (h-4 w-32, size-10 rounded-full); mark the region aria-busy. */
export function Skeleton({ className, ...props }: SkeletonProps) {
return <span aria-hidden="true" className={cn(skeletonClasses, className)} {...props} />;
}/** A placeholder while content loads: a muted block with a slow sheen. With reduced motion it is a still block. Wrap them
* in role="status" aria-busy="true" with a visually hidden "Loading …"; the skeletons themselves are hidden. */
export const skeletonClasses =
'block rounded-md bg-muted bg-[linear-gradient(90deg,transparent,color-mix(in_oklab,var(--background)_55%,transparent),transparent)] bg-[length:200%_100%] bg-no-repeat animate-[caseroom-sheen_1.6s_ease-in-out_infinite] motion-reduce:animate-none';
/** The keyframes, once in your CSS (the Caseroom theme has them). */
export const SHEEN_KEYFRAMES = '@keyframes caseroom-sheen { from { background-position: 150% 0 } to { background-position: -50% 0 } }';---
import type { HTMLAttributes } from 'astro/types';
import { cn } from '@/lib/utils';
import { skeletonClasses } from './skeleton.variants';
type Props = HTMLAttributes<'span'>;
const { class: className, ...rest } = Astro.props;
---
<span aria-hidden="true" class={cn(skeletonClasses, className)} {...rest}></span>/** A placeholder while content loads: a muted block with a slow sheen. With reduced motion it is a still block. Wrap them
* in role="status" aria-busy="true" with a visually hidden "Loading …"; the skeletons themselves are hidden. */
export const skeletonClasses =
'block rounded-md bg-muted bg-[linear-gradient(90deg,transparent,color-mix(in_oklab,var(--background)_55%,transparent),transparent)] bg-[length:200%_100%] bg-no-repeat animate-[caseroom-sheen_1.6s_ease-in-out_infinite] motion-reduce:animate-none';
/** The keyframes, once in your CSS (the Caseroom theme has them). */
export const SHEEN_KEYFRAMES = '@keyframes caseroom-sheen { from { background-position: 150% 0 } to { background-position: -50% 0 } }';<div role="status" aria-busy="true" class="grid w-full max-w-sm gap-5">
<span class="sr-only">Loading the team</span>
<div class="flex items-center gap-3">
<span aria-hidden="true" class="block bg-muted bg-[linear-gradient(90deg,transparent,color-mix(in_oklab,var(--background)_55%,transparent),transparent)] bg-[length:200%_100%] bg-no-repeat animate-[caseroom-sheen_1.6s_ease-in-out_infinite] motion-reduce:animate-none size-10 rounded-full">
</span>
<div class="grid flex-1 gap-2">
<span aria-hidden="true" class="block rounded-md bg-muted bg-[linear-gradient(90deg,transparent,color-mix(in_oklab,var(--background)_55%,transparent),transparent)] bg-[length:200%_100%] bg-no-repeat animate-[caseroom-sheen_1.6s_ease-in-out_infinite] motion-reduce:animate-none h-3.5 w-2/5">
</span>
<span aria-hidden="true" class="block rounded-md bg-muted bg-[linear-gradient(90deg,transparent,color-mix(in_oklab,var(--background)_55%,transparent),transparent)] bg-[length:200%_100%] bg-no-repeat animate-[caseroom-sheen_1.6s_ease-in-out_infinite] motion-reduce:animate-none h-3 w-3/4">
</span>
</div>
</div>
<div class="flex items-center gap-3">
<span aria-hidden="true" class="block bg-muted bg-[linear-gradient(90deg,transparent,color-mix(in_oklab,var(--background)_55%,transparent),transparent)] bg-[length:200%_100%] bg-no-repeat animate-[caseroom-sheen_1.6s_ease-in-out_infinite] motion-reduce:animate-none size-10 rounded-full">
</span>
<div class="grid flex-1 gap-2">
<span aria-hidden="true" class="block rounded-md bg-muted bg-[linear-gradient(90deg,transparent,color-mix(in_oklab,var(--background)_55%,transparent),transparent)] bg-[length:200%_100%] bg-no-repeat animate-[caseroom-sheen_1.6s_ease-in-out_infinite] motion-reduce:animate-none h-3.5 w-2/5">
</span>
<span aria-hidden="true" class="block rounded-md bg-muted bg-[linear-gradient(90deg,transparent,color-mix(in_oklab,var(--background)_55%,transparent),transparent)] bg-[length:200%_100%] bg-no-repeat animate-[caseroom-sheen_1.6s_ease-in-out_infinite] motion-reduce:animate-none h-3 w-3/4">
</span>
</div>
</div>
<div class="flex items-center gap-3">
<span aria-hidden="true" class="block bg-muted bg-[linear-gradient(90deg,transparent,color-mix(in_oklab,var(--background)_55%,transparent),transparent)] bg-[length:200%_100%] bg-no-repeat animate-[caseroom-sheen_1.6s_ease-in-out_infinite] motion-reduce:animate-none size-10 rounded-full">
</span>
<div class="grid flex-1 gap-2">
<span aria-hidden="true" class="block rounded-md bg-muted bg-[linear-gradient(90deg,transparent,color-mix(in_oklab,var(--background)_55%,transparent),transparent)] bg-[length:200%_100%] bg-no-repeat animate-[caseroom-sheen_1.6s_ease-in-out_infinite] motion-reduce:animate-none h-3.5 w-2/5">
</span>
<span aria-hidden="true" class="block rounded-md bg-muted bg-[linear-gradient(90deg,transparent,color-mix(in_oklab,var(--background)_55%,transparent),transparent)] bg-[length:200%_100%] bg-no-repeat animate-[caseroom-sheen_1.6s_ease-in-out_infinite] motion-reduce:animate-none h-3 w-3/4">
</span>
</div>
</div>
</div>