Skip to content

Theming

All colours come from CSS variables named --rs-*. Override them on :root, or on any ancestor of the scheduler.

:root {
--rs-primary: #7c3aed; /* focus, today's date, selection and drag footprint */
--rs-accent: #ede9fe; /* hover in the toolbar and menus */
--rs-today: #f8f6ff; /* today's column */
--rs-border: #d1d5db;
}

Popovers render in a portal outside the scheduler, so put overrides on :root (or on body) rather than on a wrapper.

Three ways, all setting the same dark tokens:

<html class="dark"> <!-- class, as in shadcn and Tailwind -->
<html data-theme="dark"> <!-- explicit attribute -->
<html data-theme="system"> <!-- follows prefers-color-scheme -->

Any ancestor works, not only html. Popovers render in a portal outside the scheduler, so put the class or attribute on html or body if you want them themed too. data-theme="system" is in the npm stylesheet only; the shadcn install keeps just .dark (shadcn cannot express a media query on an attribute), so there you toggle the class yourself. Your own event colours are yours to adapt.

--rs-radius (default 0.75rem) is the corner radius of the outer frame. Spacing is not a free variable: row and event sizes come from density, because the layout maths needs the real pixel heights.

Variable Used for
--rs-background, --rs-foreground Base surfaces and text
--rs-surface, --rs-surface-muted, --rs-surface-hover Grid, header and hover backgrounds
--rs-muted, --rs-muted-foreground The header band, group rows and secondary text
--rs-today Today’s column (week and longer views)
--rs-primary, --rs-ring Focus ring, the cursor, today’s date, and a tint of it for selections and the drag footprint. It is also small text and a pill background, so it should reach 4.5:1 against white (or against --rs-primary-foreground)
--rs-primary-text-mix How much of the primary is kept where it is text (100%; 60% in dark mode, mixed toward the foreground so it stays readable)
--rs-accent Hover in the toolbar and menus
--rs-unavailable The hatching on unavailable time
--rs-warning Utilization bars at 90% and over
--rs-destructive Rejected places, utilization over 100% and the now line
--rs-border, --rs-input Lines and inputs
--rs-popover, --rs-popover-foreground Event details popover
--rs-radius Corner radius of the outer frame
--rs-shadow-xs, --rs-shadow-sm, --rs-shadow, --rs-shadow-md Elevation

Every surface and text colour in the grid reads from these tokens, so a dark theme is just a second set of values. The demo above is one.

The stylesheet has no global reset and no body or html rules. Base styles are scoped to the class rs-root, and the variables are prefixed so they cannot clash with your own --background or --primary. This holds for the shadcn install too.

Tokens change colours everywhere. To restyle one part, pass classNames:

<ResourceScheduler
resources={resources}
classNames={{
toolbar: "border-b-2",
dateHeader: "uppercase tracking-wide",
resourceCell: "text-start font-semibold",
groupHeader: "bg-slate-100",
slot: "border-dashed",
event: "rounded-none shadow-md",
}}
/>
Part What it styles
root The outer element (.rs-root)
toolbar The bar with the date, arrows, Today and the view select
dateHeader Each column header: an hour in the day view, a day in the others
resourceCell The cell in the left column with a resource’s name
groupHeader A group header in the left column
slot Each empty slot of the grid
event The default event card (not one you draw with renderTimeSlot)

Your classes are merged after the defaults with tailwind-merge, so a utility that conflicts with a default replaces it: rounded-none removes the card’s rounded. Without classNames the output is exactly what it was.

The utilities have to exist in your CSS. The npm package ships a precompiled stylesheet with only the utilities it uses, so a class like bg-slate-100 works only if your own Tailwind build generates it. If it does not, the conflicting default is still removed and the part loses that style. With the shadcn install the source is in your project, so Tailwind sees the class. Plain CSS class names work in both: add the class, then write the rule yourself (and avoid a conflicting Tailwind utility if you want the default to stay).

density="compact" makes rows shorter so more resources fit on screen:

<ResourceScheduler resources={resources} density="compact" />

A lane of events is 38 px instead of 52, the event card is 34 px instead of 48, and the card shows its title and time on one line (the time is cut first when the card is narrow). Resource names get less padding. A row with a utilization bar is never shorter than 60 px, because the name and the bar need that much, so compact saves the most on rows with several lanes of events.

The default is "comfortable", which is exactly what the scheduler looked like before the option existed.

To replace whole parts (the toolbar, the event card, the popover, the headers) with your own components, use components. The older render props still work: renderEventPopover, renderTimeSlot (replace the event card), renderResourceHeader, renderDateHeader and renderEmptyCell. See the props reference.