Theme Options
Theme Options is a React admin page that lets editors control site-wide colors, typography, logos, social links, and third-party integration keys. Values are saved as a single JSON blob in wp_options and output as CSS custom properties via a static file at uploads/sanilwb/theme-vars.css, which is enqueued on the frontend.
File Map
| Purpose | File |
|---|---|
| React root — nav, save, reset orchestration | admin/assets/js/src/theme-options/ThemeOptions.jsx |
| Colors section | admin/assets/js/src/theme-options/sections/ColorsSection.jsx |
| Typography section | admin/assets/js/src/theme-options/sections/TypographySection.jsx |
| Logo & Identity section | admin/assets/js/src/theme-options/sections/LogoSection.jsx |
| Social Media section | admin/assets/js/src/theme-options/sections/SocialSection.jsx |
| Integrations section | admin/assets/js/src/theme-options/sections/IntegrationsSection.jsx |
| Variables section | admin/assets/js/src/theme-options/sections/VariablesSection.jsx |
| Defaults — single source of truth for JS and PHP | admin/assets/js/src/theme-options/data/defaults.json |
| Admin page HTML mount point | admin/templates/theme-options/page.php |
PHP: asset enqueue + window.SanilwbThemeOptions injection |
admin/class-sanil-website-builder-admin.php → enqueue_theme_options_assets() (line ~1446) |
PHP: defaults loader (reads defaults.json, derives fonts + social) |
admin/class-sanil-website-builder-admin.php → get_theme_options_defaults() (line ~863) |
| PHP: saved options merged over defaults, migration, logo seed | admin/class-sanil-website-builder-admin.php → get_theme_options() (line ~898) |
| PHP: AJAX save handler | admin/class-sanil-website-builder-admin.php → ajax_theme_options_save() (line ~997) |
| PHP: section reset handler (form POST, page reload) | admin/class-sanil-website-builder-admin.php → handle_theme_options_reset() (line ~1066) |
| PHP: builds CSS string from saved options | admin/class-sanilwb-admin-theme-options.php → build_theme_options_css() |
PHP: writes CSS to uploads/sanilwb/theme-vars.css on save/reset |
admin/class-sanilwb-admin-theme-options.php → generate_theme_options_css_file() |
| PHP: enqueues the static CSS file on the frontend | admin/class-sanilwb-admin-theme-options.php → enqueue_theme_options_css() |
| Admin CSS entry point | admin/assets/css/theme-options.scss → compiled to theme-options.css |
Data Flow
defaults.json
│
├─► PHP: get_theme_options_defaults() reads the file, derives `fonts` and `social` keys
│ ↓
│ get_theme_options() merges saved wp_options over defaults
│ ↓
│ enqueue_theme_options_assets() injects result as window.SanilwbThemeOptions.data
│
└─► JS: ThemeOptions.jsx seeds React state from injected data,
falling back key-by-key to DEFAULT_DATA
User edits in the UI
↓
handleSave() — POST to admin-ajax.php (action: sanilwb_theme_options_save)
↓
ajax_theme_options_save() — sanitizes, saves to wp_options('sanilwb_theme_options'),
syncs site icon, triggers font download,
regenerates uploads/sanilwb/theme-vars.css
↓
enqueue_theme_options_css() on wp_enqueue_scripts — enqueues the static file with a
timestamp-based version for browser cache busting
React App Architecture
ThemeOptions.jsx ← root: sidebar nav, save/reset orchestration, active section state
├── ColorsSection ← color presets and custom colors
├── TypographySection ← font role pickers; live Google Fonts preview injection on change
├── LogoSection ← desktop logo, mobile logo, featured image, site icon
├── SocialSection ← platform URL fields
├── IntegrationsSection ← API keys, site language
└── VariablesSection ← named pixel/string variables used as tokens in node settings
ThemeOptions (root)
- Reads
window.SanilwbThemeOptions.data(injected by PHP), falling back key-by-key toDEFAULT_DATAfromdefaults.json. Each top-level key is seeded independently so that a reset (which deletes one key fromwp_options) causes only that section to fall back to code defaults — the rest are unaffected. activeSectionstate controls which section component is rendered. On reset redirects, the?section=query param restores the active section so the user lands back in the right place.saveStatusstate ('idle' | 'saving' | 'saved' | 'error') drives the Save button label and color.showResetSuccessstate is set totruewhen?reset=successappears in the URL (written by PHP after a successful reset). Auto-dismissed after 3 seconds.- Google Fonts live preview: a module-level
debouncedInjectFontssingleton is called wheneverdata.fontschanges (detected via a memoized JSON key). It injects<link>tags into<head>so the Typography section shows real font previews without requiring a save first.
NAV_ITEMS reference
NAV_ITEMS is defined at the top of ThemeOptions.jsx and controls sidebar order, icon, and reset button visibility.
id |
Label | Dashicon | supportsReset |
|---|---|---|---|
colors |
Colors | dashicons-art |
Yes |
typography |
Typography | dashicons-editor-textcolor |
Yes |
logo |
Logo & Identity | dashicons-format-image |
No |
social |
Social Media | dashicons-share |
No |
integrations |
Integrations | dashicons-block-default |
No |
variables |
Variables | dashicons-editor-code |
No |
When supportsReset is true, the "Reset to Defaults" button appears in the top bar for that section. Clicking it submits a hidden form that POSTs to admin-post.php?action=sanilwb_theme_options_reset§ion={activeSection}. PHP deletes that section's key from sanilwb_theme_options in wp_options, then redirects back with ?reset=success§ion={activeSection}.
Database Storage
All options are saved under one wp_options key:
option_name: sanilwb_theme_options
The value is a JSON object with these top-level keys:
| Key | What it holds |
|---|---|
colors |
active_preset, presets[], custom[] |
fonts |
Object keyed by role (body, h1…h6, button), each with family, weight, style |
logo |
desktop, mobile, featuredImage — each { id, url } |
siteIcon |
{ id, url } |
social |
Object keyed by platform slug, value is a URL string |
integrations |
sharethis_property_id, facebook_app_id, site_language |
variables |
Array of { id, name, value, type } objects — see Variables section below |
PHP Global Injected on the Admin Page
enqueue_theme_options_assets() outputs this before the React bundle loads:
window.SanilwbThemeOptions = {
data: object, // merged saved options (output of get_theme_options())
nonce: string, // sanilwb_theme_options_save
ajaxUrl: string, // admin-ajax.php URL
resetNonce: string, // sanilwb_theme_options_reset
adminPostUrl: string, // admin-post.php URL
};
CSS Variables Output on the Frontend
build_theme_options_css() generates the CSS content. generate_theme_options_css_file() writes it to uploads/sanilwb/theme-vars.css on every save and reset. enqueue_theme_options_css() enqueues that file on wp_enqueue_scripts (frontend only). The file version is a Unix timestamp saved to wp_options('sanilwb_theme_css_version') so the browser cache busts on every update.
The file contains:
Colors — from the active preset:
:root {
--color-primary: #1464b4;
--color-secondary: #5f6368;
--color-accent: #fbbc04;
--color-text: #202124;
--color-background: #ffffff;
--color-link: #1464b4;
--color-link-hover: #0a50a6;
}
Custom colors — appended with --color-custom-{uid}.
Typography — one set of three variables per font role:
:root {
--font-body-family: 'Mukta', sans-serif;
--font-body-weight: 400;
--font-body-style: normal;
--font-h1-family: 'Mukta', sans-serif;
/* …and so on for h2–h6 and button */
}
Block editor utility classes — automatically generated so Gutenberg color choices render correctly:
.has-sanilwb-primary-color { color: var(--color-primary) !important; }
.has-sanilwb-primary-background-color { background-color: var(--color-primary) !important; }
Variables — one custom property per user-defined variable:
:root {
--sanilwb-var-1: 24px; /* number type — value stored as integer, emitted with px unit */
--sanilwb-var-2: bold; /* string type — value emitted as-is */
}
Use these CSS variables anywhere in the theme or preset SCSS files. They are always available on the frontend.
Important: After creating or editing variables in Theme Options, you must click Save to regenerate
theme-vars.css. The new--sanilwb-var-Nproperties will not appear until the file is regenerated.
Sections
Colors
File: sections/ColorsSection.jsx
Manages color presets. Each preset contains the canonical set of named color slots (primary, secondary, accent, text, background, link, link-hover). The user can activate a preset or add custom colors with arbitrary UIDs.
Supports Reset to Defaults — clears the colors key from wp_options, forcing the JS to fall back to DEFAULT_DATA.colors on the next load.
Typography
File: sections/TypographySection.jsx
Each font role (body, h1–h6, button) stores { family, weight, style }. On save, the fonts are synced to SANILWB_Font_Sync which queues a Google Fonts download to the uploads directory.
Supports Reset to Defaults — clears the fonts key from wp_options.
Logo & Identity
File: sections/LogoSection.jsx
Manages desktop logo, mobile logo, default featured image, and site icon. All images are stored as { id, url } pairs (attachment ID + URL).
On save, the site icon is synced to the WP core site_icon option so it appears in the WP admin header and browser tab. Logo and featured image data are stored only in sanilwb_theme_options — all PHP readers use get_option('sanilwb_theme_options')['logo'] directly.
Does not support Reset to Defaults.
Social Media
File: sections/SocialSection.jsx
Platform list and order is defined in defaults.json under socialPlatforms[]. Each platform has key, label, icon (dashicons class), and placeholder. The saved value is just a URL string per platform key.
To add or remove a platform, edit defaults.json only — both JS and PHP derive the list from it automatically.
Integrations
File: sections/IntegrationsSection.jsx
Stores third-party API keys and site-level settings: ShareThis property ID, Facebook App ID, and site language (nepali or english).
site_language controls the language used throughout the site — date formats, UI labels, and Nepali/English text switches. PHP reads it as:
$site_language = get_option('sanilwb_theme_options')['integrations']['site_language'] ?? 'nepali';
$use_english = $site_language !== 'nepali';
Does not support Reset to Defaults.
Variables
File: sections/VariablesSection.jsx
Variables are named, reusable values that can be inserted as tokens into pixel and text fields across Template Builder and Page Builder node settings. They let editors define a value once and reference it everywhere — changing the variable updates all nodes that use it on the next save.
Each variable has four fields:
| Field | Description |
|---|---|
id |
Auto-incremented integer. Never changes after creation. Used as the token key. |
name |
Human-readable label shown in the variable picker popover. |
value |
The stored value — an integer for number type, any string for string type. |
type |
'number' or 'string'. Controls which fields the variable appears in. |
Token format: {{sanilwb_var:N}} where N is the variable's id. This string is stored directly in a node field value when the user links a variable.
Type rules:
- number variables appear in pixel input fields (font size, border radius, padding, margin, width, height, gap, etc.). The value is emitted as --sanilwb-var-N: Xpx in theme-vars.css.
- string variables appear in text/textarea fields. The value is emitted as --sanilwb-var-N: value (no unit).
Token resolution at render time:
When a node field value is {{sanilwb_var:1}}, the compiler and JS preview both replace it with var(--sanilwb-var-1). The browser then resolves the CSS custom property to the declared value in theme-vars.css. This works in all three rendering contexts — public frontend, Template Builder canvas, and Page Builder canvas — because all three load theme-vars.css via wp_enqueue_scripts.
Adding a variable:
1. Go to Theme Options → Variables.
2. Click "Add Variable", set a name, choose type Number, and enter the pixel value (integer only, no px).
3. Click Save — this regenerates theme-vars.css with the new --sanilwb-var-N property.
4. Open any node in Template Builder or Page Builder, click the variable icon on a number field, and select the variable.
Does not support Reset to Defaults.
How defaults.json Works
defaults.json is the single source of truth for all default values. Both JS and PHP read it directly — there is no duplication.
JS side (ThemeOptions.jsx):
- Imports
defaults.jsonat build time. - Seeds React state: if a key is missing from
window.SanilwbThemeOptions.data, it falls back to the corresponding value inDEFAULT_DATA. fontsdefaults are derived at runtime:fontRoles[].defaultis used to build the initialfontsobject.socialdefaults are derived at runtime:socialPlatforms[].keyis mapped to empty strings.
PHP side (get_theme_options_defaults()):
- Reads
defaults.jsonwithfile_get_contents()and caches it statically for the request lifetime. - Derives
fontswitharray_column( $defaults['fontRoles'], 'default', 'key' ). - Derives
socialwitharray_fill_keys( array_column( $defaults['socialPlatforms'], 'key' ), '' ).
Rule: To change any default value, edit defaults.json only. Do not hardcode defaults in PHP or JS.
Reset Behavior
Only Colors and Typography support the "Reset to Defaults" button in the top bar.
Reset does not write factory values into the database. Instead, it deletes the section key from wp_options. On the next page load, the missing key causes JS to fall back to DEFAULT_DATA, which always reflects the current code defaults rather than a stale DB copy.
The reset is a plain HTML form POST (not AJAX) — the browser reloads after handle_theme_options_reset() processes it. After reload, the URL carries ?reset=success which triggers a temporary success banner in React.
Font Download on Save
When options are saved, ajax_theme_options_save() calls:
SANILWB_Font_Sync::update_entity_fonts( 'theme_options', $theme_fonts );
This queues the selected Google Fonts for download to the uploads directory. The download itself is handled by includes/class-sanilwb-font-downloader.php and includes/class-sanilwb-font-sync.php.
How to Add a New Color to the Default Palette
- Open
admin/assets/js/src/theme-options/data/defaults.json. - Add a new entry to
colors.presets[0].colors[]withkey,label, andvalue. - That's it.
get_theme_options()will automatically: - Add the new color to any existing preset that is missing it (per-preset key-merge logic).
- Output a new
--color-{key}CSS variable on the frontend. - Generate
.has-sanilwb-{key}-colorand.has-sanilwb-{key}-background-colorutility classes.
How to Add a New Font Role
- Open
defaults.jsonand add a new entry tofontRoles[]:
json
{ "key": "caption", "label": "Captions", "hint": "Small image captions", "default": { "family": "Mukta", "weight": 400, "style": "normal" } }
- Both JS and PHP derive the
fontsobject fromfontRoles, so the new role appears automatically with no other changes. build_theme_options_css()will output--font-caption-family,--font-caption-weight, and--font-caption-stylevariables automatically on the next save.
How to Add a New Social Platform
- Open
defaults.jsonand add an entry tosocialPlatforms[]:
json
{ "key": "threads", "label": "Threads", "icon": "dashicons-admin-users", "placeholder": "https://threads.net/@yourhandle" }
- The platform appears in the Social Media section automatically. No JS or PHP changes needed.
How to Add a New Integration Field
- Add the field key and default value to
defaults.jsonunderintegrations:
json
"integrations": {
"sharethis_property_id": "",
"facebook_app_id": "",
"site_language": "nepali",
"google_analytics_id": ""
}
- Add the corresponding input to
sections/IntegrationsSection.jsx— copy the pattern of an existing field row. - If PHP needs to read the value (e.g. to output a script tag), read it as:
php
get_option('sanilwb_theme_options')['integrations']['google_analytics_id'] ?? ''
How to Add a New Section
- Create
sections/YourSection.jsx. It receivesdata(the section's slice of state) andonChange(newValue)as props. - Add the section to
NAV_ITEMSinThemeOptions.jsx:
js
{ id: 'your-section', label: 'Your Section', icon: 'dashicons-admin-generic', supportsReset: false }
- Add a top-level key and its defaults to
defaults.json. - Add the state seed in the
useStateinitializer inThemeOptions.jsx(same pattern associalorintegrations). - Render the section component in the
to-contentblock, guarded byactiveSection === 'your-section'. - Add a case in
ajax_theme_options_save()if the section needs special sanitization or sync behavior. - If the section's values should output CSS variables, extend
build_theme_options_css()inclass-sanilwb-admin-theme-options.php.