Theme settings
Many themes ship with a settings page at /admin/#/theme-settings — a per-theme configuration UI where admins customise the theme without leaving the CMS.
What's customisable depends entirely on the theme. The CMS provides the framework; each theme decides what fields to expose.
Where to find it
When the active theme has a settings page, an entry Theme settings appears in the sidebar (above Plugins). Switching to a theme without a settings page hides the entry automatically.
The admin route is /admin/#/theme-settings. The wrapping ThemeSettingsRoute resolves the active theme's manifest, merges the manifest's defaultConfig with the value stored in Firestore, and hands the result to the theme's settings component.
What each built-in theme exposes
Default theme
Two tabs: General + Style.
General tab:
- Logo upload — image picker. The image is resized to 480×144 WebP and uploaded to
/theme-assets/default-logo.webp. The theme's runtime menu loader swaps the text wordmark for an<img>on the next page load. - Logo enabled — toggle to show / hide the logo
Style tab — 22 design tokens grouped by category:
- Surfaces: bg, surface, surface-low/mid/high/highest
- Foreground: on-surface, on-surface-variant, on-surface-muted
- Outlines: outline, outline-variant
- Accent: primary, on-primary, secondary, secondary-container, on-secondary-container
- Spacing: container-max, gutter, section-gap
- Radius: radius-sm/md/lg/xl
- Typography: serif font + sans-serif font, picked from a curated Google Fonts list (Newsreader, Lora, Playfair Display, Cormorant Garamond, Inter, Plus Jakarta Sans, Outfit, Manrope, DM Sans, Work Sans)
Saving runs the compileCss(config) hook — bakes the user overrides into a regenerated CSS string and uploads it to /theme-assets/default.css. No HTML re-publish needed. The next page load picks up the new CSS.
Magazine theme
Tabs: General + Style + Home.
General: logo upload (same pattern as default).
Style: similar to default but with Material 3 colour tokens stored as RGB triplets (so Tailwind's bg-primary/50 alpha-modifier works).
Home: layout toggles for the home page sidebar widgets (which magazine/most-read + magazine/promo-card blocks to surface, with their default attrs).
Corporate theme
Tabs: General + Single + Logo + Style.
General — header + nav settings (inline horizontal nav vs burger only, navbar transparency over hero).
Single — single-post sidebar:
- Show / hide author bio card (default off)
- Show / hide popular articles card (default on) + customisable title
- Show / hide CTA card (default on) + customisable title / button label / button link
Logo — logo upload, plus header CTA button label + URL (hardcoded /contact.html by default).
Style — single-font system (one sans family, defaults to Inter) + colour tokens.
How theme settings persist
The framework stores configs at settings/site.themeConfigs[<theme-id>] in Firestore:
// settings/site
{
activeThemeId: "magazine",
themeConfigs: {
"default": {
logoEnabled: true,
logoUpdatedAt: 1715000000000,
style: { vars: {...}, fontSerif: "Newsreader", fontSans: "Inter" }
},
"magazine": {
logoEnabled: true,
logoUpdatedAt: 1714000000000,
style: { vars: {...}, fontSerif: "Lora", fontSans: "Manrope" },
home: { sidebarTop: "mostRead", sidebarBottom: "promoCard" }
},
"corporate": {
single: { showAuthorBio: false, showPopularArticles: true, ... }
}
}
}
Each theme's config is preserved when you switch away. If you switch from default → magazine → default, the default theme's settings come back exactly as you left them. Switching themes doesn't reset settings.
How compileCss works
When a theme has a compileCss(config) => string hook on its manifest, the admin calls it every time the theme's CSS gets uploaded:
- On theme switch — uploads the active theme's CSS via
compileCss(savedConfig) - On settings save — same, with the new config
- On Sync theme assets click — same, ensuring the published CSS reflects the current settings
Without compileCss, the manifest's cssText (baked at build time, before any user customisation) would be re-uploaded — wiping the user's overrides. The hook is what makes runtime style customisation work.
The hook is synchronous and pure — (config) => string. The default theme's implementation:
- Take the bundled
cssText(the SCSS-compiled CSS at build time) - Find the
@import url(...)line for the Google Fonts pair, replace it with the URL for the user's chosen pair - Append a
:root { --color-bg: <user value>; ... }block at the end with every overridden var - Return the result
This regenerates the CSS without touching the build pipeline. Browsers may serve a cached version of theme-assets/<id>.css until refresh; the success toast tells the user to hard-refresh if they don't see changes.
Settings page UX
Theme settings pages all follow the same conventions:
- Tabs at the top for sections (theme decides)
- Form fields below with live preview where possible
- Save & apply button at the bottom — uploads CSS and saves config
- Sync theme assets button — re-upload from current settings without resetting (useful after editing manually)
- Reset to defaults button — clears the config, re-uploads baseline CSS
The framework guarantees:
- Saving is debounced (no spamming Flexweg with uploads during fast clicks)
- The form rehydrates when an external save lands (e.g. from another browser tab)
- An "unsaved changes" indicator (warning if you navigate away)
When changes apply
| Change | What gets re-uploaded | Visible to visitors |
|---|---|---|
| Logo upload | theme-assets/<id>-logo.webp + menu.json (with cache-bust) | Next page load |
| Style tokens / fonts | theme-assets/<id>.css | Next page load (after browser cache expiry — hard-refresh if needed) |
| Home layout (magazine) | Re-publish home only (Themes → Regenerate site → Home page only) | Visible after re-publish |
| Single sidebar toggles (corporate) | Re-publish all posts (Themes → Regenerate site → All HTML) | Visible after regen |
Most theme settings are CSS-only (instant via cache-bust). Some are template-affecting (require an HTML regen). Each theme's settings page hints at which is which.
Continue
- Switching themes — full theme replacement
- Sync theme assets — push latest CSS without re-rendering HTML
- Built-in: default / magazine / corporate — per-theme reference