Drupal Articles » Generate unique CSS classes for the body tag of every page


By David - Posted on 16 March 2009

This info is from my post at http://drupal.org/node/360484#comment-1206770
I've moved it here to work on cleaning it up and preparing it to be added to the Drupal handbook.

I always recommend this method over page-something.tpl.php templates if possible. I suggest only using different page templates if you absolutely must have completely different HTML and can't accomplish it with CSS). Having multiple page templates can get difficult to maintain if you make changes (if they're all basically the same page, you have to apply the same changes to all of them every time you change anything).

The best way is to cause Drupal to generate unique CSS classes for <body> based on the path of the page. You can then simply add a CSS rule like body.page-1 .whatever and body.page-2 .whatever

The code to do this comes from Zen theme (it's also common in many other themes). Since a few slight modifications are needed to convert Zen's code for general use, below is the modified code, which you can simply copy and paste into your own theme's files.

  1. Locate the <body> tag in your page.tpl.php template, and replace it with <body class="<?php print $body_classes; ?>">
  2. Open (or create if you don't have one) your template.php file. Use the "Find" feature of your code editor to search for "preprocess_page". If you find anything, then you will need to merge the code rather than just paste in the whole thing (more on that later). If you find nothing, then you can simply copy and paste this code into your template.php:

    function phptemplate_preprocess_page(&$vars, $hook) {
      // Classes for body element. Allows advanced theming based on context
      // (home page, node of certain type, etc.)
      $body_classes = array($vars['body_classes']);
      if (!$vars['is_front']) {
        // Add unique classes for each page and website section
        $path = drupal_get_path_alias($_GET['q']);
        list($section, ) = explode('/', $path, 2);
        $body_classes[] = phptemplate_id_safe('page-' . $path);
        $body_classes[] = phptemplate_id_safe('section-' . $section);
        if (arg(0) == 'node') {
          if (arg(1) == 'add') {
            if ($section == 'node') {
              array_pop($body_classes); // Remove 'section-node'
            }
            $body_classes[] = 'section-node-add'; // Add 'section-node-add'
          }
          elseif (is_numeric(arg(1)) && (arg(2) == 'edit' || arg(2) == 'delete')) {
            if ($section == 'node') {
              array_pop($body_classes); // Remove 'section-node'
            }
            $body_classes[] = 'section-node-' . arg(2); // Add 'section-node-edit' or 'section-node-delete'
          }
        }
      }
      $vars['body_classes'] = implode(' ', $body_classes); // Concatenate with spaces
    }


    /**
    * Converts a string to a suitable html ID attribute.
    *
    * http://www.w3.org/TR/html4/struct/global.html#h-7.5.2 specifies what makes a
    * valid ID attribute in HTML. This function:
    *
    * - Ensure an ID starts with an alpha character by optionally adding an 'id'.
    * - Replaces any character except A-Z, numbers, and underscores with dashes.
    * - Converts entire string to lowercase.
    *
    * @param $string
    *   The string
    * @return
    *   The converted string
    */
    function phptemplate_id_safe($string) {
      // Replace with dashes anything that isn't A-Z, numbers, dashes, or underscores.
      $string = strtolower(preg_replace('/[^a-zA-Z0-9_-]+/', '-', $string));
      // If the first character is not a-z, add 'n' in front.
      if (!ctype_lower($string{0})) { // Don't use ctype_alpha since its locale aware.
        $string = 'id' . $string;
      }
      return $string;
    }

  3. If nothing happens, you may need to clear the theme registry. Visit the Themes page of your site and submit it with no changes (quick way to clear the theme registry). Alternately you can visit Administer > Site configuration > Performance (admin/settings/performance) and click the "Clear cached data" button. There are also convenient cache clearing links/tools in Admin Menu module and Devel module.

If you already have a preprocess_page function

If the above works for you, ignore the rest below. If your theme's template.php already has a preprocess_page function (for instance, the default Garland theme does), you need to merge the code into it. A code editor (such as Komodo Edit) with start/end { } highlighting should help make that pretty simple.

  // Classes for body element. Allows advanced theming based on context
  // (home page, node of certain type, etc.)
  $body_classes = array($vars['body_classes']);
  if (!$vars['is_front']) {
    // Add unique classes for each page and website section
    $path = drupal_get_path_alias($_GET['q']);
    list($section, ) = explode('/', $path, 2);
    $body_classes[] = phptemplate_id_safe('page-' . $path);
    $body_classes[] = phptemplate_id_safe('section-' . $section);
    if (arg(0) == 'node') {
      if (arg(1) == 'add') {
        if ($section == 'node') {
          array_pop($body_classes); // Remove 'section-node'
        }
        $body_classes[] = 'section-node-add'; // Add 'section-node-add'
      }
      elseif (is_numeric(arg(1)) && (arg(2) == 'edit' || arg(2) == 'delete')) {
        if ($section == 'node') {
          array_pop($body_classes); // Remove 'section-node'
        }
        $body_classes[] = 'section-node-' . arg(2); // Add 'section-node-edit' or 'section-node-delete'
      }
    }
  }
  $vars['body_classes'] = implode(' ', $body_classes); // Concatenate with spaces

Also add this part separately outside of the preprocess_page function, to the bottom of your template.php:

/**
* Converts a string to a suitable html ID attribute.
*
* http://www.w3.org/TR/html4/struct/global.html#h-7.5.2 specifies what makes a
* valid ID attribute in HTML. This function:
*
* - Ensure an ID starts with an alpha character by optionally adding an 'id'.
* - Replaces any character except A-Z, numbers, and underscores with dashes.
* - Converts entire string to lowercase.
*
* @param $string
*   The string
* @return
*   The converted string
*/
function phptemplate_id_safe($string) {
  // Replace with dashes anything that isn't A-Z, numbers, dashes, or underscores.
  $string = strtolower(preg_replace('/[^a-zA-Z0-9_-]+/', '-', $string));
  // If the first character is not a-z, add 'n' in front.
  if (!ctype_lower($string{0})) { // Don't use ctype_alpha since its locale aware.
    $string = 'id' . $string;
  }
  return $string;
}

Tags —