Skip to content

Theming

trud-calendar uses CSS custom properties for theming. It works out of the box with sensible defaults, and automatically inherits shadcn/ui themes if present.

Override any variable in your CSS to customize the calendar’s appearance:

:root {
--trc-background: #ffffff;
--trc-foreground: #0a0a0a;
--trc-muted: #f5f5f5;
--trc-muted-foreground: #737373;
--trc-border: #e5e5e5;
--trc-accent: #f5f5f5;
--trc-accent-foreground: #171717;
--trc-card: #ffffff;
--trc-card-foreground: #0a0a0a;
--trc-primary: #171717;
--trc-primary-foreground: #fafafa;
--trc-ring: #171717;
--trc-radius: 0.5rem;
--trc-today-bg: #dbeafe;
--trc-today-text: #1d4ed8;
--trc-event-default: #3b82f6;
--trc-current-time: #ef4444;
--trc-hour-height: 3rem;
}
VariableControlsLight defaultDark default
--trc-backgroundCalendar background#ffffff#0a0a0a
--trc-foregroundText color#0a0a0a#fafafa
--trc-mutedMuted backgrounds (disabled, secondary)#f5f5f5#262626
--trc-muted-foregroundMuted text (secondary labels)#737373#a3a3a3
--trc-borderGrid lines, cell borders#e5e5e5#262626
--trc-primaryActive buttons, selected states#171717#fafafa
--trc-primary-foregroundText on primary backgrounds#fafafa#171717
--trc-accentHover backgrounds#f5f5f5#262626
--trc-accent-foregroundText on hover backgrounds#171717#fafafa
--trc-cardPopover/card backgrounds#ffffff#0a0a0a
--trc-card-foregroundPopover/card text#0a0a0a#fafafa
--trc-ringFocus ring color#171717#d4d4d4
--trc-radiusBorder radius0.5rem0.5rem
--trc-today-bgToday cell highlight#dbeafe#1e3a5f
--trc-today-textToday number color#1d4ed8#93c5fd
--trc-event-defaultDefault event color#3b82f6#60a5fa
--trc-current-timeCurrent time indicator line#ef4444#f87171
--trc-hour-heightHeight of each hour row3rem3rem

Add the .dark class to a parent element:

<div className={darkMode ? "dark" : ""}>
<Calendar events={events} />
</div>

The calendar includes a complete dark theme. All variables switch automatically when .dark is present on any ancestor.

:root {
--trc-border: hsl(0 0% 92%);
}
.dark {
--trc-border: hsl(215 20% 20%);
}
:root {
--trc-today-bg: hsl(262 80% 95%);
--trc-today-text: hsl(262 80% 45%);
}
.dark {
--trc-today-bg: hsl(262 50% 20%);
--trc-today-text: hsl(262 80% 75%);
}
:root {
--trc-hour-height: 5rem;
}
:root {
--trc-hour-height: 2rem;
}
:root {
--trc-radius: 0.75rem;
}
:root {
--trc-radius: 0;
}
:root {
--trc-event-default: #6366f1; /* Indigo */
}
.dark {
--trc-event-default: #818cf8;
}

Each event can have its own color via the color property:

const events = [
{ id: "1", title: "Meeting", start: "...", end: "...", color: "#3b82f6" },
{ id: "2", title: "Lunch", start: "...", end: "...", color: "#10b981" },
{ id: "3", title: "Deadline", start: "...", end: "...", color: "#ef4444" },
];

Events without a color use var(--trc-event-default).

trud-calendar is designed to integrate seamlessly with shadcn/ui. Each --trc-* variable falls back to the corresponding shadcn variable:

--trc-background: var(--background, #ffffff);
--trc-primary: var(--primary, #171717);
--trc-border: var(--border, #e5e5e5);

If your project uses shadcn v2 (the default since Tailwind v4), variables use full color values like oklch(...) or hsl(...). The calendar inherits your theme automatically — no extra configuration needed.

shadcn v1 defines CSS variables as raw HSL numbers without the hsl() wrapper:

/* shadcn v1 defines variables like this */
:root {
--border: 214.3 31.8% 91.4%;
--background: 0 0% 100%;
}

These raw values are not valid CSS colors. Since var(--border) resolves to a non-empty string, the fallback never activates — but the value 214.3 31.8% 91.4% isn’t a valid color either.

Fix: Map the variables explicitly, wrapping them with hsl():

:root {
--trc-background: hsl(var(--background));
--trc-foreground: hsl(var(--foreground));
--trc-muted: hsl(var(--muted));
--trc-muted-foreground: hsl(var(--muted-foreground));
--trc-border: hsl(var(--border));
--trc-primary: hsl(var(--primary));
--trc-primary-foreground: hsl(var(--primary-foreground));
--trc-accent: hsl(var(--accent));
--trc-accent-foreground: hsl(var(--accent-foreground));
--trc-card: hsl(var(--card));
--trc-card-foreground: hsl(var(--card-foreground));
--trc-ring: hsl(var(--ring));
}
.dark {
--trc-background: hsl(var(--background));
--trc-foreground: hsl(var(--foreground));
--trc-muted: hsl(var(--muted));
--trc-muted-foreground: hsl(var(--muted-foreground));
--trc-border: hsl(var(--border));
--trc-primary: hsl(var(--primary));
--trc-primary-foreground: hsl(var(--primary-foreground));
--trc-accent: hsl(var(--accent));
--trc-accent-foreground: hsl(var(--accent-foreground));
--trc-card: hsl(var(--card));
--trc-card-foreground: hsl(var(--card-foreground));
--trc-ring: hsl(var(--ring));
}

trud-calendar’s variables are defined inside @layer trc — a low-priority cascade layer. This means your overrides always win, whether they’re in @layer base, @layer theme, or unlayered CSS.

For this to work correctly, import trud-calendar/styles.css before @import "tailwindcss" in your CSS:

@import "trud-calendar/styles.css"; /* @layer trc — declared first = lowest priority */
@import "tailwindcss";
@source "../node_modules/trud-calendar/dist";

This ensures @layer trc is declared first in the cascade, giving it the lowest priority. Your overrides from any context will take precedence.