Skip to content Skip to navigation

How I learned the hard way to create reusable classes

Drupal is (in)famous for providing an egregious amount of class selectors to target every layer imaginable in its rendered HTML. Some superstar culprits are Field Collections, Field Groups, and complex Views. When we see so many handy, available selector classes, it's so tempting just to target them directly in your CSS. But today, I want to share a lesson I learned the hard way about why you've just gotta resist that temptation, and instead create reusable classes.

One of these things is not like the other...

Here's where my story starts – a beautiful, complex, custom design, being built out by my talented teammates as I began to translate Illustrator comps into CSS. As my team created blocks, menus, pages, views, and contexts, I chased after the functionality, skinning it as fast as I could, moving from one section of the site to the other. My CSS started small, maybe a hundred lines, but soon it began to balloon.

As I began polishing the corners of the site, I inevitably realized that many different elements needed to look the same across the site, even though sometimes they were rendered differently in the HTML. Ah, the joys of Drupal theming, where sometimes you can't change a view label from an H3 to an H2, so you just style them to look the same. But when you have dozens of different elements being rendered in subtly (and not so subtly) different markup, how can you get all the things to look the same without an overwhelming amount of CSS?

Here's what my CSS started like:

h2 {
  color: #8c1515;
  text-transform: uppercase;
  font-size: 1.2em;
}

But soon, the selectors got out of control!

h2, #my-crazy-view .view-header h2, #another-crazy-view .view-grouping h3, #block-ap9f8uqc4umweirut9p83WTF h2, .page-some-custom-type-of-page .region-lower #block-sadflkj0239u40984OMG {
  color: #8c1515;
  text-transform: uppercase;
  font-size: 1.2em;
}

OK, you get the point.

Now, stringing selectors together is better than listing duplicate CSS rules for each selector individually, but there's still a real problem with the way that we are targeting these elements across the site.

Hi, I'm a block, and I changed my name!

