Dashboard Branding white-labels the admin chrome — the dashboard sidebar and the login / auth pages — so an install can wear the platform's brand, the client's site brand, or a web designer's agency brand. It controls both the logo and the chrome colors (sidebar accents, tabs, pills, links, brand buttons), gated behind a paid-member feature toggle.
The three modes
A single radio control on Dashboard → Settings → White Label → Dashboard & Login Branding picks one of three modes (stored as branding.admin_logo_mode). Each mode drives the logo and the chrome colors together — they never diverge.
Mode (branding.admin_logo_mode) |
Logo | Chrome colors |
|---|---|---|
webprocms |
The shipped platform logo | Fixed WebProCMS purple (#495092) + teal accent (#0d9488). Identical in light and dark. |
site |
The client's site logo (the one set on the Branding page) | Auto-derived from the site's primary and secondary brand colors. No extra configuration. |
designer |
The agency's own logo (light + dark uploads) | The agency's own four swatches: primary light/dark and accent light/dark. |
The logo and colors are deliberately one decision: choosing "this site's brand" means the dashboard adopts the client's logo and their colors; choosing the designer brand adopts the agency's logo and their swatches. There is no way to mix (e.g. client logo + agency colors) — that's by design, so the admin always reads as one coherent brand.
Who can do what
- The mode radio is available to any member (it appears once
Features::enabled('dashboard_branding')is true). designermode can only be selected by a non-super once a designer logo already exists; a Super can select it first to reveal the upload UI.- The designer logo uploads and the four color swatches are Super-only — they live inside the same box and only render in
designermode for a Super. This keeps the agency's own brand assets out of reach of the client's day-to-day admins.
Feature gating — paid members only
Dashboard Branding is a registered feature (dashboard_branding in Features::all(), default: true, available: true). It is default-on for paying members and hidden for everyone else:
- The entire box and its pill-nav entry render only when
Features::enabled('dashboard_branding')is true. AdminLogo::mode()resolves towebprocmswhenever the feature is disabled, so a non-member install always shows the platform logo and colors regardless of any stored mode.- Membership enforcement is layered on top via the Memberships feature gate (Memberships → Settings → Require membership for features, registered by
MembershipsServiceProvider), which vetoes every Features entry for non-members. On an install that opts into that gate, toggling membership off instantly reverts the chrome to WebProCMS.
The color model
Chrome colors are emitted by BrandingStyleService::buildDashboard() as CSS variables in the dashboard's inline <style> block. Two token families carry the chrome brand:
--color-cms-primary(+ 19 shades) — the dashboard's brand color: links, active tabs, pills, the sidebar active state, brand buttons, and Flux's--color-accentalias.--color-cms-accent(+ 19 shades) — the accent role: stats numbers, pricing checkmarks, feature icons, charts. Never buttons or links.
Each family is resolved per mode by dashboardColorScheme(), which returns explicit light + dark bases. Light bases are written into :root; dark bases into a trailing .dark { } block (the dashboard toggles .dark on <html>).
Why solid fills keep their base in both themes
generateShadeOklch() normalizes lightness per shade — --color-cms-primary-300 is always a light shade regardless of the base. So:
- Solid fills (
bg-cms-primarybuttons, active pills) keep the base color in both light and dark mode — a colored button looks the same in both themes, which is correct. Indesignermode the dark swatch can shift hue/chroma, but it is never inverted to a light color. - Inline links and active-tab text in dark mode use the lightness-normalized shade (
dark:text-primary-300/dark:text-cms-primary-300) so they stay legible on dark backgrounds.
Auto-contrast button text
Brand buttons reference --color-cms-on-primary for their label color through the text-on-cms-primary utility. onPrimaryFor() computes it from the primary base's OKLCH lightness: a base at lightness ≥ 0.62 (a light/yellow primary) gets dark text; anything darker gets light text. This is computed separately for the light and dark primary bases, so an agency only ever picks a button background — the label contrast is automatic and stays readable on any picked color.
Designer mode color swatches
In designer mode a Super sees four color pickers, each an optional 6-digit hex stored as a Setting:
| Swatch | Setting | Fallback when blank |
|---|---|---|
| Primary (light mode) | agency.cms_primary_light |
WebProCMS purple |
| Primary (dark mode) | agency.cms_primary_dark |
the light primary, then WebProCMS purple |
| Accent (light mode) | agency.cms_accent_light |
WebProCMS teal |
| Accent (dark mode) | agency.cms_accent_dark |
the light accent, then WebProCMS teal |
Leaving a dark swatch blank reuses its light sibling — the common case where an agency only has one brand color and wants it in both themes. Saving the swatches (or switching modes) busts the BrandingStyleService cache so the new chrome appears on the next dashboard load.
Transactional email chrome
Outgoing feature emails (order notices, booking confirmations, membership dunning, activity alerts, review requests, ticket replies, affiliate invites, social-posting notices) all render a shared header. By default it's the site name as text; the branding box's Transactional Email Logo picker swaps in a logo image (capped at 40px tall) across every one of them at once. The shared renderer is EmailChrome::headerHtml() — feature-gated, so turning Dashboard Branding off reverts every email header to the site-name text. The from name was already white-label: it resolves Marketing's from-name setting, then the business name — never "WebProCMS".
(The shop's order emails keep their own richer logo support via branding.logo_url, and Client Billing keeps its business-name header — invoices identify the billing entity.)
Help & support links
The branding box's Help & Support Links repeater (label + URL rows) renders custom links at the bottom of the dashboard sidebar for every user — point clients at your agency's support desk, docs, or contact page instead of leaving them to hunt. Feature-gated like everything else in the box; blank rows are dropped on save.
What lives where
| Path | Purpose |
|---|---|
app/Support/AdminLogo.php |
Resolves the effective mode and the light / dark / login logo URLs. Gates on Features::enabled('dashboard_branding'). |
app/Services/BrandingStyleService.php |
dashboardColorScheme() (per-mode light/dark bases), onPrimaryFor() (auto-contrast), buildDashboard() (emits :root + .dark chrome tokens). |
resources/views/pages/dashboard/settings/⚡general.blade.php |
The "Dashboard & Login Branding" box — mode radio, designer logo uploads, four color swatches, email-logo picker, help-links repeater, and the save handlers. |
app/Support/EmailChrome.php |
Shared transactional-email header (logo or site name) used by every feature module's email wrapper. |
resources/css/app.css |
@theme declarations for --color-cms-accent (+ shades) and --color-cms-on-primary, plus the text-on-cms-primary utility. |
resources/views/components/app-logo.blade.php and the three auth layouts |
Render the resolved logo on the sidebar and login pages. |
tests/Feature/AdminLogoTest.php, tests/Feature/BrandingStyleServiceTest.php |
Mode resolution, feature gating, per-mode light/dark emission, auto-contrast, swatch persistence. |
Marketing angle
Run a fleet of client sites — or sell WebProCMS itself — and the admin shouldn't shout someone else's brand. Dashboard Branding lets a paid member white-label the entire dashboard and login experience in one decision: show the client's own logo and colors, or your agency's. Pick a mode and the sidebar, tabs, links, and buttons all retune to match — light and dark mode handled, button-text contrast set automatically, no CSS to write. Hand a client a dashboard that looks like their product, or stamp every build you ship with your agency's mark.
Notes
- The feature is one toggle on the Features page; flipping it off reverts the chrome to WebProCMS instantly (logo and colors) without deleting any stored mode or swatches.
sitemode follows the live site brand — change the primary/secondary on the Colors page and the dashboard chrome follows on the next load. No duplicate color entry.--color-cms-accentis provided as a token for stats/charts; it is intentionally wired to neither buttons nor links. Neutral (zinc/Flux) buttons are never brand-colored — only elements already styled withcms-primaryretune per mode.- The legacy single "Dashboard Primary Color" override was removed when colors became mode-driven;
sitemode subsumes it (the dashboard follows the site primary) anddesignermode supersedes it with explicit light/dark swatches.