feat: Branding module — customize app appearance#273
Merged
Conversation
…den asset upload/serve (no SVG, nosniff)
…mary palette (hover/light/subtle/ring) from custom color
…itions They are edited via /branding/manage, not the generic Settings admin page; registering them polluted that page (two extra 'Primary color' fields) and broke Settings color smoke tests. Values are still stored/read by key via ISettingsContracts (no definition required); defaults live in BrandingDefaults/BrandingService. Adds a branding e2e spec.
…layers The CI docker job failed restoring SimpleModule.Host.csproj because the Branding module's project files were never added to the Dockerfile's per-project COPY list (used for restore-layer caching). Add the Contracts and implementation .csproj to both Dockerfile and Dockerfile.worker (kept aligned per the worker file's convention) and the frontend workspace package.json to the Host npm-restore stage.
…ward
actions/setup-dotnet@v5 rejects a non-full SDK version ("10.0.0") when
rollForward is specified, breaking the GitHub-managed Automatic Dependency
Submission (NuGet) workflow (which runs setup-dotnet with no explicit
dotnet-version and so reads global.json). Use the full feature-band
version "10.0.100"; rollForward: latestMajor still selects the latest
installed 10.x SDK.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Adds a Branding module that lets an admin customize a SimpleModule app's appearance from one page (
/branding/manage, gated by a newBranding.Managepermission):--color-primary-hover/-light/-subtle/-ring) is derived from the chosen color viacolor-mix, so hovers and focus rings follow the brand.The admin page includes a live preview.
Screenshots
Branding applied across the app — custom top bar (message + link + dismiss), brand mark and app name, primary color on navigation/headings/buttons, and the custom footer:
Admin configuration page — Identity, Colors, Top bar, Footer, and Custom CSS, with a live preview panel:
How it works
ISettingsContracts) by key — no new DbContext. They are intentionally not registered asSettingDefinitions, so they don't appear in the generic Settings admin page (they're edited via the dedicated Branding page); defaults live inBrandingDefaults/BrandingService.brandingInertia shared prop → React chrome (app name, logo, top bar, footer) renders it on every page.IInertiaHeadContributor+ a<!--HEAD_CONTRIBUTIONS-->placeholder inindex.html, resolved per-request by the renderer (fail-open, skipped when the placeholder is absent) — injects the color overrides, custom CSS, and favicon into<head>server-side so colors apply with no flash. Reusable by any future module.GET /api/branding/assets/{kind}endpoint (FileStorage's own download is permissioned, so it can't serve a logo on the login page). Uploads and serving are restricted to a raster/icon allowlist (no SVG — it's served anonymously and could carry script), withX-Content-Type-Options: nosniff. Top-bar/footer link URLs are sanitized againstjavascript:/data:/vbscript:.Verification
/branding/manage(admin): edit name/logo/colors/top-bar/footer → save → applies app-wide (sidebar, top bar, footer, primary color + derived palette, document title) with no flash; persists across restart; anonymous asset endpoint returns 404 when unset.tests/e2e/tests/flows/branding-crud.spec.ts(API round-trip + head-injection + admin-page render + asset 404).biome check· validate-pages/i18n/framework-scope · typecheck (15/15) · fulldotnet test·dotnet build -warnaserror·npm run build· e2e smoke (66/66).Test plan
/branding/manageas an admin, changes the app name + primary color + enables the top bar/footer, saves, and confirms the chrome updates across the app.