Tutorial: Configuring Theme Options

Theme Options is the admin panel for site-wide design settings — colors, typography, logos, social links, and third-party integration keys. Values are saved as a JSON blob in wp_options and applied to the frontend as CSS custom properties via a static file at uploads/sanilwb/theme-vars.css, which is enqueued on every public-facing page.

This tutorial covers how to use and configure every section, how the CSS variables flow to the frontend, and how to extend the system (add new colors, font roles, or social platforms) by editing defaults.json.


Opening Theme Options

Go to WordPress Admin → Theme Options in the Sanil Website Builder menu group. The page opens on the last active section (Colors by default).

The left sidebar is the section navigator. Click any section name to switch to it.


Understanding the Data Flow

Before changing settings, it helps to understand how data moves through the system:

defaults.json (code)
     ↓
PHP: get_theme_options_defaults()     reads the file, caches for the request
     ↓
PHP: get_theme_options()              merges saved wp_options over defaults
     ↓
JS: window.SanilwbThemeOptions.data   injected on the admin page
     ↓
React: ThemeOptions.jsx               seeds state, falls back key-by-key to defaults.json

User saves in the UI
     ↓
AJAX POST → ajax_theme_options_save() sanitizes + writes to wp_options('sanilwb_theme_options')
                                       triggers font download
                                       regenerates uploads/sanilwb/theme-vars.css
     ↓
Frontend: theme-vars.css enqueued on wp_enqueue_scripts

The uploads/sanilwb/theme-vars.css file is regenerated on every save and reset. It is versioned with a Unix timestamp so the browser cache busts automatically.


Colors

Section: Colors

The Colors section manages a palette of named color slots. Each color slot has a consistent name used throughout the plugin and theme.

Default color slots

Slot key CSS variable Purpose
primary --color-primary Main brand color — buttons, links, accents
secondary --color-secondary Supporting brand color
accent --color-accent Highlight color — tags, call-to-actions
text --color-text Default body text color
background --color-background Default page background color
link --color-link Link text color
link-hover --color-link-hover Link hover color

Color presets

The Colors section shows one or more presets — named groups of color values that set all slots at once. Clicking a preset activates it and updates all color slots simultaneously.

To switch to a preset:

  1. Click the preset name in the Colors section.
  2. The color swatches update to show the preset's values.
  3. Click Save to apply the preset's colors to the live site.

Presets are defined in defaults.json under colors.presets[]. Each preset has a name and a colors{} object mapping slot keys to hex values.

Custom colors

Below the presets, a Custom Colors area lets you add arbitrary named colors. These appear in the generated CSS as --color-custom-{uid} variables and in the block editor as custom color options.

To add a custom color:

  1. Click Add Custom Color.
  2. Give it a label (e.g. "Promo Banner Red").
  3. Set the color value using the color picker.
  4. Click Save.

The label is for display only — the CSS variable name is generated from a uid, not the label.

Reset Colors to Defaults

Click Reset to Defaults in the top bar to clear the saved colors key from wp_options. On the next page load, the JS falls back to DEFAULT_DATA.colors (from defaults.json). This is not reversible through the UI — any custom color values you entered will be lost.


Typography

Section: Typography

The Typography section assigns a font to each font role — named slots like body, h1, h2, button, etc. Each role stores three values:

Property What it controls
Family The Google Font family name
Weight Font weight (e.g. 400, 700)
Style Normal or Italic

Changing a font role

  1. Click the Font Family dropdown for the role you want to change.
  2. Search or scroll to find a Google Font.
  3. The Typography section injects a Google Fonts <link> tag into the page <head> immediately (without saving) so you can see a live preview of the font in the picker.
  4. Select a Font Weight from the updated dropdown. The weight options filter to what the selected font actually supports.
  5. Click Save when you are happy with all roles.

What happens on save

