Theming, dark mode and print
The calendar's tokens, how dark mode and forced colors follow your theme, and what prints.
The calendar's look comes from three places: your shadcn theme (the calendar's tokens are mapped
onto it), the calendar-theme item's CSS (the parts the class names cannot express, such as the
slot lines and the DST bands), and the Tailwind classes in the copied files.
Tokens
calendar-theme adds these CSS variables to :root, mapped onto your shadcn tokens, and the
matching Tailwind colors (bg-calendar-event, text-calendar-now, …):
| Variable | Default | Used for |
|---|---|---|
--calendar-event | var(--primary) | an event's fill when it has no color |
--calendar-event-foreground | var(--primary-foreground) | its text |
--calendar-background-event | var(--chart-3) | background events |
--calendar-today | var(--chart-1) | today's column and badge |
--calendar-now | var(--destructive) | the now indicator |
--calendar-selection | var(--chart-1) at 20 % | a selection's tint |
--calendar-slot-height | 1.75rem | one time slot |
--calendar-bar-height | 1.625rem | one event row in a day grid or all-day row |
--calendar-axis-width | 3.5rem | the time axis |
Change them for the whole app in your global CSS (:root { --calendar-slot-height: 2.5rem }), or
for one calendar on its root: className="h-[36rem] [--calendar-slot-height:2.5rem]".
An event's own color and contrastColor set --event-color and --event-contrast on its
element, which the fill and the text read. Pick pairs with enough contrast (4.5:1 for the text):
the calendar passes colors through and never computes one.
Dark mode
The tokens point at shadcn's, so a dark theme (.dark, or however your app switches) changes the
calendar with everything else, with nothing to configure. calendar-theme declares its color
tokens for the dark scheme as well, so they also follow a .dark set on an element below the
root (a dark panel in a light page): a token declared on :root alone would keep reading the
light values there. Event colors you set yourself are yours to adapt, for example with
light-dark() or a dark variant of your palette.
Forced colors
In forced-colors mode (Windows contrast themes) the browser replaces colors with the user's.
The calendar's own parts are ready for it: focus is a solid outline (which forced colors keep, where
they drop box shadows), and the selection, the slot cursor, mirrors, the drag preview and the
checked view use system colors (Highlight, CanvasText, GrayText).
One part is your component: the toolbar's buttons, the view switcher and the menus are your
shadcn Button and menu components, and shadcn's Button shows focus with a box-shadow ring and
outline-none, which forced colors leave without any visible focus indicator. Fix it once in your
components/ui/button.tsx, by adding an outline that only forced colors show:
forced-colors:focus-visible:outline-2 forced-colors:focus-visible:outline-solid (forced colors
give it the system's focus color). The calendar's files do not change your components.
Printing needs no setup: the browser's print shows the whole visible range without scrollbars (every time slot, every week, every list day), leaves out the toolbar, the now indicator, the actions buttons, mirrors and other screen-only parts, and keeps event colors on paper. The time grids print their events where they sit on screen, and the root's height does not apply in print.
A known limit of this version: Firefox does not always place the time grids' positioned events correctly when a week spans several printed pages, so events near a page break can be cut at the page edge there. There are no print options yet; a print layout of its own arrives in 1.1.
Motion
Animations (a pending change's stripes, a revert's shake, the loading bar) run only when the user
allows motion (motion-safe), and nothing depends on them to be understood.