Customizer sidebar width is now variable

A common request for the Customizer has been to grant more room for the controls that appear in its sidebar. Ticket #32296 was created to allow the sidebar pane to be user-resizable with a grabber just like the Dev Tools pane in Chrome can be resized. After a lot of back and forth, the scope was reduced to remove the user-resizable aspect and to instead address a more fundamental issue that the sidebar is exceedingly and unnecessarily narrow on high-resolution displays. So while 4.8 doesn’t include a user-resizable controls pane, there is still a feature plugin for possible future merge. Important to that end, the changes that have been merged begin to introduce the fundamental prerequisite for a variable-width sidebar pane: responsive controls.

As can be seen in [40511] and [40567], no longer should Customizer controls assume that their container will always be 300px wide. Given the new responsive breakpoints for high-resolution screens, the pane can now be between 300px and 600px wide depending on the screen width. And actually this has already been the case on smaller screens (e.g. mobile) where the preview and pane aren’t shown at the same time; it now also applies to very large screens where there is ample space for both the preview and a wider pane.

Custom controls in plugins and themes should utilize alternative approaches to doing layout than using pixel widths. Use of percentage-based widths or flexbox will help ensure that controls will appear properly in larger displays, while also making controls future-compatible when the sidebar width could be user-resizable.

#4-8, #customize, #dev-notes

Customizer Improvements in 4.7

WordPress 4.7 has been the most active release on record for the customize component, with four major feature projects merging and shipping with 4.7 and over 90 tickets closed as fixed. This post summarizes the highlights of the user and developer-facing changes.

4.7 Customizer Feature Projects

Create pages within live preview during site setup

Add new pages while building menus and setting a static front page; outline your site directly in the customizer.

This project began with the ability to create posts and pages direction from the available menu items panel in the customizer, as originally proposed near the end of the 4.6 cycle:

Subsequent changes also added the ability to create new pages when assigning the front page and posts page in the Static Front Page section. Because this is now built into the core dropdown-pages customizer control, themes and plugins can also allow users to create new pages for their options instead of relying on existing content. The homepage sections in Twenty Seventeen include this new allow_addition parameter. Here’s how to register a dropdown-pages control supporting new pages:

$wp_customize->add_control( 'featured_page', array(
	'label'          => __( 'Featured Page', 'mytextdomain' ),
	'section'        => 'theme_options',
	'type'           => 'dropdown-pages',
	'allow_addition' => true, // This allows users to add new pages from this dropdown-pages control.
) );

