(READ ONLY) Margin is an open annotation layer for the internet. Powered by the AT Protocol.
margin.at
extension
web
atproto
comments
1import React from "react";
2import { clsx } from "clsx";
3
4interface EmptyStateProps {
5 icon?: React.ReactNode;
6 title?: string;
7 message: string;
8 action?: React.ReactNode | { label: string; onClick: () => void };
9 className?: string;
10}
11
12export default function EmptyState({
13 icon,
14 title,
15 message,
16 action,
17 className,
18}: EmptyStateProps) {
19 return (
20 <div
21 className={clsx(
22 "relative text-center py-14 px-6 overflow-hidden",
23 "bg-gradient-to-b from-surface-50/80 to-surface-50/20 dark:from-surface-800/60 dark:to-surface-800/20 rounded-2xl",
24 "border border-dashed border-surface-200 dark:border-surface-700",
25 className,
26 )}
27 >
28 {icon && (
29 <div className="relative flex justify-center mb-4">
30 <div className="absolute inset-0 flex justify-center items-center">
31 <div className="h-16 w-16 rounded-full bg-primary-100/60 dark:bg-primary-900/20 blur-xl" />
32 </div>
33 <div className="relative text-surface-400 dark:text-surface-500">
34 {icon}
35 </div>
36 </div>
37 )}
38 {title && (
39 <h3 className="text-lg font-display font-semibold text-surface-900 dark:text-white mb-2">
40 {title}
41 </h3>
42 )}
43 <p className="text-surface-500 dark:text-surface-400 max-w-sm mx-auto leading-relaxed">
44 {message}
45 </p>
46 {action && (
47 <div className="mt-6">
48 {typeof action === "object" &&
49 "label" in action &&
50 "onClick" in action ? (
51 <button
52 onClick={action.onClick}
53 className="inline-flex items-center gap-2 px-4 py-2 bg-primary-600 hover:bg-primary-700 text-white font-medium rounded-lg transition-colors"
54 >
55 {action.label}
56 </button>
57 ) : (
58 action
59 )}
60 </div>
61 )}
62 </div>
63 );
64}