Core.Portal.Chrome 0.3.77

Core.Portal.Chrome

Core.Portal.Chrome contains portal-safe MVC/Razor chrome primitives for product portals.

Owns

  • Auth shell and form-alert components.
  • Public auth split-shell template for MVC/Razor and Blazor portal hosts.
  • Public and app header partials.
  • Appearance controls and static chrome assets.
  • Legal footer/link models and shared legal footer renderers.
  • Product-neutral app-shell metric card primitive.
  • Product-neutral app-shell page header primitive.
  • Product-neutral legal acceptance and public-auth query primitives.
  • Product-agnostic account-support view models and partials.
  • Stable authenticated-user UID resolution for cross-portal preferences.
  • Product-neutral appearance runtime, Blazor service, and identity-profile synchronization coordinator.

Does Not Own

  • Product workflows, navigation trees, route maps, dashboards, tables, page copy, or customer/product behavior.
  • Broker-backed sign-in orchestration. Use Core.Portal.SignIn for Core auth broker login.
  • Persistence, database, API-controller, worker, database explorer, or product-repo dependencies.
  • Product-specific API clients. Any support client in this package must call product-agnostic Core endpoints only and must be optional for consuming hosts.
  • Product-specific legal copy, terms/privacy text, pricing, trial length, billing, tenant provisioning, or legal-record persistence.

Consumer Rules

Product portals should consume this package through NuGet package flow. The product host owns branding, route placement, cookie settings, tenant configuration, typed product API clients, smoke tests, and architecture allowlists.

Before adopting or upgrading the authenticated shell, a product portal must:

  • use PortalAppShell, PortalAgentShell, PortalPageHeader, PortalMetricCard, and PortalBrandLockup directly rather than wrapping or copying them;
  • load product-owned page and vendor styles before portal-app-shell.css, so the shared chrome remains authoritative;
  • treat .portal-shell*, .portal-topbar*, .portal-app-rail*, .portal-bottom-app-*, .portal-context-*, .portal-sidebar-*, .portal-activity-*, .portal-user-*, and .portal-settings-* as Core-owned selectors;
  • customize chrome through the shell models, render fragments, callbacks, and documented theme tokens rather than product CSS selectors;
  • keep page/workflow styles product-owned and verify with an architecture test that reserved chrome selectors are not reintroduced; and
  • remove retired product shell components, scripts, and styles after the shared shell is wired and locally validated.

This is the propagation checklist for additional portals. A consumer is not shell-ready while it still carries a product copy or wrapper of a Core-owned component, or while late-loaded product CSS can override reserved chrome selectors.

Shared appearance contract

Authenticated Blazor portals load _content/Core.Portal.Chrome/js/portal-preferences.js, set window.CorePortalAppearanceOptions before that script to provide product-owned storage keys, and register AddPortalAppearance(...). Components consume PortalAppearanceService; the product registers its typed API gateway as IPortalUserPreferencesStore, while the shared PortalPreferenceCoordinator owns the one-time identity-profile hydration and persistence flow. Products must not copy the JavaScript runtime, theme service, or coordinator. Storage keys remain product-owned so existing browser preferences migrate without a breaking reset.

Public Auth Shell

Use PortalPublicAuthShell and PortalPublicAuthPreferences from Blazor hosts, and the _PortalChromePublicAuthWordmark, _PortalChromePublicAuthPreferences, and _PortalChromePublicAuthBrandField partials from MVC or Razor Pages hosts. Link the compiled Core presentation theme, _content/Core.Portal.Chrome/css/portal-auth-shell.css, and _content/Core.Portal.Chrome/js/portal-auth-shell.js, then set product-owned CSS variables for brand gradients or imagery as needed. Core owns the responsive shell, masthead preference controls, Barlow typeface stack, dark-mode token binding through html[data-theme], signed-out dark semantic fallbacks, form spacing, field sizing, field validation behavior, preference handoff hooks, and alert primitives; product hosts own the sign-in form, forgot-password behavior, provider posture, proof credentials, page copy, routes, cookie names, and storage-key overrides.

Public auth forms opt into the shared behavior with data-auth-form. Mark required visible fields with normal HTML validation attributes such as required, type="email", and minlength, and provide localized messages through data-auth-required-message, data-auth-invalid-message, data-auth-min-length-message, and data-auth-match-message. Password confirmation fields can declare data-auth-match-input="<field name>". Submit buttons can declare data-auth-busy-text; buttons marked data-auth-disabled-permanent remain disabled.

