Drupal

Drupal 7: Taking control of CSS and JS aggregation

Drupal 7 includes a big re-factor of the way CSS and Javascript are aggregated. What does this mean for your sites? In short: You will see a greater number of files compared to D6 - this is normal and not usually cause for alarm. Surprisingly, more files is sometimes better. To ensure efficient aggregation, the most important thing developers can do is choose the parameters to drupal_add_css and drupal_add_js carefully. And if you encounter contrib modules that are using the wrong parameters, please file patches! This was a somewhat controversial change, and understanding the new strategy (and how to override it) requires going a bit deeper.


Filed under:

Drupal 7 includes a big re-factor of the way CSS and Javascript are aggregated. What does this mean for your sites? In short:

  1. You will see a greater number of files compared to D6 - this is normal and not usually cause for alarm. Surprisingly, more files is sometimes better.
  2. To ensure efficient aggregation, the most important thing developers can do is choose the parameters to drupal_add_css and drupal_add_js carefully. And if you encounter contrib modules that are using the wrong parameters, please file patches!

This was a somewhat controversial change, and understanding the new strategy (and how to override it) requires going a bit deeper.

Previously, all files were merged together into one giant file (at least within the same scope or media type). This strategy worked well on simple sites with few or no contrib modules. Unfortunately, many contrib modules in D6 incorrectly add conditional* CSS and Javascript files – resulting in changing aggregates as you browse from page to page. If even a few lines of code differ, the entire aggregate (including large libraries like jQuery and jQuery UI) must be re-downloaded, consuming extra bandwidth.

* "Conditional" files in this context refers to files that are added to some pages but not others - an example would be calling drupal_add_css() in a theme function. In D6 such calls should have the $preprocess value set to FALSE to prevent splitting the aggregate.

The D7 way

D7 uses a new strategy to address this - aggregates are split into three groups:
CSS_SYSTEM, CSS_DEFAULT, and CSS_THEME for styles, and JS_LIBRARY, JS_DEFAULT, and JS_THEME for Javascript. Each group is further subdivided into files that load on every page, and files that load conditionally based on the 'every_page' option. Note this option has some potential for confusion - it doesn't cause the file to be loaded on every page - it's merely a "hint" to the core system to place it in that group.

Ultimately, the intent of these divisions is to group files into functional groups that are smaller and less likely to be split by an errant conditional style or script. We get separate files for core libraries, the theme, and page-specific files. Looking at the results, a stock Drupal 7 outputs eight stylesheets (including browser styles), while D6 only has three in the default install. Is this optimal? The answer will be different for each site.

Custom tuning

D7 adds two new hooks that allow you to alter styles and scripts before they are rendered:
hook_css_alter()
hook_js_alter()

At least one contrib project taking advantage of this functionality has already been created: Core Library. I haven't had a chance to test it yet, but the module's intent is to optimize the groupings by "learning" as visitors browse your site.

One thing to note if you decide to undertake customizing these groupings: In Drupal 7.0, the insertion order of styles and scripts is always preserved, even at the expense of sometimes splitting a group. A patch has been submitted that proposes relaxing this a bit.

Back to D6

If the previous Drupal 6 strategy is a better match for your site, you can drupal_alter your way back with this code in a custom module:

/**
 * Implements hook_js_alter().
 */
function mymodule_js_alter(&$javascript) {
  uasort($javascript, 'drupal_sort_css_js');
  $i = 0;
  foreach ($javascript as $name => $script) {
    $javascript[$name]['weight'] = $i++;
    $javascript[$name]['group'] = JS_DEFAULT;
    $javascript[$name]['every_page'] = FALSE;
  }
}
 
/**
 * Implements hook_css_alter().
 */
function mymodule_css_alter(&$css) {
  uasort($css, 'drupal_sort_css_js');
  $i = 0;
  foreach ($css as $name => $style) {
    $css[$name]['weight'] = $i++;
    $css[$name]['group'] = CSS_DEFAULT;
    $css[$name]['every_page'] = FALSE;
  }
}

Similar posts

Get notified on new marketing insights

Be the first to know about new B2B SaaS Marketing insights to build or refine your marketing function with the tools and knowledge of today’s industry.