# Temas, tokens, Tailwind y modo oscuro

> Estiliza el scheduler con tokens --super-scheduler-*, cambia el acento con todos sus tonos, sigue tu modo oscuro y configura Tailwind v3 o v4 con sus capas.

Source: https://superscheduler.org/es/docs/theming/
Reviewed: 2026-10-07

Importa la hoja de estilos una vez: vive en @layer super-scheduler con selectores de especificidad cero, así que tu CSS sin capa gana sin !important. Personalízalo con tokens --super-scheduler-*; los tokens de componente funcionan en cualquier sitio, pero la familia del acento debe fijarse en :root y .dark (para todo el sitio, con el colorScheme por defecto, 'inherit') o en la raíz del control con un colorScheme explícito. Por defecto, el modo oscuro sigue una clase .dark o un atributo data-theme de un ancestro, y solo sigue al sistema operativo con colorScheme: 'auto'.

SuperScheduler se estiliza por completo mediante propiedades CSS personalizadas, clases y atributos de datos. Puedes adaptarlo a tu producto sin hacer un fork de la hoja de estilos y sin `!important`. Esta guía describe el contrato, las recetas que llegan con fiabilidad a todos los colores, el modo oscuro, las clases por slot, la densidad, el modo sin estilos y Tailwind. Casi todo se aplica a Pro; la [última sección](#lite) trata Lite.

## El contrato CSS
Importa la hoja de estilos una sola vez, junto al CSS de tu aplicación:

```css
@import 'super-scheduler/styles.css';
```

(Con un bundler también puedes escribir `import 'super-scheduler/styles.css'` en un módulo). El archivo es un único bloque `@layer super-scheduler` de selectores `:where()` de especificidad cero. Cualquier regla tuya sin capa gana sobre él, sea cual sea su especificidad.

| Superficie | Selector o prefijo |
|---|---|
| Raíz | `.super-scheduler`, con `data-color-scheme`, `data-density`, `data-lod` y `data-unstyled` |
| Partes | Clases `.super-scheduler__*` (`__event`, `__event-inner`, `__event-bar`, `__cell`, `__row-header-cell`, `__link`, ...) |
| Marcadores de parte | `[data-super-scheduler-part="event"]`, `"cell"`, `"event-meta"`, `"now-line"`, `"minimap"`, `"pane"`, ... |
| Estado | `data-selected`, `data-hovered`, `data-dragging`, `data-conflict`, `data-today`, `data-weekend`, `data-disabled`, `data-expanded`, ... |
| Contenido React | `data-super-scheduler-slot`, `data-super-scheduler-slot-ready`, `data-super-scheduler-fallback` |
| Tokens | `--super-scheduler-*` |

No dependas de los nombres de los archivos de chunks ni de identificadores generados. Los tokens de geometría como `--super-scheduler-cell-width`, `-row-height` y `-row-header-width` los escribe el motor: cambia los tamaños mediante opciones (`cellWidth`, `eventHeight`, `rowHeaderWidth`), no con CSS.

## Tokens
Hay tres tipos de tokens, y el lugar donde se declaran decide dónde puedes sobrescribirlos.

- **Primitivos**, declarados en `:root`: `--super-scheduler-white`, de `neutral-50` a `neutral-950`, de `accent-50` a `accent-950`, `danger-400/600`, `warning-400/600`, `success-400/600`, `duration`, `ease`, `ease-spring`.
- **Tokens semánticos**, declarados para cada esquema (los valores claros en `:root` y en los ámbitos claros, los oscuros en `.dark`, `[data-theme="dark"]` y `[data-color-scheme="dark"]`):

| Grupo | Tokens (todos con el prefijo `--super-scheduler-`) |
|---|---|
| Superficies y texto | `surface`, `surface-raised`, `text`, `text-muted`, `on-accent` |
| Líneas | `border`, `border-strong`, `grid-line`, `grid-break`, `row-line` |
| Acento | `accent-rgb`, `accent`, `accent-emphasis`, `accent-soft`, `accent-text`, `accent-border` |
| Estados | `danger`, `danger-soft`, `warning`, `warning-soft`, `success` |
| Sombreado del calendario | `today-bg`, `weekend-bg`, `non-business-bg`, `group`, `group-row`, `hatch` |
| Interacción | `hover-row`, `hover-cell`, `selection`, `selection-border`, `focus-ring`, `micro-bar`, `duration-bar` |
| Superposiciones y carga | `overlay`, `overlay-text`, `overlay-muted`, `overlay-subtle`, `overlay-border`, `skeleton-base`, `skeleton-highlight`, de `shadow-1` a `shadow-3` |

- **Tokens de componente**, que nunca se declaran: se leen con un valor de reserva allí donde se usan, así que puedes fijarlos en cualquier ancestro. Por ejemplo: `--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`.

## Recetas de personalización que funcionan
Como los tokens semánticos se declaran por esquema, y algunos se vuelven a declarar en la raíz del control, no todas las sobrescrituras llegan a todos los tonos. Estas cuatro recetas se han comprobado en el navegador con la hoja de estilos de la 0.1.0.

### Tokens de componente en cualquier sitio
Los tokens de componente funcionan en cualquier ancestro y con cualquier modo de esquema de color:

```css
.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;
}
```

### Colores neutros en un elemento envolvente
Con el valor por defecto `colorScheme: 'inherit'`, la mayoría de los tokens semánticos se pueden sobrescribir en un elemento envolvente: superficies, texto, líneas, sombreados y tonos de selección.

```css
.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);
}
```

Dos límites: con un `colorScheme` explícito (`'light'`, `'dark'` o `'auto'`), la raíz del control declara ella misma los tokens semánticos y un elemento envolvente ya no llega a ellos (usa la última receta). Además, `accent`, `duration-bar`, `grid-break`, `hatch`, `overlay-text`, `overlay-muted`, `overlay-subtle` y el color de la barra de scroll se declaran siempre en la raíz.

### Un acento de marca para todo el sitio
La familia del acento se calcula a partir de un token de canales, `--super-scheduler-accent-rgb` (tres números separados por espacios), más algunos primitivos para los rellenos suaves y el texto. Decláralos sin capa en `:root` y en el ámbito oscuro que use tu sitio:

```css
: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 */
}
```

La selección, el día de hoy, el anillo de foco, los tonos de hover y las microbarras lo siguen, en ambos esquemas. Esta receta necesita el valor por defecto `colorScheme: 'inherit'`.

### Un acento de marca para un solo scheduler
Para aplicar la marca a un solo scheduler, o para usar un `colorScheme` explícito, pon una clase en la raíz del control con `cssClass` y apunta a ella junto con el atributo del esquema:

```css
.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);
}
```

```tsx
// src/BrandedPlanner.tsx
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}
    />
  )
}
```
Deberías ver la selección y los rellenos de los eventos en verde en modo claro, sus equivalentes oscuros cuando `theme` es `'dark'`, y los trabajos con retraso en los colores de peligro de cada esquema. Con `colorScheme: 'auto'`, escribe las mismas reglas sobre `.brand` y sobre `.brand` dentro de `@media (prefers-color-scheme: dark)`.

> **Behavior:**
> Hay tres sobrescrituras que parecen correctas pero no funcionan. Fijar `--super-scheduler-accent` en un ancestro queda tapado por la declaración propia de la raíz. Fijar `--super-scheduler-accent-rgb` en un elemento envolvente cambia el acento en sí, pero ninguno de los tonos que se calculan a partir de él. Fijar primitivos como `--super-scheduler-accent-100` en un elemento envolvente no cambia nada, porque los tokens semánticos ya los resolvieron en `:root`.

## Esquema de color y modo oscuro
`colorScheme` se escribe en la raíz como `data-color-scheme`:

| Valor | Sigue a |
|---|---|
| `'inherit'` (por defecto) | El ancestro más cercano con la clase `dark` o con un atributo `data-theme="dark"` / `data-theme="light"` |
| `'light'`, `'dark'` | Forzado; se impone a cualquier ancestro |
| `'auto'` | El `prefers-color-scheme` del sistema operativo |

Solo `'auto'` lee el sistema operativo. Un sitio claro en un sistema oscuro sigue en claro con el modo por defecto, que es normalmente lo que quiere un producto con su propio selector de tema. El modo oscuro basado en clases de Tailwind (`<html class="dark">`) funciona tal cual con `'inherit'`.

Los menús, las burbujas y las tarjetas emergentes se montan en `document.body`, fuera del scheduler. Llevan el esquema y los tokens del scheduler, así que encajan con él.

## Colores y estados por evento
Los colores simples no necesitan CSS: `backColor`, `fontColor`, `borderColor`, `barColor` y `barBackColor` en los datos del evento, o fijados en `onBeforeEventRender`. `SuperScheduler.ColorUtil.contrasting(color)` elige un color de texto legible.

Para colores que deben seguir el modo oscuro, usa preferentemente una clase por estado y tokens de componente, como en la receta de arriba: `cssClass` en el evento y después `--super-scheduler-event-bg`, `-event-text`, `-event-border` y `-event-bar` en el CSS. Los eventos también exponen su estado de interacción como atributos: `data-hovered`, `data-dragging` y `data-resizing` en el evento que se está cambiando, y `data-conflict` con `conflictHighlight`. En la 0.1.0, los eventos seleccionados necesitan una clase propia (consulta [selección de eventos](https://superscheduler.org/es/docs/trees-columns-selection/#event-selection)). La barra fina de duración usa `--super-scheduler-duration-bar` de forma global o `--super-scheduler-event-bar` por evento; `durationBarVisible: false` la quita.

## classNames y styles
`classNames` y `styles` añaden clases o estilos en línea a partes con nombre. Los slots son `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` y `message`.

Aceptan utilidades de Tailwind además de tus propios nombres de clase. Define ambos objetos a nivel de módulo o memorízalos, y deja la posición, el tamaño, el overflow y el z-index en manos del motor: cambiarlos rompe la detección de punteros, las cabeceras fijas y la virtualización. `cssClass` es un atajo para añadir una clase más a la raíz.

## Densidad
`density` cambia los tamaños por defecto sin tocar tus opciones:

| Preset | Alto de evento | Alto de cabecera | Además |
|---|---|---|---|
| `'comfortable'` | 35 px | 30 px | Aspecto por defecto |
| `'compact'` | 28 px | 26 px | Relleno de evento `2px 4px`, texto de evento de 11 px |
| `'dense'` | 20 px | 22 px | Relleno `0 4px`, texto de 10 px, oculta `[data-super-scheduler-part="event-meta"]` |

Los tamaños del preset solo se aplican mientras `eventHeight` y `headerHeight` mantienen sus valores por defecto; los valores explícitos ganan. `control.update({ density: 'dense' })` recalcula la disposición sin volver a montar. Las filas densas son objetivos difíciles en pantallas táctiles; en móviles, ofrécelas como opción y no como valor por defecto.

## Modo sin estilos
Para un aspecto totalmente personalizado, activa `unstyled` y no importes la hoja de estilos. La raíz recibe `data-unstyled`, el motor sigue escribiendo solo los estilos en línea estructurales (posiciones, tamaños, apilamiento, cabeceras fijas) y la región live de los anuncios sigue oculta visualmente. Todo lo demás es tuyo, incluidos los indicadores de foco, la selección, el hover y el soporte de colores forzados.

## Tailwind
Los tokens son el contrato; Tailwind es una forma de consumirlos.

### Tailwind v3
`super-scheduler/tailwind` es un preset para v3. Añade colores (`bg-super-scheduler-surface`, `text-super-scheduler-text-muted`, `bg-super-scheduler-accent/20`, `text-super-scheduler-ink/60`, ...), radios (`rounded-super-scheduler`, `rounded-super-scheduler-event`), sombras (de `shadow-super-scheduler-1` a `-3`) y `duration-super-scheduler` / `ease-super-scheduler`.

```ts
// tailwind.config.ts
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],
}
```
El preflight de Tailwind v3 no tiene capa, así que se impondría a los bordes de la librería, que sí la tienen. Pon el preflight en una capa por debajo de la librería:

```css
@layer tw-base, super-scheduler;

@import 'super-scheduler/styles.css';

@layer tw-base {
  @tailwind base;
}
@tailwind components;
@tailwind utilities;
```

### Tailwind v4
Tailwind v4 no tiene presets: mapea los tokens en `@theme inline`, después de declarar el orden de las capas. Los mapeos inline leen el valor del token más cercano, así que los temas oscuros anidados funcionan, y también los modificadores de opacidad como `bg-super-scheduler-accent/20`.

```css
@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);
}
```

## Temas en Lite
Lite (`super-scheduler-lite/styles.css`) tiene seis tokens: `--super-scheduler-background`, `-text`, `-border`, `-header`, `-event` y `-focus`. Se declaran en el propio elemento `.super-scheduler-lite`, así que fíjalos en ese elemento, no en un ancestro:

```css
.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 no tiene un esquema oscuro integrado, así que los valores oscuros los declaras tú. Su hoja de estilos también usa capas y adapta los bordes y el foco a los colores forzados. Por evento, Lite acepta `backColor`, `fontColor` y `cssClass`.

## De qué es dueña tu aplicación
- El contraste de cada color que introduces, en los modos claro, oscuro y de alto contraste.
- El propio selector de tema: dónde va la clase `dark` o `data-theme`, o qué `colorScheme` pasas.
- Los estilos del contenido personalizado (HTML de los hooks, slots de React, tarjetas emergentes): usa los tokens para que siga el esquema.
- Revisar tus sobrescrituras después de cada actualización. Los tokens y los selectores de partes son el contrato estable; el orden interno de las clases y el marcado entre partes no lo son.

## Relacionado
→ https://superscheduler.org/es/examples/sports-club-courts/
→ https://superscheduler.org/es/examples/festival-stages/
→ https://superscheduler.org/es/examples/hotel-rooms/
- [Teclado, accesibilidad y táctil](https://superscheduler.org/es/docs/keyboard-accessibility-touch/#motion-contrast) para el movimiento reducido, el alto contraste y los colores forzados.
- [Slots de renderizado React y tarjetas emergentes](https://superscheduler.org/es/docs/react-render-slots/) para los tokens de las tarjetas emergentes.