When changing language, portals can preserve non-sensitive state by placing data-auth-preserve-param="<query parameter>" on fields such as email, step, or return URL. Do not mark passwords for preservation.

Cookie-backed Razor Pages hosts should provide PublicLanguageCookieName; the shared script writes the cookie and removes the transient language query parameter on reload. Query-backed Blazor hosts can omit PublicLanguageCookieName; the shared script writes the selected language into LanguageQueryParameterName while preserving marked auth state. The language selector is part of the standard auth shell and defaults to en-US; portals that expose profile or settings language preferences can read CorePortalAuthShell.getPreferences() or consume CorePortalAuthShell.readAuthPreferenceHandoff() after sign-in, then call CorePortalAuthShell.clearAuthPreferenceHandoff() once the product-owned preference store is updated.

Authenticated App Shell

Core.Portal.Chrome adapts the versioned, framework-neutral Core.Shell catalog through PortalShellCatalogAdapter. Products provide web target resolution and visibility policy; the adapter emits the existing PortalShell* models so current consumers can migrate without a flag-day rewrite. URLs, assets, cookies, storage keys, catalogs, and authorization remain product/browser concerns. Projection diagnostics retain schemaVersion, product-owned catalogVersion, catalog ID, stable IDs, denied visibility, and unresolved targets.

Use PortalAppShell for Blazor portal chrome after sign-in. Link _content/Core.Portal.Chrome/css/portal-app-shell.css, then pass product-owned PortalShellBrandModel, optional selected PortalShellContextModel, navigation groups, search entries, account links, notification rows, to-do rows, optional chat rows, and a theme-preference callback. Core owns the full-width header with a stable brand lockup and account control, the top theme rail, app rail, selected-app context sidebar, optional selected-context affordance below the app title, modal context selector, optional no-sidebar shell mode for selection/landing surfaces, collapsible navigation groups, optional sidebar link badges, topbar inline search, activity buttons with unread badges, right-side activity slideouts with mode titles for Notifications, To Do, and optional Chat, compact account menu, Profile/Appearance settings dialog, language selection, System/Light/Dark theme controls, display density controls, and app-shell spacing. Product portals own route catalogs, context choices and selection state, optional context-management routes, supported language choices and cookie/storage keys, search data, activity data sources, account/tenant identity, authorization, and durable preference persistence. Clicking the context affordance always opens the shared modal; it does not navigate. A product may provide a separate management link in the dialog for deeper administration. The account menu's Settings action opens the shared settings dialog; appearance preferences are not duplicated in the dropdown. Sidebar collapse affects only contextual navigation; it never hides or reflows the header brand lockup. Display density (compact, comfortable, spacious) is a shell-wide preference that keeps sidebar, topbar, account menu, chrome icons, and inherited content spacing on one scale. This keeps consuming portals on one chrome implementation without moving product data or workflow authority into Core.

Use PortalAgentShell for the portal-level AI assistant launcher and panel frame. Core owns its fixed lower-right placement, safe-area and mobile-bottom-bar offsets, launcher interaction, panel geometry, and empty integration state. Products own assistant prompts, messages, citations, API calls, authorization, and durable conversation state through the component's child content.

Navigation sections may expose direct links and product-owned nested PortalShellNavTreeModel collections. Core owns the tree disclosure, indentation, active-link styling, and collapsed-sidebar behavior; products own the labels, icons, routes, hierarchy, and authorization filtering.

The app rail (and its mobile bottom-bar counterpart) is uniform shared chrome: every portal renders the identical rail — width, item size, spacing, iconography scale, and selection indicator — and product stylesheets must not restyle .portal-app-rail* or .portal-bottom-app-* classes. Products influence the rail only through data (the app models: labels, icons, badges) and theme tokens (brand colors).

The authenticated shell observes viewport changes rather than sampling width only at first render. At widths up to 991px an expanded context sidebar behaves as a dismissible overlay and defaults to collapsed. Below 600px app switching moves to a fixed bottom app bar (the Outlook/Teams mobile pattern): each app renders as an icon-plus-label item with its badge, at most five slots are shown, and when there are more apps the last slot becomes a "More" trigger that opens an overflow sheet above the bar. The top-bar navigation trigger opens the contextual sidebar as a left-edge drawer with a backdrop, outside-click dismissal, and Escape dismissal; the vertical app rail stays hidden at these widths. Content, drawers, and activity panels reserve space above the bottom bar (including safe-area insets). Below 640px inline search becomes an icon-triggered full-width search surface. Activity panels use a proportional width at intermediate viewports and become full width on narrow screens. Coarse-pointer controls retain at least a 40px touch target. Product pages remain responsible for making their own page content responsive inside the shell.

