Theming
- From the admin panel
- How it resolves
- The tokens
- Why the font list is curated
- Values are validated
- Going deeper
No component in this codebase contains a literal color, radius or font. Every value resolves to a CSS custom property, which is what makes a reskin a settings change rather than a find-and-replace.
From the admin panel
Admin → Appearance controls:
- Brand name, tagline, support address and logo
- Default mode (dark or light)
- Typography, from a curated pairing list
- Accent and surface color, per mode
Changes apply on save, across the marketing site, the app, the admin panel and every generated site.
How it resolves
Three layers, most specific wins:
- Defaults —
App\Services\Theme\DesignTokens - Admin settings — the
settingstable, written by the Appearance page - Per-project overrides —
settings.theme.tokenson a project
ThemeManager merges them and emits CSS custom properties, inlined into the
document head so the first paint is already on-brand.
The tokens
/* Surfaces — layered graphite, not flat black */
--surface-base --surface-sunken --surface-raised --surface-overlay --surface-inset
/* Lines */
--border-subtle --border-default --border-strong
/* Text */
--text-primary --text-secondary --text-tertiary --text-inverse
/* One accent, never a gradient */
--accent --accent-strong --accent-muted --accent-contrast
/* Reserved for credit and billing surfaces only */
--meter --meter-muted
/* Semantics, kept desaturated so they do not fight the accent */
--success --warning --danger --info
/* Structure */
--radius-sm|md|lg|xl|pill --space-1…8 --shadow-sm|md|lg
--font-sans --font-mono --tracking-tight
Why the font list is curated
Buyers get real choice without being able to break the aesthetic. An open-ended
font picker is how a coherent design system turns into a ransom note. Add a
pairing in DesignTokens::FONT_PAIRINGS if you need another.
Values are validated
Token overrides are filtered before they reach a stylesheet: only plain colors and lengths pass, and only keys on an allow-list. A settings row cannot inject a CSS rule.
Going deeper
For changes beyond the settings page, edit DesignTokens and rebuild:
npm run build
Generated sites reference the same tokens, so a theme change reskins existing published sites on their next publish.