Theme Customization Guide
DayFlow Calendar's appearance is controlled by a set of namespaced CSS custom properties (--df-color-*). All tokens are defined inside @layer df-theme, so host applications can override them without !important — regardless of whether they use Tailwind or plain CSS.
DayFlow's shared foundation CSS is the single source of truth for theme tokens, component primitives, and semantic helper classes such as df-fill-primary and df-focus-ring.
Table of Contents
- Custom Calendar Type Colors
- CSS Variable Reference
- Override Methods
- Tailwind v4 Integration
- Creating a Custom Theme
Custom Calendar Type Colors
Basic Custom Colors
Define unique colors for each calendar type with separate light and dark variants:
const calendar = useCalendarApp({
calendarTypes: [
{
id: 'personal',
name: 'Personal',
colors: {
lineColor: '#0891b2', // cyan-600
eventColor: '#cffafe', // cyan-100
eventSelectedColor: '#a5f3fc', // cyan-200
textColor: '#164e63', // cyan-900
},
darkColors: {
lineColor: '#22d3ee', // cyan-400
eventColor: '#164e63', // cyan-900
eventSelectedColor: '#083344', // cyan-950
textColor: '#cffafe', // cyan-100
},
},
],
});Color Properties Explained
- lineColor: Border and accent color (left bar on events)
- eventColor: Event background fill
- eventSelectedColor: Background fill when an event is selected
- textColor: Text color for event title and time
Brand Color Integration
{
id: 'brand',
name: 'Brand Events',
colors: {
lineColor: '#6366f1', // Brand indigo
eventColor: '#e0e7ff', // Indigo-100
eventSelectedColor: '#c7d2fe', // Indigo-200
textColor: '#312e81', // Indigo-900
},
darkColors: {
lineColor: '#a5b4fc', // Indigo-300
eventColor: '#312e81', // Indigo-900
eventSelectedColor: '#1e1b4b', // Indigo-950
textColor: '#e0e7ff', // Indigo-100
},
}Color Generation Helper
// utils/colorGenerator.ts
interface ColorSet {
lineColor: string;
eventColor: string;
eventSelectedColor: string;
textColor: string;
}
export function generateLightColors(baseColor: string): ColorSet {
return {
lineColor: baseColor,
eventColor: lighten(baseColor, 0.9),
eventSelectedColor: lighten(baseColor, 0.8),
textColor: darken(baseColor, 0.4),
};
}
export function generateDarkColors(baseColor: string): ColorSet {
return {
lineColor: lighten(baseColor, 0.3),
eventColor: darken(baseColor, 0.6),
eventSelectedColor: darken(baseColor, 0.7),
textColor: lighten(baseColor, 0.8),
};
}CSS Variable Reference
All DayFlow theme tokens use the --df-color- prefix to avoid collisions with any host application variables.
| Variable | Purpose |
|---|---|
--df-color-background | Calendar background |
--df-color-foreground | Primary text color |
--df-color-hover | Hover state background |
--df-color-border | Borders and dividers |
--df-color-card | Card / panel background |
--df-color-card-foreground | Card text color |
--df-color-muted | Subtle background areas |
--df-color-muted-foreground | Muted / secondary text |
--df-color-primary | Primary accent (buttons, selected states) |
--df-color-primary-foreground | Text on primary-colored backgrounds |
--df-color-secondary | Secondary accent |
--df-color-secondary-foreground | Text on secondary-colored backgrounds |
--df-color-destructive | Destructive actions |
--df-color-destructive-foreground | Text on destructive backgrounds |
Semantic Helper Classes
In addition to CSS variables, DayFlow now exposes theme-aware semantic helper classes. These are useful when you render custom content inside slots, plugin UI, or wrappers that should visually match the built-in controls.
| Class Name | Meaning |
|---|---|
df-fill-primary | Primary filled surface with automatic foreground text |
df-fill-secondary | Secondary filled surface with automatic foreground text |
df-fill-destructive | Destructive filled surface with automatic foreground text |
df-tint-primary | Subtle primary selection tint |
df-hover-primary | Primary-themed hover state |
df-hover-primary-solid | Hover state for filled primary buttons |
df-text-primary | Primary text color |
df-border-primary | Primary border color |
df-ring-primary | Primary ring color token |
df-focus-ring | Focus helper that applies both primary border and ring |
Prefer these df-* helpers or --df-color-* overrides when styling DayFlow. Avoid depending on old internal Tailwind semantic names such as bg-primary, text-primary, or hover:bg-primary/90.
Override Methods
Method 1 — Container-level override (works everywhere)
Set variables directly on .df-calendar-container. Because this rule is outside any @layer, it always wins over the library's defaults regardless of Tailwind version or CSS setup.
In practice, you will often want to target both .df-calendar-container and .df-portal:
.df-calendar-containeris the root container for the visible calendar surface.df-portalis the root class DayFlow uses for floating UI rendered via portals intodocument.body, such as dialogs, dropdowns, and some picker panels
If you only override .df-calendar-container, the main calendar can pick up your theme while portal-based overlays may still use the default tokens.
/* styles/globals.css */
.df-calendar-container,
.df-portal {
--df-color-primary: #6366f1;
--df-color-background: #f9fafb;
--df-color-border: #e0e7ff;
}
/* Dark mode — target the container when the .dark class is on an ancestor */
.dark .df-calendar-container,
.dark .df-portal {
--df-color-primary: #a5b4fc;
--df-color-background: #1e1e2e;
--df-color-border: #312e81;
}This is the recommended approach for most projects. The variables are scoped to the calendar and do not affect anything outside it.
Tailwind v4 Integration
Tailwind v4 is configured entirely through CSS — there is no tailwind.config.js. DayFlow's distributed CSS already includes the shared foundation layer, so theme tokens and semantic helper classes are available immediately after import.
Choosing the right CSS file
DayFlow ships two CSS bundles:
| File | Contents | Use when |
|---|---|---|
styles.css | Full bundle including Tailwind preflight (CSS reset) | Not using Tailwind |
styles.components.css | Component styles only, no CSS reset | Already using Tailwind |
Minimal setup
/* app.css — for Tailwind projects */
@import '@dayflow/core/dist/styles.components.css';
@import 'tailwindcss';If your project does not use Tailwind, import the full bundle instead:
Utility class scanning (@source)
DayFlow's components still use Tailwind utility classes (flex, bg-white, text-gray-600, etc.) internally for layout and neutral styling. Your project's Tailwind instance must generate those classes by scanning DayFlow's files. The old semantic Tailwind names (bg-primary, text-primary, etc.) are no longer required by DayFlow internals.
When you import @dayflow/core/dist/styles.components.css, Tailwind still needs to scan DayFlow's published JavaScript files to discover the utility classes used inside the library. Tailwind's automatic source detection does not scan node_modules, so this @source step is required even with Vite + @tailwindcss/vite.
Add @source directives in your CSS entry file:
@import '@dayflow/core/dist/styles.components.css';
@import 'tailwindcss';
/* Required when consuming the published npm package */
@source '../node_modules/@dayflow/core/dist/**/*.js';
@source '../node_modules/@dayflow/plugin-drag/dist/**/*.js'; /* if using drag plugin */
@source '../node_modules/@dayflow/plugin-sidebar/dist/**/*.js'; /* if using sidebar plugin */Without this, Tailwind will not generate many of the utility classes used inside DayFlow's components and the UI will appear partially or completely unstyled.
If you are working against a local DayFlow workspace checkout instead of the published npm package, point @source at the relevant workspace source directories instead.
/* app.css — for non-Tailwind projects */
@import '@dayflow/core/dist/styles.css';Dark mode
DayFlow respects the .dark class on any ancestor element (including <html>). Apply it with JavaScript:
document.documentElement.classList.toggle('dark', isDark);System-preference dark mode (no explicit class) is also supported automatically.
Using theme.mode with Tailwind v4
Tailwind v4 compiles dark: utility classes as @media (prefers-color-scheme: dark) by default. DayFlow's theme.mode works by adding the .dark class to <html> — so if your Tailwind instance uses media-query dark mode, those dark: utilities inside DayFlow's components will not respond to the class change.
Add this to your CSS entry file to switch Tailwind to class-based dark mode:
@import '@dayflow/core/dist/styles.components.css';
@import 'tailwindcss';
/* Required for theme.mode to work: make dark: utilities respond to .dark class */
@variant dark (.dark &);Without this, theme.mode: 'dark' will still update the CSS custom properties (--df-color-*) correctly, but Tailwind's dark: utility classes inside DayFlow's components will only activate when the system preference is dark.
Practical recommendation
For most apps, use this order of precedence:
- Override
--df-color-*tokens on.df-calendar-container,.df-portal, or your own wrapper. - Reuse DayFlow's semantic helpers such as
df-fill-primaryanddf-focus-ringin custom slot/plugin markup. - Only reach for low-level utility overrides when you are changing layout rather than theme.
Applying overrides with @theme
@import '@dayflow/core/dist/styles.css' layer(dayflow);
@import 'tailwindcss';
@theme {
--df-color-primary: #6366f1;
--df-color-primary-foreground: #ffffff;
--df-color-secondary: #8b5cf6;
--df-color-secondary-foreground: #ffffff;
}Wrapping the calendar with Tailwind utilities
function ThemedCalendar({ calendar }) {
return (
<div className='rounded-2xl shadow-xl ring-1 ring-gray-200 dark:ring-gray-700'>
<DayFlowCalendar calendar={calendar} />
</div>
);
}Creating a Custom Theme
// themes/oceanTheme.ts
export const oceanTheme = {
mode: 'light' as const,
calendarTypes: [
{
id: 'deep-ocean',
name: 'Deep Ocean',
colors: {
lineColor: '#0369a1',
eventColor: '#e0f2fe',
eventSelectedColor: '#bae6fd',
textColor: '#0c4a6e',
},
darkColors: {
lineColor: '#7dd3fc',
eventColor: '#0c4a6e',
eventSelectedColor: '#083344',
textColor: '#e0f2fe',
},
},
{
id: 'coral-reef',
name: 'Coral Reef',
colors: {
lineColor: '#ea580c',
eventColor: '#ffedd5',
eventSelectedColor: '#fed7aa',
textColor: '#7c2d12',
},
darkColors: {
lineColor: '#fb923c',
eventColor: '#7c2d12',
eventSelectedColor: '#431407',
textColor: '#ffedd5',
},
},
],
};Pair it with CSS variable overrides for full visual consistency:
/* Ocean theme CSS tokens */
.df-calendar-container {
--df-color-primary: #0369a1;
--df-color-background: #f0f9ff;
--df-color-border: #bae6fd;
}
.dark .df-calendar-container {
--df-color-primary: #7dd3fc;
--df-color-background: #0c1220;
--df-color-border: #0c4a6e;
}Resources
Tools
- WebAIM Contrast Checker - Test color contrast
- Coolors - Generate color palettes
- Color Contrast Analyzer - Desktop tool
Libraries
- chroma-js - Color manipulation
- tinycolor2 - Color utilities
- color - Color conversion
Tailwind Resources
- Tailwind v4 Upgrade Guide - v3 → v4 migration
- Tailwind CSS Layers - Layer documentation
Related Documentation
- Dark Mode - Dark mode overview and API
- Calendar Types - Event categorization
- Use Calendar App - Core configuration