Portal shell terminology:

  • Theme rail: the narrow fixed accent strip across the top of the authenticated shell.
  • App rail: the icon-only rail used to switch app spaces such as Business ERP, Design, Development, DevOps, Areas, Planner, or Management.
  • Context sidebar: the collapsible left menu next to the app rail. It shows the selected app title, optional context selector, and navigation groups for that app space.
  • Context-selector app action: an app-rail item may open the shared context selector directly instead of navigating to a dedicated selection page; context management remains a separate dialog-footer destination.
  • Top bar: the full-width horizontal header. It contains the stable brand lockup, search, activity entry points, and account control.
  • Activity slideout: the right-side panel opened by Notifications, To Do, or Chat.
  • Account menu: the compact dropdown opened from the top-bar account control; it contains Settings, authorized tenant links, and Sign out.
  • Settings dialog: the modal settings surface opened from the account menu. Its left navigation starts with Profile and Appearance; appearance owns language, System/Light/Dark theme preference, and display density. System follows prefers-color-scheme and reacts to operating-system preference changes.
  • Page header: the content heading block at the top of a page. It contains an optional uppercase eyebrow, the page title, optional descriptive text, and optional actions.
  • Dashboard metric card: a summary number/status card used above grids or dashboard sections.

Use PortalPageHeader for the standard page heading block. The Eyebrow should match the current context sidebar section, the Title should match the selected menu item, and the Description should be short, close to the title, and product-owned. Core owns spacing, type scale, token colors, and density behavior.

PortalAppShell also owns the standard browser-tab title. When AutoPageTitle is true (the default) the shell renders a PageTitle of the form menu item - sidebar section - app - brand derived from the active navigation link, so tab titles automatically follow menu selection on every page. Pages backed by a navigation link should not render their own PageTitle; pages outside the navigation model (detail pages with route parameters, error pages, profile) may render their own PageTitle, which wins over the shell's.

Use PortalMetricCard for dashboard/stat cards that show a label, numeric or short value, optional caption, optional material icon, optional tooltip text, and optional navigation action. Place cards inside .portal-metric-grid for the standard responsive layout. Core owns the neutral metric-card spacing, border, typography, dark-mode behavior, density inheritance, and theme-accent binding; product portals own the metric source, labels, values, route targets, and explanatory tooltip copy.

Hydrating from an identity-backed profile

Appearance and accessibility preferences (theme, text scale, density, display timezone, language) are per-user, not per-tenant (Portal Application Standard preference-scoping ruling, 2026-07-04). The durable store is the platform user profile (GET/PUT /api/core/users/{uid}/preferences, UserPreferencesApiClient); local storage is only a pre-auth/anti-flash cache. After sign-in, a product shell fetches the profile with the typed client and calls CorePortalAuthShell.applyProfilePreferences({ theme, textScale, density, timeZone, uiLocale }) to write those values into the local cache and html[data-theme]/data-text-scale/data-density/data-time-zone/data-ui-locale, making the profile the source of truth. getPreferences() returns the current cached values plus the resolved storage keys (themeStorageKey, textScaleStorageKey, densityStorageKey, timeZoneStorageKey, localeStorageKey) so a shell can read a still-unmigrated local value once and push it up to the profile. Storage keys default to core.portal.* and are overridable through the [data-portal-auth-preferences] data-* attributes so each product can point the seam at its own cache keys.

The public-auth validation standard is:

  • client-correctable input (required fields, email format, password length, password confirmation mismatch) uses field-level validation and keeps the primary submit button disabled until valid;
  • each visible field gets a reserved .portal-validation-message slot, either supplied by the product page or created by portal-auth-shell.js, so labels/messages turn danger-colored, invalid inputs receive aria-invalid="true", and layout does not shift when validation appears;
  • auth, provider, reset-token, and upstream/server failures use visible alert banners above the form;
  • product controllers must return equivalent field-level errors for crafted or no-script POSTs rather than converting missing/malformed values into authentication failures.

Use PortalLegalFooterModel with the PortalLegalFooter Blazor component or the _PortalChromeLegalFooter Razor partial to render Terms, Privacy, copyright, and version links in a consistent public-auth footer. Product repos own the legal page routes, localized text, versions, and footer link labels; Core owns only the rendering shape and separator behavior.

Use PortalLegalAcceptanceModel as the product-neutral shape for signup consent evidence when a product needs to map its own trial/customer/signup record into a shared UI or API-adjacent contract. Product repos remain responsible for requiring consent at the correct workflow point, storing the record, and displaying it in customer/subscription admin surfaces.

