Easier form styling in Rails
Sunday, July 27th, 2008
Update:
The information in this article has been superseded by FormClassifier. Read this for background, but follow that link to download updated code.Web forms are always problematic to style. Even once you get them to the point where they look acceptable across all your supported browsers, you inevitably remember that you need a file upload control, which can’t be styled at all, leaving you to decide between going with the browser defaults or creating a custom control in Flash.
I can’t solve the file upload control problem for you, I’m afraid, but I can help you get around another common annoyance with form styling: The fact that text inputs and submit buttons are both input elements and therefore styles written for one will also be applied to the other, which is rarely what you want.
In a perfect world we could reliably use CSS 2.1 attribute selectors to target input elements with the type attribute set to “text” or “submit” but, alas, any world where people are still using Internet Explorer 6 is far from perfect. CSS3 selectors would be nice but even these 2.1 selectors would solve this problem for us.
Most people address this by setting a class attribute similar to “button” or “textfield” as appropriate on the form elements they want to style. This works great except that it’s tedious and error-prone. I use this method myself and am always having to go back and add these classes when I realize I forgot them the first time around.
When I recently had a similar problem on a Ruby on Rails application I’m working on I came up with a better answer: Ruby allows for method overriding and this can be used to our advantage when using the Rails ActionView form helpers. Simply override the form tag helpers with your own that adds the appropriate “button” or “textfield” classes automatically. Here’s an example:
Normally, the text_field_tag method works like this. The first line is the code you use in your view, and the second is what the form helper generates for you:
<%= text_field_tag "name" %>
<input id="name" name="name" type="text" />
Now, you could use :class => "textfield" here but that’s no different from including a “textfield” class in your HTML, and is best avoided for the reasons touched on above.
For the project I’m working on, I wrote new form helpers that override certain Rails methods in order to do this for me automatically. I put these new methods in a new module at app/helpers/form_helper.rb. As an example, here’s the code I use to insert a “textfield” class to all my text fields:
module FormHelper # Override text_field_tag method in order to add a default class def text_field_tag(*args) # Check options hash args = check_options_hash(args) # Append class name to string args.last[:class] += 'textfield' super(*args) end # Used by overridden form helpers above def check_options_hash(args) # Make sure an options hash exists unless args.last.kind_of?(Hash) args << {} end # Make sure :class key exists in hash, append space if it does if args.last.include?(:class) args.last[:class] += ' ' else args.last[:class] = '' end return args end end
Once this code is in place, make it accessible to all your views by adding this line to app/helpers/application_helper.rb:
include FormHelperIn a nutshell, what this does is overrides the default text_field_tag method with my own version that adds the appropriate class to the HTML options hash, then passes the supplied arguments, along with my own addition, to the original Rails text_field_tag method. I split out check_options_hash into its own method because I need to use it in each form helper I override and this helps keep things DRY.
So, using the example above, my text fields now come out looking like this:
<%= text_field_tag "name" %>
<input class="textfield" id="name" name="name" type="text" />
I can now write CSS to style input.textfield and input.button knowing that these classes will always be present on these elements, making forms easier to style and reducing time chasing down missing class attributes.

Your comments are welcome
Comment notes
<a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong> <pre lang="" line="">