Skip to content

AppShell v2: rebuild for both @dashforge/ui and @dashforge/tw to anchor the CLI scaffold #3

Description

@kensaadi

Context

AppShell is the chrome that wraps every dashboard built with Dashforge — top bar, side navigation, content area, optional secondary panels. Both the @dashforge/ui (MUI) and @dashforge/tw (Tailwind) variants exist, but they were designed early in the lib's life when the priority was "ship something composable". They work, but they don't yet meet the bar we now hold the rest of the catalogue to.

Why this matters now: the upcoming dashforge-cli init template generator will scaffold a working Dashforge app from a single command. AppShell is the visible centrepiece of that template — it's the first thing a new developer sees when they run the CLI. If AppShell looks dated, the whole library feels dated. If it looks inevitable — like the shell you'd expect from a production-grade dashboard product — every other component inherits that credibility.

Goal

Audit and rebuild AppShell for both ecosystems with the same level of attention we gave the form layer and the foundation primitives — driven by what production B2B/internal-tools dashboards actually look like today (Linear, Vercel dashboard, Notion side rail, GitHub's repo admin, Stripe dashboard, Sentry, Datadog). The output of this issue is the AppShell the CLI will lock in as the canonical scaffold for the next 12 months.

Target capability matrix

Each of the following must be supported as typed props, not as "you can wire it yourself with sx":

Side navigation

  • Two-tier hierarchy — a thin primary rail with icons + optional labels (apps / workspaces / top-level destinations) and a secondary panel that reveals contextual sections for the active primary item. Selecting a primary item should swap the secondary panel without a route round-trip.
  • Workspace / project switcher pinned at the top of the primary rail — avatar + name + a popover with instant filter ≥ N entries, recent items, "create new" footer.
  • Pinned / favourites / recent sections in the secondary panel with stable ordering.
  • Collapsible primary rail (icons only) with the state persisted per-user (localStorage hook + opt-in cookie).
  • Active state propagation — both the primary rail item AND the secondary panel item must paint as active when the route matches.
  • Nested groups in the secondary panel (collapsible sub-sections, depth ≤ 2 to keep keyboard nav sane).
  • RBAC-aware items — every nav item accepts access + visibleWhen (the universal bridge contract from Sprint 4.4). Items disappear / disable / readonly based on permissions, without consumer-side if (canSee) chains.

Top bar

  • Global search with a Cmd+K / Ctrl+K shortcut, scoped + global modes, recent searches, prominent placement (centre or left, never afterthought).
  • Breadcrumb trail auto-derived from the route hierarchy + manual override slot.
  • Notification center trigger (count badge, popover with grouped/unread states, mark-all-read).
  • User menu with avatar, status (online/away/dnd), profile shortcut, settings, sign-out.
  • Theme / density toggle in the user menu, persisted.
  • Help / docs trigger (popover or external link).

Content area

  • Skip links for keyboard / screen-reader users.
  • Optional right rail (contextual inspector / details pane) with collapse + resize.
  • Responsive breakpoints that demote the side nav to a drawer at < md and a bottom tab bar at < sm (opt-in for the mobile pattern).
  • Density modescomfortable (default) and compact, applied at the shell level so every nested component picks it up via context.

A11y + interaction

  • Landmark roles wired (banner / navigation / main / complementary / contentinfo).
  • Focus trap when the mobile drawer is open; restored to trigger on close.
  • Full keyboard nav inside the side nav — / move within a group, / expand/collapse, Home/End, Tab lands on the next landmark.
  • prefers-reduced-motion respected on every transition (drawer slide, collapse).
  • Visible focus rings everywhere (focus-visible, not box-shadow hacks).

State + persistence

  • Collapsed/expanded state of every collapsible region persisted in a single typed store (default: localStorage, pluggable via a storage prop so consumers can swap in their own).
  • Workspace switch updates the URL or a state slice, never a full reload.
  • Resize positions for the right rail persisted.

Slots + escape hatches

  • Logo slot, search slot, primary-rail header / footer, secondary-panel header / footer, top-bar trailing, content-header (sticky breadcrumb + title + page actions), footer.
  • Every visual surface accepts slotProps for utility overrides without forcing a wrapper component.

Out of scope (explicit)

  • We are NOT building a router. AppShell is decoupled from react-router-dom and next/link — consumers wire links via the as/asChild pattern already used in the rest of the lib.
  • Multi-account switcher (vs. workspace switcher) — separate issue if it ever lands.
  • Real-time notification streaming — AppShell exposes the slot, the consumer wires the data.

Deliverables

  1. Audit document under dashforge-docs-lab/src/tw-docs/content/design/appshell-audit.md (or wherever fits the docs IA) — current <AppShell> API for both ecosystems, capability gaps measured against the matrix above, decisions for each gap (ship / defer / not doing).
  2. New / revised API in both @dashforge/ui and @dashforge/tw. The prop surface must be identical across the two libs so the CLI template can swap ecosystems with one import change.
  3. Implementation in both packages.
  4. Migration guide in the release MDX for the version that ships this — every existing AppShell consumer (we have at least three: learn/dash, the docs themselves, the starter kit) should migrate cleanly with a recipe.
  5. Catalogue page under /components/app-shell showing every capability in a live demo (similar to what the Menu + Card + Avatar pages do, but more ambitious).
  6. Three starter-template variants the CLI will pick from:
    • admin — full two-tier side nav + global search + workspace switcher (the Linear / Jira shape)
    • docs — slim side nav + content header + right rail TOC (the Stripe docs shape)
    • product — top bar + tab navigation in a sub-header, no side nav (the Vercel project shape)

Acceptance

  • Audit doc merged with a green check on every row of the capability matrix above (ship / defer)
  • @dashforge/ui AppShell v2 and @dashforge/tw AppShell v2 ship in the same minor release
  • Prop surface is byte-for-byte identical across the two libs (verified by a typecheck snapshot test)
  • Three starter-template variants render correctly when scaffolded
  • Migration guide tested against learn/dash and the starter-kit consumer — both compile and render the same UI
  • Lighthouse pass: a11y 100, performance ≥ 95 on the catalogue demo
  • Keyboard-only walkthrough completes every nav action without trapping the user

Estimated effort

Larger than a Sprint 5 single ticket — likely a 2–3 week stream split into sub-issues once the audit is done. Suggest treating this as the Sprint 5 epic and using it as the umbrella; sub-issues land as the audit identifies them.

Out of scope: CLI scaffold itself

The actual dashforge-cli init implementation is a separate effort. This issue produces the artefact the CLI templates consume.

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or request

    Projects

    No projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions