BlazorNova BlazorNova
BlazorNova BlazorNova
☰
☰ Menu
Getting Started
Installation
Project Setup
Source Generator
Core Concepts
Surface System
Color Palette
Fluent CSS API
Style Overrides
CSS Precedence
BlazorNova Classes
Layout
Flexbox & Grid
Spacing
Sizing
Typography
Backgrounds
Borders
Effects
Filters
Transitions & Transforms
Interactivity
Components
Forms
BnEditForm
FormState
Monitors
MenuBar
Horizontal
Vertical
Tabs
BnTab
Icons
Icon Gallery
Buttons
BnButton
Layout
BnStackPanel
BnDrawer
Grid
BnGrid
BnPagedGrid
Navigation
BnNavigator
Carousel
BnCarousel
Charts
BnChart
PDF Viewer
BnPdfViewer
Meet
BnVideoCall

CSS Precedence & If/Else

The fluent API deduplicates CSS properties using a "last wins" rule within each prefix scope. This section explains how that interacts with pseudo-class prefixes like .Odd() and .Hover(), and how .If().Else() gives you full control when conditional styles conflict with prefix-scoped styles.

Last Wins (Same Prefix Scope)

When two utilities target the same CSS property in the same prefix scope, the later one replaces the earlier one. Internally the builder stores classes in a dictionary keyed by prefix + cssProperty, so the second assignment overwrites the first.

csharp
// Both target background-color with no prefix — red wins:
BlazorNova.New.bg_black.bg_red
// Output: "bg-red"

// Same rule applies within a prefix scope:
BlazorNova.New.Hover(h => h.bg_black.bg_red)
// Output: "hover:bg-red"

Prefix Scopes Are Independent

Each pseudo-class prefix (.Odd(), .Even(), .Hover(), .Focus(), etc.) creates its own dictionary key space. A non-prefixed bg_red and an odd:bg_blue target the same CSS property but live under different keys, so both are emitted.

csharp
// Different prefix scopes — both are emitted:
BlazorNova.New.bg_red.Odd(o => o.bg_blue).Hover(h => h.bg_green)
// Output: "bg-red odd:bg-blue hover:bg-green"

// Dictionary keys:
//   "background-color"        → "bg-red"
//   "odd:background-color"    → "odd:bg-blue"
//   "hover:background-color"  → "hover:bg-green"

This is normally the correct behaviour — you want a base colour and a hover colour to coexist. The problem arises when a runtime condition (like row selection) needs to override a prefix-scoped style.

Generated CSS Group Order

When two pseudo-class selectors have equal specificity (e.g. :nth-child(odd) and :hover both add one pseudo-class), the rule that appears later in the stylesheet wins. BlazorNova controls this ordering with an explicit priority system in the CSS generator. Groups are sorted by breakpoint pixel value first, then by pseudo-class priority, then alphabetically as a tiebreaker.

csharp
// Pseudo-class priority (lower = earlier in CSS, higher = wins cascade)
//
// Structural selectors          Interactive states
//   first   10                    hover          30
//   last    11                    focus          40
//   odd     12                    focus-within   41
//   even    13                    focus-visible  42
//                                 active         50
// Form / input states
//   disabled  20
//   enabled   21
//   checked   22
//   readonly  23
//   required  24
//
// Sort: Order (breakpoint px) → Priority → Alphabetical
//
// Result in generated CSS:
//   .odd\:bg-red:nth-child(odd)  { ... }   /* priority 12 — appears first  */
//   .hover\:bg-blue:hover        { ... }   /* priority 30 — appears later  */
//   .active\:bg-green:active     { ... }   /* priority 50 — appears last   */
//
// Same specificity → later rule wins → hover beats odd.

This ordering was introduced because an earlier alphabetical sort placed hover (h) before odd (o) in the output, causing hover highlights to be overridden by odd/even row colours. The explicit priority ensures interactive states always appear after structural selectors in the generated CSS, so a :hover rule correctly overrides :nth-child(odd) when both match the same element.

The Specificity Problem

Consider alternating row colours with a conditional selection highlight:

csharp
// Alternating rows with conditional selection — BROKEN:
NextBn.bg_Bg.text_OnBg
    .Odd(o => o.bg_AltBg)
    .Hover(h => h.bg_EmphasisBg)
    .If(() => isSelected, x => x.bg_EmphasisBg)

// When isSelected = true, output includes BOTH:
//   bg-EmphasisBg       (from If)
//   odd:bg-AltBg        (from Odd — still present!)
//
// :nth-child(odd) has higher specificity → odd colour wins.

When isSelected is true, both bg_rel2_Bg and odd:bg_rel1_Bg are emitted. In the generated CSS, :nth-child(odd) has higher specificity than a plain class, so the odd colour always wins — the selection highlight never appears.

Solving It with If/Else

.If() returns a BlazorNovaIfConditional that exposes an .Else() method. When the condition is true only the If branch is applied; when false only the Else branch runs. Because the branches are mutually exclusive, conflicting classes are never on the element at the same time.

csharp
// Alternating rows with conditional selection — FIXED:
NextBn
    .Hover(h => h.bg_EmphasisBg)
    .If(() => isSelected, x => x.bg_EmphasisBg)
    .Else(x => x.bg_Bg.text_OnBg.Odd(o => o.bg_AltBg))

// When isSelected = true:
//   hover:bg-EmphasisBg  bg-EmphasisBg
//   (no Odd classes — no specificity conflict)

// When isSelected = false:
//   hover:bg-EmphasisBg  bg-Bg  text-OnBg  odd:bg-AltBg

Move any prefix-scoped styles that would conflict with your conditional into the .Else() branch. Styles that should apply in both states (like .Hover()) stay outside the If/Else pair.

Chaining Multiple If/Else

.Else() returns BlazorNova, so you can continue the fluent chain normally. Each .If() can optionally have its own .Else(). Without .Else(), .If() behaves exactly as before — the modifier is applied when true, skipped when false.

csharp
NextBn
    .Hover(h => h.bg_EmphasisBg)           // always applied
    .If(() => isSelected, x => x.bg_EmphasisBg)  // branch A
    .Else(x => x.bg_Bg.Odd(o => o.bg_AltBg))    // branch B
    .If(() => isEditable, x => x.cursor_pointer) // independent If (no Else)
    .border.border_solid.border_Border      // always applied