Hapio Calendar

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, …):

VariableDefaultUsed for
--calendar-eventvar(--primary)an event's fill when it has no color
--calendar-event-foregroundvar(--primary-foreground)its text
--calendar-background-eventvar(--chart-3)background events
--calendar-todayvar(--chart-1)today's column and badge
--calendar-nowvar(--destructive)the now indicator
--calendar-selectionvar(--chart-1) at 20 %a selection's tint
--calendar-slot-height1.75remone time slot
--calendar-bar-height1.625remone event row in a day grid or all-day row
--calendar-axis-width3.5remthe 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.

Print

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.