So someone else is editing the site and they decide to do some cleanup of a view block display. They add a nice semantic name to that block, which is great, except that I targeted that block name specifically in my CSS, and suddenly I get an urgent email telling me the styles are broken. I quickly load up the site and open Firebug trying to see what's wrong. I don't see any CSS loading in the sidebar when I target that element, which is weird, because I know I themed that block, and that it should look like that other H2 on that other page... now what page was that on again? So, as I ponder why all of a sudden my CSS is missing (because I haven't figured out yet that the block name changed) I dig through the site trying to find an example of the CSS it should have, and finally I find it. To alleviate the problem, I create a CSS Injector rule targeting the new block name and copying the CSS I found on that other page instead of hunting down the lines of CSS in my theme files and pushing out a fix.

This sounds terrible, and it is.

But have no fear! There is a better way!

Your friends, reusable class names

So, that was the last time I mindlessly targetted Drupal-created selectors in my CSS. I say mindlessly, because sometimes it is impossible to style something without targetting a Drupal-generated selector class, but it was the last time I did that without thinking first about how it might affect the longevity of the site and the sustainability of my theme files. Instead, every chance I get, I try to create and add a custom reusable class name to the HTML that Drupal renders.

What do I mean by reusable class names?

A reusable class name is a semantic name that describes what the end result is. For example, color-block would be a nice class name for blocks that are colored differently from the default block, or views-row-lines for a style of views rows with lines in between, or top-line for a block or section style where there is a border on the top. These are all examples of real classes I have created to be reusable across a project, or several projects even. The names are functional, and not specific to certain views, features, or Drupalese. This is important, because right now, you might be thinking, well, only the people view should have lines between the rows, but then your client might come back next week asking if all the other views can look like that one.

Reusable classes become a style guide

So when you start translating your design into CSS, think about how you can break your styles into semantic classes. The way I do this is I create a Google Doc and start listing out the new classes I am creating. This document eventually becomes a style guide reference, and for each class in the document, I describe how and when to use the class, what it's for, a screenshot or code snippet, and how to apply it to a view, block, or page. Some classes even become so useful that we add them to our base themes, or reuse the same class names across several client projects. Other classes are specific to a client's design and wouldn't translate universally.

Adding your classes to Drupal

My style guide for these classes usually contains instructions for site builders on how to add the class in Drupal. Here are the most common ways to add your custom classes to Drupal:

Block Class

Using the Block Class module, you can add custom classes at the block level. This is perfect for theming different looking blocks, specific view blocks that show up in multiple places (like entity referenced content), menu blocks (like if you want a sidebar menu style), or any other custom blocks you've created. Block Class lets you add a class on a per block basis, so it lets you cherry pick your styles. I use Block Class to target the background color of the block, the heading style, the link style, and the margins and padding of a block or elements within the block.

So my CSS might look like this:

.color-block { background: sandstone; }
.color-block h2 { color: black; }
.color-block img { margin-top: 0; }
.color-block .more-link { font-size: 1.1em; }

And all you have to do is click "Configure Block," and paste color-block into the Block Class field on the block to apply your style, and if someone renames the block, your CSS won't break.

block class field

View Class

Another way to get a custom class on a View is to add it to the View class. Edit your View, and under the Advanced section you'll find a field to add your custom class name. This adds it to the view level of the HTML. What's great about this tactic is that you can add your view style to any View and not have repetition when you want a few different Views to match in style.

view class settings

Manage Display

Lastly, if you need to wrap a field or set of fields in a custom class, you can create a Field Group div in Manage Display for a content type, and add your custom class to the field group. Make sure to hide the label though, you don't want "Descriptor Group" to show up in text on the page.

Manage Display field group class

Wait, I have to do more work?

Yes, adding custom classes adds more work for you and your site editors and builders. For you, strategizing as you convert design to code, creating a style guide, and helping others build out the site with the correct classes does add more to your plate, but you'll sleep better at night knowing that your CSS is semantic and robust, and that you've promoted best practices for your team. For your site editors and builders, they now have to add your custom classes to all the right places, but that isn't too hard as long as you provide them with good documentation (marked up wireframes are usually what I provide). What's fun for them now is they get to feel that instant satisfaction of adding a class and suddenly seeing their view block look awesome, just like magic! And you can write your CSS in advance of the team building out the site, knowing that you don't have to wait to target a specific view or block name.

It's worth it.

All this hard work pays off in the long run. You develop a library of reusable class names that you and your team adopt for projects going forward, and your CSS stands the test of time and minimizes future style mixups as the site grows and changes in the hands of the client. Don't make the mistake I did on my first big custom theme project, take the time now to strategize on how reusable classes can become part of your daily practice in Drupal theming.

Comments

This is a fantastic post, we recently switched to an OO based class design and it has worked out great.

I would suggest to use the new Drupal 8 CSS coding standards for the classes as well like for a field group:

.group
.group--highlighted
.group--gallery

etc.

and for sub components or sub field groups - if needed:

.group__header

etc.

It works great!

There was a great session on this at DrupalCon Austen this year: https://austin2014.drupal.org/session/modular-css-how-create-css-reusable I know the sessions were filmed, but I can't find it on youtube at the moment.

Here is a solution that saved me, personally, the most time and code up to now:

Display Suite Extra's module displays a cogwheel next to every content field in Manage Display.
1) You can choose the (semantic) wrapper: (div, span, H1, H2, article, section, figure...).

2) You can adapt the wrapping of your field: "Drupal default", "Minimal", "Full Reset" or "Expert". I made "minimal" the default in all my websites -> that's a LOT less div's, ladies and gentlemen.

3) Last but not least, you can build up your own set of reusable classes in the DS settings, and add them to your field. So I can assign to my field "smalltext" + "space20pxunder" + "lightgrey", for example.
Result: .

Note that Fences module has similar abilities.
And, of course, YAY to block classes and views classes. Thank you for the great article!

Thanks for the note - I didn't know about that module. Worth looking at for sure.