Skip to content
SuperScheduler

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'.

Verified against v0.1.0 · reviewed October 7, 2026.md

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:

csscss
@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.

SurfaceSelector 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", ...
Statedata-selected, data-hovered, data-dragging, data-conflict, data-today, data-weekend, data-disabled, data-expanded, ...
React contentdata-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-50 to neutral-950, accent-50 to accent-950, danger-400/600, warning-400/600, success-400/600, duration, ease, ease-spring.
  • Semantic tokens, declared for each scheme (light values on :root and light scopes, dark values on .dark, [data-theme="dark"] and [data-color-scheme="dark"]):
GroupTokens (all prefixed --super-scheduler-)
Surfaces and textsurface, surface-raised, text, text-muted, on-accent
Linesborder, border-strong, grid-line, grid-break, row-line
Accentaccent-rgb, accent, accent-emphasis, accent-soft, accent-text, accent-border
Statusdanger, danger-soft, warning, warning-soft, success
Calendar shadingtoday-bg, weekend-bg, non-business-bg, group, group-row, hatch
Interactionhover-row, hover-cell, selection, selection-border, focus-ring, micro-bar, duration-bar
Overlays and loadingoverlay, 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:

csscss
.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.

csscss
.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:

csscss
: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:

csscss
.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);
}
src/BrandedPlanner.tsxtsx
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:

ValueFollows
'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:

PresetEvent heightHeader heightAlso
'comfortable'35 px30 pxDefault look
'compact'28 px26 px2px 4px event padding, 11 px event text
'dense'20 px22 px0 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.

tailwind.config.tsts
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:

csscss
@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.

csscss
@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:

csscss
.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 dark class or data-theme goes, or which colorScheme you 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.

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.