Customiser
Appearance → Customise is built into core. It appears for any theme that
declares the customiser theme support. Sections are ordinary
widgets at a widget location; look values are
ordinary settings page fields. The theme adds
no table and no admin code.
Declare it
Section titled “Declare it”osc_add_theme_support('customiser', array( 'locations' => array( 'home' => array( 'label' => 'Home page', 'types' => array('mytheme.hero', 'mytheme.products', 'core.rich_text'), 'preview' => osc_base_url(), ), ), 'settings' => array('page' => 'mytheme', 'fields' => array('brand', 'scheme', 'font', 'logo')), 'swatches' => array('brand' => array('raspberry' => '#c81f5e', 'ocean' => '#0f6fbf')), 'live' => array('brand' => 'data-brand', 'scheme' => 'data-theme', 'font' => 'data-font'),));Core checks the declaration with osc_customiser_spec() and drops anything
invalid:
locationsmust be widget locations the theme declares,typesregistered widget types, andpreviewa URL on this site.settings.fieldsmust be fields of the named settings page. Secret, custom, category-mode, rich text and translated fields are not allowed; core drops them.swatchesare printed as a background colour only when they match#rrggbb. Aselectfield with swatches is drawn as colour circles.livenames fields the preview applies without a reload, as attributes on<html>.
If nothing usable is left, osc_customiser_spec() returns null and the
screen does not appear.
Hidden sections
Section titled “Hidden sections”A section can be hidden without being deleted. Core stores this as the reserved
config key _enabled. osc_render_widget() prints nothing when it is false; a
missing key or true renders. Your render callback never sees a hidden row.
Editing a widget keeps the key.
Draft and preview
Section titled “Draft and preview”Changes are saved as a draft in the key-value store, one per administrator and
theme, for seven days. Publish writes each drafted location in one
transaction, saves the look values, clears the page cache and runs
customiser_published( $theme ).
The preview is the location’s preview URL plus ?osc_preview=1. It is honoured
only for a signed-in administrator. Otherwise the parameter does nothing. With
no draft for the active theme, the preview shows the published page. In a preview:
osc_show_widgets( $location )renders the drafted rows. An unsaved row has id0.osc_customiser_preview()istrue.osc_customiser_draft_value( $page, $name )returns the drafted look value, ornullwhen it is not drafted. Read it before the saved setting.- The response is not cacheable and sends
X-Robots-Tag: noindex, andosc_head()prints<meta name="osc-customiser" content="ADMIN ORIGIN">. osc_customiser_preview_rows( $location )gives the drafted rows if you render a location yourself.
Never print a drafted value into CSS or a URL without checking it. Put owner
choices on <html> as attributes and check them against a list in PHP.
Marking up sections for the preview
Section titled “Marking up sections for the preview”Print these only while osc_customiser_preview() is true:
<section data-sec="<?php echo osc_esc_html($rowKey); ?>"> <h2 data-field="title"><?php echo osc_esc_html($config['title']); ?></h2></section>data-sec is the row id, or the draft key of an unsaved row. data-field is
the config field name. Also print the allowed values of every live attribute
on <html>, for example data-osc-allow-data-brand="raspberry ocean".
Messages
Section titled “Messages”The admin screen and the preview talk with postMessage. Both sides check
event.origin === location.origin. Every message has type: 'osc-customiser'.
Core ships no preview-side script, only the admin side. The theme must write the
preview side: a script it enqueues only while osc_customiser_preview() is true.
That script must:
- listen for
messageevents of typeosc-customiserfrom its own origin; - apply
set,textandselectas in the table below; - send
{ ready: true }towindow.parentonce loaded; - on a click inside a
[data-sec]element, stop the click and send{ picked: <its data-sec> }, so links do not navigate while editing; - send
{ missed: true }when atextmessage finds no matching node.
Aisle’s js/aisle-preview.js is a working example.
Admin to preview:
| Message | Effect |
|---|---|
{ set: { attr: 'data-brand', value: 'ocean' } } |
Sets the attribute, only if the value is in the matching data-osc-allow-* list. A value of '' or 'system' removes the attribute instead. |
{ text: { section, field, value } } |
Sets textContent of [data-sec=section] [data-field=field]. |
{ select: section, name } |
Outlines the section. The optional name is the label shown on the outline; send the section’s display name. |
Preview to admin:
| Message | When |
|---|---|
{ ready: true } |
The page has loaded. |
{ picked: section } |
The owner clicked a section. |
{ missed: true } |
A text message had no node to change. The admin reloads the preview after saving. |
Any other change (order, hide, add, remove, images) saves the draft and reloads the preview.
Helpers
Section titled “Helpers”osc_customiser_spec(), osc_customiser_preview(),
osc_customiser_draft_value(), osc_customiser_preview_rows(),
osc_customiser_preview_url(). See helpers.
customiser_published ( $theme ) runs after a publish. Use it to rebuild
anything derived from the sections or look.