"use client";
import { useId } from "react";
/*
* Empty Calendar – a small card illustration, in a single self-contained file.
* Three stacked calendar cards with an "Empty" badge and a disabled "Join meeting" button.
* Hover the front card to lift it, hover a back card to slide it further out.
* The artwork is drawn on a 320 × 240 viewBox inside a 3 : 2 frame.
*/
export type EmptyCalendarProps = {
className?: string;
/** Accessible description of the scene */
label?: string;
};
export function EmptyCalendar({
className,
label = "Stacked calendar cards marked empty, with a disabled join meeting button",
}: EmptyCalendarProps) {
const id = "ui-ec" + useId().replace(/[^a-zA-Z0-9_-]/g, "");
return (
{/* React 19 hoists this into once, no matter how many instances render */}
{/* Back cards – hover to slide them out further */}
{/* Front card with a soft colored glow underneath */}
{/* Colored bottom edge, matching the glow */}
);
}
/** One calendar card: title, "Empty" badge, placeholder lines and a disabled button */
function CalendarCard({ shadow }: { shadow: string }) {
return (
<>
Calendar
EMPTY
{/* "Not available" icon + label, centered together */}
Join meeting
>
);
}
/* ---------- Styles ---------- */
const css = /* css */ `
.ui-ec {
position: relative;
width: 100%;
aspect-ratio: 3 / 2;
overflow: hidden;
font-family: "Inter", ui-sans-serif, system-ui, -apple-system, sans-serif;
}
.ui-ec-art {
position: absolute;
inset: 0;
display: block;
width: 100%;
height: 100%;
}
/* Placeholder lines shimmer softly */
.ui-ec-bar { animation: ui-ec-shimmer 2.4s ease-in-out infinite; }
@keyframes ui-ec-shimmer {
0%, 100% { fill: #EFF1F4; }
50% { fill: #DDE1E5; }
}
/* The glow under the front card breathes */
.ui-ec-blob {
transform-origin: 160px 152px;
animation: ui-ec-breathe 5s ease-in-out infinite;
}
@keyframes ui-ec-breathe {
0%, 100% { opacity: 0.85; transform: scale(1); }
50% { opacity: 1; transform: scale(1.06); }
}
/* Front card: lifts and tilts on hover */
.ui-ec-front {
transform-origin: 160px 90px;
transition: transform 0.35s cubic-bezier(0.22, 0.61, 0.36, 1);
}
.ui-ec-front:hover { transform: translate(0, -8px) rotate(2deg); }
/* Back cards: fanned out, slide further on hover */
.ui-ec-back {
transform-box: fill-box;
transform-origin: center;
transition: transform 0.35s cubic-bezier(0.22, 0.61, 0.36, 1);
}
.ui-ec-back-left { transform: translate(-22px, 0) rotate(-3deg); }
.ui-ec-back-left:hover { transform: translate(-48px, 4px) rotate(-7deg); }
.ui-ec-back-right { transform: translate(22px, 0) rotate(3deg); }
.ui-ec-back-right:hover { transform: translate(48px, 4px) rotate(7deg); }
@media (prefers-reduced-motion: reduce) {
.ui-ec-bar, .ui-ec-blob { animation: none; }
.ui-ec-front, .ui-ec-back { transition: none; }
}
/* Dark mode: follows a .dark class or data-theme="dark" on any ancestor, e.g. */
:where(.dark, [data-theme="dark"]) .ui-ec-card { fill: #232326; stroke: rgba(255, 255, 255, 0.08); }
:where(.dark, [data-theme="dark"]) .ui-ec-drop { flood-opacity: 0.4; }
:where(.dark, [data-theme="dark"]) .ui-ec-edge { stop-color: #3a3a3f; }
:where(.dark, [data-theme="dark"]) .ui-ec-title { fill: #8f8f8f; }
:where(.dark, [data-theme="dark"]) .ui-ec-badge { fill: rgba(250, 176, 130, 0.16); }
:where(.dark, [data-theme="dark"]) .ui-ec-bar { fill: #2a2a2d; }
@media (prefers-reduced-motion: no-preference) {
:where(.dark, [data-theme="dark"]) .ui-ec-bar { animation-name: ui-ec-shimmer-dark; }
}
:where(.dark, [data-theme="dark"]) .ui-ec-btn { fill: #2a2a2d; stroke: rgba(255, 255, 255, 0.08); }
:where(.dark, [data-theme="dark"]) .ui-ec-btn-ink { stroke: #6b6b70; }
:where(.dark, [data-theme="dark"]) .ui-ec-btn-label { fill: #6b6b70; }
@keyframes ui-ec-shimmer-dark {
0%, 100% { fill: #2a2a2d; }
50% { fill: #36363b; }
}
`;