BlazorBlueprint.Primitives
3.1.2
See the version list below for details.
dotnet add package BlazorBlueprint.Primitives --version 3.1.2
NuGet\Install-Package BlazorBlueprint.Primitives -Version 3.1.2
<PackageReference Include="BlazorBlueprint.Primitives" Version="3.1.2" />
<PackageVersion Include="BlazorBlueprint.Primitives" Version="3.1.2" />
<PackageReference Include="BlazorBlueprint.Primitives" />
paket add BlazorBlueprint.Primitives --version 3.1.2
#r "nuget: BlazorBlueprint.Primitives, 3.1.2"
#:package BlazorBlueprint.Primitives@3.1.2
#addin nuget:?package=BlazorBlueprint.Primitives&version=3.1.2
#tool nuget:?package=BlazorBlueprint.Primitives&version=3.1.2
BlazorBlueprint.Primitives
Headless, unstyled Blazor primitive components with ARIA attributes and keyboard support. Build your own component library using these composable primitives.
Features
- Headless & Unstyled: Complete control over styling — primitives provide behavior, accessibility, and state management without imposing any visual design
- Built with Accessibility in Mind: Includes ARIA attributes and keyboard interaction support
- Composition-Based: Flexible component composition patterns for building complex UIs
- Type-Safe: Full C# type safety with IntelliSense support
- State Management: Built-in controlled and uncontrolled state patterns
- Keyboard Support: Keyboard interaction support for interactive components
- Two-Layer Portal Architecture: Category-scoped portals (Container and Overlay) for efficient rendering
- .NET 8: Built for the latest .NET platform
Installation
dotnet add package BlazorBlueprint.Primitives
Setup
Register services in Program.cs:
builder.Services.AddBlazorBlueprintPrimitives();
Add the portal host to your root layout (MainLayout.razor):
<BbPortalHost />
Add a single import to _Imports.razor:
@using BlazorBlueprint.Primitives
Available Primitives
| Primitive | Description |
|---|---|
| Accordion | Collapsible content sections with single or multiple item expansion |
| Checkbox | Binary selection control with indeterminate state and BbCheckboxIndicator sub-component |
| Collapsible | Expandable content area with trigger control |
| Dialog | Modal dialogs with backdrop, focus management, and portal rendering |
| Dropdown Menu | Context menus with items, checkbox items, separators, and keyboard shortcuts |
| Floating Portal | Unified floating content infrastructure with ForceMount and positioning |
| Hover Card | Rich preview cards on hover with delay control |
| Label | Accessible labels for form controls with automatic association |
| Popover | Floating panels for additional content with positioning |
| Radio Group | Mutually exclusive options with keyboard navigation |
| Select | Dropdown selection with cascading type inference and display text resolution |
| Sheet | Side panels that slide in from viewport edges |
| Switch | Toggle control with BbSwitchThumb sub-component for automatic data-state sync |
| Table | Data table with header, body, rows, cells, and pagination |
| Tabs | Tabbed interface with keyboard navigation |
| Tooltip | Brief informational popups with hover/focus triggers |
Services
| Service | Description |
|---|---|
IPortalService |
Two-layer portal management with Container and Overlay categories |
IFocusManager |
Focus trapping and restoration for overlays |
IPositioningService |
Floating UI positioning with auto-update |
IKeyboardShortcutService |
Global keyboard shortcut registration and management |
DropdownManagerService |
Coordinates open/close state across multiple dropdowns |
API Reference
Accordion
<BbAccordion Type="AccordionType.Single" Collapsible="true" DefaultValue="item-1">
<BbAccordionItem Value="item-1">
<BbAccordionTrigger>Section 1</BbAccordionTrigger>
<BbAccordionContent>Content 1</BbAccordionContent>
</BbAccordionItem>
</BbAccordion>
| Parameter | Type | Default | Description |
|---|---|---|---|
Type |
AccordionType |
Single |
Single (one item open) or Multiple (many items open) |
Collapsible |
bool |
false |
When Single, allows closing all items |
Checkbox
<BbCheckbox @bind-Checked="isChecked" Indeterminate="@isIndeterminate">
<BbCheckboxIndicator />
</BbCheckbox>
| Parameter | Type | Default | Description |
|---|---|---|---|
Checked |
bool |
false |
Checked state |
Indeterminate |
bool |
false |
Shows partial/mixed state |
BbCheckboxIndicator renders the appropriate check or indeterminate SVG icon automatically based on parent state:
| Parameter | Type | Default | Description |
|---|---|---|---|
ChildContent |
RenderFragment? |
null |
Custom content instead of default icons |
Size |
int |
14 |
SVG icon size in pixels |
StrokeWidth |
int |
3 |
SVG stroke width |
Select
<BbSelect TValue="string" @bind-Value="selected" @bind-Open="isOpen">
<BbSelectTrigger>
<BbSelectValue Placeholder="Choose..." />
</BbSelectTrigger>
<BbSelectContent>
<BbSelectItem Value="@("a")" Text="Option A" />
<BbSelectItem Value="@("b")" Text="Option B" />
</BbSelectContent>
</BbSelect>
Select uses [CascadingTypeParameter] — child components infer TValue from the parent. Supports ItemClass for parent-level item styling.
| Parameter | Type | Default | Description |
|---|---|---|---|
Value |
TValue? |
— | Selected value (two-way bindable) |
Open |
bool |
false |
Open state (two-way bindable) |
ItemClass |
string? |
null |
CSS classes cascaded to all BbSelectItem children |
Dialog
<BbDialog @bind-Open="isOpen">
<BbDialogTrigger>Open</BbDialogTrigger>
<BbDialogPortal>
<BbDialogOverlay />
<BbDialogContent>
<BbDialogTitle>Title</BbDialogTitle>
<BbDialogDescription>Description</BbDialogDescription>
<BbDialogClose>Close</BbDialogClose>
</BbDialogContent>
</BbDialogPortal>
</BbDialog>
Sheet
<BbSheet>
<BbSheetTrigger>Open</BbSheetTrigger>
<BbSheetPortal>
<BbSheetOverlay />
<BbSheetContent Side="SheetSide.Right">
<BbSheetTitle>Title</BbSheetTitle>
<BbSheetDescription>Description</BbSheetDescription>
<BbSheetClose>Close</BbSheetClose>
</BbSheetContent>
</BbSheetPortal>
</BbSheet>
| Parameter | Type | Default | Description |
|---|---|---|---|
Side |
SheetSide |
Right |
Top, Right, Bottom, Left |
Popover
<BbPopover>
<BbPopoverTrigger>Open</BbPopoverTrigger>
<BbPopoverContent Side="PopoverSide.Bottom" Align="PopoverAlign.Center">
Content here
</BbPopoverContent>
</BbPopover>
| Parameter | Type | Default | Description |
|---|---|---|---|
Side |
PopoverSide |
Bottom |
Top, Right, Bottom, Left |
Align |
PopoverAlign |
Center |
Start, Center, End |
CloseOnEscape |
bool |
true |
Close when Escape key pressed |
CloseOnClickOutside |
bool |
true |
Close when clicking outside |
Tooltip
<BbTooltip DelayDuration="700" HideDelay="0">
<BbTooltipTrigger>Hover me</BbTooltipTrigger>
<BbTooltipContent>Tooltip text</BbTooltipContent>
</BbTooltip>
| Parameter | Type | Default | Description |
|---|---|---|---|
DelayDuration |
int |
700 |
Milliseconds before showing |
HideDelay |
int |
0 |
Milliseconds before hiding |
HoverCard
<BbHoverCard OpenDelay="700" CloseDelay="300">
<BbHoverCardTrigger>Hover for preview</BbHoverCardTrigger>
<BbHoverCardContent>Rich preview content</BbHoverCardContent>
</BbHoverCard>
| Parameter | Type | Default | Description |
|---|---|---|---|
OpenDelay |
int |
700 |
Milliseconds before showing |
CloseDelay |
int |
300 |
Milliseconds before hiding |
Dropdown Menu
<BbDropdownMenu ItemClass="px-2 py-1.5 cursor-pointer rounded hover:bg-accent">
<BbDropdownMenuTrigger>Menu</BbDropdownMenuTrigger>
<BbDropdownMenuContent>
<BbDropdownMenuItem>Cut</BbDropdownMenuItem>
<BbDropdownMenuItem>Copy</BbDropdownMenuItem>
<BbDropdownMenuItem Href="https://example.com" Target="_blank">Visit Site</BbDropdownMenuItem>
<BbDropdownMenuCheckboxItem @bind-Checked="isEnabled">Enable</BbDropdownMenuCheckboxItem>
</BbDropdownMenuContent>
</BbDropdownMenu>
| Parameter | Type | Default | Description |
|---|---|---|---|
ItemClass |
string? |
null |
CSS classes cascaded to all menu items |
BbDropdownMenuItem supports Href and Target for link items — renders as <a> when Href is set.
Switch
<BbSwitch @bind-Checked="isEnabled" class="relative h-6 w-11 rounded-full bg-input">
<BbSwitchThumb class="pointer-events-none block h-5 w-5 rounded-full bg-background shadow-lg" />
</BbSwitch>
BbSwitchThumb automatically syncs data-state ("checked" / "unchecked") from the parent via cascading parameter.
Radio Group
<BbRadioGroup TValue="string" @bind-Value="selected" ItemClass="flex items-center gap-2">
<BbRadioGroupItem Value="@("a")">Option A</BbRadioGroupItem>
<BbRadioGroupItem Value="@("b")">Option B</BbRadioGroupItem>
</BbRadioGroup>
| Parameter | Type | Default | Description |
|---|---|---|---|
ItemClass |
string? |
null |
CSS classes cascaded to all radio items |
Tabs
<BbTabs DefaultValue="tab1" Orientation="TabsOrientation.Horizontal"
ActivationMode="TabsActivationMode.Automatic">
<BbTabsList>
<BbTabsTrigger Value="tab1">Tab 1</BbTabsTrigger>
</BbTabsList>
<BbTabsContent Value="tab1">Content</BbTabsContent>
</BbTabs>
| Parameter | Type | Default | Description |
|---|---|---|---|
Orientation |
TabsOrientation |
Horizontal |
Horizontal, Vertical |
ActivationMode |
TabsActivationMode |
Automatic |
Automatic (on focus), Manual (on click) |
Table
<BbTable TData="Person">
<BbTableHeader>
<BbTableRow>
<BbTableHeaderCell>Name</BbTableHeaderCell>
<BbTableHeaderCell>Email</BbTableHeaderCell>
</BbTableRow>
</BbTableHeader>
<BbTableBody>
@foreach (var person in people)
{
<BbTableRow>
<BbTableCell>@person.Name</BbTableCell>
<BbTableCell>@person.Email</BbTableCell>
</BbTableRow>
}
</BbTableBody>
</BbTable>
| Parameter | Type | Default | Description |
|---|---|---|---|
SelectionMode |
SelectionMode |
None |
None, Single, Multiple |
SortDirection |
SortDirection |
None |
None, Ascending, Descending |
Portal Architecture
Primitives use a two-layer portal system for rendering overlay content:
- Container portals (
PortalCategory.Container): Dialog, Sheet — full-screen overlays - Overlay portals (
PortalCategory.Overlay): Popover, Select, Dropdown, Tooltip, HoverCard — positioned floating content
Each category has its own host (BbContainerPortalHost, BbOverlayPortalHost), so opening a tooltip doesn't cause Dialog portals to re-render. BbPortalHost is a convenience wrapper that renders both.
BbFloatingPortal keeps content mounted in the DOM when closed (ForceMount defaults to true), hidden via CSS. A data-state attribute ("open" / "closed") on the portal content enables CSS animations.
Controlled vs Uncontrolled
All stateful primitives support both controlled and uncontrolled modes:
Uncontrolled (Component manages its own state)
<BbDialog>
<BbDialogTrigger>Open</BbDialogTrigger>
<BbDialogPortal>
<BbDialogOverlay />
<BbDialogContent>Content</BbDialogContent>
</BbDialogPortal>
</BbDialog>
Controlled (Parent component manages state)
<BbDialog @bind-Open="isDialogOpen">
<BbDialogTrigger>Open</BbDialogTrigger>
<BbDialogPortal>
<BbDialogOverlay />
<BbDialogContent>
<button @onclick="() => isDialogOpen = false">Close</button>
</BbDialogContent>
</BbDialogPortal>
</BbDialog>
@code {
private bool isDialogOpen = false;
}
Design Philosophy
BlazorBlueprint.Primitives follows the "headless component" pattern popularized by Radix UI and Headless UI:
- Separation of Concerns: Primitives handle behavior and accessibility; you handle the design
- Composability: Build complex components by composing simple primitives
- No Style Opinions: Zero CSS included — bring your own design system
- Accessibility by Default: ARIA attributes and keyboard navigation built-in
When to Use
Use BlazorBlueprint.Primitives when:
- Building a custom design system from scratch
- Need complete control over component styling
- Want to match a specific brand or design language
- Integrating with existing CSS frameworks or design tokens
Consider BlazorBlueprint.Components when:
- Want beautiful defaults with shadcn/ui design
- Prefer zero-configuration setup with pre-built CSS
- Need to ship quickly without custom styling
Documentation
For full documentation, examples, and API reference, visit:
License
Apache License 2.0 - see LICENSE for details.
Contributing
Contributions are welcome! Please see our Contributing Guide.
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | net8.0 is compatible. net8.0-android was computed. net8.0-browser was computed. net8.0-ios was computed. net8.0-maccatalyst was computed. net8.0-macos was computed. net8.0-tvos was computed. net8.0-windows was computed. net9.0 was computed. net9.0-android was computed. net9.0-browser was computed. net9.0-ios was computed. net9.0-maccatalyst was computed. net9.0-macos was computed. net9.0-tvos was computed. net9.0-windows was computed. net10.0 was computed. net10.0-android was computed. net10.0-browser was computed. net10.0-ios was computed. net10.0-maccatalyst was computed. net10.0-macos was computed. net10.0-tvos was computed. net10.0-windows was computed. |
-
net8.0
- Microsoft.AspNetCore.Components.Web (>= 8.0.14)
NuGet packages (1)
Showing the top 1 NuGet packages that depend on BlazorBlueprint.Primitives:
| Package | Downloads |
|---|---|
|
BlazorBlueprint.Components
Pre-styled Blazor components built with shadcn/ui design and Tailwind CSS. Beautiful defaults that you can customize to match your brand. |
GitHub repositories
This package is not used by any popular GitHub repositories.
| Version | Downloads | Last Updated |
|---|---|---|
| 3.5.0 | 255 | 3/11/2026 |
| 3.4.0 | 892 | 3/6/2026 |
| 3.3.0 | 404 | 3/4/2026 |
| 3.2.0 | 969 | 2/28/2026 |
| 3.1.2 | 122 | 2/28/2026 |
| 3.1.1 | 106 | 2/28/2026 |
| 3.1.0 | 110 | 2/28/2026 |
| 3.0.1 | 734 | 2/17/2026 |
| 3.0.0 | 119 | 2/17/2026 |
| 2.3.2 | 463 | 2/10/2026 |
| 2.3.1 | 94 | 2/10/2026 |
| 2.2.0 | 397 | 2/8/2026 |
| 2.1.1 | 159 | 2/7/2026 |
| 2.1.0 | 92 | 2/7/2026 |
| 2.0.0 | 301 | 2/2/2026 |
## What's New in v3.0.0
### Breaking Changes
- All Razor components now use the `Bb` prefix (e.g., `Dialog` -> `BbDialog`, `Select` -> `BbSelect`)
- `BlazorBlueprint.Primitives.*` consumer-facing types (`AccordionType`, `SheetSide`, `SortDirection`, `PaginationState`, `PopoverSide`, `PopoverAlign`, `PopoverPlacement`, `PositioningStrategy`) moved to root `BlazorBlueprint.Primitives` namespace
- `PositioningOptions.Strategy` changed from `string` to `PositioningStrategy` enum
- `SelectItem.TextValue` renamed to `SelectItem.Text`
- `IPortalService` redesigned with two-layer portal architecture: `PortalCategory.Container` and `PortalCategory.Overlay`
- `RegisterPortal(id, content)` replaced by `RegisterPortal(id, content, category)`
- `GetPortals()` replaced by `GetPortals(PortalCategory)`
- `OnPortalsChanged` replaced by `OnPortalsCategoryChanged`
### New Components & Sub-Components
- `BbCheckboxIndicator` — auto-renders check/indeterminate SVG icon based on parent state, with configurable `Size` and `StrokeWidth`
- `BbSwitchThumb` — auto-syncs `data-state` attribute from parent `BbSwitch` via cascading parameter
- `BbFloatingPortal` — unified floating content infrastructure with `ForceMount` (default: `true`) and `data-state` attribute for CSS animations
- `BbCategoryPortalHost`, `BbContainerPortalHost`, `BbOverlayPortalHost` — category-scoped portal hosts for efficient rendering
- `BbPortalHost` — convenience wrapper rendering both Container and Overlay portals
### New Features
- `ItemClass` parameter on `BbDropdownMenu`, `BbRadioGroup`, and `BbSelect` — cascades CSS classes to all child items
- `[CascadingTypeParameter]` on `BbSelect<TValue>` — child components infer `TValue` from parent
- `Href` and `Target` parameters on `BbDropdownMenuItem` — renders as `<a>` for link items
- `ISelectDisplayContext` interface for non-generic display text access
- `PortalCategory` enum for scoped portal rendering
- `IPortalService.HasHost` property and `RegisterHost()`/`UnregisterHost()` methods
- `ForceMount` on `BbFloatingPortal` — keeps portal content mounted when closed, eliminating re-mount overhead
### Performance
- Menu keyboard navigation moved from C# to JavaScript via `menu-keyboard.js` — zero interop round-trips for arrow keys, Home, End, Enter, Space
- Dialog, Sheet, and Drawer escape key handling moved to JavaScript via `escape-keydown.js`
- `BbFloatingPortal` defaults to `ForceMount=true` — no re-mount overhead on open/close cycles
- `FocusManager.GetModuleAsync()` now thread-safe with `SemaphoreSlim`
### Bug Fixes
- `autoUpdate` callback in positioning.js now guards against stale DOM elements during Blazor render cycles
- `FocusManager` and `PositioningService` disposal now catches `JSDisconnectedException` for Blazor Server circuit disconnect safety
- Select display text state change optimization to prevent unnecessary re-renders