Theming
CoolAdmin's two-stylesheet design system. css/theme.css carries the legacy token set under :root; css/app.css carries the modern overlay tokens under body.app. Six built-in accent presets. Dark mode hooks into Bootstrap's data-bs-theme attribute.
Last updated May 22, 2026
CoolAdmin ships two complete design systems side by side. Both are token-driven CSS — change a custom property, the cascade picks it up.
The two token surfaces
| Stylesheet | Activated by | Token prefix | Source |
|---|---|---|---|
css/theme.css | always loaded | --ca-* | src/scss/_variables.scss |
css/app.css | <body class="app"> | --m-* | src/scss/app/_variables.scss |
Pages opt into the modern overlay by adding class="app" to <body>. Every demo page in the repo does this. Removing the class falls back to the legacy design — theme.css keeps working untouched.
Legacy tokens — --ca-* (theme.css)
Declared at the top of src/scss/_variables.scss, emitted to :root in theme.css:
:root {
/* Brand */
--ca-primary: #4272d7;
--ca-primary-rgb: 66, 114, 215;
--ca-secondary: #6c757d;
--ca-success: #28a745;
--ca-warning: #ffc107;
--ca-danger: #dc3545;
--ca-info: #17a2b8;
/* Surfaces */
--ca-body-bg: #f8f9fa;
--ca-card-bg: #ffffff;
--ca-sidebar-bg: #2c3e50;
--ca-topbar-bg: #ffffff;
/* Text */
--ca-text: #333333;
--ca-text-muted: #6c757d;
--ca-text-on-dark: #ffffff;
/* Borders + dividers */
--ca-border: #e9ecef;
--ca-divider: #f1f3f5;
/* Shadows */
--ca-shadow-sm: 0 1px 3px rgba(0, 0, 0, 0.05);
--ca-shadow: 0 4px 12px rgba(0, 0, 0, 0.08);
--ca-shadow-lg: 0 12px 32px rgba(0, 0, 0, 0.12);
/* Layout */
--ca-sidebar-width: 240px;
--ca-topbar-height: 70px;
--ca-radius: 4px;
--ca-transition: 150ms ease;
}
Sixteen tokens covering brand colors, semantic status, surfaces, text, borders, shadows, and layout dimensions. Change --ca-primary and the whole legacy design retones — buttons, links, focus rings, sidebar accents.
Dark mode (legacy)
The legacy theme has a dark-mode hook tied to Bootstrap 5’s data-bs-theme attribute:
<html data-bs-theme="dark">
That selector rebinds the same --ca-* tokens to dark-mode values:
[data-bs-theme="dark"] {
--ca-body-bg: #121417;
--ca-card-bg: #1c1f24;
--ca-sidebar-bg: #0f1115;
--ca-text: #e9ecef;
/* ...rebinds every other --ca-* */
}
There’s no built-in toggle UI for this yet — set the attribute manually or wire your own toggle. The dark variants exist for every token, so once data-bs-theme="dark" is set the whole legacy design works in dark mode.
Modern overlay tokens — --m-* (app.css)
The modern overlay has its own, separate token set scoped under body.app:
body.app {
/* Surfaces */
--m-bg: #f4f6fa;
--m-surface: #ffffff;
--m-surface-2: #f8fafc;
--m-sidebar: #1c2333;
--m-sidebar-soft: #28304a;
/* Borders + dividers */
--m-border: #e4e7ec;
--m-border-soft: #eef0f3;
--m-divider: #f1f3f5;
/* Text */
--m-text: #1f2937;
--m-text-muted: #475569;
--m-text-faint: #94a3b8;
--m-text-on-dark: #f1f5f9;
/* Brand — anchored to the original CoolAdmin blue */
--m-accent: #4272d7;
--m-accent-rgb: 66, 114, 215;
--m-accent-hover: #355cb8;
--m-accent-soft: #eaf0fc;
/* KPI accents — used on the dashboard stat tiles */
--m-c1: #4272d7; /* blue (revenue) */
--m-c2: #11998e; /* teal (orders) */
--m-c3: #f97316; /* orange (users) */
--m-c4: #ec4899; /* pink (conversion) */
/* Semantic */
--m-success: #10b981; --m-success-soft: #ecfdf5;
--m-warning: #f59e0b; --m-warning-soft: #fffbeb;
--m-danger: #ef4444; --m-danger-soft: #fef2f2;
/* Effects */
--m-shadow-xs: 0 1px 2px rgba(15, 23, 42, 0.04);
--m-shadow-sm: 0 1px 3px rgba(15, 23, 42, 0.06), 0 1px 2px rgba(15, 23, 42, 0.04);
--m-shadow-md: 0 4px 12px rgba(15, 23, 42, 0.06), 0 2px 4px rgba(15, 23, 42, 0.04);
--m-shadow-lg: 0 12px 32px rgba(15, 23, 42, 0.10);
--m-radius-sm: 6px;
--m-radius: 10px;
--m-radius-lg: 14px;
--m-font: "Inter", "Inter Variable", -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
}
The modern tokens are richer than the legacy set — multi-step shadows, three radii, four KPI accent colors, soft variants of every semantic color.
The six theme presets
src/scss/app/_theme-presets.scss extends the body class to redefine the brand token cascade:
body.app.theme-blue {
--m-accent: #4272d7;
--m-accent-hover: #355cb8;
--m-accent-soft: #eaf0fc;
}
body.app.theme-purple {
--m-accent: #7c3aed;
--m-accent-hover: #6b2fd0;
--m-accent-soft: #ede9fe;
}
body.app.theme-teal {
--m-accent: #0d9488;
--m-accent-hover: #0a7d72;
--m-accent-soft: #ccfbf1;
}
body.app.theme-rose {
--m-accent: #e11d48;
--m-accent-hover: #be1239;
--m-accent-soft: #ffe4e6;
}
body.app.theme-amber {
--m-accent: #d97706;
--m-accent-hover: #b86204;
--m-accent-soft: #fef3c7;
}
body.app.theme-graphite {
--m-accent: #334155;
/* ... */
}
Apply by adding class="app theme-purple" to <body>. Every UI element that reads var(--m-accent) retones automatically — links, buttons, focus rings, badges, charts.
The floating theme switcher in the bottom-right of every page applies these by toggling the class at runtime — see Theme switcher for the runtime mechanics.
Customizing the brand color
Two paths depending on which design you’re using:
Legacy (body without .app): edit --ca-primary in src/scss/_variables.scss:
:root {
--ca-primary: #ff6b35;
--ca-primary-rgb: 255, 107, 53;
}
Modern overlay (body.app): add a new preset to src/scss/app/_theme-presets.scss:
body.app.theme-coral {
--m-accent: #ff6b35;
--m-accent-rgb: 255, 107, 53;
--m-accent-hover: #e85a28;
--m-accent-soft: #fff1e9;
}
Then register it in the theme switcher’s JS array in js/main-vanilla.js:
const THEMES = [
{ id: 'blue', label: 'Blue', color: '#4272d7' },
{ id: 'coral', label: 'Coral', color: '#ff6b35' }, // ← add here
// ...
];
Rebuild (npm run build) — the switcher widget picks up the new preset on its next render.
Adding a new token
Both legacy and modern overlay accept new tokens with no machinery. Just add the declaration:
/* src/scss/app/_variables.scss */
body.app {
--m-accent-purple: #7c3aed;
}
/* Use anywhere */
.my-component {
background: var(--m-accent-purple);
}
Modern tokens use the --m-* prefix; legacy uses --ca-*. Don’t mix prefixes — they’re scoped to different stylesheets and only available in their respective contexts.
What “modern” gets you over “legacy”
Both stylesheets work. The modern overlay adds:
- Inter typography with stylistic-set font features (
cv11,ss01,ss03) - Multi-step shadows (
--m-shadow-xs/sm/md/lg) tuned for thin layered cards - Three radii (
--m-radius-sm: 6px,--m-radius: 10px,--m-radius-lg: 14px) - KPI accent colors for stat tiles (blue / teal / orange / pink)
- Soft variants of every semantic color for chip / badge backgrounds
- The six theme presets documented above
- Component partials for the new patterns: kanban, inbox, data-table, invoice, wizard, command palette, theme switcher, toast, skeleton loaders, error pages
Legacy gets you the original CoolAdmin look. Modern is what every demo page ships with.
Conventions
- CSS variables, never hex. If you find yourself writing
#4272d7in a partial, usevar(--ca-primary)orvar(--m-accent)instead. - State classes use
is-—is-open,is-active,is-dragging. Bootstrap’sshowandactiveclasses are used where they fit Bootstrap conventions; CoolAdmin’s own components useis-*. data-attributes for JS hooks —data-bs-toggle(Bootstrap),data-sort(data-table),data-theme(presets),data-page(sidebar active state). Avoidids for behavior.
See also
- Theme switcher — the runtime UI for swapping presets
- Pug + SCSS pipeline — where these partials live and how they compile
- Architecture — the bigger picture