Architecture
NeoUI is built on a two-layer architecture that separates behavior from styling, and is designed first for .NET 10's Auto rendering mode.
Two-Layer Overview
NeoUI.Blazor
Styled components layer
100+ production-ready components with shadcn/ui design.
Pre-built CSS included. Built on top of the primitives layer.
Full theme support via CSS custom properties.
NeoUI.Blazor.Primitives
Headless accessibility layer
15 headless, unstyled components providing behavior only.
WCAG 2.1 AA accessibility, keyboard navigation, ARIA.
Bring your own styles — or use NeoUI.Blazor on top.
Every styled component in NeoUI.Blazor is built
on a matching primitive. This means accessibility is never an afterthought — it is baked in at
the foundation and the styled layer cannot regress it.
Auto Rendering Mode
All NeoUI components support .NET 10's Auto rendering mode, which starts with interactive Server-Side Rendering for fast first load, then seamlessly transitions to WebAssembly once the runtime has been downloaded in the background.
Fast initial load
Server renders the first frame immediately — no WASM download delay.
Rich interactivity
After WASM downloads, the app runs entirely client-side.
Seamless transition
Switching between modes is transparent to the user and to your components.
Portal System
Overlay components (Dialog, Sheet, Drawer, Popover, Tooltip, DropdownMenu) render
their content outside the component tree using a portal. This prevents common
CSS stacking context issues where a parent with overflow: hidden
or a low z-index would clip the overlay.
NeoUI uses four dedicated hosts that you add to the end of your main layout:
ToastViewport
Renders toast notification messages in a configurable screen position
(e.g. BottomRight,
TopCenter).
DialogHost
Required when using DialogService
for programmatic dialogs — a convenience API to show dialogs from C# without
adding <Dialog> markup to every page.
ContainerPortalHost
Inline overlays: Popover, Tooltip, DropdownMenu, ContextMenu, HoverCard. Positioned relative to their trigger.
OverlayPortalHost
Full-screen overlays: Dialog, AlertDialog, Sheet, Drawer. Rendered above everything with a backdrop.
Place All Hosts After @Body
@Body
in the layout file, and outside any scrollable container. Placing them inside a div with
overflow: hidden will clip overlays.
CSS Variable Theming
Every color in every component is driven by a CSS custom property. There is no hardcoded palette. This means:
- Themes switch by updating variable values — no class changes, no component re-renders
- Full shadcn/ui and tweakcn.com theme compatibility
- Dark mode by adding
.darkto the<html>element - Instant runtime theme switching via
ThemeService
See the Theming guide for the full variable reference.
AsChild Pattern
Trigger components (e.g. DialogTrigger,
PopoverTrigger) accept an
AsChild property.
When set, the trigger renders its child element directly instead of its own default
<button> — allowing you to use
any styled component as the trigger without losing the overlay behavior.
This is the standard pattern from Radix UI and shadcn/ui and enables full compositional flexibility.
Performance by Design
Performance is a first-class design constraint in NeoUI, not an afterthought. While NeoUI is fundamentally Pure Blazor — no JavaScript framework, no React, no Vue — certain behaviors are deliberately handed off to lightweight JavaScript helpers when doing so produces a measurably better user experience.
Keyboard navigation via JS
Keyboard interactions in interactive components (DropdownMenu, Select, Command, Combobox) are handled directly in JS rather than routing each keystroke through Blazor's event loop. This gives sub-frame response times — pressing ↑ or ↓ produces immediate visual feedback before the Blazor render cycle completes.
Consistent input experiences across rendering modes
Input components that require precise cursor control — masked inputs, OTP slots, numeric formatters — use JS-backed event interception so text composition (IME), cursor positioning, and mobile keyboard behavior work correctly regardless of whether the component is running on Blazor Server, WebAssembly, or in Auto mode. Without this, IME-heavy scripts (CJK) and mobile soft keyboards produce inconsistent behavior across rendering modes.
Selective re-renders via split portal hosts
ContainerPortalHost
and OverlayPortalHost
are deliberately separate components. Opening a tooltip or popover only triggers a
re-render of ContainerPortalHost,
not the heavier overlay host. Heavy dialog renders don't cascade into lightweight overlay interactions.
JS as an optimization, not a dependency
Intentional Motion
NeoUI's animations are designed to be felt rather than noticed. Every motion is purposeful, short-lived, and grounded in physical feedback — enhancing perceived quality without drawing attention to itself.
All animations are CSS-based: @keyframes
compiled into components.css,
combined with Tailwind animation utilities and CSS custom properties for duration and easing.
No JavaScript animation library is required.
Accordion & Collapsible
Height animates using CSS custom properties
(--radix-accordion-content-height).
The HeightAnimation
utility component generalises this pattern for any dynamic-height content.
Popover, Tooltip & DropdownMenu
Fade and scale from the anchor point using
animate-in /
animate-out
Tailwind classes. Enter and exit use separate keyframes for asymmetric timing.
Dialog, Sheet & Drawer
Slide in from the appropriate edge or center on open; slide out on close. The backdrop fades independently from the panel so each can have its own easing.
Motion component
A declarative animation system with 20+ presets (fade, slide, scale, blur) for animating any element in your own pages and layouts, not just NeoUI components.
Respects prefers-reduced-motion
prefers-reduced-motion
media query. When set, animations are removed or reduced to simple opacity transitions so
users who are sensitive to motion are not affected.
Key Design Principles
Accessibility first
WCAG 2.1 AA compliance baked into the primitives layer. Keyboard navigation and ARIA on every interactive component.
Zero configuration
Pre-built CSS ships with the NuGet package. No Tailwind, no Node.js, no build pipeline needed in the consuming project.
Composition over inheritance
Components are designed to be composed together. The AsChild pattern and render fragment slots make this natural.
Pure Blazor
No JavaScript framework dependencies. The small JS helpers (theme init, clipboard) are plain vanilla JS.
Feature-based organisation
Each component lives in its own folder with markup (.razor) and logic (.razor.cs) co-located.
Flat namespace
All components live in NeoUI.Blazor. One using directive gives you everything — no per-component imports.