Article  |  Development

Block radios and checkboxes with simple_form

Reading time: Less than a minute

Block radios and checkboxes with simple_form

There’s a common issue I've been having with the default way that simple_form renders the markup for checkboxes and radio buttons.

It’s been irking me for some time, because all that needs to happen is have an element that wraps both label and input together so we can style them as a block.

But currently, the markup that renders is similar to this (with some of our custom bootstrap classes mixed in):

<div class="controls">
  <label class="radio">
    <input class="radio_buttons required" type="radio" value="true">
    First Option
  </label>
  <label class="radio">
    <input class="radio_buttons required" type="radio" value="false">
    Second Option
  </label>
</div>

This works fine… if I want to have these inline to each other. For example a simple ‘Yes’ or ‘No’.

But what if I have a collection of 5+ that would work better vertically down the page?

I need these radios to be block line elements so they will extend the width of the page. But there’s an issue when labels become block elements at 100%.

It means that all of the white space of the block element all the way to 100% of the containing element will be selectable and toggle the radio or checkbox because the element is a label that is configured to toggle the input.

Finally after much trial and error I found a few things:

  • Although you can wrap an :input in the simple_form config with as many different divs as you want, you can’t wrap individual pairs together. (As far as I can tell!) You can wrap the whole collection but not the pairs.
  • To get around this you have to change the config.boolean_style from :nested to :inline. It makes the markup look more like this:
<div class=”controls”>
  <span class=”radio”>
    <input class=”radio_buttons required” type="radio" value="true">
    <label class=”collection_radio_buttons”>First Option</label>
  </span>
  <span class=”radio”>
    <input class=”radio_buttons required” type="radio" value="false">
    <label class=”collection_radio_buttons”>Second Option</label>
  </span>
</div>

Wohoo! Now that we have everything wrapped in spans we can target them to set our display block on them.

For this I made a custom wrapper in the simple_form.rb config for the elements I knew I wanted the checkboxes or radios to be block elements. Looks like this:

config.wrappers :block_checkbox_and_radio, :tag => 'div', :class => 'control-group', :error_class => 'error' do |b|
  b.use :html5
  b.use :placeholder
  b.use :label
  b.wrapper :tag => 'div', :class => 'controls' do |input|
    input.use :input, :wrap_with => {:class => 'block-wrapper' }
    input.use :error, :wrap_with => { :tag => 'span', :class => 'help-inline' }
    input.use :hint,  :wrap_with => { :tag => 'p', :class => 'help-block' }
  end
end

Now you can just target those spans inside the block-wrapper class with your CSS and you should be good to go!

Have a project that needs help?