Repository navigation
Admin shell and component set #2434
Copy link
Copy link
Closed
Labels
type:choreRepository, tooling or housekeepingRepository, tooling or housekeepingv2FluentCMS v2 (v2 branch)FluentCMS v2 (v2 branch)
Milestone
Description
Goal
Stand up the admin as a Blazor Web App in FluentCMS.Admin (ADR-0006) with the styling pipeline, the theme and the owned component set from ADR-0011, so that the first feature screens start from working, tested primitives instead of building them. The first slice is the one-week spike ADR-0011 leaves open, and its outcome goes into the ADR before the ADR is accepted. No feature screen is part of this issue.
First slice: the spike (one week)
- Build Dialog, DropdownMenu and Command both ways: on our own headless primitives (native
<dialog>, the Popover API and the interop module below) and on Blazor Blueprint's headless layer (Apache 2.0). - Compare the two on the points ADR-0011 names: keyboard and screen-reader behaviour, RTL, behaviour over an interactive-server circuit including reconnect, dependency size, and the project's maintenance activity.
- Write the outcome into the "Open until the spike" section of ADR-0011 in its own pull request and set the status to accepted (ADR-0001). The rest of this issue continues only after that pull request is merged.
- Delete the losing variant. Either way, no styled component comes from a third-party library.
Shell
- Blazor Web App in
FluentCMS.Admin: routes, root component and layout, interactive server render mode for application pages, static server rendering available for stateless pages. Composed by Host; Admin keeps referencing only Core (ADR-0004). - Root element carries the
dirattribute and the theme class. The home route stays a placeholder until F-DSK (F-DSK: Review desk as the home screen #2422) makes the desk the landing page. - One small JavaScript interop module for what the platform does not cover: focus trapping, roving tab index and global keyboard shortcuts. It is the only JavaScript in the admin.
- Default reconnect handling, so the spike and the component tests can exercise behaviour over a lost circuit (ADR-0006).
Styling and theme
- Tailwind CSS v4 through the standalone CLI: an MSBuild target in the Admin project downloads the pinned binary once per machine into a cache, verifies its checksum, and compiles the stylesheet before each build. An MSBuild property pointing at a pre-downloaded binary serves offline and CI builds. No Node, no npm, no JavaScript Tailwind plugins.
- CI caches the binary keyed on its pinned version.
- shadcn tokens verbatim as CSS variables (background, foreground, primary, muted, border, ring, radius and the rest), light and dark themes switched by a class on the root element and defaulting to the system preference.
- A C# port of tailwind-merge for class merging and a small variants helper in the CVA style, so shadcn examples translate one-to-one into Razor.
- Lucide icons as inline SVG components generated from the icon set.
Component set
- Components live in
FluentCMS.Admin/Components/Ui/, no separate project, no package. Each is self-contained: Razor markup with Tailwind classes in the shadcn style, parameters limited to what the screens use, no base-class hierarchy. - The initial set from ADR-0011: Button, Input, Textarea, Select, Checkbox, Switch, RadioGroup, Badge, Card, Separator, Tabs, Dialog, Sheet, DropdownMenu, Popover, Tooltip, Command, Toast, Table (QuickGrid with the shadcn table classes), Kbd, FormField (label, description, error, and a slot for the active rule hints FRM-03 needs; the hints themselves come with F-FRM), and DiffView for the desk. DiffView renders the structured diff model from Core, which F-CHG (F-CHG: Change sets with diff, atomic publish and one-click revert #2418) defines, so it is the last component and may land alongside that slice.
- Localization and RTL from the first component: every user-facing string goes through
IStringLocalizerresources; layout uses Tailwind's logical utilities (start and end, not left and right); the rootdirattribute flips the direction. - bUnit tests per component for rendering and keyboard behaviour, in
tests/FluentCMS.Tests. - The
/uishowcase page rendering every component with its keyboard hints, in light and dark themes. Reachable only in the Development environment until F-USR (F-USR: Users, sign-in and the flat administrator role #2428) provides sign-in, then restricted to signed-in users. It is the living documentation for contributors and the visual regression target. - Document the Tailwind standalone step and the offline path under "Building and testing" in
CONTRIBUTING.md.
Out of scope
- Any feature screen: the desk (F-DSK: Review desk as the home screen #2422), generated forms (F-FRM: Generated forms as the human fallback editor #2426), the media screen (F-MED: Media library with required alt text #2427), sign-in and user management (F-USR: Users, sign-in and the flat administrator role #2428).
- Authentication and authorization (F-USR, F-USR: Users, sign-in and the flat administrator role #2428); the
/uirestriction uses them once they exist. - The structured diff model (F-CHG, F-CHG: Change sets with diff, atomic publish and one-click revert #2418).
- Playwright tests for the desk flows (F-DSK, F-DSK: Review desk as the home screen #2422).
- Shipping the component set as a package, and plugin-contributed admin screens (EXT-03): M3, decided with the plugin SDK.
Depends on
- Create the v2 solution skeleton (Create the v2 solution skeleton #2433): the Admin project, the test projects and the CI job.
- Needed before the sign-in page in F-USR (F-USR: Users, sign-in and the flat administrator role #2428), the desk in F-DSK (F-DSK: Review desk as the home screen #2422), generated forms in F-FRM (F-FRM: Generated forms as the human fallback editor #2426) and the media screen in F-MED (F-MED: Media library with required alt text #2427).
Acceptance
From the consequences of ADR-0011 and UX-04:
- The spike outcome is recorded in ADR-0011 and the ADR is accepted. Beyond what the spike chose for the headless layer,
FluentCMS.Adminhas no runtime dependency on a UI library. dotnet buildcompiles the stylesheet on a machine without Node; a build with the pre-downloaded binary succeeds offline; CI downloads the binary once and serves it from cache afterwards./uirenders every component of the initial set in both themes with its keyboard hints, is usable at phone width, passes automated WCAG 2.1 AA checks (UX-04), and renders correctly with the rootdirattribute set tortl.- Every component has bUnit tests for rendering and keyboard behaviour. Focus trapping in Dialog, roving focus in DropdownMenu and Command, and global shortcuts work over an interactive-server circuit and still work after a reconnect, because the interop module keeps focus, scroll and key capture on the client.
- No user-facing string in the shell or the components is hard-coded; all come from
IStringLocalizerresources. - A shadcn documentation example for a component in the set translates into Razor with the same class names and the same keyboard behaviour.
Metadata
Metadata
Assignees
Labels
type:choreRepository, tooling or housekeepingRepository, tooling or housekeepingv2FluentCMS v2 (v2 branch)FluentCMS v2 (v2 branch)
Activity
Accept ADR-0001 to ADR-0010 and link the announcement (#2436)
pournasserian commentedon Oct 17, 2026
Spike result (first slice): write the headless primitives in this repository, native HTML first, with one interop module. Pull request #2444 carries the shell, the Tailwind pipeline, the winning Dialog, DropdownMenu and Command, the spike record (
docs/spikes/2026-10-admin-primitives.md) and ADR-0011 set to accepted.Why, in short: Blazor Blueprint's headless layer has no Command primitive, so one of the three components would have been ours anyway or a dependency on its styled package. Its overlays keep their open state on the server and need a portal host inside an interactive subtree, which per-page render modes make awkward; the native variant keeps working through a lost circuit and after a pause and resume, and needs no portal host, no scoped services and no per-component interop. Keyboard, screen-reader and RTL behaviour were otherwise equivalent, with the native variant ahead on three details (inert page behind a modal, focus on the first menu item, focus back to the trigger after Escape). The shadcn wrappers around Blueprint were as long as the primitives themselves, the package adds 891 KB of assembly and 212 KB of JavaScript for two components, and the project has shipped three major versions in eight months from one maintainer.
To see both variants side by side before deciding: commit 4595d28 on the branch (
git checkout 4595d282, run the host in Development, open/ui-spike). The final commit deletes the losing variant.Add the admin shell and settle the ADR-0011 primitives spike (#2444)
Add sign-in, first-run setup and web hardening (#2450)
Add the audit log screen with export download and item history (#2460)
Add the user management screen (#2466)
Add the theme toggle, reconnect display and /ui showcase (#2468)
Add the field-level change set diff (#2475)
Add the DiffView component (#2479)
Add the Textarea, Select, Switch, RadioGroup, Separator and Kbd compo…
Add the Tabs, Sheet, Popover, Tooltip and Toast components (#2493)
Add global keyboard shortcuts and one roving tab index to the interop…
Merge classes like tailwind-merge and add a cva-style variants helper (…
Add keyboard-first review with roving focus, browser tests and WCAG c…