Beautify your Drupal forms
There are a lot of small details that reveals if a website has been built with Drupal; I often detect them pretty quickly when landing on a page. The URL structure, the search form, the user login page... all of these tend to be pretty Drupal-ish, except when some time has been spent in theming and styling.
Most of my clients are expecting to have their website looking the least Drupal-ish possible: they want the nice CMS engine but appreciate having something as customized as possible when it comes to the appearance. I recently had to spend a bit of time tweaking forms to give them a "sexier" look. Among the many modifications and enhancements we did, two hacks may be of some interest for some of the folks out there.
"Nicer" buttons
I am not a huge fan of adding markup for the sake of design; I am actually pretty picky when it come to semantic, standard and the need for keeping styling as unobtrusive as possible (I actually spend half of my time annoying my staff with these concepts, the other half being dedicated to define how many 饺子 (jiaozi) fit in a Chinese developer's stomach).
Anyway, there come times when the design needs to have those "rounded buttons" ("Must... control... fist of death"). Here is how you can have all of your form buttons to be styled at once; we first need to add a few lines to the template.php file of our theme to override the theme_button function:
// Override theme_button
function phptemplate_button($element) {
// Make sure not to overwrite classes.
if (isset($element['#attributes']['class'])) {
$element['#attributes']['class'] = 'form-'. $element['#button_type'] .' '. $element['#attributes']['class'];
}
else {
$element['#attributes']['class'] = 'form-'. $element['#button_type'];
}
// We here wrap the output with a couple span tags
return '<span class="button"><span><input type="submit" '. (empty($element['#name']) ? '' : 'name="'. $element['#name'] .'" ') .'id="'. $element['#id'].'" value="'. check_plain($element['#value']) .'" '. drupal_attributes($element['#attributes']) ." /></span></span>\n";
}
The last line is the only part we actually modify; we wrap our buttons with a pair of <span> tags that we are going to style.
Now we need to style it, that means as well preparing the pictures. These are the left and right part of the button, though we double the pictures to use a sprite effect when the user click on the button (pseudo class :active):
- bg-button-left.png
- bg-button-right.png
We combine this with the following CSS code that we add to one of our stylesheets (style.css from your theme for example):
/**
* Form buttons
*/
span.button {
background: transparent url('bg-button-right.png') no-repeat right top;
padding: 4px 10px 5px 0;
}
span.button:active {
background-position: right -36px;
}
span.button span {
background: transparent url('bg-button-left.png') no-repeat left top;
padding: 4px 0 5px 10px;
}
span.button span:active {
background-position: left -36px;
}
span.button span input {
background: transparent;
border: 0;
color: #494949;
font-size: 12px;
padding: 0;
}
Here is an example of the display you should have:
Now, you can easily use the same kind of method on many other elements, including menus, like the local tasks. Let me illustrate this with a screenshot from a recent project:

Field descriptions as popups
This is another one I thought of while working on a previous project; the user found the content submission forms way too cluttered. It was mainly due to the fact that all the contributors had been attributed the administrator role, thus displaying a lot of unnecessary fields and options (Authoring information, URL path settings, Menu settings, ...). We changed them to the contributor role that was designed for their use of the website and everything was fine. However, I wondered how we could make the forms even cleaner; a lot of the content forms the contributors were going to use displayed a lot of descriptions below the field. I think it's important to add this information when creating custom types with CCK, though it can add a lot of unnecessary information when either the field label already gives enough information (the Username field is indeed pretty straight forward) or the user is very familiar with the interface, which is the case when for example a contributor is creating new entries everyday.
What about then, simply hiding these bits of information and displaying them as a helper popup when the field is hovered or gain the focus? Well that's exactly what were going to do.
Once again, we need to override something. Since we want this to happen when we have a form (thus fields) we are going to use the theme_form function. We just need to load a jQuery script, we do so by adding the following to our template.php file in our theme:
function phptemplate_form($element) {
// Add a jQuery script for the popups
drupal_add_js(path_to_theme() .'/popup.js');
// Anonymous div to satisfy XHTML compliance.
$action = $element['#action'] ? 'action="' . check_url($element['#action']) . '" ' : '';
return '<form '. $action .' accept-charset="UTF-8" method="'. $element['#method'] .'" '. 'id="'. $element['#id'] .'"'. drupal_attributes($element['#attributes']) .">\n<div>". $element['#children'] ."\n>/div></form>\n";
}
Now let's have a look at this popup.js script:
$(document).ready(function() {
$('form .form-item input + .description').hide().prev('input')
.hover(
function() {
$(this).next('.description').show();
},
function() {
$(this).next('.description').hide();
}
);
});
This will hide input descriptions and show them when hovering the input. In terms of usability, it would be better to ensure the descriptions are displayed when the input gain focus as well, but I will keep things simplehere.
Now, we style a bit the description layers:
/**
* Form descriptions popups
*/
form .form-item .description {
background: #FFE8B1;
border: 1px solid #FFD571;
border-width: 1px 2px 2px 1px;
color: #A16400;
font-size: 11px;
padding: 7px;
position: absolute;
width: 250px;
}
And here is the expected result (just hover the field):
What then?
Well, as I said before these are simple examples:
- when you style buttons as explained earlier you have quite some special cases to deal with: buttons displayed on a layer that has a background will need specific pictures that takes it into account, inline forms need a bit of styling as well...
- the popup script works fine but need a bit of customization to take textareas, radios and checkboxes into account, the behavior could be improved to take the field focus into account, the style of the popup could be improved as well...

- reply
Submitted by Marian (not verified) on Thu, 2009-01-15 19:39.Very nice example on how to phptemplate along with jquery. Thank you, I'v learned something new.
- reply
Submitted by Rafael Silva (not verified) on Thu, 2009-01-15 20:51.Very good tips. Problably I'll use them in a future project I'm working.
Thank you for sharing this!
- reply
Submitted by akahn (not verified) on Thu, 2009-01-15 23:12.Great post, it's wonderful to improve Drupal's standard elements and make it beautiful.
The code in your first theme override function could be made a lot clearer if you break it out over several lines rather than doing all the concatenation and using the ternary operator all in the return statement.
- reply
Submitted by Wim Leers (not verified) on Thu, 2009-01-15 23:13.By removing the form item descriptions and only showing them on hover/focus, you've done more than just beautifying the form. You've cleaned it up and made it more understandable, as you already indicated yourself.
IMO Drupal core should take a similar approach. Maybe you'd like to make that your Drupal 7 contribution? :)
- reply
Submitted by hunvreus on Sun, 2009-01-18 15:02.Thanks Wim, glad that it's useful to some. However, I am still unsure of its superiority in terms of UI over the current display. I am working on a coupe modules and am about to start working on Drupal 7; I'd like to see a couple things change in the way forms, and especially autocomplete is handled. I'll see if people are interested in this approach then.
- reply
Submitted by manuee (not verified) on Fri, 2009-01-16 06:51.Never thought of doing this before, thank you!
The jquery code looks very clean - nice job!
- reply
Submitted by Anonymous (not verified) on Fri, 2009-01-16 17:47.Kind enough to lend us the code/css for your tabs and secondary tabs. They are simply beautiful!
Thanks!
btw: The screenshot with the name "Roman Berder" posted above is the one I'm referring too. Awesome!
- reply
Submitted by hunvreus on Sun, 2009-01-18 13:02.I am a bit in a rush to finish the new release of a module (I'm on it Gabor!) but I will take the time this week to clean the code and share it with you all.
- reply
Submitted by Drupalbased (not verified) on Fri, 2009-02-06 05:57.Thank you so much, my latest drupal theme had rounded buttons all around and i had no idea how to get it working but you just saved my site by not having to change the design simply because i couldn't implement it
Thanks!
- reply
Submitted by Antonio (not verified) on Wed, 2009-02-11 01:39.Very nice work! I just have noticed that in IE 7 and 6 the buttons are broken. Another issue is that if you click on the right edge of the button then only one part is changing the background. Do you have any update on the CSS where this issues are fixed?
Anyway, thank you for the great job! :)
- reply
Submitted by hunvreus on Thu, 2009-02-12 09:32.We noticed that; I will have my Windows reinstalled on my VM and will post the IE fix in the enxt few days.
- reply
Submitted by elv (not verified) on Wed, 2009-02-11 23:19.The popup idea is really nice! But I tried to use it on a site today and it seems to only work with text fields. The description for radio buttons, textareas and selects is always visible, and breaks in some admin pages.
I tried to add a display:none to the descriptions, but then they would show only for regular text fields.
It's a d6.9 site with a theme based on Zen, if that helps.
Oh by the way, I spotted a minor typo in the field description popup template code. Last line ends with:
."\n>/div>\n";
instead of
."\n\n";
- reply
Submitted by hunvreus on Thu, 2009-02-12 09:38.Well the code here is mainly a proof of concept. If you want to support more input it takes a bit more hacking. You will as well need to add a few CSS rules depending to take care of all the special cases you may encounter in the admin interface, through forms...
- reply
Submitted by Steve Krueger (not verified) on Thu, 2009-02-19 04:07.I rewrote a bit of the code to loop over multiple input types as well as adding a class to the description field. This allows you to style the popup independently if the user happens to not have JS enabled.
jQuery code:
var formElementSelect = ["input", "select", ".resizable-textarea", ".form-checkboxes", ".form-radios", ".option"];
$.each(formElementSelect, function() {
$('form .form-item '+ this +' + .description').hide().addClass('popup').prev(this).hover( function() {
$(this).next('.description').show();
},
function() {
$(this).next('.description').hide();
}
);
});
CSS:
.form-item {
position: relative; }
.form-item .description.popup {
position: absolute;
width: 400px;
background: #fefacb;
border: 1px solid #E6DB55;
border-width: 1px 2px 2px 1px;
padding: 7px;
z-index: 99;
-moz-border-radius: 5px;
margin-top: 5px;
display: none; }
- reply
Submitted by Anonymous (not verified) on Sat, 2009-02-28 00:31.Any fix for IE6/7, yet? This is a great alternative to http://www.filamentgroup.com/lab/styling_the_button_element_with_sliding_doors/ because that method uses which is not supported by Drupal.
- reply
Submitted by Anonymous (not verified) on Wed, 2009-03-18 04:29.I did manage to get this to work with ie6/7 as well as safari3 and opera, however, only ff seems to support the :active bits. Unfortunately, my image dimensions are different than your so I am not entirely sure which particular css settings of mine allow the buttons to render ok with ie6/7:
I am passing attributes=>array('class'=>'dpbutton') as an #attribute for the form submit button (so I can specify when I want a fancy button or not)
span.dpbutton {
background: transparent url('/images/buttonend.gif') no-repeat right;
padding: 15px 5px 15px 0px;
font-size:1.3em;
}
span.dpbutton:active {
background: transparent url('/images/buttonendpressed.gif') no-repeat right;
padding: 15px 5px 15px 0px;
font-size:1.3em;
}
span.dpbutton span {
white-space: nowrap;
background: transparent url('/images/button.gif') no-repeat left;
padding: 15px 0px 15px 5px;
margin: 10px 0 0 0;
}
span.dpbutton span:active {
background: transparent url('/images/buttonpressed.gif') no-repeat left;
}
span.dpbutton span input {
background: transparent;
border: 0 none;
color: #fff;
font-size: 12px;
font-weight: bold;
margin: 5px 0 0 0;
padding: 5px 0 5px 0;
}
As you can see I'm using separate images for non/active, rather than the same one with positioning. Hope this helps you genericise a ie solution.
- reply
Submitted by John (not verified) on Mon, 2009-04-06 04:53.Thanks for such a clear tutorial. This kind of info is surprisingly hard to find.
- reply
Submitted by Anonymous (not verified) on Sat, 2009-05-02 05:57.simple fix is to add display:inline-block
however then the first span doesn't need a top padding
see css below
background: transparent url(images/bg-button-right.png) no-repeat right top;
padding:0 10px 5px 0; display:inline-block;
}
span.button:active {
background-position: right -36px;
}
span.button span {
background: transparent url(images/bg-button-left.png) no-repeat left top;
padding: 2px 0 5px 10px; display:inline-block;
}
span.button span:active {
background-position: left -36px;
}
- reply
Submitted by Andy (not verified) on Sun, 2009-06-07 08:30.Terrific THANKS!!
I want to add that in Firefox the textarea´s help is always being show, no matter if you´re hovering that field or not....
Could it just be me?
Thanks for the fantastic tutorial!
Andy
- reply
Submitted by Nick (not verified) on Fri, 2009-06-12 20:19.In a long form with lots of help messages, moving the mouse over the form with tooltips like this triggers so many tool tips as to be distracting. It might be worth including a simple delay function like hover intent
http://cherne.net/brian/resources/jquery.hoverIntent.html
to tweak the behaviour so users only see the tool tip if they mean to ask for it, and a quick swipe of the mouse doesn't yield anything.
- reply
Submitted by ben (not verified) on Sun, 2009-07-19 23:36.this is not only worth a bookmark.it is worth some discussion and might be worth a module, too.
- reply
Submitted by Andreas (not verified) on Wed, 2009-07-22 01:27.Here you can find the discussion about improving this idea and making a module of it:
http://groups.drupal.org/node/24392
- reply
Submitted by Jeff (not verified) on Thu, 2009-08-06 10:35.Well I am shocked and dismayed but this tutorial, the image buttons are nice but the hide() function for form descriptions is just plain inaccessible.
-1
- reply
Submitted by Anonymous (not verified) on Tue, 2009-11-03 03:00.thanks too much for your time.
- reply
Submitted by Tom Kirkpatrick (not verified) on Wed, 2009-11-04 03:48.See http://drupal.org/project/form_tooltips for a module that implements this.