CustomizationApplies toLite and Pro
Themes, tokens, Tailwind and dark mode
Import the stylesheet once: it lives in @layer super-scheduler with zero-specificity selectors, so your unlayered CSS wins without !important. Re-skin with --super-scheduler-* tokens; component tokens work anywhere, but the accent family must be set on :root and .dark (site-wide, default colorScheme 'inherit') or on the control root with an explicit colorScheme. Dark mode follows an ancestor .dark class or data-theme attribute by default, and the operating system only with colorScheme: 'auto'.
SuperScheduler is styled entirely through CSS custom properties, classes and data attributes. You can make it match your product without forking the stylesheet and without !important. This guide describes the contract, the recipes that reliably reach every color, dark mode, per-slot classes, density, the unstyled mode and Tailwind. Most of it applies to Pro; the last section covers Lite.
The CSS contract
Import the stylesheet once, next to your application's own CSS:
@import 'super-scheduler/styles.css';(In a bundler you can also write import 'super-scheduler/styles.css' in a module.) The file is a single @layer super-scheduler block of zero-specificity :where() selectors. Any unlayered rule of yours wins over it, whatever its specificity.
| Surface | Selector or prefix |
|---|---|
| Root | .super-scheduler, with data-color-scheme, data-density, data-lod and data-unstyled |
| Parts | .super-scheduler__* classes (__event, __event-inner, __event-bar, __cell, __row-header-cell, __link, ...) |
| Part markers | [data-super-scheduler-part="event"], "cell", "event-meta", "now-line", "minimap", "pane", ... |
| State | data-selected, data-hovered, data-dragging, data-conflict, data-today, data-weekend, data-disabled, data-expanded, ... |
| React content | data-super-scheduler-slot, data-super-scheduler-slot-ready, data-super-scheduler-fallback |
| Tokens | --super-scheduler-* |
Do not depend on chunk file names or generated identifiers. Geometry tokens such as --super-scheduler-cell-width, -row-height and -row-header-width are written by the engine: change sizes through options (cellWidth, eventHeight, rowHeaderWidth), not CSS.
Tokens
Tokens come in three kinds, and where they are declared decides where you can override them.
- Primitives, declared on
:root:--super-scheduler-white,neutral-50toneutral-950,accent-50toaccent-950,danger-400/600,warning-400/600,success-400/600,duration,ease,ease-spring. - Semantic tokens, declared for each scheme (light values on
:rootand light scopes, dark values on.dark,[data-theme="dark"]and[data-color-scheme="dark"]):
| Group | Tokens (all prefixed --super-scheduler-) |
|---|---|
| Surfaces and text | surface, surface-raised, text, text-muted, on-accent |
| Lines | border, border-strong, grid-line, grid-break, row-line |
| Accent | accent-rgb, accent, accent-emphasis, accent-soft, accent-text, accent-border |
| Status | danger, danger-soft, warning, warning-soft, success |
| Calendar shading | today-bg, weekend-bg, non-business-bg, group, group-row, hatch |
| Interaction | hover-row, hover-cell, selection, selection-border, focus-ring, micro-bar, duration-bar |
| Overlays and loading | overlay, overlay-text, overlay-muted, overlay-subtle, overlay-border, skeleton-base, skeleton-highlight, shadow-1 to shadow-3 |
- Component tokens, never declared: they are read with a fallback where they are used, so you can set them on any ancestor. Examples:
--super-scheduler-radius,-event-radius,-event-padding,-event-bg,-event-text,-event-border,-event-bar,-link,-link-hover,-now-line,-handle-target,-hover-*,-minimap-*,-pane-splitter,-zoom-hud-bg.
Re-skinning recipes that work
Because semantic tokens are declared per scheme, and a few are declared again on the control root, not every override reaches every tint. These four recipes were checked in the browser against the 0.1.0 stylesheet.
Component tokens anywhere
Component tokens work on any ancestor, in every color scheme mode:
.planning {
--super-scheduler-radius: 8px;
--super-scheduler-event-radius: 6px;
--super-scheduler-event-padding: 2px 6px;
--super-scheduler-link: #8b5cf6;
--super-scheduler-now-line: #e11d48;
}Neutral colors on a wrapper
With the default colorScheme: 'inherit', most semantic tokens can be overridden on a wrapper element: surfaces, text, lines, shading and selection tints.
.planning {
--super-scheduler-surface: #fbfaf7;
--super-scheduler-border: #e6e1d6;
--super-scheduler-weekend-bg: rgb(120 100 60 / 0.05);
}
.dark .planning {
--super-scheduler-surface: #16140f;
--super-scheduler-border: rgb(255 255 255 / 0.08);
}Two limits: with an explicit colorScheme ('light', 'dark' or 'auto'), the control root declares the semantic tokens itself and a wrapper no longer reaches them (use the last recipe). And accent, duration-bar, grid-break, hatch, overlay-text, overlay-muted, overlay-subtle and the scrollbar color are always declared on the root.
A brand accent for the whole site
The accent family is computed from one channel token, --super-scheduler-accent-rgb (three space-separated numbers), plus a few primitives for the soft fills and text. Declare them unlayered on :root and on the dark scope your site uses:
:root {
--super-scheduler-accent-rgb: 10 140 80;
--super-scheduler-accent-100: #e0f2e9; /* accent-soft: event fills */
--super-scheduler-accent-200: #c9e6d6; /* border in light mode */
--super-scheduler-accent-800: #04361f; /* accent-text, accent-emphasis */
}
:root.dark {
--super-scheduler-accent-rgb: 80 200 140;
--super-scheduler-accent-950: #0c2a1b; /* accent-soft in dark mode */
--super-scheduler-accent-100: #d8f5e6; /* accent-text in dark mode */
}Selection, today, focus ring, hover tints and micro-bars all follow, in both schemes. This recipe needs the default colorScheme: 'inherit'.
A brand accent for one scheduler
To brand one scheduler, or to use an explicit colorScheme, put a class on the control root with cssClass and target it together with the scheme attribute:
.brand[data-color-scheme='light'] {
--super-scheduler-accent-rgb: 10 140 80;
--super-scheduler-accent-100: #e0f2e9;
--super-scheduler-accent-200: #c9e6d6;
--super-scheduler-accent-800: #04361f;
}
.brand[data-color-scheme='dark'] {
--super-scheduler-accent-rgb: 80 200 140;
--super-scheduler-accent-950: #0c2a1b;
--super-scheduler-accent-100: #d8f5e6;
}
.job--late {
--super-scheduler-event-bg: var(--super-scheduler-danger-soft);
--super-scheduler-event-border: var(--super-scheduler-danger);
--super-scheduler-event-text: var(--super-scheduler-danger);
}import { useMemo } from 'react'
import { SuperSchedulerComponent } from 'super-scheduler'
import type { SchedulerProps, SuperScheduler } from 'super-scheduler'
import 'super-scheduler/styles.css'
type Job = { status: 'planned' | 'late' | 'done' }
// Stable objects: a new classNames or styles object is applied again on every render.
const CLASS_NAMES: SuperScheduler.SchedulerClassNames = {
event: 'planning-event',
rowHeaderCell: 'planning-row-header',
}
const STYLES: SuperScheduler.SchedulerStyles = {
timeHeaderCell: { fontVariantNumeric: 'tabular-nums' },
}
// A class per state; the colors live in CSS, so they follow light and dark mode.
const statusClass: NonNullable<SchedulerProps['onBeforeEventRender']> = (args) => {
const job = args.data as SuperScheduler.EventRenderData<Job>
args.data.cssClass = `job job--${job.status}`
}
export function BrandedPlanner(props: {
theme: 'light' | 'dark'
resources: SuperScheduler.ResourceData[]
jobs: SuperScheduler.EventData<Job>[]
}) {
const events = useMemo(() => props.jobs.slice(), [props.jobs])
return (
<SuperSchedulerComponent
// Extra class on the control root: the brand CSS targets `.brand[data-color-scheme=...]`.
cssClass="brand"
// Follows the application's own theme switch.
colorScheme={props.theme}
density="compact"
classNames={CLASS_NAMES}
styles={STYLES}
onBeforeEventRender={statusClass}
startDate="2026-10-01"
days={31}
scale="Day"
resources={props.resources}
events={events}
/>
)
}You should see green selection and event fills in light mode, their dark counterparts when theme is 'dark', and late jobs in the danger colors of each scheme. With colorScheme: 'auto', write the same rules on .brand and on .brand inside @media (prefers-color-scheme: dark).
Color scheme and dark mode
colorScheme is written to the root as data-color-scheme:
| Value | Follows |
|---|---|
'inherit' (default) | The nearest ancestor with the dark class or a data-theme="dark" / data-theme="light" attribute |
'light', 'dark' | Forced; beats any ancestor |
'auto' | The operating system's prefers-color-scheme |
Only 'auto' reads the operating system. A light site on a dark system stays light with the default mode, which is usually what a product with its own theme switch wants. Tailwind's class-based dark mode (<html class="dark">) works with 'inherit' as is.
Menus, bubbles and hover cards are mounted in document.body, outside the scheduler. They carry the scheduler's scheme and tokens, so they match it.
Per-event colors and states
Simple colors need no CSS: backColor, fontColor, borderColor, barColor and barBackColor on the event data, or set in onBeforeEventRender. SuperScheduler.ColorUtil.contrasting(color) picks a readable text color.
For colors that should follow dark mode, prefer a class per state and component tokens, as in the recipe above: cssClass on the event, then --super-scheduler-event-bg, -event-text, -event-border and -event-bar in CSS. Events also expose interaction state as attributes: data-hovered, data-dragging and data-resizing on the event being changed, and data-conflict with conflictHighlight. Selected events need a class of your own in 0.1.0 (see event selection). The thin duration bar uses --super-scheduler-duration-bar globally or --super-scheduler-event-bar per event; durationBarVisible: false removes it.
classNames and styles
classNames and styles add classes or inline styles to named parts. The slots are root, scroll, corner, timeHeader, timeHeaderCell, rowHeader, rowHeaderCell, treeToggle, grid, row, cell, event, eventInner, eventBar, area, separator, link, selection, shadow, rectangle, crosshair, dragCard, tooltip, skeleton, empty, error and message.
They accept Tailwind utilities as well as your own class names. Define both objects at module level or memoize them, and leave position, size, overflow and z-index to the engine: changing them breaks hit testing, sticky headers and virtualization. cssClass is a shortcut for one more class on the root.
Density
density changes the default sizes without touching your options:
| Preset | Event height | Header height | Also |
|---|---|---|---|
'comfortable' | 35 px | 30 px | Default look |
'compact' | 28 px | 26 px | 2px 4px event padding, 11 px event text |
'dense' | 20 px | 22 px | 0 4px padding, 10 px text, hides [data-super-scheduler-part="event-meta"] |
The preset sizes apply only while eventHeight and headerHeight keep their defaults; explicit values win. control.update({ density: 'dense' }) relays out without remounting. Dense rows are hard targets on touch screens; offer them as a choice rather than a default on phones.
Unstyled mode
For a fully custom look, set unstyled and do not import the stylesheet. The root gets data-unstyled, the engine keeps writing only structural inline styles (positions, sizes, stacking, sticky headers), and the live region for announcements stays visually hidden. Everything else is yours, including focus indicators, selection, hover and forced-colors support.
Tailwind
The tokens are the contract; Tailwind is one way to consume them.
Tailwind v3
super-scheduler/tailwind is a v3 preset. It adds colors (bg-super-scheduler-surface, text-super-scheduler-text-muted, bg-super-scheduler-accent/20, text-super-scheduler-ink/60, ...), radii (rounded-super-scheduler, rounded-super-scheduler-event), shadows (shadow-super-scheduler-1 to -3), and duration-super-scheduler / ease-super-scheduler.
import superSchedulerPreset from 'super-scheduler/tailwind'
// Tailwind v3. The preset adds colors, radii, shadows and easing utilities that read the
// --super-scheduler-* tokens (bg-super-scheduler-surface, rounded-super-scheduler-event...),
// so they follow the scheduler's light and dark values.
// In a CommonJS config: presets: [require('super-scheduler/tailwind')].
export default {
content: ['./index.html', './src/**/*.{ts,tsx}'],
darkMode: 'class',
presets: [superSchedulerPreset],
}Tailwind v3's preflight is unlayered, so it would beat the library's layered borders. Put preflight in a layer below the library:
@layer tw-base, super-scheduler;
@import 'super-scheduler/styles.css';
@layer tw-base {
@tailwind base;
}
@tailwind components;
@tailwind utilities;Tailwind v4
Tailwind v4 has no presets: map the tokens in @theme inline, after declaring the layer order. Inline mappings read the nearest token value, so nested dark themes work, and opacity modifiers such as bg-super-scheduler-accent/20 work too.
@layer theme, base, super-scheduler, components, utilities;
@import 'tailwindcss';
@import 'super-scheduler/styles.css';
@custom-variant dark (&:where(.dark, .dark *));
@theme inline {
--color-super-scheduler-surface: var(--super-scheduler-surface);
--color-super-scheduler-surface-raised: var(--super-scheduler-surface-raised);
--color-super-scheduler-text: var(--super-scheduler-text);
--color-super-scheduler-text-muted: var(--super-scheduler-text-muted);
--color-super-scheduler-border: var(--super-scheduler-border);
--color-super-scheduler-border-strong: var(--super-scheduler-border-strong);
--color-super-scheduler-grid-line: var(--super-scheduler-grid-line);
--color-super-scheduler-grid-break: var(--super-scheduler-grid-break);
--color-super-scheduler-row-line: var(--super-scheduler-row-line);
--color-super-scheduler-accent: var(--super-scheduler-accent);
--color-super-scheduler-accent-emphasis: var(--super-scheduler-accent-emphasis);
--color-super-scheduler-accent-soft: var(--super-scheduler-accent-soft);
--color-super-scheduler-accent-text: var(--super-scheduler-accent-text);
--color-super-scheduler-accent-border: var(--super-scheduler-accent-border);
--color-super-scheduler-on-accent: var(--super-scheduler-on-accent);
--color-super-scheduler-ink: rgb(var(--super-scheduler-ink-rgb));
--color-super-scheduler-danger: var(--super-scheduler-danger);
--color-super-scheduler-danger-soft: var(--super-scheduler-danger-soft);
--color-super-scheduler-warning: var(--super-scheduler-warning);
--color-super-scheduler-warning-soft: var(--super-scheduler-warning-soft);
--color-super-scheduler-success: var(--super-scheduler-success);
--color-super-scheduler-today-bg: var(--super-scheduler-today-bg);
--color-super-scheduler-weekend-bg: var(--super-scheduler-weekend-bg);
--color-super-scheduler-non-business-bg: var(--super-scheduler-non-business-bg);
--color-super-scheduler-group: var(--super-scheduler-group);
--color-super-scheduler-group-row: var(--super-scheduler-group-row);
--color-super-scheduler-hover-row: var(--super-scheduler-hover-row);
--color-super-scheduler-hover-cell: var(--super-scheduler-hover-cell);
--color-super-scheduler-selection: var(--super-scheduler-selection);
--color-super-scheduler-selection-border: var(--super-scheduler-selection-border);
--color-super-scheduler-focus-ring: var(--super-scheduler-focus-ring);
--color-super-scheduler-micro-bar: var(--super-scheduler-micro-bar);
--color-super-scheduler-duration-bar: var(--super-scheduler-duration-bar);
--color-super-scheduler-hatch: var(--super-scheduler-hatch);
--color-super-scheduler-overlay: var(--super-scheduler-overlay);
--color-super-scheduler-overlay-text: var(--super-scheduler-overlay-text);
--color-super-scheduler-overlay-muted: var(--super-scheduler-overlay-muted);
--color-super-scheduler-overlay-subtle: var(--super-scheduler-overlay-subtle);
--color-super-scheduler-overlay-border: var(--super-scheduler-overlay-border);
--color-super-scheduler-skeleton-base: var(--super-scheduler-skeleton-base);
--color-super-scheduler-skeleton-highlight: var(--super-scheduler-skeleton-highlight);
--radius-super-scheduler: var(--super-scheduler-radius, 12px);
--radius-super-scheduler-event: var(--super-scheduler-event-radius, 8px);
--shadow-super-scheduler-1: var(--super-scheduler-shadow-1);
--shadow-super-scheduler-2: var(--super-scheduler-shadow-2);
--shadow-super-scheduler-3: var(--super-scheduler-shadow-3);
--ease-super-scheduler: var(--super-scheduler-ease);
--ease-super-scheduler-spring: var(--super-scheduler-ease-spring);
}Lite theming
Lite (super-scheduler-lite/styles.css) has six tokens: --super-scheduler-background, -text, -border, -header, -event and -focus. They are declared on the .super-scheduler-lite element itself, so set them on that element, not on an ancestor:
.bookings .super-scheduler-lite {
--super-scheduler-event: #dcfce7;
--super-scheduler-focus: #15803d;
}
.dark .bookings .super-scheduler-lite {
--super-scheduler-background: #111827;
--super-scheduler-text: #f3f4f6;
--super-scheduler-border: #374151;
--super-scheduler-header: #1f2937;
--super-scheduler-event: #14532d;
}Lite has no built-in dark scheme, so dark values are yours to declare. Its stylesheet is also layered, and it adapts borders and focus to forced colors. Per event, Lite accepts backColor, fontColor and cssClass.
What your application owns
- Contrast of every color you introduce, in light, dark and high-contrast modes.
- The theme switch itself: where the
darkclass ordata-themegoes, or whichcolorSchemeyou pass. - Styles for custom content (HTML from hooks, React slots, hover cards): use the tokens so it follows the scheme.
- Checking your overrides after upgrades. Tokens and part selectors are the stable contract; internal class order and markup between parts are not.
Related
Sports club court bookingA padel net snaps mid-morning. Move the clinic, close the court and keep every coach where the rota says. Festival stage schedulingA line check runs into a set’s buffer. Zoom to five minutes, trim it, and see every room and crew the act depends on. Hotel room planningA shower leaks in Room 104. Rehouse the next guest, block the room for the plumber and find the nights that are already full.
- Keyboard, accessibility and touch for reduced motion, high contrast and forced colors.
- React render slots and hover cards for the hover card tokens.
Related examples
- Court ClubSports club court bookingA padel net snaps mid-morning. Move the clinic, close the court and keep every coach where the rota says.
- Aurora LiveFestival stage schedulingA line check runs into a set’s buffer. Zoom to five minutes, trim it, and see every room and crew the act depends on.
- Casa NomaHotel room planningA shower leaks in Room 104. Rehouse the next guest, block the room for the plumber and find the nights that are already full.