I'm new to Drupal, but with a fair amount of background in html, css, cherrypy and google app engine. It hasn't been easy getting to know my way around the new framework. Things that were a piece of cake for me in html & css, and even easier in a python framework often took me days in Drupal. No doubt an experienced Drupal coder could have worked up a solution in minutes. But that doesn't change the fact that the framework doesn't come easily to beginners.

One example is building a custom form. The webforms module didn't have quite what I needed, so I had no choice but to roll up my sleeves and build something from scratch. It seemed way overkill to have to learn the form API and the basics of writing custom modules to get that simple task accomplished. And it didn't help that while the documentation for Drupal is far better than for some other frameworks (cherrypy included), it usually started two or three steps past what a newbie needs to know.

Another example is figuring out how to style the first form I built in Drupal. All I wanted was to add a bit of css code to my HTML output. To get a simple thing like that done, I had to spend a few days hunting around and trying things out. From the relevant pages I could find, it looked at first like I'd have to go through all kinds of hoops: modify my theme's template.php file, add a custom php template file, code a theme override function, etc.

Fortunately (after browsing through the code written for some of the more popular custom modules available at drupal.org/project/Modules) I found a very simple way to handle the latter task. In the interest of saving time for other newbies who are working on the same problem, here's what I figured out (I'm assuming here that you've already built the module that will serve up your custom form):

1. in the custom module function that builds your form (i.e. the function to which your menu callback points, or that you invoke with drupal_get_form if you're building your form via the nodeapi hook), define any needed class or id attributes for the form elements you want to style, as in the following example:

function custom_form_callback() {
  $form['customer_name'] = array(
    '#type' => 'textfield',
    '#title' => t('Customer Name'),
    '#size' => 50,
    '#attributes' => array('class' => 'name-textbox'),
  );
  $form['country'] = array(
    '#type' => 'radios',
    '#title' => t('Country'),
    '#options' => array(t('Canada'), t('USA')),
    '#attributes' => array('id' => 'custom-form-radios'),
  );
  ...
  $form['submit'] = array(
    '#type' => 'submit';
    '#value' => t('Submit'),
  );
  return $form;
}

2. add the following line to the top of your form callback function:

  drupal_add_css(drupal_get_path('module', 'name_of_custom_module') .'/name_of_custom_module.css');

3. in the same subdirectory where your custom module resides, create the file 'name_of_custom_module.css' and populate it with whatever styling you require, as for instance:

  .name-textbox {
    font-size: large;
    color: green;
  }

  #custom-form-radios div{
    display:inline;
  }

That's it. Your form should now display with the styling you defined. In most cases you can get the same result even without defining any class or id attributes. Just inspect the source code for your form, pick out the class attributes that Drupal automatically adds when building forms, and build your css file around those class names, as in the following example:

  #custom-form-callback form-radios div{
    display:inline;
  }

I added the attribute arrays to my form callback function just to show what you'd need to do if you had more than one element of the "radios" type and wanted to format each of them differently.

Comments

newbuntu’s picture

I know a little bit of css, not well versed. I wonder if this is the way to reformat a form, so I can display fields in certain way.

For example, for a field called "First Name", I want to it to display like:
"First Name:" [text value field here] on the same line.
Not like the default:
First Name
[text value field]

Similarly, I want display radio (or check boxes) like:
Country: * USA * Canada
Not like:
Country
* USA
* Canada

The default form spans everything in multiple lines. It takes too much screen space.

ricardo37’s picture

With css that's an easy one to accomplish. It may depend a little on how your form is built. But for simple forms, adding the following to your css file will do what you want (at least it worked in my test environment):

.form-item label {
  display:inline;
}

If your form is a little more complicated, then you'll need to add a class name here and there to your form elements, and set the display property to inline for the elements with the class name of your choice (again, inspect the HTML source of your form to see which elements to target with your css).

dynlist’s picture

The same question here. Please advise me how to use my own style sheet for an EXISTING field. Can it be done in hook_form_alter()?
In my case, the Status is there generated by Drupal as below.

'status' =>
array
'#type' => string 'radios' (length=6)
'#title' => string 'Status' (length=6)
'#default_value' => string '1' (length=1)
'#options' =>
array
...

Can I insert an attributes to load my old CSS? I have tried the code below, but failed.

$form['status']['#attributes'] = array('id' => 'custom-form-radios');

Thanks!

ricardo37’s picture

I double-checked and see that it doesn't work very well to set a form element's id property the way I suggested. Doing it this way results in the element having TWO id attributes, the one assigned automatically by Drupal and the other you've assigned. CSS gets confused and doesn't process the element properly.

