Contract-freeze · UX approval · FF-EPIC-13 / FF-EPIC-10

White-label tenant portal — shell & login

One shell, many portals. The topbar, side panel, app grid, and login all reskin to the active portal's name, logo, and accent — sourced from GET /api/v1/portal/context and applied entirely through design-system token overrides (no raw hex). Approving these frames approves the flows, components, and packages in the build inventory below, so implementation can't quietly invent a different architecture. Built in the fuse-seam design system.

Flows to approve

Flow · portal-shell → PortalShell · route /

🏢 Branded tenant shell

Topbar + side panel + app grid, reskinned by portal context. Shown for CorpABC and for the root FuzeFront default to prove it's the same shell.

approval: per-flow frames 1 · 2 · 4
Flow · portal-login → PortalLoginFlow · route /login

🔑 White-label login

Tenant-branded sign-in served on the portal host — logo, name, accent, custom copy — with auth served same-origin by the hidden platform.

approval: per-flow frames 3 · 5

Walk the frames

Frame 1 · portal-shell

Branded tenant shell (CorpABC)

Accent, logo, name applied to topbar, active nav, avatar, seam, app-icons.

data-portal = corpabc
Frame 2 · portal-shell

Root FuzeFront shell

Identical markup; only data-portal flips to root. Reskin is token-only.

data-portal = root
Frame 3 · portal-login

White-label login (CorpABC host)

Branded sign-in with custom copy, SSO, forgot-password; public context paints pre-auth.

data-portal = corpabc
Frame 4 · portal-shell

Light / dark theming

Portal accent layered over both light and dark theme tokens — orthogonal layers.

data-theme × data-portal matrix
Frame 5 · both flows

States (contract)

Loading no-flash · error+retry · suspended · unknown-host fallback · cross-portal rejection.

fail-closed UX

Build inventory

Approving the frames approves this inventory. It mirrors manifest.json → build exactly — flows, React components, the net-new design-system primitive, and the npm package.

Flows

  • portal-shellPortalShell · /
  • portal-loginPortalLoginFlow · /login

React components

  • PortalBrandingProvider — boots & caches portal context
  • PortalThemeScope — applies accent as scoped token overrides
  • BrandedTopBar
  • BrandedSidePanel
  • PortalAppGrid
  • PortalBrandLockup (logo + name, favicon swap)
  • WhiteLabelLoginCard
  • BrandingBoundary — loading / error / retry
  • PortalUnavailableNotice — suspended / cross-portal

Design-system primitives

  • BrandTokenScopeNET-NEW in @fuzefront/design-system: applies a validated portal accent as scoped CSS-var overrides over base tokens, with WCAG-AA contrast fallback + malformed-color rejection. Owned by frontend-engineer.
  • Logo slot primitive (img/initials fallback, never a broken-image icon) — add if not already present.

npm packages

  • @fuzefront/portal-branding-ui (new private UI package)

Consumes (existing — do not rebuild)

  • @fuzefront/design-system (base tokens + primitives)
  • host shell ThemeContext (extended, not replaced)
  • @fuzefront/portal-client (from contract-designer)

Contract binding

endpointGET /api/v1/portal/context → portal id, slug, branding, identity policy, auth entry (FF-EPIC-10-S2, public/pre-auth)
client@fuzefront/portal-client (authored by contract-designer — dependency, not built here)
brandingbranding.name, branding.logo, branding.favicon, branding.accent, branding.tagline (portals.branding jsonb, FF-EPIC-09)
fail-closedsuspended → 403, unknown host → root fallback (UI) / 404 (API), cross-portal token → re-auth (FF-EPIC-10-S1/S3)
feature flagfuzefront.platform.multi-tenant-portals (reused master switch, default OFF — FF-EPIC-09-S4)

No raw hex/spacing/type in any frame — all values are DS tokens (tokens.css is the definition layer). The white-label seam is [data-portal] re-pointing --accent-*; light/dark is [data-theme] re-pointing surfaces.

Start the walkthrough →
Review · white-label-portal approve or reject each flow ↓
portal-shellapproved · izzywdev
portal-loginapproved · izzywdev