WordPress: A different approach in managing stylesheets for different pages

Managing custom scripts and stylesheets for different pages can be a pain in the butt since you have to keep editing the functions.php file in order to enqueue and dequeue several stylesheets for a particular page according to your needs. This have several drawbacks:

  1. Editing functions.php file – This is not really a big deal if you have a definite number of pages with custom templates that uses custom stylesheets. You can just work with functions.php when you are developing the theme. But what if the number of pages that uses custom template & CSS grows, like mine? I have to edit functions.php every time I have a new project page and that isn’t cool. I should be able to do this without editing functions.php.
  2. Changing themes – This is another problem. If I change my theme, I need to remember the previous modification that I did for my previous functions.php. This isn’t efficient and will cause a lot of mistakes. I know. I just change my theme and was surprise when several of my pages broke. Then, I remembered the functions.php file. D’oh!

Today, I decided to share a different way on how to manage different stylesheets and scripts for a particular page. It is still in beta stage; I am working on making it better. I just want to share the general idea here.

The Common Way

First things first, lets revisit the common way of enqueuing and dequeuing scripts/styles for a particular page in functions.php. This involves the old if statement:

function change_style() {
   if ( is_page( 'my-project' ) ) {
     wp_dequeue_style( 'main-stylesheet' );
     wp_enqueue_style( 'project-stylesheet' );
   } 
}

add_action( 'wp_enqueue_scripts', 'change_style', 999 );

Passing in the page’s slug in the conditional statement will dequeue the main stylesheet and enqueue a different one instead. It is rather simple but it will clog your functions.php file if you have several pages that are using custom CSS and scripts.

Plugins & Custom Fields Are Your Friends

In order to avoid modifying functions.php whenever you change your theme, using plugins are a good way to go. Wouldn’t it be awesome if we have a meta box that lists all stylesheet handler and allows you to include or exclude them according to pages? That’s where custom fields come in. What I have in mind is something like the screenshot below:

shot
The sexy meta box that makes my life easier!

Assumptions

Since this plugin is still in it’s beta stage, I am making some assumptions here. First, it assumes that you have registered all the styles in functions.php as shown below:

function script_and_style() {
    wp_register_style( 'bones-stylesheet', get_stylesheet_directory_uri() . '/library/css/style.css', array(), '', 'all' );
    wp_register_style( 'bones-ie-only', get_stylesheet_directory_uri() . '/library/css/ie.css', array(), '' );
    wp_register_style( 'bones-prism-stylesheet', get_stylesheet_directory_uri() . '/library/css/prism.css', array(), '' );
    wp_register_style( 'bones-anchorific-stylesheet', get_stylesheet_directory_uri() . '/library/cssmin/anchorific.css', array(), '', 'all' );
}

add_action( 'wp_enqueue_scripts', 'script_and_style', 999 );

Another assumption is that these are the final list of your stylesheet and you’re are not planning to add any stylesheets in the future. In the real world, this is not ideal but since I am demonstrating a general idea instead of writing a complete plugin, it is a safe assumption for now.

Meta Box Creation

This is a general structure of our plugin. Our constructor contains an array of stylesheet handlers that we have enqueued through our functions.php. We will also register a new meta box.

class WpCssSep {
   public function __construct() {
      $this->styles = [ 'bones-stylesheet', 'bones-ie', 'bones-prism-stylesheet', 'bones-anchorific-stylesheet' ];
      add_action( 'add_meta_boxes', array( &$this, 'wpcs_add_meta_box' ) );
   }

   public function wpcs_add_meta_box() {
       add_meta_box( 'wp_css_separator', __( 'Manage Stylesheets', 'wpcs_lang' ), array( &$this, 'wpcs_meta_box' ), 'page', 'side', 'high' );
   }
}

$manage = new WpCssSep();

Next, we will work on the HTML for the Meta box code. I separated the HTML in another method for clear organization. Do not forget to defined a nonce as it is important for security.

We will also get the custom field if it exists.

public function wpcs_meta_box( $page ) {
   wp_nonce_field( plugin_basename( __FILE__ ), 'wpcs_nonce' );
   // get the custom field if it exists
   $status = get_post_meta( $page->ID, '_wpcs_stylesheet_separator', true );

   echo "<p>These are your stylesheet handlers. You can include or exclude styles in this page:</p>";

   foreach ( $this->styles as $style ) {
      $stat = ( isset( $status[ $style ] ) ) ? $status : null;
      $this->wpcs_template( $style, $stat );
   }
}

