BlazorNova's CSS is not a static stylesheet — it is generated at build time by a
Roslyn source generator embedded in BlazorNovaGenerator. The generator
produces only the CSS your project actually uses, keeping the output small and
always in sync with your code.
On every build the generator walks the full compilation — every .cs and
.razor file in the project — looking for BlazorNova fluent
property chains and method calls. Each unique utility class it finds is added to a set.
When the build finishes it writes the union of all found classes (plus normalisation
resets and surface CSS variables) to the file specified by
RelativeOutputDirectory in your config.
// The generator detects every usage like these:
BlazorNova.New.flex.gap_4.items_center // member access chain
NextBn.bg_Bg.text_OnBg // surface-aware chain
.Hover(h => h.bg_EmphasisBg) // pseudo-state variant
.If(() => isActive, x => x.font_bold) // conditional class
.Md(x => x.w_[480px]) // responsive breakpointBecause the generator uses Roslyn's full semantic model it can follow your code accurately — including classes produced inside conditional expressions, helper methods, and modifier lambdas.
When BlazorNova runs inside a Razor Class Library (RCL), the generator does not write a CSS file. Instead it embeds a compact list of every CSS class the library uses as metadata attributes directly in the compiled assembly. These are the breadcrumbs.
When your application builds and references that RCL, the generator reads the
breadcrumbs from the referenced assemblies and merges them with the classes found in
your own code. The single generated.css it produces covers your app
and every BlazorNova-powered library you depend on — no extra configuration
required.
// In a Razor Class Library, the generator writes NO css file.
// Instead it embeds usage metadata in the compiled assembly:
[assembly: BlazorNovaUsage("flex gap-4 bg-Surface1-Bg hover:bg-Surface2-Bg ...")]
// The consuming app's generator reads these attributes from all
// referenced assemblies and merges them into one generated.css.
// No extra steps needed — it just works.
The generated CSS uses CSS custom properties (variables) for every surface colour.
When you override LightPalette or DarkPalette in your config,
those overrides flow through the variables to every component on the page — including
components from third-party RCLs built with BlazorNova.
A library author never hard-codes colours. Their components reference surface tokens
(var(--BnSurface1-Bg), var(--BnSurfacePrimary1-Bg), etc.)
that your config defines. Install a BlazorNova component library, set your palette
once, and the entire UI — yours and theirs — is themed consistently.
// Your app's config is the single source of truth for colours:
public class MyAppConfig : BlazorNovaConfig
{
public override string RelativeOutputDirectory { get; } = "/wwwroot/generated";
public override ThemePalette LightPalette { get; } = new MyLightPalette();
public override ThemePalette DarkPalette { get; } = new MyDarkPalette();
}
// Both your components and every installed BlazorNova library
// automatically pick up these colours via CSS custom properties.
// No per-library configuration. No CSS overrides needed.--BnSurface*
custom properties for every palette entry in both light and dark themes.