This post is about Drupal 7 – Learn how to add CSS classes to blocks in Drupal 8.
The concept of Modular CSS has been around for years. The basic idea is tackling your CSS code in a systematic way through class and file naming conventions. The main goal being improved workflow through organization, readability and reusability. Some of the more recent modular CSS architectures are Object Oriented CSS (OOCSS) and Scalable and Modular Architecture for CSS (SMACSS). I tend to prefer SMACCS as Jonathan Snook has presented it as a set of guidelines rather than rules. He leaves it open for individual style and interpretation as every developer and every project is different. If you want to see a great example of Modular CSS in action, go blow your mind looking at the Twitter Bootstrap Components Library. The creators have done an excellent job abstracting the components in a way that makes them easy to understand and flexible via classes and subclasses.
The idea of Modular CSS really appeals to me, especially having worked on large complex sites with multiple front-end developers collaborating together. While Drupal is perfect for these large scale projects, adding your own CSS classes to a Drupal theme ( essential when working with modular CSS ) is often unintuitive, inconsistent and tiresome at best. To remedy this, I've been working on a systematic approach to adding classes to themes that has so far proved extremely useful. In this post we're going to see how we can easily manage classes on one of Drupal's most used components, Blocks, using a preprocess function.
Preprocess Functions
Preprocess ( and Process ) Functions are the key to taking control of your classes in Drupal. Preprocess functions are called before every theme hook is sent to a theme function and template file (.tpl). In my mind preprocess functions have 2 uses:
- Altering and adding variables that are available to theme functions and .tpl's. This is how we'll be adding our classes.
- Sending these variables to an alternate theme function or .tpl via theme hook suggestions.
I prefer managing my classes via preprocess functions over template files because I find it way more flexible and concise. I no longer need duplicate template files just to add or remove a single class.
Adding Classes to Blocks
As is often the case with Drupal, there are a few options out there for adding classes to blocks, including Skinr, Fusion Accelerator and Block Class. All of these offer the power of adding classes through the admin UI which may be right for you and your site administrators. This hasn't been a requirement for most sites I've worked on. Your mileage may vary. I prefer to define my classes directly in the theme where I'm already writing my CSS, everything is already in code and there is no need to export settings from the database. So let's see how we can do this using default core functionality.
Looking at the core block.tpl.php file, by default there are 3 elements to which we can add classes during preprocess: the block container itself ($classes
), the block title ($title_attributes
) and block content ($content_attributes
). As of this writing, in order to preprocess the block content classes, you must remove the hard coded class attribute from the template yourself or apply a core patch. Below is what block.tpl.php looks like after removing the hard coded content class.
<br /> <div id="<?php print $block_html_id; ?>" class="<?php print $classes; ?>"<?php print $attributes; ?>></p> <p> <?php print render($title_prefix); ?><br /> <?php if ($block->subject): ?><br /> <h2<?php print $title_attributes; ?>><?php print $block->subject ?></h2><br /> <?php endif;?><br /> <?php print render($title_suffix); ?></p> <p> <div <?php print $content_attributes; ?>><br /> <?php print $content ?><br /> </div><br /> </div><br />
Now let's create our preprocess function and start adding some classes. Below is an example of our entire function.
<br /> <?php</p> <p>/**<br /> * Implements hook_preprocess_block()<br /> <em>/</p> <p>function mytheme_preprocess_block(&$vars) {<br /> /</em> Set shortcut variables. Hooray for less typing! <em>/<br /> $block_id = $vars['block']->module . '-' . $vars['block']->delta;<br /> $classes = &$vars['classes_array'];<br /> $title_classes = &$vars['title_attributes_array']['class'];<br /> $content_classes = &$vars['content_attributes_array']['class'];</p> <p> /</em> Add global classes to all blocks <em>/<br /> $title_classes[] = 'block-title';<br /> $content_classes[] = 'block-content';</p> <p> /</em> Uncomment the line below to see variables you can use to target a field <em>/<br /> #print $block_id . '<br/>';</p> <p> /</em> Add classes based on the block delta. <em>/<br /> switch ($block_id) {<br /> /</em> System Navigation block <em>/<br /> case 'system-navigation':<br /> $classes[] = 'block-rounded';<br /> $title_classes[] = 'block-fancy-title';<br /> $content_classes[] = 'block-fancy-content';<br /> break;<br /> /</em> Main Menu block <em>/<br /> case 'system-main-menu':<br /> /</em> User Login block */<br /> case 'user-login':<br /> $title_classes[] = 'element-invisible';<br /> break;<br /> }<br /> }<br />
This may look like a lot but it's actually pretty simple. Let's break it down. First off, we create some shortcut variables to save us some typing down the road and make our code more legible.
The first variable we create is
$block_id
which is just a string combining the name of the module creating the block and the block's delta— separated by a dash. This gives us an easy way to target individual blocks when we start adding classes. One thing to note, some blocks, including core custom blocks, use an auto-incremented integer for their delta value. This can present issues if you are moving blocks from dev to staging to production and you're not careful. I recommend using the boxes module to help mitigate this.The next three variables,
$classes
, $title_classes
and $content_classes
are references to the arrays that will eventually be rendered into class attributes. To add classes to an element, we just need to add them to its corresponding array.After we have our shortcuts set, we can started adding classes to our blocks. We first start by adding some global classes,
.block-title
and .block-content
, to all block titles and block content containers respectively. You may or may not find this helpful. If you're interested in stripping out all the classes that core and other modules have added to your blocks up to this point, this would be the place to do it. To remove those classes, just reset your variable to an empty array like so $classes = array();
Next, we can start adding our classes on a per block basis. I like to keep a simple print statement handy that prints out the
$block_id
for all blocks on the page. When styling a new block, I just uncomment this line and copy the id from the browser into my code editor. To target individual blocks, we create a simple switch statement based on the $block_id
. For each block we need to style, we write a case and add the corresponding classes there.In the first case we are adding three classes to the System Navigation block, one to the block itself, one to its title element and one to its content wrapper. In the second instance we are actually grouping two cases together so we can add the same classes to multiple blocks. Here we are adding
.element-invisible
, Drupal's built in class for hiding content in an accessible manner, to the titles of the Main Menu and User Login Blocks.As we add blocks, we add more switch statements. That's all we really need to do to take over our block classes. I've found this alone can be a tremendous boost to productivity while styling a Drupal site. You can get pretty far with blocks alone. However, with modifications to core theme functions and templates, similar to what we did with block.tpl.php above, this technique can be adapted to manage classes on nodes, fields, menus and most other themeable output.
I've submitted a session proposal to DrupalCon Munich on this topic called Stay Classy Drupal - Theming with Modular CSS. If you're interested in learning more about taking control of your classes or have run into trouble trying to do so, leave a comment to let the organizers know you're interested in the session.