Theme Customization

jekcms ships a theme customizer at admin/theme-customize.php that reads the active theme's theme.json and renders a tabbed editing UI for every option the theme exposes. Changes save to the database per-site — the theme's files never get modified, so a theme update won't wipe your customizations.

Opening the customizer

Appearance → Customize opens the panel. The left sidebar shows tabs rendered from the active theme's customizer.tabs config; the right pane shows a live preview that reloads as you tweak values.

If the active theme has no customizer.tabs defined, the customizer still offers the built-in Custom CSS and Custom HTML tabs.

Tabs come from theme.json

A theme declares its customizer UI like this:

{
    "customizer": {
        "tabs": {
            "branding": {
                "label": "Branding",
                "icon": "palette",
                "fields": {
                    "accent": {
                        "type": "color",
                        "label": "Accent color",
                        "default": "#2563eb",
                        "css_var": "--color-accent"
                    },
                    "logo": {
                        "type": "url",
                        "label": "Logo URL",
                        "default": ""
                    }
                }
            }
        }
    }
}

Each tab has label, optional icon (from Lucide icon set), optional description, and a fields map.

Supported field types

| Type | What it renders | Stored as | |---|---|---| | color | Color picker with hex input | #rrggbb string | | font | Google Fonts dropdown + weight selector | {"family": "...", "weight": "..."} | | range | Slider — honors min, max, step, unit | Number | | toggle | Single checkbox / switch | Boolean | | select | Dropdown from options | Option key | | textarea | Multi-line text input | String | | code | Monospace textarea with syntax highlighting | String | | url | URL input with validation | String | | text | Single-line text input | String |

Reading values in templates

Theme templates read customizer values with theme_option('tab.field'):

<?php $accent = theme_option('branding.accent'); ?>
<a href="..." style="color: <?= htmlspecialchars($accent) ?>">Link</a>

The key is {tab_key}.{field_key}. If the user hasn't set a value, the field's default from theme.json is returned.

Automatic CSS variable emission

Most of the time you don't need theme_option() — you just need CSS. Any field with a css_var mapping is auto-emitted as a :root variable.

Call output_theme_customization_css() once in your <head>:

<head>
    <!-- ... -->
    <?php output_theme_customization_css(); ?>
</head>

This generates:

<style id="theme-customization">
    :root {
        --color-accent: #2563eb;
        --font-body: 'Inter', sans-serif;
        --layout-max-width: 1200px;
    }
</style>

Then in your stylesheet, reference the vars:

.btn-primary { background: var(--color-accent); }

User changes the accent color → the inline <style> block updates → the button repaints. No cache rebuild.

Example: overriding accent color

A visitor visits the site:

  1. Theme's theme.json sets branding.accent default to #2563eb
  2. Admin opens Customize → Branding → Accent color, picks #dc2626
  3. Value is saved to the site's theme_customization JSON blob
  4. On next render, output_theme_customization_css() emits --color-accent: #dc2626
  5. Every var(--color-accent) reference — buttons, links, focus rings — turns red

No file edits, no recompile.

Custom CSS

The built-in Advanced → Custom CSS tab lets you write arbitrary CSS that's appended after the theme's stylesheet. Useful for one-off tweaks without touching the theme folder.

/* Custom CSS tab */
.site-header { border-bottom: 2px solid var(--color-accent); }

Custom HTML injection

Advanced → Custom HTML exposes two slots:

  • Head — injected before </head>. Use for analytics snippets, verification meta tags, fonts.
  • Footer — injected before </body>. Use for chat widgets, late-load scripts.

Both are rendered verbatim — jekcms doesn't sanitize. Only paste code you trust.

Dark mode compatibility

jekcms sets <html data-theme="dark"> when the user opts into dark mode. Write dark-mode overrides in your stylesheet:

:root { --color-bg: #ffffff; }
html[data-theme="dark"] { --color-bg: #0f172a; }

The customizer itself doesn't render a separate set of dark-mode fields — themes typically compute dark-mode values from the light-mode ones in CSS, or ship two css_var mappings under different names.

Be the first to know

New features, release notes & CMS guides — a couple of emails a month, no spam.