feat(branding): end-to-end branding — header, footer, colour ramp, title & emails - #222
Merged
Merged
Conversation
Add a footer to the authenticated and admin app shells (previously none) and make the public footer + auth header branding-driven instead of hardcoded. - lib/brand.ts: single source for framework brand metadata + footer links - BrandingMark: optional stacked caption (backward compatible) - BrandingFooter: reusable footer (brand lockup + © year · MIT + links), app/public variants - SidebarLayout: flex-column main with sticky-bottom BrandingFooter - PublicLayout: use shared BrandingFooter; nav links via BRAND_REPO_URL - AuthCardShell: brand lockup now driven by the branding shared prop Claude-Session: https://claude.ai/code/session_01GYc5NpnAkEAohj9apMfFok
- BrandingFooter: wrap BrandingMark in a flex container so the badge stays attached to its wordmark (BrandingMark is a fragment; the footer's justify-between row otherwise split the lockup into three items) - BrandingMark: add size='lg' + badgeClassName so AuthCardShell keeps its prominent 36px glowing badge (was silently shrunk to 32px/flat) - lib/brand.ts: centralize BRAND_ACCENT gradient + BRAND_DEFAULT_APP_NAME; use across BrandingFooter, AuthCardShell, PublicLayout (kills 3x dup + unifies the unbranded fallback name) Claude-Session: https://claude.ai/code/session_01GYc5NpnAkEAohj9apMfFok
Deploying simple-module-python with |
| Latest commit: | ead121d |
| Status: | ✅ Deploy successful! |
| Preview URL: | https://bd6de6f0.simple-module-python.pages.dev |
| Branch Preview URL: | https://worktree-branding-header-foo.simple-module-python.pages.dev |
Extend branding beyond the header/footer lockup so a configured deployment is branded end to end: - Derive the full --color-primary-50..900 ramp from the single brand hex so the logo-badge gradient and auth mesh blobs follow the tenant colour, not just bg-primary buttons (packages/ui/src/lib/color.ts + BrandingHead). - Brand the document <title> on client navigations (lib/app-title + app.tsx) and server-render title/favicon/theme-color into the root template before hydration via a duck-typed branding_head() Jinja global (no SM009 coupling). - Carry the app name in transactional email subjects/bodies, threaded through a decoupled app_name provider (users stays independent of branding). Tests: colour/app-title utils, branding_head, rendered shell (title + theme-color hot-swap), and email branding. make lint + make test green. Claude-Session: https://claude.ai/code/session_01GYc5NpnAkEAohj9apMfFok
- Reject control chars in app_name at the source (BrandingSettings + update DTO via a shared clean_app_name helper) — a CR/LF previously passed a bare strip() and would then break every transactional email's Subject header. - Drop the server-rendered favicon <link> and its mirrored file_storage route from framework code (SM009-adjacent coupling); the favicon is already applied client-side by BrandingHead. branding_head() now surfaces only plain settings strings (title + theme-color). - BrandingHead: move setTitleAppName into an effect (no side-effect during render) and keep the server-rendered theme-color meta in sync on a runtime colour change, restoring it on unmount. - color.ts: derive BASE_REFERENCE_CHROMA from the 600 ramp step instead of a duplicated magic literal. Tests: control-char rejection (settings + API 422), theme-color sync, updated branding_head test. Affected suites green. Claude-Session: https://claude.ai/code/session_01GYc5NpnAkEAohj9apMfFok
Uh oh!
There was an error while loading. Please reload this page.
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 freeto 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
SidebarLayout) — previously only the public landing page had a footer.<main>is now a flex column so the footer sticks to the bottom.AuthCardShell) brand lockup now read thebrandingshared prop (appName/logoUrl) instead of literalsimple_module/pythontext — correct for white-labelled deploys.packages/ui/src/lib/brand.ts(BRAND_ACCENTgradient,BRAND_DEFAULT_APP_NAME, repo URL, license, footer links), killing 4× duplication and unifying the unbranded fallback name.BrandingMarkgains an optional stackedcaption, asize="lg"variant, and abadgeClassNameshadow override — it's now the single brand-lockup primitive used by the header, footer, and auth shell.BrandingFootercomponent (app/publicvariants) with unit tests.Verification
justify-betweenflex bug that detached the footer badge from its wordmark); final pass clean.© <year> · MITcaption, Docs/Changelog/GitHub links resolve, no horizontal overflow, nav collapses on mobile. Evidence in.qa/screenshots/.make lint✓ ·make test✓ (1405 Python + 26 JS tests pass; e2e excluded as inpr.yml).Round 2 — end-to-end theming (commit
482784c)The lockup work above brands what's visible; this round makes a configured deployment branded everywhere a tenant value should reach:
packages/ui/src/lib/color.tsderives the full--color-primary-50…900ramp from the single brand hex (hex→OKLCH, re-hue + chroma-scale, preserving the designed lightness ladder);BrandingHeadwrites it inline on:root. The compiled CSS confirms.from-primary-600{--tw-gradient-from:var(--color-primary-600)}, so the logo-badge gradient and auth mesh blobs now follow the tenant colour (previously they stayed emerald while onlybg-primarybuttons changed). Near-grey picks → grey ramp; vivid picks stay vivid.<title>+ pre-hydration head.lib/app-title.ts+app.tsxseed the app name into Inertia's title callback (tabs readDashboard — Acme), and a duck-typedbranding_head()Jinja global server-renders the branded<title>, favicon<link>, and<meta name="theme-color">before React hydrates (no default-brand flash; correct for crawlers). Noframework→pluginimport (SM009 clean).app_nameprovider —usersnever importsbranding(readsapp.stateby name).Round 2 verification (
/ship)ead121d: (1) a CR/LF inapp_namecould break emailSubjectheaders → now rejected at the source via a sharedclean_app_name; (2) dropped a framework→file_storage route coupling (server favicon link) in favour of the existing client-side favicon; (3) moved a side-effect out of a React render body + madetheme-colorreactive; (4) derived a duplicated chroma constant; (5) accepted the framework default-name literal as required by the SM009/users↛brandingdecoupling. Pass 2 re-review: clean./qa, normal depth): 0 bugs. Verified live that setting a magenta#c026d3re-hues the full ramp — sidebar badge gradient→ oklch(… 322.9°), auth-screen mesh blobs,--primary, and<meta theme-color>all follow it; tab title → the app name; revert-to-default cleanly restores emerald. Evidence in.qa/reports/qa-report-branding-round2-iteration-1.md.color.test.ts), title util (app-title.test.ts),branding_head(test_branding_head.py), rendered shell title + theme-color hot-swap + control-char rejection (test_branding.py), email branding (test_mailer.py).make lint✓ (exit 0) ·make test✓ (1413 Python + 41 JS tests pass).Test plan
/and/dashboard/— confirm the footer renders the brand lockup + Docs/Changelog/GitHub links/auth/logincard shows the prominent glowing brand badgehttps://claude.ai/code/session_01GYc5NpnAkEAohj9apMfFok