In the last month, I've been working on a project that requires a very custom look and feel for login/register/adding content forms, and worked what I've learned into a presentation. Now I've got a little running list of tips for whipping Drupal forms into shape.
- Check out the Stealther plugin. If you are theming a form that logged in users don't see (like the Login or Registration forms) Firefox's Stealther plugin is invaluable. Since it temporarily disables saved information – caches, cookies, histories, form information – you can quickly toggle back and forth to see what the logged out user sees.
- Let anonymous users see Devel information. You can extend the Stealther idea even further using the Devel module. Install and expose the Devel menu. If you are on a private development site, give anonymous users full permissions on the Devel module. This will allow you do use the devel tools (theme developer, clear cache, run cron,
$dpm();etc.) without being logged into the site, so you can easily inspect and theme forms for anonymous users. Be SURE to disable these permissions once done using them. - Keep your CSS organized.If you are doing a LOT of CSS theming of your forms throughout your site, consider making a separate forms.css file. That can help you keep your styles organized. Just make sure you add the call to that stylesheet (
stylesheets[all][] = forms.css) in your .info file and clear you cache to get Drupal to recognize it. - Theme "active" fields.For usability, highlight the field the user's cursor is in. Using a CSS3 class, change the current field's background color or border:
input:focus {
background-color: orange;
border: 1px solid black;
} - Substitute jQuery for CSS3 tricks for usability. Override tip #4 with this additional twist. IE does not support many CSS3 pseudo-classes (including IE8), so use jQuery for focus/hover/after effects instead of CSS3 for cross-browser compatibility. Use jquery to add a class to your input upon "focus", then style that class in your stylesheet:
$("input").focus(function() {
$(this).addClass("focus");
});and:
input.focus {
background-color: orange;
border: 1px solid black;
}You can apply this class to the input wrapper instead with
$(this).parent().addClass("focus");Also, use this method to add classes when hovering over elements.
- Theme fields that produce errors. Drupal adds an
errorclass to input fields that produce an error when submitted. Use that class to guide users to where their problem is with a form by outlining it or other styling. Also check out this tutorial for applying the error class to the field wrappers so you can also theme the field labels or the entire form element area producing the error. - Use dpm($form);. If you are using a function to override parts of your form in your template.php file (and you have the Devel module installed), you can use
dpm($form);in your function to check your variables and figure out what is overrideable content. It prints out your variables in a nice, expandable stack. - Check for different permissions or variables before loading or unsetting form elements. Inside your function, you can also check for various states. For example, if you are overriding a node creation form, you can check to see if you are on the Preview page or not with:
<?php
if ($preview) {
do stuff...
}
?>Or check to see what kinds of permissions the user filling out the form has:
<?php
if ($is_admin) {
do stuff for the administrator...
}
?><?php
if ($logged_in) {
do stuff for logged in users...
}
?><?php
if (! logged_in) {
do stuff for anonymous users...
}
?><?php
if (user_access('administer nodes')) {
do stuff for a user with those particular permissions...
}
?> - Consider using a form tpl.php file. Once you have created your function to override your template, you can easily add a tpl.php template file, which is often easier for themers more comfortable with the XHTML structure than the PHP. However, because TPL files render about five times slower than functions, try to do as much as you can with a function. If it starts getting hairy and your function is super long, maybe then it should be moved to a tpl.php file.
- ...But be sure to load your hidden form elements! If you go with the method in #9 and add a your-form.tpl.php file, make sure to render the hidden form elements. If not, your form will not submit! I keep this bit at the end of my tpl.php file, just to be on the safe side:
<?php
print drupal_render($form['form_build_id']);
print drupal_render($form['form_id']);
print drupal_render($form['form_token']);
drupal_render($form);
?>Update: A reader below has pointed out that this method, while it works now, relies on things in the Form API that could potentially change in the future. To avoid this problem, use
<?php print drupal_render($form); ?>instead. In this scenario, however, you will need to unset any fields that you do not want to show because<?php print drupal_render($form); ?>will print everything that hasn't rendered already on your form.
References:
- Check out Chp. 6 of Emma Jane Hogbin and Konstantin Käfer's Front End Drupal.
- For making form tpl.php files, check out Theming the User Registration Form in Drupal 6, but keep Tip #10 in your pocket when you use it!
- To compare changing form elements at the module level and the theme layer, check out Modifying Forms in Drupal 5 and 6.
And please add your favorite tricks in the comments!



Thanks! Tip #2 (and all of them) really helped me a lot. I'm trying to style my login/register form and can't find some of the elements. Thought I couldn't use Theme Developer module because you have to be logged in to use it and the only time this form appears is when you are not logged in.
<?phpprint drupal_render($form['form_build_id']);
print drupal_render($form['form_id']);
print drupal_render($form['form_token']);
drupal_render($form);
?>
Should read:
<?phpprint drupal_render($form);
?>
The method you use relies on FAPI implementation details and those can change between minor releases (they did change once already).
As drupal_render doesn't output rendered elements twice, there's no harm.
Aha, that makes sense. I will add that note in - that those hidden variables are subject to change, and
<?php print drupal_render($form); ?>is preferred. If they use that instead, users will definitely need to unset every field they aren't using because<?php print drupal_render($form); ?>will print any variable they haven't called already in their form template.