The method I described still works for class attributes, but in a slightly odd way: the class name is given to a div surrounding the element, and to every child within the element. That can still get you the results you want in most cases, but is a little ugly.

To ensure that the class name appears only in the surrounding div tag, you need to add one more step to the process.

4. Define a theme override function within your custom module as follows:

function theme_custom_form_callback($form) {
  $form['country']['#attributes']['class'] = 'custom-form-radios';
  $output = drupal_render($form);
  return $output;

If you use this approach you may also have to alter your css file a little to ensure that it's picking up the right part of your form element to style (open the source for the page once you've browsed it, and inspect the hierarchy of html tags to make sure you're targeting the right one).

Note that Drupal won't let you use the theme override function approach to give an id attribute to your form element; you'll be stuck using class names (which will work just as well, as long as you pick a unique class name).

newbuntu’s picture

thanks for the detailed response. I'll play with it to see the effect. Most of the fields I plan to re-style are CCK fields, I suppose they are the same as far as your method is concerned?

Thanks again.

allmyhairisgonethxdrupal’s picture

I registered just to post a comment and thanks to your post. That is because after reading through this stuff for two months, I finally found someone's three year old post that tells it like it is. Drupal is HARD. I mean REALLY hard to learn, and I am a seasoned programmer. I have never wasted so much time looking for little things and going round and round as I have with Drupal. Your straight-shooting explanation of how to style a form has really come in handy, so thanks. I wish there were more posts of EXACTLY this quality on here.

allmyhairisgonethxdrupal’s picture

I hate people who say they are a "seasoned programmer." Egomaniacs.

jaypan’s picture

Or realists. If someone has been programming for 20 years, I'd say it's probably a relatively accurate term.

Contact me to contract me for D7 -> D10/11 migrations.

adamdicarlo’s picture

Egomaniacs?

Or are they just ordinary programmers... who bathe in salt, cumin, and perhaps a little oregano?

adamdicarlo’s picture

I'm curious if you were seasoned in terms of working with open-source or closed-source -- it's my hypothesis that Drupal is *very* hard to learn for closed-source programmers who are just coming to open-source, because they're not used to reading the code of the systems they're using, and even as they start, it takes longer to grok what you're reading. That was my experience when I first learned Drupal.

ehsankhfr’s picture

And also new-comers in open-source should try to get used to sharing their code with others! Nothing can stay as secret in this society! :)

ehsankhfr’s picture

It's suitable for Drupal 6 as it has been written in the year 2008.

However, if anyone is going to do it in Drupal 7, you should change the attribute values to array as follows:

$form['customer_name'] = array(
'#type' => 'textfield',
'#title' => t('Customer Name'),
'#size' => 50,
'#attributes' => array('class' => array('name-textbox')),
);
$form['country'] = array(
'#type' => 'radios',
'#title' => t('Country'),
'#options' => array(t('Canada'), t('USA')),
'#attributes' => array('id' => array('custom-form-radios')),
);

tajinder.minhas’s picture

if you want to theme all forms nicely you can use hook_theme function to get template file for forms and then put avery form element in the structure provided by themer

Regards
Tajinder Singh Minhas
Software Engineer
(SDG SIPL)

jaypan’s picture

if you want to theme all forms nicely you can use hook_theme function to get template file for forms and then put avery form element in the structure provided by themer

Here is a tutorial on the matter: Themeing Drupal 7 Forms (Including CSS and JS)

Contact me to contract me for D7 -> D10/11 migrations.

jaypan’s picture

However, if anyone is going to do it in Drupal 7, you should change the attribute values to array as follows:

$form['customer_name'] = array(
'#type' => 'textfield',
'#title' => t('Customer Name'),
'#size' => 50,
'#attributes' => array('class' => array('name-textbox')),
);
$form['country'] = array(
'#type' => 'radios',
'#title' => t('Country'),
'#options' => array(t('Canada'), t('USA')),
'#attributes' => array('id' => array('custom-form-radios')),
);

The above is only partially correct. Only classes should be an array, not other attributes. The reason being that classes can have multiple values, while all other attributes only have a single value. So you would do something like the following:

$form['country'] = array
(
  '#type' => 'radios',
  '#title' => t('Country'),
  '#options' => array(t('Canada'), t('USA')),
  '#attributes' => array
  (
    'id' => 'some_custom_id',
    'class' => array('some_custom_class_1', 'some_custom_class_2'),
  ),
);

Contact me to contract me for D7 -> D10/11 migrations.

junestag’s picture

Terrific! Thanks for the tutorial. Worked perfectly.

gulshan kumar’s picture

Thanks