Skip to main content

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.

Default theme reference

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).

Magazine theme reference

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.

Corporate theme reference

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:

  1. Take the bundled cssText (the SCSS-compiled CSS at build time)
  2. Find the @import url(...) line for the Google Fonts pair, replace it with the URL for the user's chosen pair
  3. Append a :root { --color-bg: <user value>; ... } block at the end with every overridden var
  4. 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

ChangeWhat gets re-uploadedVisible to visitors
Logo uploadtheme-assets/<id>-logo.webp + menu.json (with cache-bust)Next page load
Style tokens / fontstheme-assets/<id>.cssNext 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