Bootstrap Icons
Bootstrap Ruby on Rails Icons
October 11, 2022 - Robert C.

Bootstrap Icon Packages

Bootstrap also has an icon package. It’s great to use because, at the start of many projects, many clients haven’t really decided on an icon pack yet, and while the bootstrap icons are nothing fantastical, they are, like the bootstrap CSS a workhorse. They don’t have everything, and there are more complete packs, but bootstrap icons are clean, look good small or large, and will work as a stand-in (or fallback) for quite some time on greenfield projects.

Including the icons and SVG

So this is a bit of a hiccup. I don’t want to include any CSS from bootstrap (or any of its javascript or other assets) in the project. But I do feel it’s necessary to include the SVG files for the icons. Again my goal is to have these icons as a fallback or default on projects where people haven’t picked an icon pack yet. That can be complicated if you don’t include some icons.

The other objective

Remember that our goal is not to tell people they have to do it one way or another but in this modification were adding in a default. So we need that default to be easy to override.

A note on the method

Before we dive in, I would like to address the way I access the icons. It’s not the fastest, there are faster ways. But not in ways that don’t mean including CSS. Making it more complicated, CSS should be able to override other CSS, but where font-based icons are concerned that’s rarely the case. Using SVG files may be a tiny bit slower, but it’s much easier to use, and should not have a measurable performance hit on modern host operating systems. I will explain a bit more later.

Getting the icons

This time we’re going to create a script to download the bootstrap icons and extract just the SVG files, then place those files in the proper directory. Again, there are other ways to do this. Git submodules for example. But I don’t want the entire project or even most of it. Just the SVG files.

#!/bin/bash
# scripts/update_icons.sh

FIRST=`pwd`

cd /tmp/
git clone https://github.com/twbs/icons.git
cd icons/icons/
cp *.svg $FIRST/icons/

cd $FIRST

The helper and the class

The helper is very simple and just calls ‘BootstrapHelpers::Icon.render’. Let’s look at the class:

def self.render(name)
  if File.exist?("#{Rails.root}/app/icons/#{name}.svg")
    File.open("#{Rails.root}/app/icons/#{name}.svg").read.html_safe
  else
    File.open("#{__dir__}/../../icons/#{name}.svg").read.html_safe
  end
rescue Errno::ENOENT
  'X'.html_safe
end

def self.enumerate
  output = []
  Dir::foreach(__dir__ + '/../../icons/') do |filename|
    next if filename == '.' || filename == '..'
    next if File.directory? filename

    output << File.basename(filename, File.extname(filename))
  end
  output
end

‘render’ is doing the heavy lifting it checks the project ‘app/icons’ directory for an svg that matches, and if found uses it. If not found, it returns to using the ones included in the gem (bootstrap icons). Finally, if it errors, due to a missing file (or likely a bad name), then it just shows a plain old X.

‘enumerate’ is just a way to list out what icons are available.

<%= row do %>
  <%= col size: 1 do %>
    <p class='text-center text-<%= %w[success warning danger info].sample %>'>
        <%= icon 'wsssjsjs' %><br/>
        Can't Exist
    </p>
  <% end %>
  <% @names.each do |name| %>
    <%= col size: 1 do %>
      <p class='text-center text-<%= %w[success warning danger info].sample %>'>
          <%= icon name %><br/>
          <%= name %>
      </p>
    <% end %>
  <% end %>
<% end %>

Our sample icons page, has an example of an icon that can’t exist, and iterates over the known icons and displays them.

@names = BootstrapHelpers::Icon.enumerate

The controller is quite simple. That sample call just randomizes the color a bit to make the page more interesting.

Icons

Spreading the icons around

Let’s actually use the icons. First let’s add some icons to the navbar.

<!-- /app/views/layouts/_main_navigation.html.erb -->
<%= navbar classes: 'bg-dark navbar-expand-lg navbar-dark ps-4' do |nav| %>
  <%= nav.brand content: "#{icon('todo')} Todo".html_safe %>
  <%= nav.toggler target: 'mainMenu' %>
  <%= nav.collapse id: 'mainMenu' do |c| %>
    <%= nav.items class: 'ms-auto' do |i| %>
      <%= i.link "#{icon('list-task')} Tasks".html_safe, items_path, class: 'btn btn-outline-secondary' %>
      <%= i.link "#{icon('plus-square')} New Task".html_safe, new_item_path, class: 'btn btn-outline-primary ms-4 me-4' %>
    <% end %>
  <% end %>
<% end %>

Icons on the Navbar

A very clean way to add an icon. And if you look at the repository you will notice were using a custom icon ‘todo’ (though it’s still just an SVG from bootstrap icons)

Next let’s use the new icon helpers in the alert and flash helpers.

def generate_icon(icon, level)
  return '' unless icon
  return icon.html_safe if icon.is_a? String

  case level.to_s
  when 'success'
    Icon.render('check-circle-fill')
  when 'info'
    Icon.render('info-circle-fill')
  else
    Icon.render('exclamation-triangle-fill')
  end
end

That is much more maintainable, and more extendable. Keep in mind that if you want to use font-awesome or some other icon pack you can. You can either put their svgs in /app/icons or just pass in the normal markup.

Conclusions

While I don’t like including so many files, if we’re going to have a fallback icon pack, then bootstrap icons are a good choice. Reading from the file to output a string also seems odd, but it’s the best way to tackle this. It keeps the files out of the asset pipeline, which seems like an odd goal, but you don’t want them in your pipeline if you’re never going to use them.

YOU MAY ALSO LIKE

software developer writing code
September 14, 2022 - Robert C.

Bootstrap: Accessibility and the Grid

adding helpers
August 12, 2022 - Robert C.

Adding the First Helpers in Ruby on Rails

next post
October 18th, 2022 - By Derek H.

Be the Team Member Everyone Loves