The Earl of Bubblehum presents

Bootstrap
Helpers

Include Bootstrap components in your apps with some fun Ruby helpers.

The Earl of Bubblehum, a magician in a tall hat with a gumball beard, raising a wand. Star bh on GitHub

Overview

Bootstrap 6 is a great framework that requires many lines of HTML for a simple component such as a dialog:

<button type="button" class="btn-solid theme-primary" data-bs-toggle="dialog" data-bs-target="#exampleDialog">
  Open dialog
</button>
<dialog class="dialog" id="exampleDialog">
  <div class="dialog-header">
    <h1 class="dialog-title">Dialog title</h1>
    <button type="button" class="btn-close" data-bs-dismiss="dialog" aria-label="Close"></button>
  </div>
  <div class="dialog-body">
    <p>This is a native dialog element. It uses the browser’s built-in modal behavior for accessibility and focus management.</p>
  </div>
  <div class="dialog-footer">
    <button type="button" class="btn-solid theme-secondary" data-bs-dismiss="dialog">Close</button>
    <button type="button" class="btn-solid theme-primary">Save changes</button>
  </div>
</dialog>

With Bootstrap helpers, the code above can be rendered in just three lines:

<%= dialog 'What is in the hat?', title: 'A gumball, mostly' do %>
  And whatever the wand last turned into one.
<% end %>

How to install

Bh is a Rails engine. It brings Bootstrap 6 with it, so nothing else goes in the Gemfile.

# Gemfile
gem 'bh', '~> 6.7.0'

Rails 8.1 and Ruby 3.2 are the minimum.

The form Bootstrap docs

One builder dresses every field, so a view says what it wants and never a class.

class ApplicationController < ActionController::Base
  default_form_builder Bh::FormBuilder
end
<%= form.fieldset 'The word' do %>
  <%= form.label :word, 'Six letters of it' %>
  <%= form.pin_field :pin %>
  <%= form.submit 'Say it' %>
  <%= form.button 'Whisper it', type: :button %>
<% end %>

What it draws

The word

4
8
2
9
1
3
API reference →

Combobox Bootstrap docs

A long <select> becomes a searchable menu — and stays a select, so the form submits exactly as before.

<%= form.combobox :shape, shapes, { prompt: 'Pick a shape' }, multiple: true %>

What it draws

Gumballs + 2 more ▾
✓ Gumballs 38,405
✓ Ribbons 9,120
✓ Stars 4,067
All
API reference →

The 6-digit code Bootstrap docs

The six slots a one-time code is typed into, drawn from one real field — so a phone can offer the code straight from the text that brought it.

<%= form.pin_field :pin_confirmation, required: true %>

What it draws

4
8
2
9
1
3
API reference →

Phone numbers

A number that formats itself as it is typed and stores as ten bare digits, so no page has to agree on how to write one.

<%= form.phone_field :phone %>

What it draws

415-555-0000
API reference →

Dialogs Bootstrap docs

A link and the dialog it opens, with the backdrop, the focus trap and Esc that the browser's own element brings. Nothing is scripted, and no page invents an id.

<%= dialog 'What is in the hat?', title: 'A gumball, mostly' do %>
  And whatever the wand last turned into one.
<% end %>

What it draws

What is in the hat?

A gumball, mostly ✕
And whatever the wand last turned into one.
API reference →

Toasts Bootstrap docs

The flash, as toasts at the bottom right. They stand before any script runs and leave on their own.

<%= toasts %>

What it draws

The wand was polished. ✕
The hat would not open. ✕
API reference →

Chat

A thread of bubbles with a field under it, drawn from anything that can say who spoke, when, and whether it arrived.

<%= chat_with url: questions_path, hints: Chat::HINTS, messages: messages %>

What it draws

the Earl · 5 minutes ago

The hat is warm. Ask it something.

you · 4 minutes ago ✓

How many gumballs in the beard?

the Earl · just now · one wave

API reference →

The palettes

Nine of them, linked one at a time after the stylesheet. Each repaints Bootstrap's own families, and every one of them is legible in both light and dark.

  • bootstrap
  • dawn
  • dracula
  • gruvbox
  • monokai
  • nord
  • one_dark
  • solarized
  • tokyo_night

Swapping one for another

The scheme controller does it, and remembers what the reader picked.

<button type='button' data-controller='scheme' data-action='scheme#rotate'
        data-scheme-themes-value='["bootstrap","nord","dracula"]'
        data-scheme-path-value='/bh/theme' data-scheme-storage-value='scheme'>
  Another palette
</button>
API reference →

Trying it out

The gem carries a dummy app that draws every helper on one page. From the root of a clone:

bin/setup
rails s

localhost:3000 is every component at once, and ?flash=1 adds the toasts.

for the Earl

200
Sends to a GitHub discussion with your GitHub handle