effects
Skeleton Shimmer Card
A card-shaped skeleton / content-placeholder loader rendered with pure CSS. It mimics a typical content card layout: a circle avatar placeholder on the left, two short headline lines to its right, and two full-width body lines below — all filled with a muted token-tinted block color. A diagonal highlight stripe sweeps left-to-right continuously via a CSS translateX keyframe animation, creating the classic shimmer effect that signals loading state without a spinner. Uses role="status" and aria-label for screen-reader feedback. Width and sweep speed are prop-configurable via CSS custom properties; colors are entirely token-driven so the skeleton themes correctly in both light and dark contexts.
Ported from Nawsome (MIT)
Install
1. Register the namespace (once per project):
// components.json — register the @bottega namespace once
{
"registries": {
"@bottega": { "url": "https://bottega.ariacode.ca/r/{name}.json" }
}
}2. Add the component:
npx shadcn add @bottega/skeleton-shimmer-cardUsage
<SkeletonShimmerCard />Props
| Prop | Type | Default | Description |
|---|---|---|---|
| label | string | Loading content… | Accessible label for the loading region (default "Loading content…"). Rendered as aria-label on the role="status" wrapper. |
| width | string | — | Controls the overall scale of the card. The component is 240 × 130px by default; pass a CSS length to override, e.g. "320px". |
| speed | string | — | Duration of one shimmer sweep cycle (default "1.2s"). Accepts any CSS time value — "0.8s" for faster, "2s" for slower. |
| className | string | Additional class names merged onto the root element. |
Source
import type React from "react";
import styles from "./skeleton-shimmer-card.module.css";
export type SkeletonShimmerCardProps = {
/**
* Accessible label for the loading region (default "Loading content…").
* Rendered as aria-label on the role="status" wrapper.
*/
label?: string;
/**
* Controls the overall scale of the card. The component is 240 × 130px
* by default; pass a CSS length to override, e.g. "320px".
*/
width?: string;
/**
* Duration of one shimmer sweep cycle (default "1.2s").
* Accepts any CSS time value — "0.8s" for faster, "2s" for slower.
*/
speed?: string;
/** Additional class names merged onto the root element. */
className?: string;
};
/**
* A card-shaped skeleton / content-placeholder loader.
* Renders a circle avatar placeholder, two headline lines, and two body lines,
* with a horizontal shimmer sweep travelling left-to-right.
*/
export function SkeletonShimmerCard({
label = "Loading content…",
width,
speed,
className = "",
}: SkeletonShimmerCardProps) {
const rootStyle: React.CSSProperties & Record<string, string> = {};
if (width) rootStyle["--skeleton-width"] = width;
if (speed) rootStyle["--skeleton-speed"] = speed;
return (
<div
role="status"
aria-label={label}
className={[styles.card, className].filter(Boolean).join(" ")}
style={rootStyle}
>
{/* decorative shimmer sweep — hidden from AT */}
<span aria-hidden="true" className={styles.shimmer} />
<div aria-hidden="true" className={styles.wrapper}>
<div className={styles.circle} />
<div className={styles.line1} />
<div className={styles.line2} />
<div className={styles.line3} />
<div className={styles.line4} />
</div>
{/* visually-hidden "Loading…" text for screen readers that don't read aria-label on non-interactive roles */}
<span className={styles.srOnly}>{label}</span>
</div>
);
}
Dependencies
- @bottega/tokens