public function wpcs_template( $s, $v ) {
    $html = '<div style="margin-bottom: 3px"><label>' . $s . '</div></label>';
    $html .= '<div style="margin-bottom: 10px"><label>';
    $html .= '<input type="radio" name="' . $s . '" id="' . $s . "-include" . '" value="1"'. checked( $v[ $s ], 1, false ) .'>';
    $html .= "Include";
    $html .= '</label>&nbsp;<label>';
    $html .= '&nbsp;<label><input type="radio" name="' . $s . '" id="' . $s . "-exclude" . '" value="2"' . checked( $v[ $s ], 2, false ) . '>';
    $html .= "Exclude";
    $html .= "</label></div>";

    echo $html;
}

This allows us to have a meta box like the screenshot shown above. Next we will need to save the information stored in the meta box and also enqueue/dequeue stylesheet based on this information.

Saving Meta Box and Process Stylesheet

We will be adding two new lines in our constructor function. It is needed in order to save the information and also enqueue/dequeue stylesheet based on that particular information.

public function __constructor() {
    $this->styles = [ 'bones-stylesheet', 'bones-ie', 'bones-prism-stylesheet', 'bones-anchorific-stylesheet' ];
    add_action( 'add_meta_boxes', array( &$this, 'wpcs_add_meta_box' ) );
    // save the meta box when the page is saved
    add_action( 'save_post', array( &$this, 'wpcs_save_meta_box' ) );
    // enqueue/dequeue stylesheet based on meta box information
    add_action( 'wp_enqueue_scripts', array( &$this, 'wpcs_process_style_queues' ), 1000 );
}

The save meta box action is shown below:

public function wpcs_save_meta_box( $page_id ) {
   if ( ! wp_verify_nonce( $_POST[ 'wpcs_nonce' ], plugin_basename( __FILE__ ) ) ) {
        return $page_id;
   }
   // Ignore everything when autosaving
   if ( defined( 'DOING_AUTOSAVE' ) && DOING_AUTOSAVE ) 
        return $page_id;
   
   // Check the user's permissions.
   if ( 'page' == $_POST[ 'post_type' ] ) {
        if ( ! current_user_can( 'edit_page', $page_id ) )
             return $page_id;
   } else {
        if ( ! current_user_can( 'edit_post', $page_id ) )
             return $page_id;
   }

   $clean = array();
   // validate input
   foreach ( $this->styles as $style ) {
        if ( isset( $_POST[ $style ] ) && ( (int) $_POST[ $style ] == 1 || (int) $_POST[ $style ] == 2 ) ) {
              $clean[ $style ] = (int) $_POST[ $style ];
        } else {
              continue;
        }
   }
   // create and save custom field
   update_post_meta( $page_id, '_wpcs_stylesheet_separator', $clean );
}

And finally, the action to enqueue and dequeue scripts based on the information saved through the meta box.

public function wpcs_process_style_queues() {
   global $post;
   // get the custom field based on the current page that is being viewed
   $statuses = get_post_meta( $post->ID, '_wpcs_stylesheet_separator', true );
   
   // traverse through the custom field and enqueue/dequeue style based on the value
   if ( is_array( $statuses ) ) {
       foreach ( $statuses as $key => $value ) {
          if ( $value == 1 ) {
               wp_enqueue_style( $key );
          } elseif ( $value == 2 ) {
               wp_dequeue_style( $key );
          }
       }
   }
}

And there you have it!

Drawbacks to Keep in Mind

One drawback is that stylesheet handlers need to be inserted manually; I don’t think so there are ways to insert the handlers automatically as these handlers are not stored in the database.

Another drawback is that it will also create problems when you change to a new theme since you may not be using the same CSS handler name. A bit of manual work is needed when it comes to this. One possible way to minimize this particular drawback is to remind the users to change their CSS handler name in the settings page when they switch to a new theme (or add new handlers).

Final Thoughts

Of course, there is a lot of improvement that needs to be think about when it comes to this plugin. I haven’t upload it to the plugin repository yet as there are some features I am planning to add. Some of the features are:

  1. Allows you to insert all the stylesheet handlers’ name through a settings page instead of storing it as a static array
  2. Support enqueue and dequeue for scripts as well
  3. Come up with a not-so-stupid name for the plugin

Keep in mind that it is fine to leave the values empty; it is not a problem if you didn’t select ‘include’ or ‘exclude’ for a particular stylesheet handler. The plugin does nothing when there are no values assign to a particular stylesheet handler.

So far, this is what I have in mind. I would love to find possible ways to minimize the drawbacks mentioned above. This is just a general idea. I would try new features myself before releasing it to the wild. Will update more on it’s development process. If you want to download the plugin file, download it here.

  • Joachim Richter

    Hi,
    I love your idea, any changes or news on it ? Will the plugin work with the latest wordpress 4.2.4. ?

  • Joachim Richter

    I want to use this for my website which is using woocommerce, that means it has to work with custom post types, wdyt ? You wrote somthing about nonce, did you include that in the plugin ?

    • Nah, I shelved the idea since I no longer need something like this.