Additionally, a proposal for term statuses was developed as a first step toward expanding the menus functionality to work for creating and previewing taxonomy terms in a future release (see #37915).

Improvements to the Sliding Panels UI

Customizer navigation is now faster, smoother, and more accessible.

This project tackled a series of tickets focused on improving the usability of the “sliding panels” UI in the customizer controls pane. The first step was to refactor the section and panel markup so that sections and panels are not logically nested. This is the biggest internal change to the UI and has a dedicated post going into the details:

This primary change resolved numerous problems with sections and panels not opening and closing properly, and eliminated situations where navigation to leave a section could become hidden. The next step was making section and panel headers “sticky” so that navigation is easier to access within long sections (such as for a menu); see #34343.

Finally, hover and focus styling for navigation in the customizer has been updated to use the blue-border approach found elsewhere in core, including for the device-preview buttons in the customizer, in #29158. This completes a refresh of the customizer controls pane’s UI design that began in WordPress 4.3 with #31336. The core UI now uses the following consistent UI patterns in the customizer:

  • White background colors are used only to indicate navigation and actionable items (such as inputs)
  • The general #eee background color provides visual contrast against the white elements
  • 1px #ddd borders separate navigational elements from background margins and from each other
  • 15px of spacing is provided between elements where visual separation is desired
  • 4px borders are used on one side of a navigation element to show hover or focus, with a color of #0073aa
  • Customizer text uses color: #555d66, with #0073aa for hover and focus states on navigation elements

Plugins and themes should follow these conventions in any custom customizer UI that they introduce, and inherit core styles wherever possible.

Any custom sections and panels, as well as customizations to the core objects in plugins and themes, should be tested extensively to ensure that they continue functioning as intended with all of these changes in 4.7. It’s particularly important to ensure that things like the use of color match the core conventions so that the user experience is seamless between functionality added by plugins and core.

Customize Changesets (formerly Transactions)

Browse your site and switch themes more seamlessly within the customizer, as your changes automatically persist in the background.

This project rewrote the internals of the customizer preview mechanism to make changes persistent. Each change made to a setting in the customizer is saved to a changeset (a new customize_changeset post type), facilitating future features such as scheduled changes, revisions, or saving and sharing drafted changes. Changesets also open the door to using the customizer to preview Ajax requests, headless sites, and REST API calls for mobile apps. In 4.7, changesets enable switching themes in the customizer without needing to decide between publishing or losing your customizations, as they’re automatically persisted in the background.

For more details on changesets, check out the two dedicated posts:

Custom CSS

Fine-tune your site and take your theme customizations to the next level with custom css in the customizer.

#35395 introduced a user-oriented custom CSS option in the customizer. Now that the base functionality is in place, it will be further enhanced in #38707 in future releases. Read the feature proposal for details on the implementation and why it’s important for core:

There’s also a dedicated post that walks through the process of migrating existing custom CSS options in themes and plugins to the core functionality – be sure to follow those steps if your plugin or theme does custom CSS:

Other Changes with Dedicated Posts

4.7 features several other features deserving special attention. Read the posts for visible edit shortcuts (which expand the functionality of customizer partials), video headers (which extend the custom header feature), and starter content for more information:

Additional User-facing Changes

With over 90 tickets fixed in the customize component in 4.7, we can’t cover everything here. But, here are a few more highlights:

Improved Custom Background Properties UI

#22058 introduces a more comprehensive and more usable custom background properties UI when a custom background is set up. There are now presets to control all of the detailed options at once, and the individual options are presented in a more visual way. Background size and vertical position are also now available as standalone options when using a custom preset.

Theme developers should update their add_theme_support() calls for custom-background to specify the default size, vertical position, and preset to reflect their default background image CSS. Perhaps the most significant improvement here is the ability for users to easily set up fixed full-screen backgrounds – and the ability for themes to make that behavior default if desired.

And even more…

4.7 also:

  • Loads the frontend preview iframe more naturally, eliminating a lot of weirdness with JS running in an unexpected location and ensuring that JS-based routing will work (#30028)
  • Allows the search results page to be previewed, and any forms that use the GET method in general can now be submitted whereas previously they would do nothing when submitted (#20714)
  • Hides edit post links in the customizer by default. Plugins, such as Customize Posts, can restore the links if they make post editing available in the customizer (#38648), although the visible edit shortcuts should generally be used instead.
  • Shows a cursor: not-allowed for mouse users when hovering over external links in the preview, as these can’t be previewed
  • Officially removes support for the customizer in Internet Explorer 8, preventing users of this outdated browser from accessing the customizer at all (#38021)

Additional Developer-oriented Changes

Hue-only Color Picker

#38263 adds a hue-only mode to the Iris color picker, wpColorPicker, and WP_Customize_Color_Control. Built for Twenty Seventeen’s custom colors functionality, the hue-only mode allows users to select a hue and saves the hue degree as a number between 0 and 359. To add a hue-color control:

$wp_customize->add_control( new WP_Customize_Color_Control( $wp_customize, 'colorscheme_hue', array(
	'mode' => 'hue',
	'section' => 'colors',
) ) );

As demonstrated in Twenty Seventeen’s custom colors strategy, the hue-selection strategy opens up a whole new world of possibilities for custom color options in themes. Rather than introducing numerous text and background color options and requiring users to adjust them to ensure that adequate color contrast is provided, themes can consolidate their color options into one or more hue pickers. Then, the corresponding use of hsl colors in CSS allows themes to define color patterns where users customize color hues without impacting the lightness of a color option, thereby preserving the designer’s use of proper contrast between text and background colors, and any use of color lightness for visual hierarchy. Check out the implementation in Twenty Seventeen for inspiration (including instant live preview).

Fix Sections that .cannot-expand

When creating custom customizer sections that, for example, display an external link but don’t actually expand to show section contents, the cannot-expand class can be added to the section title container to prevent JS events and CSS hover/focus styles from being applied. Be sure to also remove the tabindex="0" from the container if you copy the core code since your custom section shouldn’t be focusable if it can’t expand (and any contained links or buttons would be keyboard-focusable automatically). See #37980 for details.

Allow Plugins to do Comprehensive Late Validation of Settings

To account for custom subclasses of WP_Customize_Setting that don’t apply the customize_validate_{{$setting_id}} filter, this filter now will be applied when WP_Customize_Manager::validate_setting_values() is called. This ensures that plugins can add custom validation for every setting. For more, see #37638.


Huge thanks to the 61 people (and counting) receiving props for the 120+ customize component commits in 4.7 (as of RC2): @westonruter, @celloexpressions, @afercia, @sirbrillig, @ryankienstra, @helen, @ocean90, @melchoyce, @bradyvercher, @folletto, @johnbillion, @delawski, @karmatosed, @georgestephanis, @dlh, @curdin, @valendesigns, @mattwiebe, @michaelarestad, @joemcgill, @sstoqnov, @lgedeon, @mihai2u, @coreymcollins, @stubgo, @utkarshpatel, @desrosj, @odysseygate, @johnregan3, @aaroncampbell, @mapk, @iseulde, @mrahmadawais, @vishalkakadiya, @sayedwp, @hugobaeta, @jonathanbardo, @jorbin, @tristangemus, @deltafactory, @kkoppenhaver, @seancjones, @Presskopp, @Mista-Flo, @nikeo, @adamsilverstein, @lukecavanagh, @coffee2code, @peterwilsoncc, @presskopp, @pento, @Kelderic, @sebastian.pisula, @mckernanin, @FolioVision, @MikeHansenMe, @welcher, @cdog, @grapplerulrich, @iamfriendly, @flixos90.


#4-7, #customize, #dev-notes

Video Headers in 4.7

WordPress 4.7 extends the Custom Header feature to introduce support for video.

Video headers are considered decorative elements — like header images, but with motion. With that in mind, they play automatically, loop by default, and don’t have sound. They work best when paired with an image, so they can progressively enhance the experience when video is supported.

Header media UI in the customizer when a theme supports video.

Adding theme support

Adding support for video headers to a theme requires three basic steps:

Registering theme support

Support for video headers can be registered when adding support for custom headers in a theme.

add_theme_support( 'custom-header', array(
 'video' => true,
) );

Adding support this way does a few things:

  • Renames the “Header Image” customizer section to “Header Media.”
  • Registers customizer controls for selecting a video from the media library or entering a URL to a YouTube video.
  • Enables support for Selective Refresh for header images.

Displaying the header

In previous versions of WordPress, generating the image tag manually was the recommended way to display a header image. WordPress 4.4 introduced the_header_image_tag() to take advantage of the responsive image improvements.

In WordPress 4.7, the_custom_header_markup() unifies support for header images and videos and is the recommended method for displaying custom headers.

It prints a div that will contain a header image if one is set in the customizer and will also enqueue the wp-custom-header.js script if a video is set in the customizer. The script determines if the environment supports video, and if so, it will progressively enhance the header by replacing the image with a video.

Styling the play/pause button

When videos are ready to play, the wp-custom-header.js script inserts a button for pausing and playing the video to help improve accessibility. Core does not apply any CSS to the button in order to make it easier for themes to style. Themes should ensure the button is visible, fits within the design, and add icons if desired.

Pause Button
<button type="button" id="wp-custom-header-video-button" class="wp-custom-header-video-button wp-custom-header-video-play">Pause</button>

Play Button
<button type="button" id="wp-custom-header-video-button" class="wp-custom-header-video-button wp-custom-header-video-pause">Play</button>

The text for the button can be modified using the header_video_settings filter.

Styling custom headers

When styling custom headers, it’s important to be aware of the various elements that can be used for header media.

A container div with a wp-custom-header class will always be rendered when a header image or video is available. The div may contain an image, video, or iframe depending on the source of the video, so each of those elements needs to be considered.

The following selectors should be fairly standard for creating responsive headers:

.wp-custom-header iframe,
.wp-custom-header img,
.wp-custom-header video {
	display: block;
	height: auto;
	max-width: 100%;

Accessibility considerations

A button to toggle the play/pause state of the video is automatically rendered to help users who may be distracted or disoriented by motion. Voice assistance is also available using wp.a11y.speak, and like the button text, the strings can be modified using the header_video_settings filter.

Bandwidth considerations

To alleviate concerns about bandwidth, videos are only loaded on the front page for viewports that are at least 900 pixels wide and 500 pixels tall. The maximum file size is also capped at 8MB; however, we strongly encourage smaller files be used whenever possible.

Filtering the front page restriction

By default, videos are only loaded on the front page and only the header image is shown on other pages calling the_custom_header_markup(). Themes that need to display the header video on pages other than the front page can:

  • Define a custom callback for the video-active-callback header argument.
  • Use the is_header_video_active filter.

Testing the environment for video support

Themes may also want to customize the criteria used to determine whether or not a video should be embedded. The header_video_settings filter can be used to modify the minimum viewport width and height.

On the front end, the wp.customHeader.supportsVideo() method can be redefined. For instance, it might be desirable to test the user agent to prevent videos from loading on mobile devices that don’t support autoplay. As browsers introduce bandwidth APIs, it may also be worthwhile to disable video on devices with limited bandwidth.

Selective Refresh enabled by default

When registering support for video headers in a theme, header image settings in the customizer are updated to use the postMessage transport to take advantage of the Selective Refresh API introduced in WordPress 4.5. This ensures header images and videos can be updated in the customizer without refreshing the preview window.

If the_custom_header_markup() template tag isn’t being used, themes will need to update the custom header partial to use a custom render_callback, or change the transport for the header_image and header_image_data settings back to refresh.

Creating custom video handlers

Locally hosted mp4 and mov files, as well as YouTube videos, can be used for video headers by default, but it’s possible to add support for additional sources as well.

The wp-custom-header.js script exports a wp.customHeader.handlers global variable that contains a list of video handlers. Each handler accepts information about the current video to determine if it can process it, and if so, it creates the video and inserts it into the DOM.

Core registers two handlers, one for native video, and one for YouTube videos. Each handler extends a base class exposed at wp.customHeader.BaseVideoHandler and implements a basic interface to make sure all videos receive the same level of support.

In the customizer, there is validation to ensure that local videos are a supported format and file size, and that external video links are to YouTube. This validation needs to be filtered to account for custom handlers, either with the customize_validate_external_header_video and customize_validate_header_video filters to filter the core validation functions, or by changing the validation_callback on the header_video and external_header_video customizer settings. See the documentation on customizer validation for more details.

For an example of registering a custom video handler in a plugin, take a look at how this plugin registers support for Vimeo.

New functions and hooks

  • has_header_video() – Checks whether a header video has been set in the customizer.
  • is_header_video_active() – Checks whether a header video is eligible to be shown for the current request.
  • get_header_video_url() – Retrieve the header video URL. May be a local attachment URL or a URL for an external source.
  • the_header_video_url() – Display the header video URL.
  • has_custom_header() – Checks whether a header image or video is set in the customizer and is available for the current request.
  • get_custom_header_markup() – Retrieve the markup for displaying a custom header image (this does not include video support).
  • the_custom_header_markup() – Display the custom header markup and enqueue a script for rendering video in supported environments.


  • is_header_video_active – Whether a header video should be shown for the current request if available.
  • header_video_settings – Settings that are exported to the wp-custom-header.js script during initial page load and when updating the custom header partial in the customizer preview. The default values are:
    • videoUrl – URL for the selected video.
    • mimeType – MIME type of the selected video.
    • posterUrl – URL for the fallback header image.
    • width – Video width.
    • height – Video height.
    • minWidth – Minimum viewport width to embed a video.
    • minHeight – Minimum viewport height to embed a video.
    • l10n – An array of button text and accessibility strings.

Theme support arguments

When calling add_theme_support( 'custom-header' ), two new arguments are available:

  • video – Registers support for video headers.
  • video-active-callback – Defines a callback used to determine whether a header video should be shown for the current request. Defaults to is_front_page.

#4-7, #custom-header, #customize, #dev-notes, #themes

Extending the Custom CSS editor

With the Custom CSS project merging into WordPress Core, some of y’all may be looking to extend it and do more advanced stuff.  Maybe you help run an existing plugin (like me) that has already provided a Custom CSS input to WordPress core and you’re now looking to migrate that data over.  Or maybe you want to change how it outputs.  Here’s what I’ve found so far in my work converting Jetpack’s Custom CSS module to be an enhancement layer on top of the Core implementation, providing legacy feature parity.

Disclaimer: This is just what I’ve found to be useful so far, the Jetpack update is still a work in progress as I write this.

Data Structure

Core’s data store is in a Custom Post Type named custom_css, and the CSS is stored in the post_content.  It sets up a new post for each theme’s custom css, and only the active theme’s one is used.  There’s no accounting for parent/child themes — it uses the slug from the current stylesheet (child theme) as the post_name; that is, Custom CSS lookups are indexed by the return value of get_stylesheet().  Core does not yet have have a UI for displaying the revisions for changes to Custom CSS or a way display the saved Custom CSS of inactive themes, but revisions are enabled on the post type, so no data is lost until the revision viewer makes its way into core (or the user activates a plugin that provides similar functionality). Follow #31089 for more on revisions in the customizer, for all settings not just for Custom CSS.

Getting The Custom CSS

The generated CSS itself can be gotten via the wp_get_custom_css() function, which just returns the CSS for the current theme as a string. This function is used in the wp_head callback when the CSS is printed into a style tag.  One of the more useful functions in the Core implementation for advanced development is wp_get_custom_css_post( $stylesheet = '' ) — this will return either null or the WP_Post object if the site has any Custom CSS saved for the current site.  If you’re building a custom revision viewer, this will be the post you’ll key off of to fetch the revisions.

Filters on Read and Update

The wp_get_custom_css() function applies a wp_get_custom_css filter to the styles just before they’re returned.  This allows for targeted tweaks such as minifying the output on the front-end before it’s echoed by stripping out excess whitespace or the like.  This filter is not meant for a theme or plugin adding styles to the front-end of the site — for that, consider enqueueing your stylesheet normally and adding any dynamic bits via wp_add_inline_style() — this way it will also handle if a child theme or plugin wants to dequeue the parent stylesheet.

Jetpack has historically provided LESS and Sass (SCSS) preprocessing for our Custom CSS module.  We’re extending the Core implementation via two filters in the WP_Customize_Custom_CSS_Setting class by storing the pre-compiled code in $post->post_content_filtered — so it is versioned correctly, but if the user disables Jetpack, the compiled CSS will still be available in $post->post_content with no data loss for the user.

When implementing a pre-processor extension to the Custom CSS functionality in core you have to do some swapping between the underlying setting value and the value that gets displayed:

  1. Replace the post_content with the post_content_filtered as the initial setting value via the customize_value_custom_css filter.
  2. Add a wp_get_custom_css filter in the customizer preview (when the customize_preview_init action triggers) to compile the value into CSS just-in-time.
  3. Override the default JavaScript live-preview functionality to instead register a partial for the wp-custom-css style element so that whenever the custom CSS is modified it can be re-compiled on the server and rendered via selective refresh.
  4. When the Custom CSS setting is saved in the customizer, send the saved pre-processed value to post_content_filtered and compile the value to store into post_content.

For a standalone example of building a pre-processor, see the Custom SCSS Demo plugin on GitHub.


The Core implementation also is including only very basic sanitization, to the point where it would be dangerous to allow users without unfiltered_html to edit CSS.  If your plugin is adding further sanitization to the saved CSS, you can broaden the user base by remapping the edit_css capability (which Core defaults to unfiltered_html) like so:

add_filter( 'map_meta_cap', 'mycss_map_meta_cap', 20, 2 );
function mycss_map_meta_cap( $caps, $cap ) {
  if ( 'edit_css' === $cap ) {
    $caps = array( 'edit_theme_options' );
  return $caps;

Migrating an Existing option to Core CSS

Does your plugin or theme have a custom CSS option stored as an option or a theme_mod? Consider migrating content from your custom setting to the core functionality and hiding your custom UI. Here’s a general migration script, which can be located where you see fit in the context of your original code:

if ( function_exists( 'wp_update_custom_css_post' ) ) {
	// Migrate any existing theme CSS to the core option added in WordPress 4.7.
	$css = get_theme_mod( 'custom_theme_css' );
	if ( $css ) {
		$core_css = wp_get_custom_css(); // Preserve any CSS already added to the core option.
		$return = wp_update_custom_css_post( $core_css . $css );
		if ( ! is_wp_error( $return ) ) {
			// Remove the old theme_mod, so that the CSS is stored in only one place moving forward.
			remove_theme_mod( 'custom_theme_css' );
} else {
	// Back-compat for WordPress < 4.7.

I hope some of this has been useful to folks interested in diving deeper into modifying the Core Custom CSS editor.  It’s still somewhat early days for the feature, so please reach out in #core-customize on Slack with any unexpected use cases or concerns!

#4-7, #css, #customize, #dev-notes

Visible Edit Shortcuts in the Customizer Preview

#27403 added visible edit shortcuts to the customizer preview, making it easier to see which elements of your site are editable in the customizer and how to edit them. Here’s a demo with Twenty Fifteen (note that the ability to toggle icons off has since been removed):

Implementation: Selective Refresh Partials

Visible edit shortcuts are an extension of the existing “shift-click-to-edit” functionality built into customizer partials. Partials are sections of the front end of the site, in the customizer preview, that are associated with customizer settings. When settings change, their associated partials are selectively refreshed via an Ajax call without reloading the entire preview. Partials are to the customizer preview what controls are to the customizer editing pane: a visual representation of a setting.

Buttons are now injected into partials within the preview to expose this relationship visually and to users of all input modes. However, the role of the customizer preview is to provide an accurate representation of the frontend of the site as it’ll appear once changes are published. Accordingly, visible edit shortcuts pose a challenge as they have the potential to significantly hamper the preview-ability of the preview.

To balance between discoverability and providing an accurate preview, the initial core iteration showed a flash of the buttons when the preview first loads, then hid them. To show the shortcuts, or to toggle them on and off, you could click/tap anywhere in the preview (except on a link or button). Keyboard users had a screen-reader-text button (visible on focus) to toggle the shortcuts on and off. This functionality was removed in [39131] and icons are currently persistently visible when customizing but hidden when the controls are collapsed, and supplemental usability testing validated this decision.

Background & Prior Implementations

Shift-click to edit an element in the customizer preview was first implemented with the widget customizer project in WordPress 3.9. Visual approaches to exposing this functionality were explored, but left for a future release. Selective refresh was also first proposed, and put on hold pending development of the concept.

The first core implementation of selective refresh came with menu management in the customizer in WordPress 4.3. Menus include shift-click-to-edit on a per-menu-item bases, further demonstrating the powerful potential of associating portions of the customizer preview with their associated settings and controls. currently supports a similar feature with visible edit icons in the customizer. This approach serves as the inspiration for the final UI being introduced in core, with additional UX adjustments and a complete rewrite of the implementation to make it compatible with as many themes as possible.

Adding Theme Support

Theme support for this feature is all about supporting selective refresh, which was added in WordPress 4.5. In some cases, a small amount of additional CSS will be required to ensure that the shortcuts are positioned properly. Edit shortcuts will be enabled by default for all themes, but are contingent on themes supporting selective refresh.

Selective Refresh for Widgets

See the post from WordPress 4.5 for adding support for selective refresh for widgets. In most cases, add_theme_support( 'customize-selective-refresh-widgets' ) is the only requirement:

Selective Refresh for Menus

Menus support selective refresh out of the box unless: a custom nav menu walker is used, the echo argument is false, or wp_nav_menu isn’t used. In those cases, you’ll need to add support directly. Some themes may still be missing full support for selective refresh of menus, which has been enabled by default since WordPress 4.3.  Reference the post for details, but note that it predates the core implementation of an API for selective refresh:

Selective Refresh for Custom Options

Custom logo (since 4.5) and header video (since 4.7) support selective refresh automatically if you use the core features via add_theme_support. Other core options such as the site title and tagline or header images can support selective refresh if you register partials for them and set their settings’ transport argument to postMessage. Here’s an example from Twenty Fifteen:

$wp_customize->get_setting( 'blogname' )->transport        = 'postMessage';
$wp_customize->get_setting( 'blogdescription' )->transport = 'postMessage';

$wp_customize->selective_refresh->add_partial( 'blogname', array(
	'selector' => '.site-title a',
	'render_callback' => 'twentyfifteen_customize_partial_blogname',
) );
$wp_customize->selective_refresh->add_partial( 'blogdescription', array(
	'selector' => '.site-description',
	'render_callback' => 'twentyfifteen_customize_partial_blogdescription',
) );

Where the render callbacks call bloginfo( 'name' ); and bloginfo( 'description' ); For more details on adding support for selective refresh for custom theme options, reference the official customizer documentation.

Support in Default Themes

Twenty Eleven through Sixteen support selective refresh as of WordPress 4.5, and also support edit icons for most of their features as a result. Twenty Fourteen and Sixteen require a few very minor positioning tweaks to ensure that all of the icons are visible. This is typical of what most themes could expect needing to add.

Twenty Seventeen will be a great showcase for this new functionality, as the first theme to ship natively with selective refresh support and with visible edit shortcuts. A few additional adjustments in this new theme will ensure that every option can be previewed with selective refresh and provides visible edit shortcuts where appropriate.

Limitations & Future Iterations

The biggest limitation of the current approach is that implementation is entirely dependent on themes supporting it. However, unlike with many other theme-supported features, there is no add_theme_support for visible edit shortcuts. Where themes are already using selective refresh, shortcuts will be available out of the box in WordPress 4.7. To add theme support for edit shortcuts, themes need to add theme support for selective refresh, another newer customizer feature that allows the customizer preview to update granularly. Selective refresh provides superior user experience to the default refresh behavior because the preview context is not lost when changes are made.

Edit shortcuts currently rely on the presence of selective refresh partials for every setting that needs an icon. Some settings may be previewed exclusively with JavaScript via postMessage. Icons also aren’t needed for every option; for example, layout or color options are broader than a specific area of the site, so they aren’t associated with a particular edit icon in the preview. In the future, a structured JavaScript API for partials in the customizer preview could facilitate adding icons to JS-previewed settings that don’t use selective refresh.

Visible edit shortcuts are also the first step toward exploring the potential to edit elements of a site directly within the customizer preview. For this to be fully investigated, it’s imperative that a majority of themes and customizer option support selective refresh so that areas of the preview are associated with the appropriate customizer settings and so that context-disrupting full page reloads can be minimized.

Contributors & Call for Help

@sirbrillig led development of the feature for core based on the equivalent feature on Core props went to @sirbrillig, @mattwiebe, @celloexpressions, @melchoyce, @westonruter, and @afercia. Thanks to everyone who has contributed so far!

Now, your help is needed! Here’s what you can do to make this feature shine in WordPress 4.7:

  • Theme authors: add support for selective refresh to your themes. Start with widgets and make sure it’s working for menus, then make sure you’re using the core custom logo feature. Then, add selective refresh to the site title and tagline, header images, and any custom options with associated regions on the frontend.
  • Theme authors: adjust icon positioning in your theme’s CSS. You can add styles to.customize-partial-icon button to adjust all icons, and scope that to a specific container or even .customize-partial-icon-setting_id to adjust a particular edit icon. Note: these were updated with [39136].
  • Everyone: test edit shortcuts with your current theme with WordPress 4.7 Beta 1 (or newer). Most themes should be able to support them on widgets, menus, and logos with minimal effort. Contact your theme’s developer with any bugs or missing edit icon support, refer them to this post, and ask them to add support for visible edit shortcuts.
  • Everyone: test as many themes as possible and look for anywhere the shortcuts don’t display as expected, or at all. Contact the theme author with your findings, refer them to this post, and ask them to improve support for visible edit shortcuts in their themes.

#4-7, #customize, #dev-notes, #themes

Customize Update 2016-10-23

This is the weekly update post for the customize component. It includes a summary of this week’s meeting, recent commits, and next week’s meeting agenda.

4.7 Feature Proposals & Merges

Three proposal posts have been published and approved since our last update:

All five of the major customize feature projects proposed for WordPress 4.7 have been successfully merged (in order):

Work continues on follow up tickets for many of these projects. Please test everything in the customizer and report any bugs to trac. We still have a few pending enhancements that need to be completed by this Wednesday, 10/26, 4.7 beta 1, sorted by priority:

  1. #27403 Improve UI for linking areas of Customizer preview to corresponding controls (desktop and mobile) – has-patch, needs testing and adjustments
  2. #38263 Color picker: add a hue-only mode – has-patch, needed for Twenty Seventeen
  3. #28536 Add browser history and deep linking for navigation in Customizer preview – needs-patch punted
  4. #38164 Customize: assign static front page and posts page to new pages – has-patch
  5. #37964 Allow customizer controls to be encapsulated by accepting pre-instantiated settings – has-patch, needs adjustments
  6. #22058 Improve custom background properties UI – has-patch
  7. #29158 Customizer UI Design lacks contrast for visual hierarchy and does not match wp-admin – has-commit, has-patch for revisions

Weekly Customize Meetings

Our past two weekly meetings have focused on preparing our projects for commit. We’ll continue our weekly 4.7 meetings into beta and RC pending the volume of customize tickets remaining in the milestone. For this week’s meeting, our priority will be ensuring that the 7 remaining customize enhancements for 4.7, listed above, are committed before beta 1.

Our weekly update posts will continue on a reduced schedule now that the bulk of 4.7 development is complete, and we’ll also continue posting dev notes for changes in 4.7.

Recent Customize Commits

Here are the customize-related commits for the past two weeks:

  • [38765]: Customize: Ensure `customize_validate_{$setting->id}` filters apply on input post_values for WP_Customize_Setting subclasses that neglect to apply the filter themselves.
  • [38766]: Customize: Improve message displayed in widgets panel when there are no widget areas currently displayed in the preview.
  • [38767]: Customize: Show Pages section first and pre-expanded in list of available menu items.
  • [38794]: Customize: Move Pages below Custom Links in available nav menu items panel.
  • [38807]: Customize: Skip triggering initial click on pages section for available nav menu items if already open.
  • [38810]: Customize: Implement customized state persistence with changesets. 
  • [38811]: Customize: Split out link `click.preview` and form `submit.preview` event handlers…
  • [38813]: Customize: Introduce a new experience for discovering, installing, and previewing themes in the customizer.
  • [38829]: Customize: Introduce custom CSS for extending theme styles.
  • [38830]: Customize: Fix unit tests when `twentyfifteeen` is `WP_DEFAULT_THEME` instead of twentyfifteen.
  • [38831]: Customize: Improve the labeling of background and header images in the list mode of the media library.
  • [38837]: Twenty Seventeen: Fix a PHP warning on fresh installs.
  • [38850]: Tests: Prevent Twenty Seventeen from interfering with Customizer tests.
  • [38851]: Tests: Prevent Twenty Seventeen from interfering with Customizer ajax tests.
  • [38853]: Customize: Add sticky headers for panels and sections.
  • [38862]: Customize: Revert part of [38859] which caused sections to get deactivated in the customizer.
  • [38867]: Twenty Seventeen: Add theme support for customize selective refresh.
  • [38881]: Customize: Keep previously uploaded header images in place.

Big thanks to those who contributed to patches committed this week: @johnregan3, @celloexpressions, @folletto, @westonruter, @deltafactory, @coreymcollins, @desrosj, @pento, @delawski, @davidakennedy, @afercia, @karmatosed, @ryankienstra, @valendesigns, @utkarshpatel, @stubgo, @lgedeon, @ocean90, @mihai2u, @dlh, @aaroncampbell, @jonathanbardo, @jorbin.

We’re always looking for more contributors; check out the open customize tickets and swing by #core-customize in Slack to get involved. Tickets in the Future Release milestone will be considered first for 4.8 if they have a patch.

#4-7, #customize

Customize Changesets Technical Design Decisions

2016-11-15: Updated the description for how and when setting changes are written into the changeset, and also how the iframe window is loaded during a refresh when there are pending changeset yet to be written into the changeset. For more see comment. Also included information about storing modifying user with each setting change, among other tweaks.

This is a technical deep-dive into Customize Changesets (#30937), a proposed feature formerly known as Customizer Transactions. Because changesets make some very low-level changes to how the customizer works, I felt it was important for these technical details to be shared concurrently with a 4.7 merge proposal since developers of themes and plugins that extend the customizer heavily will need to be well advised of the changes. As such, again, this is a long and technical post. Consider it a pre-merge dev note.

Feedback on the transactions/changesets idea has been positive although somewhat quiet. After the initial patch and the Customize Snapshots feature plugin, there is now a third iteration on the patch, which is currently in testing and review, and I believe it will be ready for inclusion in WordPress 4.7. I’ll first recap the customizer in general and then explain how changesets will open up a lot of new possibilities for live preview and also fix a lot of defects while we’re at it.

The customizer is WordPress’s framework for doing live previews of any change on your site. Its purpose is to eliminate the “save and surprise” behavior that happens when making changes in the WP admin. All changes you make in the customizer are transient and nothing impacts the live site until you hit the Save & Publish button. A user can feel free to experiment with making changes to their site and be secure in the knowledge that they aren’t going to break their site for visitors since they can preview the impacts of their changes. A very powerful aspect of the customizer is that you can make as many changes to as many settings as you want, including changing the site title, header image, background image, nav menu, widgets, and other aspects of your site, and all of these changes are previewed together and will go live together when published: changes in the customizer are bundled/batched together.

The TL;DR for this post is that customize changesets make changes in the customizer persistent, like autosave drafts. For users, the customizer tab can be closed and re-opened and the changes will persist. Users can make changes to one theme and switch to another in the customizer without losing the changes upon switching. A customizer session can be bookmarked to come back to later or this URL can be shared with someone else to review and make additional changes (the URLs expire after a week without changes). The new APIs make possible many new user-facing features in future releases and feature plugins, including saving drafts, submitting changesets as pending for review, scheduling changes, and more.

Limitations before Changesets (WP≤4.6)

In saying that unsaved changes made in the customizer (before changesets) are transient, I did not mean that they somehow get saved in an actual transient. No, they are much more ephemeral than that. Changes in the customizer currently only exist in your browser’s memory. If you try leaving the customizer with unsaved changes you get prompted with an “Are you sure?” (AYS) dialog because once you leave customize.php all of the changes you made will be permanently lost. This issue is especially painful in the context of switching themes in the current customizer: if you modify the site’s title and static front page and then try switching to a different theme, that AYS dialog will appear and warn you that your changes will be lost (since switching a theme currently requires the customizer reload). Not a great user experience. (See #36485.)

Since none of the changes made in the current customizer are persistent in any way, this also has implications for how changes are previewed. When a full refresh or selective refresh is done in the preview, all of the changed settings have to be sent with each request so that the customized state is available to the preview filters to apply in the response, and as such each request has needed to use the POST method. What this in turn has meant is that links in the preview can’t work normally. When you click a link the URL gets sent via postMessage from the preview to the parent frame and where an Ajax POST request is then made to that URL and the response is then written into an about:blank iframe window via document.write(). This causes a couple issues: JavaScript running in the preview will return /wp-admin/customize.php when looking at window.location as opposed to the expected URL; this largely prevents using JavaScript for URL routing, and it causes problems with JS libraries like jQuery UI (see #23225, #30028). Additionally, any REST API calls to GET data will not include the customized state applied in the responses (without hacky workarounds with _method).

Fundamentals for Changesets

The core concept being introduced in Customize Changesets is that the customized state for a given live preview session should be persistent in the database. Each time the customizer is opened a random UUID is generated to serve as the identifier for the changes in that session: the changeset. When changes are made to settings the values will get sent in an Ajax request to be written into a customize_changeset custom post type whose post_name is the UUID for the customizer session. Once the changes have been written into the changeset post, then any request to WordPress (including to the REST API) can be made with a customize_changeset_uuid query param with the desired UUID and this will result in the customizer being bootstrapped and the changes from that changeset being applied for preview in the response. Changes are written into the changeset post at the AUTOSAVE_INTERVAL or whenever focus is removed from the controls window or at beforeunload.

The data from changesets is designed to be incorporated into the existing customizer API to maximize backwards compatibility. Namely, the way that the customizer obtains the customized state is by making calls to WP_Customize_Manager::unsanitized_post_values(). When there is a changeset associated with the given UUID, then the values from the changeset will serve as the base array that is returned by this method. If there is any $_POST['customized'] in the request, then it will be merged on top of this base array. Likewise, calls to $wp_customize->set_post_value() will also result in the supplied values being merged on top of underlying changeset data.

Since a UUID query param is all that is needed to apply the customized state, this means that the customizer iframe can now just use the frontend preview URL with UUID query param directly in its src attribute and load the iframe window naturally, as opposed to using about:blank with a document.write(). When there are pending changes not yet written into the changeset, then the iframe window is loaded by programmatically submitting a form with a POST method containing the pending customized data. Likewise, the UUID will also get added as a query parameter to customize.php itself via history.replaceState() which means you can freely reload the customizer and your changes will persist.

The customize_changeset post uses the post_content to store the changed values in a JSON-encoded object, with the setting IDs as keys mapping to objects that contain the value and other setting params like type.A submitted setting value will only be written into the changeset if it passes the validation constraints, if the setting is recognized, and if the user can do the capability associated with the setting. An example changeset:

    "blogname": {
        "value": "My Blog",
        "type": "option",
        "user_id": 1
    "twentysixteen::header_textcolor": {
        "value": "blank",
        "type": "theme_mod",
        "user_id": 2

You’ll note that the header_textcolor setting has a theme slug as a prefix. This is done so that changes to the theme mods won’t be carried over from theme to theme when switching between them in a customizer session. If you set the text color in Twenty Sixteen but then switch over to Twenty Seventeen to try changing colors there, you can then switch back to Twenty Sixteen in the same customizer session and the text color you set before the theme switch will be restored. Additionally, when activating a theme in the customizer and there are theme mods for other themes that do not get saved, these theme mods will get stashed in a customize_stashed_theme_mods option to then be restored the next time the theme is previewed in the customizer. Lastly, the user_id for the user who last modified a given setting is stored with the setting’s params in the changeset so that at the time of saving (publishing) the changes into the database the user can be switched-to so that filters (such as Kses) apply as expected.

The customize_changeset posts by default get created with the auto-draft status so that they will get automatically garbage-collected after a week of no modifications. When the Save & Publish button is pressed, a customize_save Ajax request will be sent with a status of publish for that post. When a customize_changeset post transitions to publish, the customizer will hook in to load the values from the changeset post into an active changeset and then iterate over each of the settings and save each one. Upon publishing, a changeset post will by default then get immediately trashed so that it will also get garbage-collected.

Since transitioning a changeset post to the publish status is what causes its setting values to be saved, this means that setting changes can be scheduled (see #28721). Since setting values are only written into the changeset if they pass validation and authorization checks, the values can be saved asynchronously without a user with the required capabilities being logged in. An update to the site title can be scheduled for publishing even though no user with edit_theme_options will be logged-in when WP Cron runs. Here’s how this can be done with WP-CLI:

wp post create \
    --post_type=customize_changeset \
    --post_name=$( uuidgen ) \
    --post_status=future \
    --post_date="2017-01-01 00:00:00" \
    --post_content='{"blogname":{"value":"Happy New Year!"}}'

There is no new UI for changesets being proposed for 4.7 (other than the addition of the changeset_uuid query param) and the removal of the theme switch AYS dialog. (Note that the AYS dialog remains for the customizer otherwise since there is no UI that lists changesets to allow the user to navigate back to that state.) Adding a UI for scheduling changesets to core can be explored in future releases.

Extension Opportunities for Plugins

There are also exciting new APIs that plugins will be able to leverage to create new UIs and extend functionality.

  • The show_ui flag on the custom post type can be turned on so that all changeset posts can be listed. When viewing the edit post screen for given changeset, a metabox can be added to show the contents of the changeset.
  • By adding adding support for revisions to the customize_changeset post type, published changesets will no longer be automatically trashed (and garbage-collected). With this there is automatic revisions and an audit trail for changes made in the customizer (see #31089).
  • A “Save Draft” button can be added next to “Save & Publish” that calls{status: 'draft'}). This will prevent the changeset from being an auto-draft and thus garbage-collected.
  • Users could collaborate together on a single changeset with setting-level concurrency locking.
  • Normally when updating a changeset post, a revision is prevented from being created even when they are enabled. However, when making an update to a changeset via where the status is supplied (such as draft) then a new revision will be made. These revisions can be browsed from the edit post screen. Since the JSON in the post_content is pretty-printed, the diffs are easy to read.
  • The publish_posts meta capability for customize_snapshots post type can be overridden to something other than customize, and for users without the new capability a “Submit” button can be added to the UI to submit a changeset for review via{status: 'pending'}).
  • Changesets can be scheduled for publishing from the customizer by hooking up a button to make a call like to{status: 'future', date: '2017-01-01 00:00:00'}).
  • The customize_changeset post type supports title even though it is not displayed anywhere by default. A plugin could add a “commit message” UI for changesets where there could be a title could be entered in an input field and sent via{status: 'draft', title: 'Add 2017 greeting'}).
  • The can_export flag can be turned on for the registered post type so that customize_changeset posts can be exported.

Several of these features already exist in the Customize Snapshots plugin among several others, but the plugin is currently using its own standalone snapshots API with a customize_snapshot post type rather than re-using what is being proposed in 4.7 for core, as the plugin is a prototype for changesets in core. The plugin will be able to be refactored after changesets are committed to core, allowing a lot of code to be removed from it. I’ve also put together a couple of these UI features in a little Gist plugin.

Selective Refresh and Seamless Refresh

In the original proposal for “transactions”, I said that selective refresh was prerequisite for getting transactions/changesets into core. The reason for this is that the initial patch got rid of the PreviewFrame construct in the customizer JS API since I thought it was no longer necessary. I thought that a refresh should just be invoked simply with location.reload() in the iframe. The problem with full page refreshes via location.reload() is that they are not seamless due to a “flash of reloading content”. However, when working on the latest patch I grew to appreciate the approach of loading a new iframe in the background and hot-swapping with the foreground once loaded. And anyway, I realized there was no need to remove the “seamless refresh” functionality. Creating a new iframe for each change to the previewed URL also prevents the browser history from unexpectedly being amended.

Aside: Now that the iframe is loaded with a regular URL supplied in its src attribute, you can now manually refresh the preview iframe just like you would refresh any other iframe. Just context click (right click) on the preview frame and select “Reload Frame” from the context window.

Frontend Preview

Again, now that the customizer preview is loaded into the iframe via a regular URL, you can grab the value of the iframe[src] (or iframe[data-src] in the case of loading a preview with changes not yet written into the changeset) and actually open it in a separate window altogether and not have the customizer controls pane present at all. When you look at the preview URL from the iframe you’ll see the normal frontend URL you’re previewing along with three additional query parameters such as:

  • customize_changeset_uuid=598cdda1-b785-431d-8c6d-6c421300ed9f
  • customize_theme=twentysixteen
  • customize_messenger_channel=preview-1

Only the first one, customize_changeset_uuid, is required to bootstrap the customizer and preview the state. The second one, customize_theme, must be supplied when previewing a theme switch. Otherwise, it can be omitted. And lastly, when the customize_messenger_channel param is present, the customizer preview will hide the admin bar and yield control of navigation to the parent frame: clicks on links and submitting forms will continue to preventDefault with the intended url destination being sent to the parent frame to populate wp.customize.previewer.previewUrl which then causes the iframe[src] to be set to that URL. Maintaining this method of navigation from the preview is important for compatibility with plugins that add custom params to the URL being previewed by intercepting updates to the previewUrl. When a changeset is published, a new UUID is sent in the response and it is sent to the preview in the saved message and it replaces the old UUID in the URL via history.replaceState(). Also note that calls to history.replaceState() and history.pushState() are wrapped so that the customize state query prams will be injected into whatever url is supplied. Whenever the URL is changed with JavaScript, the new URL will be sent to the parent frame along with the wp.customize.settings for the autofocused panels, sections, and controls. This allows for JS-routed apps to take control over contextual panels, sections, and controls.

When the customize_messenger_channel query param is absent, then links can be clicked and forms submitted without any preventDefault. The admin bar will be shown and the Customize link will include the changeset UUID param. The customizer preview JS will ensure that the customizer state query params above will persist by injecting them into all site links and by adding them as hidden inputs to all site forms. If a link or form points to somewhere outside of the site (and is thus not previewable) then the mouse will get a cursor:not-allowed and wp.a11y.speak() will explain that it is not previewable (see #31517). The customizer preview will keep sending keep-alive messages up to the controls pane parent window so that if a user does end up getting routed to an non-previewable URL, the customizer controls pane will know that the preview is no longer connected and so when a setting with postMessage transport is modified it will intelligently fallback to using refresh instead.

The way that REST API requests get the customized state included is via the jQuery.ajaxPrefilter(). If an Ajax request is made with a URL to somewhere on the site, then the customize state query params will be appended to the URL.

Since changesets can only be modified by an authorized user and since previewing is a read-only operation, the URL for the customize preview including the UUID can be shared with unauthenticated users to preview those changes. Since a UUID is random it serves not only as a unique identifier but it also effectively serves as a secret key so that such customizer previews cannot be guessed. Note that unauthenticated users would still be forbidden from accessing the preview URL if they supply a customize_theme that differs from the active theme, as they do not have the switch_themes capability.

Aside: Since the customizer preview can be loaded by itself in its own window, this opens the door to being able to use the customizer for frontend editing and bootstrapping the customizer while on the frontend.

Another aside: The customizer should be able to point to a headless frontend to preview, as long as the frontend looks for the customize_changeset_uuid query param and passed it along in any REST API calls, while also including the customize-preview.js file.

REST API Endpoints

The initial changesets patch does not include REST API endpoints for the 4.7 release. However, there should be a feature plugin developed that adds endpoints for listing all changesets, inspecting the contents of a changeset, and the other CRUD operations. With these endpoints in place, entirely new customizer interfaces can be developed that have nothing to do with customize.php at all. Namely, mobile apps could create changesets and include the associated UUID in REST API requests for various endpoints to preview changes to the app itself. Some initial read-only endpoints were added to the Customize Snapshots plugin.

Summary of API Changes


  • Added: The WP_Customize_Manager class’s constructor now takes an $args param for passing in the changeset UUID, previewed theme, and messenger channel. This $args param is intended to be used instead of global variables, although the globals will still be read as a fallback for backwards compatibility.
  • Changed: The query param for theme switches has been changed from theme to a prefixed customize_theme. This will reduce the chance of a conflict, although theme will still be read as a fallback.
  • Added: New custom post type: customize_changeset.
  • Changed: Modified semantics of WP_Customize_Manager::save() to handle updating a changeset in addition to the previous behavior of persisting setting values into the database.
  • Added: WP_Customize_Manager::changeset_uuid(), which returns the UUID for the current session.
  • Added: WP_Customize_Manager::changeset_post_id(), which returns the post ID for a given customize_changeset post if one yet exists for the current UUID.
  • Added: WP_Customize_Manager::changeset_data() which returns the array of data stored in the changeset.
  • Changed: WP_Customize_Manager::unsanitized_post_values() now takes parameters for whether to exclude changeset data or post input data. Also, post input data will be ignored by default if the current user cannot customize.
  • Added: customize_changeset_save filter to modify the data being persisted into the changeset.
  • Added: WP_Customize_Manager::publish_changeset_values() which is called when a customize_changeset post transitions to a publish status.
  • Added: Theme mods in a changeset which are not associated with the activated theme will get stored in a customize_stashed_theme_mods option so that they can be restored the next time the inactive theme is previewed.
  • Added: WP_Customize_Manager::is_cross_domain().
  • Added: WP_Customize_Manager::get_allowed_urls().
  • Added: wp_generate_uuid4(), a new global function for generating random UUIDs.
  • Changed: Settings are registered for nav menus and widgets even if the user cannot edit_theme_options so that a changeset containing values for them can be previewed.
  • Deprecated: WP_Customzie_Manager::wp_redirect_status().
  • Deprecated: WP_Customize_Manager::wp_die_handler().
  • Deprecated: WP_Customize_Manager::remove_preview_signature().


  • Added: wp.customize.state('previewerAlive') state containing a boolean indicating whether the preview is sending keep-alive messages.
  • Added: wp.customize.state('saving') state containing a boolean indicating whether is currently making a request.
  • Added: wp.customize.state('changesetStatus') state containing the current post status for the customize_changeset post.
  • Added: wp.customize.requestChangesetUpdate() method which allows a plugin to programmatically push changes into a changeset. This allows additional params to be attached to a given setting in a changeset aside from value, and it also allows settings to be removed from a changeset altogether by sending null as the setting params object.
  • Added: changeset-save event is triggered on wp.customize with the pending changes object being sent to to the server.
  • Added: changeset-saved event is triggered on wp.customize when a changeset update request completes successfully, with a changeset-error event triggered on a failure.
  • Added: changeset-saved message is sent to the preview once a changeset has been saved so that JS can safely make make REST API request with the customized state being available.
  • Added: The millisecond values used in debounce and setInterval calls are now stored in wp.customize.settings.timeouts rather than being hard-coded in the JS.
  • Changed: Seamless refreshes will wait for any pending changeset updates to complete before initiating.
  • Changed: The beforeunload event handler for the AYS dialog now has a customize-confirm namespace.
  • Added: Settings for the changeset are exposed under wp.customize.settings.changeset, including the current UUID and the post status.
  • Moved: wp.customize.utils has been moved from customize-controls to customize-base.
  • Added: wp.customize.utils.parseQueryString() to parse a query string into an object of query params.
  • Fixed: Prevent modified nav menus from initiating selective refresh with each page load when customizer is loaded via HTTPS.
  • Fixed: Links in the preview that are just internal jump links no longer have preventDefault called. See #34142.
  • Added: wp.customize.isLinkPreviewable() to customize-preview which returns whether a given link can be previewed.


The work on transactions/snapshots/changesets has been going on for almost 2 years now. I want to thank Derek Herman (@valendesigns) for a lot of his work on the Customize Snapshots plugin that took the key concepts from the initial transactions proposal and started to combine them with a lot of compelling user-facing features.

I hope that changesets will make the Customize API all the more powerful to build compelling cutting-edge applications in WordPress.

#4-7, #customize, #dev-notes

Customize Changesets (formerly Transactions) Merge Proposal

This is a merge proposal and overview of Customize Changesets (#30937), a project formerly known and proposed as Customizer Transactions back in January 2015. The customizer is WordPress’s framework for doing live previews of any change on your site. One of the biggest problems the customizer faces right now is that changes are ephemeral. If you navigate away, you lose what you are working on. Additionally, you can not share proposed changes with others, nor can you take the changes you are working on and save to continue working later.

Imagine a WordPress user named Tina. Tina is building a website for her daughter’s band. The band has been getting more and more popular, and Tina wants to experiment with some new widgets in the sidebar but also wants to be able to share the proposed solutions with her husband Matthew. Right now, Tina and Matthew would need to be in the same room to collaborate. Or Tina would need to take screenshots, but that kind of defeats the purpose of live preview. If only there was a way to make customizer changes persistent without publishing them.

Introducing Customize Changesets

Customize changesets make changes in the customizer persistent, like autosave drafts. Users can make changes to one theme and switch to another in the customizer without losing the changes upon switching. A customizer session can be bookmarked to come back to later or this URL can be shared with someone else to review and make additional changes (the URLs expire after a week without changes). The new APIs make possible many new user-facing features in future releases and feature plugins, including saving long-lived drafts, submitting changesets as pending for review, scheduling changes, seeing the previewed state on the frontend without being in an iframe, sharing preview URLs with others who do not have customizer access, and others.

Customize changesets allow each change you make in the customizer for a given live preview session to be persistent in the database. A unique identifier (a UUID like f67efbbf-c663-4271-ab1c-95ce1d447979) for each live preview session is generated and as soon as a change is made, the change setting value is sent in an Ajax request to be written into a custom post type whose post_name is the UUID for the customizer session. Once the changes have been written into the changeset post, then any request to WordPress (including to the REST API) can be made with a customize_changeset_uuid query param with the desired UUID and this will result in the customizer being bootstrapped and the changes from that changeset being applied for preview in the response. The unique UUID means that customizer sessions can be sent to other users and also that they can be used as query parameters on the front end.

Design and Technical Decisions

For the initial core merge, no UI changes are being proposed. The feature will be only be exposed as the new query parameter on the URL. Adding a UI to this feature will happen in a future release. As such, the proposal for customize changesets is similar to the proposal for including the REST API infrastructure: it provides a foundation for new core features in future releases and a platform for plugins to add new features. Nevertheless, while the customize changesets patch doesn’t introduce any new features it does fix several long-standing issues related to incompatibilities between JavaScript running on the site’s frontend when previewed in the customizer. Under the hood, the customize changesets patch touches on many of the lowest level pieces of the customizer. Please check out the Customize Changesets Technical Design Decisions to see what is happening under the hood.


Please test! If you use any plugins that extend the customizer, please ensure that there aren’t any regressions. The patch is intended to be fully backwards compatible and users shouldn’t notice any difference in normal use. Two things to look for when testing is as soon as you make a change, you should see a customize_uuid query param added to the URL. You should be able to reload and find your changes persist (note the AYS dialog is retained because there is no UI yet for listing changesets). Also, when you navigate around the preview it should feel much more natural like normal browsing as opposed to having a fade effect. Otherwise, previewing settings that require refresh should still work as normal, as will settings that preview with JavaScript and selective refresh.

The patch is in a GitHub pull request and you can apply the patch via:

grunt patch:

If you’re using the Git repo from then you can check out the branch directly via:

git remote add -f xwp && git checkout xwp/trac-30937

I’d appreciate code review feedback directly on the pull request. For any revisions to the patch, please open a pull request to that trac-30937 branch if possible.

The Future

In future releases we can explore new UIs to take advantage of the new capabilities that changesets provide. New UIs can provide a way to schedule changes, the ability to undo the last change, show an audit log (revision history) for changes, collaborative editing of a customizer changeset, and so on. Future feature projects will explore many of these and feature plugins will start to prototype them.

Thanks to @jorbin who contributed to this proposal post.

#4-7, #customize, #feature-projects, #proposal

Feature Proposal: Better theme customizations via custom CSS with live previews

When people ask “why WordPress?”, some of the most common answers center around flexibility for users of all kinds, whether they’re building their sites primarily through code or UI. Let’s take the story of a user who does a little of both – we’ll call her Becky.

Becky is a pretty savvy user. She knows that you’re supposed to make child themes instead of hacking on a theme directly, because updates can wipe out your changes. She found that out the hard way when she first started using WordPress – she hardly knew what CSS or PHP were, but she knew there was a theme editor in the admin and that she could make tweaks to colors or remove the author byline pretty easily without having to figure out this FTP stuff. Later on, most colors could be changed with the customizer so having a child theme just to remove an author byline seemed like overkill, but it was certainly better than having it reappear every time her site updated, especially with auto updates turned on.

After a couple years with the same theme on her personal site, Becky felt it was time to change things up. She was pleasantly surprised to find some new features that made getting a theme set up a lot easier, especially when live previewing them. Still, though, that pesky author byline remained, and since her last child theme copied a template to get rid of the byline, she would have to set up a whole new one to do it again. Then Becky found an “Edit CSS” option and realized she could hide things using CSS without having to go through the entire child theme process. Now, it turns out that those CSS tweaks didn’t come with live previewing, and that functionality was provided by a certain plugin, but Becky got what she needed to get done a lot faster than she would have otherwise, and ended up with the site she wanted.

This isn’t one specific story, but it is a combination of user stories many have heard, witnessed, or even personally experienced. You could replace Becky with @helen and it would be 100% accurate. The theme editor is a dangerous but useful entry point to more deeply customizing your site – rather than outright removing it and cutting off that introduction not just to WordPress code but to the concept of web development at large, why not provide a far safer and more user-friendly alternative? This post will explain why custom CSS with live previewing is valuable for WordPress and propose an implementation for inclusion in 4.7.

Proposed solution: Custom CSS with live preview

When bridging the gap between advanced user and beginning developer, desired changes are typically visual tweaks, such as changing a font size or hiding something, that are theme-specific. These sorts of changes should not require that users take risks editing live files that might white screen their sites or jump immediately into developer-facing tasks such as using FTP. Therefore, the scope of this feature has been defined as a custom CSS editor that leverages the customizer for a user-friendly live preview experience. This live preview allows for users to try various tweaks to a theme before saving and setting their changes live.

There are hundreds of thousands (if not millions) of users making use of custom CSS plugins or other themes/plugins that have custom CSS capabilities, and the frequent suggestion of CSS fixes in support forums justify a core need for this functionality. When plugins and themes interact in unexpected ways, CSS snippets are often an efficient solution to fixing the particular problem on particular sites.

The CSS editor takes inspiration from the many plugins offering similar solutions, but with an updated approach that offers instant live previewing in the customizer. The proposal for 4.7 looks like this:


Notably, previewing CSS in the customizer allows the site to be navigated and previewed on different sized devices by leveraging existing core features, allowing users to visualize the impact of their changes across their site. Error messages are shown for common syntax mistakes to help users ensure that their CSS is formatted properly before saving.

In future releases, the interface can be iterated on to further improve usability. The long-term design vision provides functionality such as revisions, syntax highlighting, and in-preview selector helpers, and can be implemented iteratively over time (click through for the full version):


CSS would be stored in a custom post type (without admin UI), with a post stored for each theme. The editor would be used to supplement and override theme styles rather than editing them directly, as users have long been advised that directly editing files may lead to lost changes during updates. Instead, custom CSS safely stays available through updating and switching themes, starting fresh for each new theme. Projects such as customize changesets (#30937) and revisions for customizer settings (#31089) would bring future enhancements to this feature and further leverage the opportunities that come with storing the data in post objects.

This is proposed as core functionality rather than remaining as plugin territory because it is designed as the first step toward a next generation of the existing theme editor in core, with a more refined feature set and safer, more user-oriented focus. The theme editor is not proposed to be removed at this time, though with the introduction of this feature it likely makes sense to introduce more friction before accessing the editor (#31779).


To improve the user experience further, it is critical that a link to documentation and resources for learning CSS be included with useful help text. This could initially be the “CSS” codex page but would ideally live on a user or developer handbook of some sort eventually (perhaps the theme developer handbook?). This help text must be much more succinct than the help tab on the existing theme editor, conveying what CSS is, where to learn about specific rules, and explaining that it’s specific to each theme in only a few lines.

Help is needed to create a resource for using custom CSS in WordPress, and locate it on There are some related resources on make/training and has a good introductory page that they may be willing to contribute. Translated versions will eventually be needed as well. If anyone is interested in improving this aspect of the feature, which will presumably live on, please comment on this post.

Security, Capabilities, and Multisite

While the proposal includes basic validation, it is not possible to fully sanitize CSS. For this reason, a new meta capability will be introduced for managing css, unfiltered_css. By default, this is mapped to the unfiltered_html capability.

Site administrators on multisite networks do not have the unfiltered_html capability by default. A plugin that remaps unfiltered_css to a different capability can be created to provide this access on multisite, where custom CSS is especially useful given the need to restrict the number of themes and child themes in the network. This is an area of potential evolution over time.

Related Customize API Improvements

There are a couple of customizer API improvements introduced as part of the implementation of custom CSS in the customizer. A new “Code Editor” customizer control (WP_Customize_Code_Editor_Control) is used for the CSS editor and can also be utilized in plugins and elsewhere in the future. It currently handles line numbers and basic code styling, and will eventually add enhancements such as syntax highlighting.

Additionally, the WP_Customize_Section class has a new “description_hidden” parameter, which locates the section description in the section header behind the help icon toggle (“?”), functioning in the same manner as the customizer panel descriptions.


@johnregan3 is leading development of this project, based on initial work by myself (@celloexpressions). @folletto is leading design efforts, with a focus on the long-term growth of the feature for maximum usability.

The implementation takes inspiration from many of the numerous plugins and services that implement custom CSS, specifically including:

  • Simple Custom CSS (@johnregan3)
  • Modular Custom CSS (@celloexpressions)
  • Custom CSS in the design upgrade (Automattic)
  • Jetpack (Automattic)

Testing, Feedback, and Next Steps

Your help is needed in giving feedback on this proposal and testing the feature! To test, please apply the patch either via Trac or the PR (helpful reminder: grunt patch handles both) and try some custom CSS in the customizer using various themes.

Pending approval of this proposal, the next steps will be to finalize and commit the patch on #35395. Code review is ongoing in the GitHub PR linked on the ticket. Feedback on the feature in general and the specific implementation is encouraged via the comments on this post, with any more technical implementation discussion happening on the Trac ticket or GitHub PR.

#4-7, #css, #customize, #proposal, #themes

Customize Update 2016-10-09

This is the weekly update post for the customize component. It includes a summary of this week’s meeting, recent commits, and next week’s meeting agenda.

4.7 Feature Proposals

The feature proposal for themes was published last week; the deadline for feedback/review is this Wednesday, October 12th:

Look for feature proposals for custom CSS and customize changesets (formerly “transactions”) this week.

Weekly Customize Meeting Summary

On Monday we held our weekly 4.7 customize component meeting in #core-customize on Slack [logs]. Participants: @celloexpressions, @braad, @aaroncampbell, @abdulwahab, @westonruter, @karmatosed, @folletto. This summary also contains a few notes on action since the meeting.

4.7 Projects

  • Customize changesets (transactions) – #30937 – @westonruter
  • Create pages within live preview during site setup – #37914#37915, #38002#38164 – @celloexpressions
    • @boonebgorges published a proposal for term statuses; however, this will likely happen in a future release, along with #37914 and #37915 (pending a final punt decision from Boone).
    • The design team decided to punt providing a path to edit newly-created pages, #38002.
    • The remaining item for 4.7 is #38164 – extending this functionality to creating new pages for static front pages. A design approach was agreed upon in the design chat, and the code can be lifted from the Customize Posts plugin to build this out.
  • A new experience for themes in the customizer – #37661 –@celloexpressions
    • The feature proposal was published, and there are currently no comments. See also the make/flow post and make/design post, which have some feedback.
    • Additional design issues and usability adjustments have been completed and frozen pending a couple of minor tweaks and major bugs.
    • The deadline for feedback from other teams is October 12th, leaving a week for final code review and commit. We’re awaiting feedback from security, docs, polyglots, flow (if any beyond what was provided on the post), and most notably accessibility with the latest patch.
  • Code-editing gateways, via CSS – #35395 – @johnregan3
    • @johnregan3 has prepared a new patch to incorporate the design by @folletto as well as the dev-feedback from last week’s chat. A few minor adjustments are needed, then this will be ready for final review and commit.
    • The feature proposal post is in review and should be published within the next day or two.
  • Customizer browser history#28536 – @westonruter
    • Parts of this are blocked by changesets, which is higher priority for now. Changesets also offer improvements here natively.
    • If it’s okay with @helen, we’s like to pursue this in the week before beta, after the major feature deadline, since it’s a smaller feature.
  • Improving sliding panels UI – #34391, #34343, #29158 – @delawski
    • An initial commit is in trunk, dev note published, and only a few minor bugs have come up so far.
    • @delawski shared a patch for #34343 that needs testing. We discussed pursuing #35186 as an alternative in the future, but that it would cause compatibility issues since the default section markup would (probably) change.
    • @folletto propsed a design for the remaining elements of #29158, which @celloexpressions will create a new patch for next week.
  • Twenty Seventeen – a few related projects are targeting 4.7 with significant customize changes
    • #27403: Improve UI for linking areas of Customizer preview to corresponding controls (desktop and mobile)
    • #37974: Add multi-panel feature to pages through add_theme_support
    • #38172: Enable Video Headers in Custom Headers

Additional Tickets Needing Attention

  • Customizer notifications – #35210 – needs UX feedback and a patch
    • @delawski will try to work on this in time for 4.7.
    • This ticket is holding up some of the other tickets on the 4.7 milestone, such as #22037 and #29932, as well as aspects of changesets (transactions).

Ticket Scrub

We ensured that all 4.7 customize non-bugs have owners or were punted. See these remaining open tickets by owner here.

Recent Customize Commits

Here are the customize-related commits for the past week:

  • [38709] Customize: Improve the widgets and menu items search
  • [38742] Customize: Add workaround for Safari bug causing preview frame to be unscrollable via mousewheel after a refresh.

Big thanks to those who contributed to patches committed this week: @ryankienstra, @melchoyce, @afercia, @westonruter, @tristangemus.

We’re always looking for more contributors; check out the open customize tickets and swing by #core-customize in Slack to get involved. Fun fact: we’re 5 commits away from the 1000th commit that references customize.

Agenda for 2016-10-10 Meeting

Our next regularly-scheduled meeting is Monday, October 10, 2016, 17:00 UTC. Agenda:

4.7 Projects

Additional Tickets Needing Attention

  • #37335: Standardized API for themes to add info/links to the customizer
    • If the theme review team wants this solution in core for 4.7, we need a decision on their end in the next week. Otherwise, the ticket can be closed as maybelater. Their current recommendation is to use a custom section (that cannot expand). cc @greenshady

Ticket Scrub

We’ll see you next week!

#4-7, #customize