Hacking Rails HTML form helpers to add CSS classes

I really like using Rails form helpers, but one thing I found myself doing often is assigning classes to elements that I want to style a bit differently than the rest. But, I have to remember to do that for every element that I want it applied to. Luckily the nature of Ruby lets you open up classes and modules to redefine methods, so I was able to hack up the ActionView tag helpers directly in my Rails app. The result is that by default, all form elements generated by the helpers get a CSS class name based on what type of element it is.

How is this useful? Well for instance, I like to style my HTML submit buttons to be fixed width and center aligned. With CSS, I can’t style a submit button specifically because it’s an input element. There are also input elements that can be text fields, radio buttons, and checkboxes, but I don’t want it to apply to them as well. So the path of least resistance to style a submit button exclusively is to assign it a class every time I use it. It can get tedious to have to remember that every time I build a new form in my application.

So here is my solution: hacking the ActionView tag helper module to make any form element automatically have a class assigned to it based on what type of element it is. In the case of a submit element, it will have a class submit_button. Then all I have to do is define the selector for input.submit_button in my stylesheet and I’m done. All submit buttons will now be styled the way I want, and I don’t have to remember to assign them a class every time I place one in a template.

Add this code to your application.rb file (or anywhere you’d like) and your form elements will get nice CSS classes by default. It will even keep any class names you assign to your tags and append the default one at the end. I’ve verified it working using ActionPack-1.12.5 and later (Rails 1.1.6 – 1.2.3).

  module ActionView
    module Helpers
      module TagHelper        def tag(name, options = nil, open = false)
          add_default_class(name,options)
          "<#{name}#{tag_options(options) if options}" + (open ? ">" : " />")
        end        def content_tag(name, content_or_options_with_block = nil, options = nil, &block)
          if block_given?
            options = content_or_options_with_block if content_or_options_with_block.is_a?(Hash)
            add_default_class(name,options)
            content = capture(&block)
            concat(content_tag_string(name, content, options), block.binding)
          else
            add_default_class(name,options)
            content = content_or_options_with_block
            content_tag_string(name, content, options)
          end
        end        private        def add_default_class(name,options)
          options = Hash.new if options.nil?
          class_name = case name.to_s
          when 'textarea'
            'text_area'
          when 'select'
            'select'
          else
            nil
          end          if class_name.nil? && options.has_key?('type')
            class_name = case options['type']
            when 'text'
              'text_field'
            when 'password'
              'password_field'
            when 'file'
              'file_field'
            when 'radio'
              'radio_button'
            when 'checkbox'
              'check_box'
            when 'submit'
              'submit_button'
            when 'image'
              'submit_image'
            when 'button'
              'button'
            else
              nil
            end
          end
          return options if class_name.nil?
          if options.has_key?('class')
            options['class'] += " #{class_name}"
          else
            options['class'] = "#{class_name}"
          end
        end
      end
    end
  end

1 Response to “Hacking Rails HTML form helpers to add CSS classes”


  1. 1 Joshua Nichols

    You actually can target specific types of form fields. More generally, you can actually target with any element with a particular attribute and value.

    input[type=submit] {
    width: 100px;
    }

    But of course, this doesn’t work with older version of IE, but has been fixed with IE 7.

Leave a Reply