Monica UI Development Guide
This skill is for Monica Blazor UI work with MudBlazor v9.
All script paths in this document are relative to the mo-ui-development skill directory.
Project-local temporary state for this skill is stored under:
.tmp/mo-ui-development/mudblazor-source.json- saved MudBlazor source root.tmp/mo-ui-development/mudblazor-css-variables.json- generated machine-readable CSS variable list
MudBlazor Source Access (Use Only When Needed)
MudBlazor source inspection is not required for every UI task. Use it when:
- MudBlazor API usage or runtime behavior is uncertain
- You need to inspect component internals, styles, or unit tests
- You are verifying migration details for MudBlazor v9
- You need to refresh the authoritative CSS variable list from source
Before a source-dependent task, run:
python scripts/check_mudblazor_source.py
If the current task is source-dependent and the check fails, you must stop that work immediately. Do not continue by guessing from memory, migration notes, or outdated examples.
Required recovery flow for source-dependent work:
- Ask the user for their local MudBlazor source path, or ask them to download MudBlazor source first.
- After the user provides the path, the agent saves it into
.tmp/mo-ui-development/mudblazor-source.json. - Only continue after the check succeeds.
Use this command to persist the path into the project-local temp config and verify it:
python scripts/check_mudblazor_source.py --save-source-root <your-local-mudblazor-source-root>
Do not ask the user to set environment variables for this workflow.
If the task is not source-dependent and the existing references are enough, continue without source inspection.
MudBlazor v9 Source-First Rules
- For any source-dependent UI task, MudBlazor source availability is mandatory.
- Treat local MudBlazor source as the source of truth for uncertain APIs or behavior.
- If source is unavailable, stop the source-dependent task until the user provides a valid source path.
- Preferred source entry points:
src/MudBlazor/Components/...src/MudBlazor/Styles/...src/MudBlazor.UnitTests/...
- Use
rgfor quick lookup afterpython scripts/check_mudblazor_source.pyreports the resolved source root:
rg -n "ShowAsync|ShowMessageBoxAsync|GetDefaultConverter|IReversibleConverter" <resolved-mudblazor-source-root>\src
Critical UI Rules
1. CSS Isolation
- Never use
<style>tags in.razor. - Use
.razor.cssfiles. - CSS isolation applies to HTML elements, not Razor components.
- For MudBlazor styling, wrap with a container and use
::deep.
<div class="table-wrapper">
<MudTable Items="@items" />
</div>
.table-wrapper ::deep .mud-table {
background-color: var(--mud-palette-surface);
}
2. Icons
Always use @ for icon expressions:
<MudIconButton Icon="@Icons.Material.Filled.Add" />
3. Generic Component T Parameter
Always specify T for generic MudBlazor components:
<MudSwitch T="bool" @bind-Value="@isEnabled" />
4. Lifecycle and JS Interop
- Do not run JS interop in
OnInitializedAsync. - Use
OnAfterRenderAsync(firstRender)for JS interop and heavy first-load tasks. - Use
CancellationTokenfor async loading tasks.
5. MudBlazor v9 Async APIs
- Use async methods only (
ShowAsync,ShowMessageBoxAsync, etc.). - Do not use removed sync APIs from older versions.
6. Converters and Custom Form Components
- Use v9 converter interfaces (
IConverter,IReversibleConverter). - Custom form components must implement
GetDefaultConverter().
7. Offline/Intranet Requirements
- No online font/CDN dependencies for runtime UI assets.
- Keep static resources local (
wwwroot/fonts, local CSS/JS assets).
8. Layout-Owned AppBar and Viewport Height
- Keep AppBar height and remaining viewport height owned by the shell layout.
- In
MoMainLayout.razor.css, expose--mo-appbar-height: var(--mud-appbar-height, 64px)on.mo-layout. - AppBar and navigation components must consume the layout variable (
height: var(--mo-appbar-height)orheight: 100%when the parent already owns the height). - Full-height pages must rely on the parent container with
height: 100%,min-height: 0, and local overflow handling instead ofcalc(100vh - 64px),calc(100vh - 56px), or similar hardcoded offsets. - Loading, empty, and placeholder states should consume available space with flex/grid alignment when the parent height is available, instead of using large fixed top/bottom padding for visual centering.
- Keep scrolling in
.mo-body-contentor the page's own scroll containers; do not move scrolling back tobody.
9. Theme-First Visual Simplicity
- Prefer simple, quiet layouts that mostly rely on the active MudBlazor theme.
- Do not introduce gradients, glow effects, decorative shadows, or custom multi-color surfaces unless the user explicitly asks for a branded visual treatment.
- Favor
var(--mud-palette-surface),var(--mud-palette-background-gray),var(--mud-palette-lines-default), andvar(--mud-palette-text-secondary)over inventing new color systems. - Use CSS isolation primarily for layout, spacing, centering, sizing, and overflow control. Do not use it to repaint large parts of MudBlazor unless there is a clear product requirement.
- When list or card UIs become dense, remove redundant metadata first. Prefer a minimal primary view and move secondary details into dialogs, drawers, or detail panes.
- If centered alignment looks wrong, fix the container layout first (
display,align-items,justify-content,min-height,min-width) before adding margin or padding hacks.
MudBlazor CSS Variable Workflow (Required)
A. Initialize or Update Variable List
Run this when you need to refresh the generated variable list from MudBlazor source:
python scripts/sync_mud_css_variables.py
This workflow is source-dependent. If .tmp/mo-ui-development/mudblazor-source.json is missing or points to an invalid path, stop and follow the source recovery flow above.
This script reads:
src/MudBlazor/Components/ThemeProvider/MudThemeProvider.razor.cs
and updates:
.tmp/mo-ui-development/mudblazor-css-variables.json(authoritative machine-readable list of real variables)
B. Validate CSS/Razor Usage
Validate all CSS and Razor files under a project/repo root:
python scripts/validate_mud_css_variables.py --root D:\Code\MoLibrary
JSON output:
python scripts/validate_mud_css_variables.py --root D:\Code\MoLibrary --json
C. Safe Auto-Fix Mode
Apply safe deterministic replacements, then revalidate:
python scripts/validate_mud_css_variables.py --root D:\Code\MoLibrary --fix
Safe auto-fix scope is intentionally limited. Remaining unknown variables require manual review.
Browser Storage (IMoBrowserStorage)
- Use
IMoBrowserStorageinstead of rawIJSRuntimefor local/session storage access. - Load persisted UI state in
OnAfterRenderAsync(firstRender)to avoid flash/reset issues. - Use
BrowserStorageExtensionsfor table state patterns.
See:
references/browser-storage-guide.md
Localization (i18n)
- Do not hardcode user-facing text.
- Use decentralized module resources with marker class + JSON resource files.
- Keep module resource marker classes and JSON folders under the project root
Localization/directory. - Prefer nested JSON objects and access them with colon-separated keys such as
Page:TitleorRuntimeConfigDialog:Intro. - Do not use flat dot-style keys such as
Page.Titlefor new Monica UI work. - Keep
zh-CN.jsonanden-US.jsonsynchronized. - For page content, use the module-local resource marker and JSON files.
- For
RegisterLocalizedComponent(...)navigation/AppBar text,displayNameKeyandcategoryKeymust exist inMonica.UI/Localization/UIRegistryResource/*.json, because the UI registry resolves them withIStringLocalizer<UIRegistryResource>. - When adding a new page to navigation, add the corresponding
Pages:*:Titlekey toUIRegistryResourcein addition to the page module resource when needed.
Validation command:
python scripts/validate_localization.py
See:
references/localization-guide.md
Service Error Handling in Components
For Res/Res<T> usage, IResultEnvelope, and the IsFailed pattern in UI service calls, use the mo-development skill.
References
references/module-structure-guide.mdreferences/blazor-best-practices.mdreferences/component-reference.mdreferences/migration-guide-v9.mdreferences/css-isolation-fix-workflow.mdreferences/theme-css-guide.mdreferences/offline-requirements.mdreferences/browser-storage-guide.mdreferences/localization-guide.md.tmp/mo-ui-development/mudblazor-css-variables.json(real available CSS variable list, generated)references/mudblazor-css-variables.md(semantic usage guide, manually maintained)
Scripts
scripts/check_mudblazor_source.py- Verify the saved project-local MudBlazor source path and optionally persist it into.tmp/mo-ui-development/mudblazor-source.json.scripts/sync_mud_css_variables.py- Initialize/update real MudBlazor CSS variable JSON into.tmp/mo-ui-development/mudblazor-css-variables.json.scripts/validate_mud_css_variables.py- Validate MudBlazor variable usage in CSS/Razor files and apply safe auto-fixes using the generated.tmpvariable list by default.scripts/validate_localization.py- Validate localization keys (missing/unused/sync) and verifyRegisterLocalizedComponentkeys againstUIRegistryResource.scripts/font_downloader.py- Download fonts for offline usage.
Quick Checklist
- Run the source check only for source-dependent work
- If source check fails during source-dependent work, stop and ask the user for a valid local source path
- Save the provided source path into
.tmp/mo-ui-development/mudblazor-source.json - Confirm uncertain APIs from MudBlazor source before continuing source-dependent work
- Use CSS isolation (
.razor.css) with wrapper +::deep - Use MudBlazor v9 async APIs
- Use valid MudBlazor CSS variables only
- Run CSS variable validation when styling changes
- Use
IMoBrowserStoragefor browser persistence - Keep AppBar height and viewport compensation in the shell layout, not in page CSS
- Use localization for all user-facing text
- Add AppBar/navigation keys to
UIRegistryResourcewhen usingRegisterLocalizedComponent
Page Complexity Checklist
Before creating or modifying a page, verify it stays within architecture limits. See mo-architecture skill for full Page Decomposition Rules.
Pre-Flight Check (Before Writing a Page)
- Page markup ≤ 200 lines (composition only, no inline business logic)
- Page
@codeblock ≤ 150 lines (lifecycle + event wiring) - Total page file ≤ 350 lines
- Page injects Facades directly — no intermediate
*UIServicewrapper - State lives in
UI{Name}/State/, not in page fields - No
CancellationTokenSource,Interlocked, or polling loops in page code
Red Flags During Review
| Symptom | Action |
|---|---|
| Page > 350 lines | Extract components to UI{Name}/Components/ |
> 10 private fields in @code |
Extract state class to UI{Name}/State/ |
CancellationTokenSource in page |
Move polling to State/ or Support/ |
> 5 Can*() guard methods |
Move to state class with computed properties |
| Duplicated loading skeletons | Extract shared loading component |
Flat root Services/ in UI module |
Restructure to UI{Name}/State/ + UI{Name}/Support/ |
*UIService wrapping a Facade |
Remove wrapper, inject Facade directly |
Correct UI Module Structure
Monica.{Name}.UI/
├── Pages/
│ └── UI{Name}Page.razor # Thin shell ≤ 350 lines
├── UI{Name}/
│ ├── Components/ # Extracted UI sections
│ ├── Dialogs/ # Dialog components
│ ├── State/ # Page state, polling, loading
│ └── Support/ # Coordinators, resolvers, formatters
For a real anti-pattern case study (UIAIRAGManagePage.razor — 1,614 lines), see mo-architecture skill → references/refactoring-examples.md.
For State class implementation patterns (data bag, async Facade-calling, polling/concurrency), see mo-architecture skill → references/page-state-pattern.md.
Converted and distributed by TomeVault — claim your Tome and manage your conversions.