Need some help with your project? Contact me

How to theme forms and add custom classes wherever we want in Drupal 7

In this article we are going to talk about theming forms. What exactly about it? Since theming forms can be a huge topic, I will stick to inserting your own custom classes wherever you can in a given form.

Have you ever needed a form element to have some special class you could target in your css? Or even yet, have you ever used a css framework like Bootstrap that tells you: ok, you need the form element to have class X in order to pick up these nice styles? Well, it can be a bit tricky to find exactly where you need to go, hook into what, override what theme function etc.

So in this tutorial I'm going to show you how to get your classes into 5 different form "places":

  • the <form> tag itself
  • the submit button
  • the element input itself
  • the form element wrapper <div>
  • the element label

Before we go further, this tutorial assumes you are already familiar with Drupal hooks and know how to implement one at the basic level. We are going to talk about form_alter hooks a lot which means that you can use more than one for the job: for instance hook_form_alter() or hook_form_FORM-ID_alter(), whichever best suits your need. You can read more about hooks in this section.

1. The form tag: `

`

If you want to add your class to a form tag you can do so inside of a form_alter hook. Once you are targeting the form(s) you want, you'll need to add some information to the $form array that gets passed through the hook:

$form['#attributes']['class'][] = 'your-class';

What happens here? The $form variable is a renderable array that also takes #attributes. There can be many types of attributes, one being class. This is as well an array so we make sure we add another item to the class array.

How do you know all this? When I debug the $form variable with the Devel module, I see no #attributes array in there at all...

WeIl, if you look at theme_form() itself - which is the Drupal core theme in charge of rendering forms - you'll notice that the #attributes array exists and gets transformed into HTML attributes by the drupal_attributes() core function. So, whatever you put in there will be transformed into attributes.

2. The form submit button

Adding a class to the submit button - the input element rather - is similar to adding one to the <form> tag but a bit tricker.

One thing you can do is override the entire theme_button() function in your theme and add your class there. Not really recommended if all you are doing is adding a class because you are copying all that code just to add a couple of more words. For more complex operations, you can do that.

Instead, you can use the same form_alter hook as before, and insert it there. We'll look at a couple of different forms to notice the difference between how you might approach this.

First of all, you can have a custom form that you declared yourself in the module. In this case, all you have to do is specify the #attributes when declaring the submit button. Something like this:

$form['submit'] = array(
  '#type' => 'submit',
  '#value' => t('Submit'),
  '#attributes' => array(
    'class' => array(
      'your-custom-class'  
    ),
  ),
); 

If the form however is declared by another module and you don't - nor you should - want to hack it, you can use the form_alter hook of your choice. Considering that this is the form submit button declaration (so no classes defined in the #attributes array originally):

$form['my_submit'] = array(
  '#type' => 'submit',
  '#value' => t('Next'),
);

In the form alter you can add the following line:

$form['my_submit']['#attributes']['class'][] = 'my-custom-class';

Where my_submit is the machine name of the submit button. This will have the same effect.

Now let's take the Search block form as an example and say you want to add the class to the Search button. In your $form array, you'll notice an actions array. Looking this up in the Form API reference, you'll see that this is in fact a wrapper around one or multiple form buttons. So going inside that, we can find the submit button. So to add a class to that button, we do something like this:

$form['actions']['submit']['#attributes']['class'][] = 'your-custom-class';

So it's basically about finding out where the submit button is in the $form array. And since the actions form element is defined as a renderable array as well, you can add a class to that too:

$form['actions']['#attributes']['class'][] = 'your-custom-class';

So now the class is on the wrapper <div> of one or multiple buttons in the form.

3. Form elements

Similar to the form buttons, to add classes to form elements (the actual input, select, etc fields), you need to find them in the $form array of your form_alter hook and then add to the #attributes array. So to take the same Search block form, we can add our custom class like so:

$form['search_block_form']['#attributes']['class'][] = 'your-custom-class';

Easy peasy. And same as above, if you are the one declaring the form, you can just add the #attributes array with the class right there.

4. Form element wrapper <div>

When I say form element wrapper <div>, I mean the <div> surrounding the form element itself and the label. So how do we add our custom class to this? If you try with the form_alter hook as before, you won't be able because the form elements are rendered using their own custom theme implementation of theme_form_element(). And I've found that using the preprocess hook for that theme function does not pass the class to the wrapping <div> (as I thought it should).

So then what we can do is override the theme function itself - in our custom theme of course - and add the class there. Just copy the theme implementation from the API page into your theme template.php file and change its name from theme_form_element() to your_theme_name_form_element(). Then perform inside it any additional checks to make sure you only target the class name addition to the elements you want. The important thing then is to add the class into this array:

$attributes['class'] = array('form-item');

You can see below it all sorts of checks to add further classes that have some sort of meaning for the form element. You can insert yours in there somewhere.

5. Form element label

Adding your own class to the label is similar to what we did in the previous point: you have to override the right theme function. In this case it is theme_form_element_label().

Here though, you have to be careful. The $attributes['class'] array into which we've been putting classes is no longer an array but a simple string. So you need to avoid wiping out any other classes that Drupal might want to add to the label in various circumstances. So right before the function returns the output, add something like this:

$attributes['class'] .= ' my-custom-class';

This way you are appending to any class string that may come before. For some reason, the $attributes['class'] is not an array here. I don't know. Maybe you figured out why and there is a better way of doing this.

Conclusion

In this article we've seen what we need to do to add our own custom css classes to Drupal 7 forms. It's not a walk in the park but it's not so difficult either. One thing you can be sure of though is that it takes a lot of trial and error until you get everything right.

Comments

One can use function drupal_html_class($class) to make sure the $class used is clean, valid CSS id that uses lower-case only without spaces.

Really detailed and useful explanation. Thanks.

Since the time of this nice writeup, someone was kind enough to allow this to be done by none developers as well, through the well known Field UI.
I found it while desperately searching for a quick solution for a HUUUGE entity form.
It's only a sandbox, but so far so good...
https://www.drupal.org/sandbox/gbirch/2210563

Thanks a lot. It all works fine. But sometimes, I would like to override theme function only in context of certain form. Do you have any article or advice to that?

i am trying this code in my module but it is not change class of a form am i right any thing wrong here?
$form['title'] = array(
'#attributes' => array(
'class'=>'col-md-6'
)
);

I know this tut is for D7, but after kinting my $form the structure looks to be the same.

$form['field_dates']['#attributes']['class'][] = 'my-custom-class';

However the code above does not seem to add a class no matter what I try. Any thoughts?

I want to change the form uc cart form of ubbercart by overriding the builtform function for the uc cart class

i how like to know how,you can use this class define in the form in JQuery to manipulate the form.
for example if i want to hide the value of '#title' that was created in the form.

Add new comment

You can post comments in Markdown and basic HTML tags.
For code blocks, wrap your code within '~~~'. For example:
~~~
$var = 'my variable';
~~~