Theming
Customize the kit’s visual identity without breaking its semantic contract. Tokens control the global theme; Tailwind sources are for structural component changes.
Review custom installation1. Workflow
Identify the right customization level
tokens.css
Global identity
Colors, typography, radius, shadows, overlays, and scrollbars.
input.css
Components
Dimensions, spacing, density, animations, and .ui-* styles.
tailwind.config.js
Tailwind scale
New utilities, breakpoints, spacing, and theme extensions.
public/app.css. This file is replaced on every build.
# Développement : recompilation continue
npm run dev:css
# Production : CSS minifié
npm run build:css
2. Colors
Change semantic roles
Components use roles such as primary, muted, or destructive, never a hard-coded business color. Keep this principle when changing the palette.
Primary
Secondary
Success
Warning
Danger
Info
Muted
Accent
- Write colors in HSL without commas or the hsl() function.
- Always define the role / role-foreground pair.
- Check light and dark theme contrast separately.
/* assets/css/tokens.css */
:root {
--background: 42 33% 98%;
--foreground: 222 47% 11%;
--card: 0 0% 100%;
--card-foreground: 222 47% 11%;
--popover: 0 0% 100%;
--popover-foreground: 222 47% 11%;
--primary: 198 89% 32%;
--primary-foreground: 0 0% 100%;
--secondary: 198 28% 92%;
--secondary-foreground: 198 89% 20%;
--muted: 42 20% 93%;
--muted-foreground: 220 9% 38%;
--accent: 198 35% 90%;
--accent-foreground: 198 89% 20%;
--destructive: 0 72% 42%;
--destructive-foreground: 0 0% 100%;
--success: 142 72% 27%;
--success-foreground: 0 0% 100%;
--warning: 38 92% 50%;
--warning-foreground: 26 83% 14%;
--info: 217 78% 42%;
--info-foreground: 0 0% 100%;
--border: 38 18% 82%;
--input: 38 18% 82%;
--ring: 198 89% 32%;
}
3. Typography
Choose and load font families
Public --ui-font-* variables separate the physical loading of fonts from their application in the kit.
/* assets/css/tokens.css */
:root {
--ui-font-sans: "Public Sans", ui-sans-serif, system-ui, sans-serif;
--ui-font-mono: "JetBrains Mono", ui-monospace, monospace;
--ui-font-body: var(--ui-font-sans);
--ui-font-heading: "Manrope", var(--ui-font-sans);
}
/* public/fonts/ui-fonts.css */
@font-face {
font-family: "Public Sans";
src: url("/fonts/public-sans/PublicSans-Variable.woff2") format("woff2");
font-weight: 100 900;
font-style: normal;
font-display: swap;
}
:root {
--ui-font-sans: "Public Sans", ui-sans-serif, system-ui, sans-serif;
}
Then load /fonts/ui-fonts.css in the layout, before or after /app.css. Use WOFF2 and font-display: swap.
4. Radius and shadows
Radius and shadows
The --radius token feeds the rounded-lg, rounded-md, and rounded-sm Tailwind variants. The four shadows feed shadow-* classes.
/* assets/css/tokens.css */
:root {
--radius: 0.875rem;
--shadow-sm: 0 1px 2px rgb(15 23 42 / 0.06);
--shadow: 0 2px 6px rgb(15 23 42 / 0.10);
--shadow-md: 0 8px 20px rgb(15 23 42 / 0.12);
--shadow-lg: 0 18px 40px rgb(15 23 42 / 0.16);
}
5. Components
Adjust density and internal styles
For a targeted change, override canonical .ui-* classes in an @layer components block placed after the kit definitions.
/* À la fin de assets/css/input.css */
@layer components {
.ui-btn-md {
@apply h-10 px-5;
}
.ui-card {
@apply shadow-md;
}
.ui-table-compact .ui-td {
@apply px-3 py-2;
}
}
6. Tailwind
Extend utilities
Always start from the exported file and add extensions without replacing colors, borderRadius, fontFamily, and boxShadow mappings. Keep content paths too: without them, Tailwind can remove classes used in EJS partials or JavaScript modules from the build.
// tailwind.config.js
export default {
darkMode: 'class',
content: [
'./src/views/**/*.ejs',
'./public/ui/**/*.js',
'./assets/css/**/*.css',
],
theme: {
extend: {
// Conservez ici colors, borderRadius, fontFamily et boxShadow du kit.
spacing: {
18: '4.5rem',
},
screens: {
'3xl': '1920px',
},
},
},
};
7. Dark mode
Define a dedicated palette
Dark mode is enabled by the dark class on html. Do not derive the entire dark palette automatically from the light palette: control every surface and foreground.
/* assets/css/tokens.css */
.dark {
--background: 222 47% 7%;
--foreground: 210 40% 98%;
--card: 222 47% 9%;
--card-foreground: 210 40% 98%;
--popover: 222 47% 10%;
--popover-foreground: 210 40% 98%;
--primary: 196 94% 67%;
--primary-foreground: 222 47% 7%;
--secondary: 217 33% 17%;
--secondary-foreground: 210 40% 98%;
--muted: 217 33% 17%;
--muted-foreground: 215 20% 68%;
--accent: 217 33% 20%;
--accent-foreground: 210 40% 98%;
--border: 217 33% 20%;
--input: 217 33% 20%;
--ring: 196 94% 67%;
}
<!-- Dans <head>, avant le rendu de la page -->
<script>
(function () {
const stored = localStorage.getItem('ui-theme');
const prefersDark = window.matchMedia('(prefers-color-scheme: dark)').matches;
if (stored === 'dark' || (!stored && prefersDark)) {
document.documentElement.classList.add('dark');
}
})();
</script>
8. Build and review
Build and review the theme
npm run build:css
Visual checklist
- • text and controls have sufficient contrast in light and dark modes
- • focus is visible with the ring color
- • success, warning, danger, and info states are legible
- • borders are visible on cards, tables, and fields
- • popovers and modals are distinct from the background
- • fonts load without significant layout shift
- • components are tested at main breakpoints
- • no manual change in public/app.css