Custom CSS boxes in themes

Recently, we’ve had a big discussion over whether themes could allow users to enter CSS into arbitrary text boxes (custom CSS). There was some confusion over what was allowed and, if allowed, what would be the proper method of handling it. After much debate, we’ve come to the following conclusion:

  • It’s preferred that theme authors leave this feature to plugins.
  • However, it is allowed if handled safely.
  • The edit_theme_options capability is required (like all theme options).
  • The wp_filter_nohtml_kses(), wp_strip_all_tags(), or equivalent function must be used to sanitize the data before it’s saved into the database.

Why leave to plugins?

There are cases where it makes sense for custom CSS to be applied globally and to be applied on a per-theme basis. Generally-speaking, that’s a decision that’s best left up to the user, which they can decide based on the various plugins available for handling this.

Validating CSS. Our guidelines are not going to require that theme authors validate the CSS. Our guidelines are only concerned with making sure the data is safe. That means that users could add in arbitrary code that’s not valid CSS. This means it’d be on you, the theme author, to handle the inevitable support requests.

You are, of course, welcome to use a library such as CSSTidy to tidy the CSS up, but that’s a large library and probably best left in plugins. Just realize that the more complex your theme code is the harder it is to review and the longer it’ll take us.

Edit: Another good reason to leave this out of themes is multisite. There are many instances where a site owner wouldn’t want everyone with a blog on their site to have this sort of CSS editing capability. Adding this option will most likely mean that a good number of multisite installs will never use your theme.

Edit #2: To add to the multisite scenario, it might be worth considering using the edit_themes capability instead of edit_theme_options.

How to do it correctly?

I’m going to share an example of handling this correctly based on the comments from our recent discussion. This is only going to focus on the Customizer because it’s our recommended way to handle theme options.

The following is example code to start from:

<?php 

add_action( 'customize_register', 'prefix_theme_customize_register' );

function prefix_theme_customize_register( $wp_customize ) {

	$wp_customize->add_section(
		'prefix_section_1',
		array(
			'title'       => __( 'Section 1', 'prefix' ),
			'description' => __( 'Section 1 description.', 'prefix' ),
			'priority'    => 30
		)
	);

	$wp_customize->add_setting(
		'example_css',
		array(
			'default'              => '',
			'capability'           => 'edit_theme_options',
			'sanitize_callback'    => 'wp_filter_nohtml_kses',
			'sanitize_js_callback' => 'wp_filter_nohtml_kses'
		)
	);

	$wp_customize->add_control(
		'prefix_example_css_control',
		array(
			'label'    => __( 'Custom CSS', 'prefix' ),
			'section'  => 'prefix_section_1',
			'settings' => 'example_css',
			'type'     => 'textarea'
		)
	);
}