import type { ReactNode } from "react";
/**
* Public-site design system, faithful to AtomCMS's "atom" theme. The signature
* element is the content-card: a surface card with a primary-tinted header
* (icon circle + title + subtitle) and a padded body. These mirror the atom
* Blade components (content-card, stat blocks, empty states) so every public
* page shares one consistent, themeable look.
*/
export function ContentCard({
icon,
title,
subtitle,
action,
children,
className,
padded = true,
}: {
icon?: string;
title?: ReactNode;
subtitle?: ReactNode;
action?: ReactNode;
children?: ReactNode;
className?: string;
padded?: boolean;
}) {
const hasHead = title != null || icon != null || subtitle != null || action != null;
return (
{hasHead ? (
{icon ? (
{icon}
) : null}
{title != null ?
{title}
: null}
{subtitle != null ?
{subtitle}
: null}
{action ?
{action}
: null}
) : null}
{children}
);
}
/** A value + label tile (profile stats, rare-value summaries, …). */
export function StatBlock({
value,
label,
icon,
}: {
value: ReactNode;
label: string;
icon?: string;
}) {
return (
{icon ? {icon} : null}
{value}
{label}
);
}
/** Friendly empty state with an icon and a muted line. */
export function EmptyState({ icon = "📭", children }: { icon?: string; children: ReactNode }) {
return (
);
}
/** Online / offline pill with a status dot. */
export function OnlineBadge({ online }: { online: boolean }) {
return (
{online ? "Online" : "Offline"}
);
}
/** Leaderboard rank chip — medals for the top three, number otherwise. */
export function RankBadge({ position }: { position: number }) {
const medal = position === 1 ? "🥇" : position === 2 ? "🥈" : position === 3 ? "🥉" : null;
return (
{medal ?? `#${position}`}
);
}
/* ── Skeleton loaders ────────────────────────────────────────── */
export function Skeleton({ className, style }: { className?: string; style?: React.CSSProperties }) {
return (
);
}
export function SkeletonCard({ lines = 3 }: { lines?: number }) {
return (
{Array.from({ length: lines }).map((_, i) => (
))}
);
}
export function SkeletonTable({ rows = 5 }: { rows?: number }) {
return (
{Array.from({ length: rows }).map((_, i) => (
0 ? "1px solid color-mix(in srgb, var(--color-text-muted) 12%, transparent)" : undefined }}>
{i === 0 ? (
<>
>
) : (
<>
>
)}
))}
);
}