BlazorNova replaces raw CSS class strings with a type-safe, IntelliSense-driven fluent builder. Every Tailwind-style utility is available as a C# property, so misspellings are caught at compile time and discovery is instant via autocomplete.
BlazorNova.New creates a fresh builder with no surface context.
Chain utility properties to compose your class list, then drop the expression
directly into a class="@(...)" attribute — the builder's
ToString() emits the final CSS class string.
@* In a Razor file — the expression becomes a class string *@
<div class="@@BlazorNova.New.flex.gap_4.items_center.p_3">
<span class="@@BlazorNova.New.font_bold.text_lg">Hello</span>
</div>
@* Renders as: *@
<div class="flex gap-4 items-center p-3">
<span class="font-bold text-lg">Hello</span>
</div>
Because each property returns the same BlazorNova instance, chains
can be as long as you need. Properties use lowercase with underscores
to mirror Tailwind naming — flex_row is flex-row,
max_w_xl is max-w-xl. This keeps the mental mapping
instant for anyone familiar with Tailwind, while the uppercase methods
(Sm, Hover, If) clearly mark structural
operations like breakpoints and conditionals.
Utility properties are lowercase with underscores — a deliberate departure from C# naming conventions. This is a domain-specific API, not a general class library. The lowercase style:
flex_ to see all flex utilities// Utility properties — lowercase, underscore = hyphen
BlazorNova.New.flex_row // → flex-row
BlazorNova.New.max_w_xl // → max-w-xl
BlazorNova.New.whitespace_pre // → whitespace-pre
BlazorNova.New.bg_EmphasisBg // → bg-EmphasisBg (surface-aware)
// Structural methods — PascalCase
BlazorNova.New.Sm(x => x.flex_col) // breakpoint
BlazorNova.New.Hover(x => x.opacity_80) // pseudo-state
BlazorNova.New.If(() => cond, x => x.font_bold) // conditional
When the built-in scale doesn't have the value you need, use method-style utilities
that accept a string parameter. These generate Tailwind arbitrary-value classes
like w-[480px].
// Width/height with exact values
BlazorNova.New.W("480px").H("100vh")
// Grid with custom template
BlazorNova.New.GridCols("220px 1fr").gap_4
// Z-index
BlazorNova.New.Z("101")
// Custom border
BlazorNova.New.Border("2px solid red")The builder tracks which CSS property each utility maps to. If you set the same property twice, the later value wins — just like writing CSS. This means modifiers and conditionals can safely override defaults without accumulating conflicting classes.
// px_4 sets the "padding-inline" CSS property
// px_0 also sets "padding-inline" — so it overwrites px_4
BlazorNova.New.px_4.py_2.px_0
// Output: py-2 px-0 (px_4 is gone)
// This is why BnModifier overrides work cleanly —
// the modifier runs after defaults, and later values
// for the same CSS property replace earlier ones.
Inside any component that inherits BlazorNovaComponentBase, two
additional builders are available:
NextBn — starts one surface level deeper than the parent. Use this
for the component's own background and content areas.CurrentBn — stays at the same surface level as the parent. Use this
for elements that should blend with the container rather than elevate.
Surface-aware utilities like bg_Bg, text_OnBg, and
bg_rel1_Bg resolve to different colours depending on the surface
context — see the Surface System docs for details.
// Inside a component inheriting BlazorNovaComponentBase:
// NextBn — one level deeper than the parent surface
// Use for the component's own background
<div class="@@NextBn.bg_Bg.text_OnBg.rounded_md.p_3">
@@ChildContent
</div>
// CurrentBn — same level as the parent surface
// Use for elements that should blend with the container
<button class="@@CurrentBn.bg_Bg.text_OnBg
.Hover(h => h.bg_EmphasisBg)">
@@Label
</button>.If() adds classes only when a condition is true at render time.
Chain .Else() for the alternate branch. Conditions are evaluated
each time the builder's ToString() is called, so they stay reactive.
BlazorNova.New
.flex.items_center.p_2
.If(() => isActive, x => x.font_bold.bg_EmphasisBg)
.Else(x => x.opacity_70)
// Multiple independent conditions
BlazorNova.New
.w_full
.If(() => hasError, x => x.border_red_500.border_2)
.If(() => isDisabled, x => x.opacity_50.cursor_not_allowed)Hover, focus, disabled, and other pseudo-states are expressed as methods that take a lambda. The source generator detects these and emits the correct CSS selectors.
BlazorNova.New
.bg_Bg.text_OnBg
.Hover(x => x.bg_EmphasisBg.cursor_pointer)
.Focus(x => x.outline_2.outline_blue_500)
.Disabled(x => x.opacity_50.cursor_not_allowed)
.Active(x => x.scale_95)
Breakpoint methods (Sm, Md, Lg, etc.)
wrap utilities in responsive media queries. Like Tailwind, breakpoints are
mobile-first — Md means "at medium and above."
// Mobile-first: base styles + overrides at larger sizes
BlazorNova.New
.flex_col.gap_2 // mobile default
.Sm(x => x.flex_row) // >= 640px
.Md(x => x.gap_4) // >= 768px
.Lg(x => x.gap_8) // >= 1024px
// Available breakpoints:
// XXS(320) XS(480) Sm(640) Md(768) Lg(1024) XL(1440) XXL(1920) XXXL(2560)
When you need a class that isn't in the generated API — a third-party class, a
custom utility, or a one-off override — use .AddClass("class-name")
to inject it into the builder's output.
// Inject a custom or third-party class
BlazorNova.New.flex.gap_4.AddClass("my-custom-animation")
// Inject surface-specific classes from a palette
BlazorNova.New.AddClass(SurfacePalette.GetInputClasses())The BlazorNova source generator scans your Razor and C# files for fluent API usage and emits only the CSS classes your project actually uses. This means:
BnModifier.Create(x => x.…) lambdas are scanned too — use this
exact pattern so the generator can find your classes