When font settings are saved:

  1. ajax_theme_options_save() calls SANILWB_Font_Sync::update_entity_fonts('theme_options', $fonts).
  2. This queues the selected Google Fonts for download to the uploads directory (uploads/sanilwb/fonts/).
  3. generate_theme_options_css_file() writes new font CSS variables to theme-vars.css.

Generated CSS variables

One set of three variables is generated per font role:

:root {
    --font-body-family: 'Mukta', sans-serif;
    --font-body-weight: 400;
    --font-body-style: normal;
    --font-h1-family: 'Lato', sans-serif;
    --font-h1-weight: 700;
    --font-h1-style: normal;
    /* …h2–h6, button */
}

The theme applies these variables to the matching HTML elements. For example, the theme stylesheet uses font-family: var(--font-body-family) on the body element.

Reset Typography to Defaults

Click Reset to Defaults to clear the saved fonts key from wp_options. The JS falls back to the fontRoles[].default values from defaults.json.


Logo & Identity

Section: Logo & Identity

This section manages image assets for the site brand.

Field What it stores Where it is used
Desktop Logo Attachment { id, url } $common['logo_html'] in all preset templates
Mobile Logo Attachment { id, url } Some presets show a different (smaller) logo on mobile
Default Featured Image Attachment { id, url } Fallback image for posts without a featured image
Site Icon (Favicon) Attachment { id, url } Browser tab icon, WP admin header
  1. Click Select Image next to the field.
  2. The WordPress Media Library opens. Upload a new file or select an existing one.
  3. Click Select. The image URL populates the field and a preview appears.
  4. Click Save.

On save, the Site Icon is also synced to WordPress core's site_icon option so it appears in the admin header and browser tabs immediately.

Important: Logo and featured image data are stored only in sanilwb_theme_options. All PHP code that reads the logo uses get_option('sanilwb_theme_options')['logo'] directly — it does not rely on WordPress's built-in custom logo feature.

Why there is no Reset button for Logo & Identity

Resetting logos would delete the saved attachment IDs, leaving the site without a logo. There is no safe "default" logo to fall back to, so this section intentionally omits the reset button.


Social Media

Section: Social Media

This section stores a URL for each social platform. The URLs are output in the footer and other preset templates that include social links.

Each platform has a text input for its URL. Leave a platform blank if you don't use it — blank values are not output in templates.

The platform list (order, labels, icons) is defined entirely in defaults.json under socialPlatforms[]. To add or remove a platform, edit defaults.json — no JS or PHP changes are needed.

Example: adding a Threads platform

Open admin/assets/js/src/theme-options/data/defaults.json and add to socialPlatforms[]:

{
    "key": "threads",
    "label": "Threads",
    "icon": "dashicons-admin-users",
    "placeholder": "https://threads.net/@yourhandle"
}

The Threads field now appears automatically in the Social Media section without any other changes. On save, the value is stored as social.threads in the options JSON.


Integrations

Section: Integrations

This section stores third-party API keys and site-level configuration.

Field Key What it does
ShareThis Property ID sharethis_property_id Property ID for the ShareThis share widget
Facebook App ID facebook_app_id App ID for Facebook Open Graph / sharing
Site Language site_language 'nepali' or 'english' — controls date formats and UI text throughout the site

Site language

The site_language setting controls which date format and UI labels are used across the frontend. PHP reads it as:

$site_language = get_option('sanilwb_theme_options')['integrations']['site_language'] ?? 'nepali';
$use_english   = $site_language !== 'nepali';

Set it to english if you are building an English-language site. The default is nepali.


How to Use CSS Variables in Presets and Stylesheets

The CSS variables from Theme Options are available globally on the frontend. Use them in any preset's style.scss or the theme's own stylesheets:

.my-banner {
    background-color: var(--color-primary);
    color: var(--color-background);
    font-family: var(--font-h2-family);
    font-weight: var(--font-h2-weight);
}

You can also use them in manifest slot default values so preset settings automatically pull from the current theme colors:

{
    "id": "nav_bg",
    "type": "color",
    "label": "Navigation Background",
    "default": "var(--color-primary)",
    "breakpoints": ["desktop"]
}

