Theme Skins
Theme skins let one Frontbacked theme ship multiple visual looks. A site owner can choose the skin they want, while the theme developer keeps the page structure, FQL state, FRL rules, and assets in one theme.
A skin is CSS. Use it for color systems, fonts, spacing, background images, and any presentation-only differences.
Basic Setup
Create a skins/ folder in the theme root:
my-theme/
index.html
signup.html
assets/
skins/
midnight.default.light.css
midnight.default.dark.css
solar_flare.light.css
solar_flare.dark.css
terra_bloom.css
Then reference the active skin from each HTML page:
<link rel="stylesheet" href="$skin.css" data-frontbacked-skin>
When Frontbacked serves the page, it replaces $skin.css with the CSS file for the selected skin. If the active site skin is solar_flare and the selected mode is dark, the rendered page receives:
<link rel="stylesheet" href="/skins/solar_flare.dark.css" data-frontbacked-skin>
data-frontbacked-skin is recommended because it gives frontbacked.js a stable link element to update when the visitor switches modes.
Filename Inference
skins/manifest.json is optional. Without it, Frontbacked reads the skin list from CSS filenames.
| Filename | Meaning |
|---|---|
midnight.default.light.css | midnight skin, default skin, light mode. |
midnight.default.dark.css | midnight skin, default skin, dark mode. |
solar_flare.light.css | solar_flare skin, light mode. |
solar_flare.dark.css | solar_flare skin, dark mode. |
terra_bloom.css | terra_bloom skin with one stylesheet for every mode. |
default.css | A one-file starter skin. |
If no file has .default., Frontbacked chooses default.css when it exists. If there is no default.css, it chooses the first skin alphabetically.
Skin display names are generated from filenames. For example, solar_flare becomes Solar Flare.
Optional Manifest
Add skins/manifest.json when you want custom names, ordering, descriptions, previews, or a default mode:
{
"default": "midnight",
"defaultMode": "system,dark",
"order": ["midnight", "solar_flare", "terra_bloom"],
"skins": {
"midnight": {
"name": "Midnight",
"description": "A quiet dark-first skin with a bright trading accent.",
"defaultMode": "dark",
"modes": ["light", "dark"],
"preview": "/skins/previews/midnight.png"
},
"solar_flare": {
"name": "Solar Flare",
"modes": ["light", "dark"]
},
"terra_bloom": {
"name": "Terra Bloom"
}
}
}
The manifest never has to repeat CSS file paths. The CSS filenames still define which files exist.
defaultMode can be a concrete mode such as light or dark, or a system preference:
{
"defaultMode": "system,dark"
}
system uses the visitor device preference in the browser. system,dark means use the device preference when available and use dark as the server-rendered fallback.
Use hidden: true for a skin entry when you want to keep a CSS file in the repo without offering it to site owners yet.
Admin Selection
Skin management lives in the site admin panel (/fb-admin). The available skins are read from the resolved theme version's source map, so each version can have a different skin list.
Frontbacked stores the selected skin by commit/version:
{
"theme_skins": {
"commit_123": {
"skin": "solar_flare"
}
}
}
Published sites and the local frontbacked-server both use the same resolver:
- URL skin override:
?skin=solar_flare - Version skin setting:
settings.theme_skins[commitId].skin skins/manifest.jsonskin default- Filename-inferred skin default
If a newer version has no skin selected yet, Frontbacked can fall back to the last selected skin name when that skin exists in the newer version. Mode is separate from site settings. The active mode comes from the visitor's URL/cookie preference first, then from defaultMode in skins/manifest.json, then from filename inference.
Runtime Switching
frontbacked.js exposes skin helpers:
Frontbacked.setSkinMode("dark");
Frontbacked.setSkinMode("system,dark");
Frontbacked.toggleSkinMode();
toggleSkinMode() switches between light and dark when both exist. For custom modes, it cycles through the modes listed for the skin.
The runtime updates the data-frontbacked-skin link and saves only the visitor's mode preference. Site owners choose the skin from fb-admin.
Frontbacked does not expose public setSkin or toggleSkin methods to themes. A theme should not let visitors choose a different skin; it should only let them switch the mode of the skin chosen by the site owner.
CSS Pattern
Keep shared layout CSS in your normal theme CSS. Put visual differences in skins:
:root {
--page-bg: #07111f;
--panel-bg: #101a2b;
--text: #f8fafc;
--accent: #38bdf8;
}
body {
background: var(--page-bg);
color: var(--text);
}
.hero {
background-image: url("/assets/images/skins/midnight-hero.jpg");
}
This keeps skins small and makes it easier to add more looks without duplicating pages or JavaScript.