Frontbacked Docs

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.

FilenameMeaning
midnight.default.light.cssmidnight skin, default skin, light mode.
midnight.default.dark.cssmidnight skin, default skin, dark mode.
solar_flare.light.csssolar_flare skin, light mode.
solar_flare.dark.csssolar_flare skin, dark mode.
terra_bloom.cssterra_bloom skin with one stylesheet for every mode.
default.cssA 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 start from dark when the browser preference is not available yet.

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 come from the active theme version's skins/ folder and optional skins/manifest.json, so each published version can offer its own skin list.

Frontbacked chooses the active skin in this order:

  1. URL skin override: ?skin=solar_flare
  2. The site owner's selected skin for the active theme version
  3. skins/manifest.json skin default
  4. 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.

Skin Mode API

frontbacked.js exposes skin helpers:

Frontbacked.setSkinMode("dark");
Frontbacked.setSkinMode("system,dark");
Frontbacked.toggleSkinMode();
Frontbacked.skins.current();
Frontbacked.skins.list();
Frontbacked.skins.setSiteSkin("solar_flare");

toggleSkinMode() switches between light and dark when both exist. For custom modes, it cycles through the modes listed for the skin.

Frontbacked 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 visitor 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. For authenticated admin UIs, Frontbacked.skins.setSiteSkin(skinId) can update the site-selected skin through the protected admin endpoint, while Frontbacked.skins.current() and Frontbacked.skins.list() let the UI show the current skin and available choices.

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.