This means switching a Theme Options color preset instantly updates the default value for that slot on any preset that references it.


How to Extend Theme Options

All extensions start from defaults.json. Both JS and PHP derive their defaults from this single file — there is no duplication.

Adding a new color to the default palette

Open admin/assets/js/src/theme-options/data/defaults.json. Find colors.presets[0].colors[] and add:

{ "key": "highlight", "label": "Highlight", "value": "#ffd700" }

That's all. On the next save, get_theme_options() adds highlight to any existing preset that is missing it, and build_theme_options_css() outputs --color-highlight: #ffd700 automatically.

Adding a new font role

Add to fontRoles[] in defaults.json:

{
    "key": "caption",
    "label": "Captions",
    "hint": "Small image captions",
    "default": { "family": "Mukta", "weight": 400, "style": "normal" }
}

Both PHP and JS derive the fonts object from fontRoles, so the new role appears in the Typography section and generates --font-caption-family, --font-caption-weight, --font-caption-style variables on the next save.

Adding a new social platform

Add to socialPlatforms[] in defaults.json:

{
    "key": "bluesky",
    "label": "Bluesky",
    "icon": "dashicons-share",
    "placeholder": "https://bsky.app/profile/yourhandle"
}

The platform appears in the Social section immediately. No JS or PHP changes needed.

Adding a new integration field

  1. Add the key and default to defaults.json under integrations:

json "integrations": { "sharethis_property_id": "", "facebook_app_id": "", "site_language": "nepali", "google_analytics_id": "" }

  1. Add an input field to sections/IntegrationsSection.jsx following the pattern of an existing field row.
  2. Read the value in PHP where needed:

php $ga_id = get_option('sanilwb_theme_options')['integrations']['google_analytics_id'] ?? '';

Adding a completely new section

  1. Create admin/assets/js/src/theme-options/sections/YourSection.jsx. It receives data (that section's state slice) and onChange(newValue) as props.
  2. Add the section to NAV_ITEMS in ThemeOptions.jsx: js { id: 'your-section', label: 'Your Section', icon: 'dashicons-admin-generic', supportsReset: false }
  3. Add a top-level key to defaults.json with its default values.
  4. Seed the state in ThemeOptions.jsx's useState initializer, following the same pattern as social or integrations.
  5. Render the section component inside the to-content block guarded by activeSection === 'your-section'.
  6. Add a case in ajax_theme_options_save() if the section needs special sanitization or sync behavior on save.
  7. If the section outputs CSS variables, extend build_theme_options_css() in admin/class-sanilwb-admin-theme-options.php.

Common Mistakes

Colors changed in Theme Options but presets still show the old color

The CSS variables are written to uploads/sanilwb/theme-vars.css on save. If the file was not regenerated (save failed silently), the old file is still served. Check the browser DevTools Network tab to confirm theme-vars.css is loading and has the new values. Also confirm the file's ?ver= timestamp updated.

A font picked in Typography isn't showing on the frontend

The font file must be downloaded first. SANILWB_Font_Sync queues the download on save — if the download hasn't completed yet, the browser will fall back to the sans-serif generic family. Wait a moment and reload. If it still doesn't work, check uploads/sanilwb/fonts/ to see if the font files arrived.

Resetting Colors wiped my custom colors too

Reset clears the entire colors key from wp_options, which includes both the active preset and all custom colors. This is intentional — the reset restores the code defaults from defaults.json. There is no selective reset. Back up your custom color values before clicking Reset.

A defaults.json change isn't appearing in the admin

PHP caches the defaults file statically per request (get_theme_options_defaults() uses a static variable). If you are testing in the same browser tab where the page was loaded before the change, reload the admin page to get a fresh PHP request.

The site icon isn't showing in the browser tab

On save, the Site Icon is synced to WordPress core's site_icon option. If it's still not showing, check get_option('site_icon') in a plugin or with WP-CLI to confirm the ID was written. Also check whether a caching plugin is serving an old <head>.