Plan: docs/plans/2026-08-28-v4-tailwind-prefix.md in the internal devkit repo (not this one).
Fixes #496. Breaking — targets a v4 major.
The fault
BB ships a prebuilt Tailwind stylesheet. A consumer running their own Tailwind build ends up with two stylesheets writing into the same utilities cascade layer. Tailwind gives md:grid-cols-4 priority over sm:grid-cols-2 by sort order within a single build, not by specificity — both are one class. Across two builds that ordering is gone, and priority at the join is decided by which <link> came second.
Both directions break, which is why no ordering fixes it:
- BB second — a consumer's
sm:grid-cols-2 md:grid-cols-4 collapses, because BB re-emits sm:grid-cols-2 later in the document.
- BB first — BB's own
hidden sm:flex pagination collapses, because the consumer emits hidden later.
This reproduces in our own demo today: demos/BlazorBlueprint.Demo.Server/App.razor:9-11 loads app.css then blazorblueprint.css.
The fix
Prefix BB's utilities so the two builds cannot produce the same class name, and give them their own cascade layer. Verified against tailwindcss v4.3.0:
@layer bb-theme, bb-base, bb-components, bb-utilities, utilities, bb;
@import 'tailwindcss/theme.css' layer(bb-theme) prefix(bb);
@import 'tailwindcss/preflight.css' layer(bb-base);
@import 'tailwindcss/utilities.css' layer(bb-utilities) prefix(bb);
Emits .bb\:hidden, .bb\:sm\:flex. Consumer Class="..." values stay unprefixed and are unaffected.
Scope
- 966
class="…" strings in Components .razor, 17 in Primitives, 119 .cs files building class strings.
- The rewrite is mechanical: the current build emits 933 distinct utility tokens, and that set is the exact allowlist. Verification is a diff of the built CSS — every utility selector gains
bb\: and nothing else changes.
ClassNames.cn() needs a decision: consumer overrides currently win by string merge, and TailwindMerge keeps both when only one side is prefixed. See step 4 of the plan.
- The demo's
@source on library sources must go; the demo should be a real consumer.
Ordering
#490 must land first. It touches 299 files and would conflict badly with a mechanical class rewrite.
Also worth batching into the same major: #428.
Plan:
docs/plans/2026-08-28-v4-tailwind-prefix.mdin the internal devkit repo (not this one).Fixes #496. Breaking — targets a v4 major.
The fault
BB ships a prebuilt Tailwind stylesheet. A consumer running their own Tailwind build ends up with two stylesheets writing into the same
utilitiescascade layer. Tailwind givesmd:grid-cols-4priority oversm:grid-cols-2by sort order within a single build, not by specificity — both are one class. Across two builds that ordering is gone, and priority at the join is decided by which<link>came second.Both directions break, which is why no ordering fixes it:
sm:grid-cols-2 md:grid-cols-4collapses, because BB re-emitssm:grid-cols-2later in the document.hidden sm:flexpagination collapses, because the consumer emitshiddenlater.This reproduces in our own demo today:
demos/BlazorBlueprint.Demo.Server/App.razor:9-11loadsapp.cssthenblazorblueprint.css.The fix
Prefix BB's utilities so the two builds cannot produce the same class name, and give them their own cascade layer. Verified against tailwindcss v4.3.0:
Emits
.bb\:hidden,.bb\:sm\:flex. ConsumerClass="..."values stay unprefixed and are unaffected.Scope
class="…"strings in Components.razor, 17 in Primitives, 119.csfiles building class strings.bb\:and nothing else changes.ClassNames.cn()needs a decision: consumer overrides currently win by string merge, and TailwindMerge keeps both when only one side is prefixed. See step 4 of the plan.@sourceon library sources must go; the demo should be a real consumer.Ordering
#490 must land first. It touches 299 files and would conflict badly with a mechanical class rewrite.
Also worth batching into the same major: #428.