Use PortalPublicAuthQuery.BuildPasswordStepPath and PortalPublicAuthQuery.ResolveEmailAddress for auth handoffs. New visible URLs should use emailAddress; username is retained only as a compatibility fallback for existing form posts or old links. Public sign-in pages should ask for one identifier first, accepting email address, phone number, or PIN when those methods are enabled, then route to the password, SSO, linked-provider, phone, or PIN step resolved for that identifier. Social/external providers render below a single or separator as full-width neutral provider buttons using the shared .login-provider-list, .login-provider-button, and .login-provider-mark-* classes. Provider marks must follow the vendor's current brand guidance; replace inline/CSS marks with approved image assets when a provider requires exact artwork.

Secondary public-auth pages should expose a fast back affordance near the top of the form using .portal-auth-back-link and an SVG chevron-left icon. The product chooses the target route, but Core owns the touch target, border, hover/focus, and theme behavior.

No packages depend on Core.Portal.Chrome.

.NET 10.0

Version Downloads Last updated
0.3.78 5 07/20/2026
0.3.77 4 07/20/2026
0.3.76 1 07/19/2026
0.3.75 2 07/19/2026
0.3.74 1 07/19/2026
0.3.73 2 07/18/2026
0.3.72 2 07/17/2026
0.3.71 2 07/17/2026
0.3.70 2 07/17/2026
0.3.69 2 07/17/2026
0.3.68 2 07/17/2026
0.3.67 2 07/17/2026
0.3.66 2 07/16/2026
0.3.65 2 07/16/2026
0.3.64 2 07/15/2026
0.3.63 2 07/15/2026
0.3.62 2 07/15/2026
0.3.61 2 07/15/2026
0.3.60 3 07/14/2026
0.3.59 4 07/14/2026
0.3.58 2 07/14/2026
0.3.57 2 07/14/2026
0.3.56 2 07/14/2026
0.3.55 2 07/14/2026
0.3.54 2 07/14/2026
0.3.53 2 07/14/2026
0.3.52 2 07/14/2026
0.3.51 2 07/14/2026
0.3.50 2 07/13/2026
0.3.49 2 07/13/2026
0.3.48 2 07/13/2026
0.3.47 2 07/13/2026
0.3.46 2 07/13/2026
0.3.45 2 07/13/2026
0.3.44 2 07/13/2026
0.3.43 2 07/13/2026
0.3.42 2 07/13/2026
0.3.41 2 07/13/2026
0.3.40 2 07/12/2026
0.3.39 2 07/12/2026
0.3.38 2 07/12/2026
0.3.37 2 07/12/2026
0.3.36 2 07/12/2026
0.3.35 2 07/12/2026
0.3.34 2 07/12/2026
0.3.33 2 07/12/2026
0.3.32 2 07/12/2026
0.3.31 2 07/11/2026
0.3.30 2 07/11/2026
0.3.29 2 07/11/2026
0.3.28 2 07/11/2026
0.3.27 2 07/05/2026
0.3.26 3 07/05/2026
0.3.24 1 07/03/2026
0.3.23 5 06/23/2026
0.3.22 2 06/22/2026
0.3.21 1 06/21/2026
0.3.20 2 06/19/2026
0.3.19 2 06/19/2026
0.3.18 2 06/19/2026
0.3.17 2 06/19/2026
0.3.16 1 06/19/2026
0.3.15 2 06/19/2026
0.3.14 2 06/19/2026
0.3.13 1 06/19/2026
0.3.12 1 06/19/2026
0.3.11 2 06/19/2026
0.3.10 2 06/19/2026
0.3.9 1 06/19/2026
0.3.8 2 06/19/2026
0.3.7 2 06/19/2026
0.3.6 2 06/18/2026
0.3.5 2 06/18/2026
0.3.4 3 06/10/2026
0.3.0 3 06/02/2026
0.2.0 3 06/02/2026
0.1.3-local.1 2 06/10/2026
0.1.2 4 05/31/2026
0.1.2-local.1 2 06/10/2026
0.1.1 11 05/28/2026
0.1.0 52 05/23/2026
0.1.0-alpha.7 2 05/21/2026
0.1.0-alpha.6 2 05/21/2026
0.1.0-alpha.5 3 05/21/2026
0.1.0-alpha.4 3 05/21/2026
0.1.0-alpha.3 2 05/21/2026
0.1.0-alpha.2 2 05/20/2026