Theme & Colors
Customize MonorailCSS color palettes, font families, and other design tokens
The theme is where your design tokens live. Colors, fonts, spacing, breakpoints — every value MonorailCSS knows about is keyed by a CSS custom property name (like --color-blue-500 or --font-display) and emitted as a real CSS variable in the output. Customizing the theme means adding, replacing, or aliasing those keys.
Adding a Color Palette
The most common customization: introduce a new palette so utilities like bg-brand-500 and text-brand-700 resolve to your brand colors.
public static CssFramework Build()
{
var theme = new ThemeModel().AddColorPalette("brand", new Dictionary<string, string>
{
{ "50", "oklch(0.985 0.013 280)" },
{ "100", "oklch(0.961 0.027 280)" },
{ "200", "oklch(0.918 0.058 280)" },
{ "300", "oklch(0.852 0.108 280)" },
{ "400", "oklch(0.745 0.165 280)" },
{ "500", "oklch(0.638 0.207 280)" },
{ "600", "oklch(0.554 0.207 280)" },
{ "700", "oklch(0.464 0.182 280)" },
{ "800", "oklch(0.378 0.151 280)" },
{ "900", "oklch(0.298 0.116 280)" },
{ "950", "oklch(0.220 0.090 280)" },
}.ToImmutableDictionary());
return new CssFramework(new CssFrameworkSettings { Theme = theme });
}
Once registered, every utility that consumes the --color-* namespace works with your new palette: bg-brand-500, border-brand-200, ring-brand-300, from-brand-50, etc.
What about the shade keys?
The standard scale is 50, 100, 200, 300, 400, 500, 600, 700, 800, 900, 950 — the same shades the built-in palettes use. You don't have to provide every shade, but you do need to provide the ones you reference in markup. Missing shades simply won't generate utilities.
Overriding a Single Shade
Need to nudge a single color without rebuilding a palette? Use Theme.Add directly with the fully-qualified key:
public static CssFramework Build()
{
var theme = new ThemeModel()
.Add("--color-blue-500", "#1d4ed8")
.Add("--color-blue-600", "#1e40af");
return new CssFramework(new CssFrameworkSettings { Theme = theme });
}
This changes only --color-blue-500 and --color-blue-600. The rest of the blue scale, and every other palette, are untouched.
Aliasing a Palette
Aliases let you give a semantic name (primary, accent, surface) to an existing palette. Behind the scenes the alias becomes a thin layer of CSS variables pointing at the source palette, so swapping the source later is a one-line change.
public static CssFramework Build()
{
var theme = new ThemeModel().MapColorPalette("sky", "primary");
return new CssFramework(new CssFrameworkSettings { Theme = theme });
}
After this runs, bg-primary-500 emits the same color as bg-sky-500, but the markup is in design-system terms.
Starting From an Empty Theme
If you don't want any of the built-in defaults — no Tailwind palette, no preset spacing scale — start from Theme.CreateEmpty() and build up only what you need:
public static CssFramework Build()
{
var theme = ThemeModel.CreateEmpty()
.AddColorPalette("brand", new Dictionary<string, string>
{
{ "500", "#3b82f6" },
{ "700", "#1d4ed8" },
}.ToImmutableDictionary());
return new CssFramework(new CssFrameworkSettings { Theme = theme });
}
This is useful for design systems that ship a constrained token set.
Adding Font Families
Font families are stored under the --font-* namespace and surface as font-{name} utilities.
public static CssFramework Build()
{
var theme = new ThemeModel()
.AddFontFamily("display", "'Inter', system-ui, sans-serif")
.AddFontFamily("mono", "'JetBrains Mono', ui-monospace, monospace");
return new CssFramework(new CssFrameworkSettings { Theme = theme });
}
Now font-display and font-mono are available alongside the built-in font-sans and font-serif.
Next steps
- Bundle utilities into reusable selectors with component classes.
- Add your own utility classes with custom utilities.