Themes
The design system supports multiple branding themes that can be switched at runtime.
Available Themes
e-INFRA CZ Theme
Elter Theme
EOSC Theme
Usage
Import setup CSS file for desired themes in your entry point CSS file or main component. If you want to use multiple themes, import all of them:
// app/globals.css or index.tsx
import '@e-infra/design-system/setup.css'; // Default theme
import '@e-infra/design-system/eosc_setup.css'; // EOSC theme
import '@e-infra/design-system/elter_setup.css'; // ELTER themeTheme Fonts
The branding themes set a font family, but the library does not ship the font files. Each theme reads a CSS variable that your app is responsible for defining:
| Theme | Variable | Font | Applies to |
|---|---|---|---|
default | — | inherits your app's font | everything |
eosc | --font-roboto | Roboto | body and headings |
elter | --font-montserrat | Montserrat | headings (h1–h6) only; body uses Helvetica Neue, which needs no loading |
If the variable is missing the theme degrades gracefully — it falls back to a locally installed copy of the font, then to the system sans-serif — but you will not get the intended branding. To load the fonts properly:
Next.js (next/font)
// app/layout.tsx
import { Roboto, Montserrat } from 'next/font/google';
const roboto = Roboto({ subsets: ['latin'], display: 'swap', variable: '--font-roboto' });
const montserrat = Montserrat({ subsets: ['latin'], display: 'swap', variable: '--font-montserrat' });
export default function RootLayout({ children }) {
return (
<html lang="en" className={`${roboto.variable} ${montserrat.variable}`}>
<body>{children}</body>
</html>
);
}Any other bundler
Load the fonts however you normally would (a <link> to Google Fonts, a
self-hosted @font-face, …) and point the variable at the family name:
:root {
--font-roboto: "Roboto";
--font-montserrat: "Montserrat";
}Gotcha if you write your own theme: always give
var()a fallback inside afont-familystack —var(--font-x, "X"), sans-serif, nevervar(--font-x), sans-serif. An undefined custom property with no fallback makes the entire declaration invalid at computed-value time, so the browser throws the whole stack away rather than continuing to the next entry. On<html>that means falling all the way back to the default serif (Times New Roman).
Theme Persistence
Theme preferences are automatically persisted in localStorage:
theme-branding:"default","eosc"or"elter"theme-color-mode:"light","dark", or"system"
The theme is restored on page reload without additional configuration.
Shade Ramps
Each color family includes a 50-950 shade ramp for styling:
// Subtle background
<div className="bg-primary-50">Light tint</div>
// Hover state
<div className="bg-primary-600">Darker shade</div>
// Text on light background
<div className="text-primary-900">Near-black</div>Custom Themes from 3 Colors
You don't need to fork setup.css to brand the system. Import the custom theme
generator and define just three colors — primary, secondary, and tertiary. It
derives the full 50–950 shade ramps, readable foregrounds, and a sensible dark
mode for you. Nothing else (radius, spacing, transitions, animations) is exposed
or overridable, so consumers can't accidentally break layout or motion.
1. Import the generator
// app/globals.css or index.tsx
import '@e-infra/design-system/setup.css'; // required base
import '@e-infra/design-system/custom_setup.css'; // 3-color generator2. Define your three brand colors
Set them on the [data-theme="custom"] selector anywhere in your own CSS:
/* your-theme.css */
[data-theme="custom"] {
--brand-primary: #6d28d9;
--brand-secondary: #db2777;
--brand-tertiary: #0d9488;
}3. Activate the theme
<html data-theme="custom"> {/* light */}
<html data-theme="custom" class="dark"> {/* dark */}That's it. bg-primary, text-tertiary-foreground, bg-primary-100, and every
other token-based utility now reflect your colors in both light and dark mode.
How it works
The library is built with Tailwind v4 @theme inline, so utilities compile to
var(--primary) and read their value at runtime. The generator scopes a set
of color-mix()-derived ramps and auto-contrast foregrounds to
[data-theme="custom"], seeded from your three --brand-* inputs — no rebuild,
no JavaScript, and the internal hand-tuned themes (default, EOSC) are untouched.
Optional fine-tuning
All of these have automatic fallbacks; set them only if you want more control:
| Token | Purpose |
|---|---|
--brand-primary-dark / --brand-secondary-dark / --brand-tertiary-dark | Pin explicit dark-mode seeds (otherwise the light seeds are auto-lightened). |
--brand-fg-threshold | Lightness cutoff (default 0.62) for flipping foreground text between black and white. |
Requires evergreen browsers for
color-mix()and relative color syntax — Chrome 119+, Safari 16.4+, Firefox 128+.
Need full control?
For a completely bespoke theme (custom surfaces, semantic colors, charts, or
hand-tuned ramps), define a [data-theme="yourtheme"] block that overrides the
raw tokens directly — see the shipped setup.css,
eosc_setup.css,
and elter_setup.css
as references for the complete token list.