Styling the application
The application is currently looking a bit plain:

We can change this by using Bootstrap[1], which is a front-end CSS and JavaScript framework that comes with a collection of styles that we can apply to our application. When we use these styles, our new project form will turn into this:

You’ll notice the asterisk next to the "Name" field here. This is because the
"Name" field is a required field, and that’s because we have a validation on our
Project
model for the presence of this field. We’re going to be using a gem called bootstrap_form
to style our form in this way.
The bootstrap_form
gem not only makes our form neater, but also makes the code for generating a form much simpler. Whereas we have this now:
<div>
<%= form.label :name %>
<%= form.text_field :name %>
</div>
<div>
<%= form.label :description %>
<%= form.text_field :description %>
</div>
The bootstrap_form
gem allows us to write this instead:
<%= f.text_field :name %>
<%= f.text_field :description %>
The Bootstrap framework also lends itself to more than just forms. We’ll be using it in this section to style the flash messages from our application, as well as adding a navigation bar to the top of the screen.

Let’s get started!
Installing Bootstrap
The first thing that we need to do is to install the bootstrap
gem,
which comes with Sass versions of the Bootstrap assets. Sass bills itself as "CSS with Superpowers", and we’ll be using it to write our CSS for our application.
This bootstrap
gem is the recommended way to install Bootstrap in to a Rails application. Let’s add this gem to our application now by running this command:
bundle add bootstrap -v {bootstrap_version}
To add Bootstrap’s styles to your application, you’ll need to make your main
application stylesheet a Sass file, by renaming
app/assets/stylesheets/application.css
to application.css.scss
, adding the
.scss
extension to the end of the filename. This extension tells the asset pipeline that this file should be processed using the Sass pre-processor, before it’s presented as a CSS file. This pre-processing will convert any Sass-specific code to CSS code, so that browsers can understand it.
Let’s replace the entire contents of that application.css.scss
file
with this:
@import "bootstrap";
This line imports all of Bootstrap’s CSS components.[2] With these assets now imported, we can restart our server and go to http://localhost:3000 to see some changes immediately:

The styling of our home page has changed to reflect Bootstrap’s default
styling! That was easy. It’s all hard up against the left side of the page
though. We can fix this by wrapping all of the content in a container in
app/views/layouts/application.html.erb
:
<body>
<div class="container-fluid">
<% flash.each do |key, message| %>
<div><%= message %></div>
<% end %>
<%= yield %>
</div>
</body>
This element will shift the content away from the left-hand side of the page by using Bootstrap’s container-fluid
class[3]:

Improving the page’s header
Next up, let’s make the header section of our page look a little bit nicer,
starting with the main heading. Bootstrap provides utility classes[4] that
we can use to give a bit of style, so let’s wrap these around the h1
in
app/views/projects/index.html.erb
.
<div class="pb-2 mt-4 mb-2 border-bottom">
<h1>Projects</h1>
</div>
It adds some nice spacing around the important header, and an underline. So far so good. Now we can look at the actions we can take on this page - the most obvious action is to create a new project, so we’ll make our "New Project" link stand out.
Ultimately, we want to make the "New Project" be part of the page header, and we’ll put it on the right hand side to give the page a bit of balance.
In app/views/projects/index.html.erb
, change the code from this:
<div class="pb-2 mt-4 mb-2 border-bottom">
<h1>Projects</h1>
</div>
<%= link_to "New Project", new_project_path %>
To this:
<div class="pb-2 mt-4 mb-2 border-bottom">
<h1>Projects</h1>
<ul class="actions">
<li><%= link_to "New Project", new_project_path,
class: "btn btn-success" %></li>
</ul>
</div>
The relevant page actions is now part of the header, in a list (as some pages
will have multiple actions to take, such as edit or delete). And adding the two
btn btn-success
classes to any HTML element will change its appearance into a
button, like this:

The btn-success
turns it into a specific type of button.[5] We can
add a little bit more flair to this button by adding an icon from the Font
Awesome project[6], turning it into this:

To get to that newest, best looking version of the button, let’s add the
font-awesome-rails
gem to our application with this command:
bundle add font-awesome-rails -v 4.7.0.6
Just like the bootstrap
gem, the font-awesome-rails
gem also comes
with some assets. The assets from the font-awesome-rails
gem give us a whole
range of icons, shown on the Font Awesome icons page:
http://fortawesome.github.io/Font-Awesome/icons/. To use these icons, we must
first run bundle
and restart the Rails server. Then we’ll need to
add another @import
line to app/assets/stylesheets/application.css.scss
,
after the bootstrap line that you have already:
font-awesome
to your application.css.scss
@import "bootstrap";
@import "font-awesome";
To add the icon to the button, you can use the fa_icon
helper — this comes from the font-awesome-rails
gem — as part of the
link in app/views/projects/index.html.erb
, like this:
<li><%= link_to fa_icon("plus") + " New Project", new_project_path,
class: "btn btn-success" %></li>
It will now look like this:

Positioning actions with custom CSS
This header looks a bit weird. What we’d like it to be is this:

The header over there on the left, and the actions over there on the right.
To do this, we can add a couple of lines of CSS to app/assets/stylesheets/application.css.scss
:
.page-header {
@extend .pb-2, .mt-4, .mb-2, .border-bottom, .row;
h1,
h2,
h3,
h4,
h5,
h6 {
@extend .col-sm-12, .col-md-6;
}
ul.actions {
@extend .col-sm-12, .col-md-6;
@extend .list-unstyled, .list-inline;
li {
@extend .list-inline-item;
}
text-align: right;
}
}
This does a couple of things:
-
Provides us with a single class that we can use to apply all the page header utility styles in
.page-header
. We will change our view shortly to use this class. -
Uses Bootstrap’s
.row
and.col-*
classes to specify the widths of the headers and actions. This will ensure that they display neatly on all screen sizes. When we usecol-sm-12
, it says that on small screens that element should take up 12 columns (the entire screen). Withcol-md-6
, this means on medium screens (and above) those same elements will only take up 6 columns, half the screen. -
Removes the dot to the left of the button with
.list-unstyled
onul.actions
. -
Makes all potential actions inline with
.list-inline
. The default behaviour is to display each of them on their own line. -
Moves all the content in
ul.actions
to the right withtext-align: right
, pushing all actions to the right-hand-side of the screen.
When we make these changes to application.scss
, we’ll need to also change the class used in app/views/projects/index.html.erb
:
<div class="pb-2 mt-4 mb-2 border-bottom">
This should now be changed to:
<div class="page-header">
We’ve configured our CSS to apply the same utility classes for us (by using @extend
), so we do not have to do that in this tag any longer.
With these changes in place, when we refresh the page we’ll see our new header:

Applying the style elsewhere
You can apply similar styling to the views for the new
, edit
and show
actions. The new
and edit
views have no action menus, so their page headers
can simply be tweaked to add the .page-header
wrapper element:
<div class="page-header">
<h1>New Project</h1>
</div>
<div class="page-header">
<h1>Edit Project</h1>
</div>
Improving the show view
Improving the ProjectsController#show
view is a little more work, as it has links of different types. One
link edits; the other deletes. The header section of the page currently looks
like this:

Let’s change that header to look like this:
<div class="page-header">
<h1><%= @project.name %></h1>
<ul class="actions">
<li><%= link_to fa_icon("pencil") + " Edit Project",
edit_project_path(@project), class: "btn btn-primary" %></li>
<li><%= link_to fa_icon("trash") + " Delete Project",
project_path(@project),
method: :delete,
data: { confirm: "Are you sure you want to delete this project?" },
class: "btn btn-danger" %></li>
</ul>
</div>
Like before, there’s a <div class="page-header">
around the header, and our
links are now in a list of action links. We’ve chosen different button styles
for this page - btn-primary
for editing, and btn-danger
for deleting.
btn-danger
links are bright red, indicating a dangerous action. There’s also
some appropriate icons for the links.
These links will now be styled nicely:

Further down on the page, we have the tickets area. Let’s change this header now too:
<header class="page-header">
<h2>Tickets</h2>
<ul class="actions">
<li>
<%= link_to fa_icon("plus") + " New Ticket",
new_project_ticket_path(@project),
class: "btn btn-success" %>
</li>
</ul>
</header>
This will make the whole page look great:

Now that’s all looking a lot better, but we can do a lot better in our code!
Semantic styling
As it stands at the moment, whenever we have a "New", "Edit" or "Delete" link
in our application, we’re going to have to add all this markup around it with
the fa_icon
and the class
attribute. Rather than repeating all that
markup, we can use semantic styling. All the "New" links are going to look
the same way, all the "Edit" links are going to look the same way and all the
"Delete" links are going to look the same way, so why not style them all in an
easier fashion? Plus, if we decide later down the track that all "New" links
should now be styled differently, we’ll only have to update the code in one
place - in the stylesheet. Very DRY of us.
Styling buttons
To start fixing this, we can go back to app/views/projects/index.html.erb
and change the "New Project" link to this:
<%= link_to "New Project", new_project_path, class: "new" %>
The new
class will contain all the stylings for any "New" link in our
application, including the icon. To make this work, we’ll need to write some
Sass in app/assets/stylesheets/application.css.scss
which adds the same
stylings as we had before:
a.new {
@extend .btn, .btn-success;
&:before {
font-family: "FontAwesome";
@extend .fa-plus;
padding-right: 0.5em;
}
}
This new code first adds the btn
and btn-success
classes to this element
using the @extend
directive from Sass. This directive allows us to extend
any element’s styling with any other element’s styles. Next, we use the
&:before
rule which allows us to place content before the content within
an element. In this case, we’re setting the font-family
to "FontAwesome" so
that it uses the icon font. Then we’re using @extend
again to add the same
Plus icon that we added earlier using fa_icon
. The final line, padding-right
,
adds padding to the right-hand-side of this element so that the icon
and the "New Project" text have space between them.
If we look at our "New Project" link again, it will still have the same styles:

All of this has been accomplished with less styling in the view, and more in
the CSS file where it belongs. We can use these same techniques for the "Edit"
and "Delete" links inside of app/views/projects/show.html.erb
converting
them to just this:
<ul class="actions">
<li><%= link_to "Edit Project", edit_project_path(@project),
class: "edit" %></li>
<li><%= link_to "Delete Project", project_path(@project),
method: :delete,
data: { confirm: "Are you sure you want to delete this project?" },
class: "delete" %></li>
</ul>
Next, we can add styles for the edit
and delete
classes to
app/assets/stylesheets/application.css.scss
, in much the same way as we added
styles for the new
class:
a.edit {
@extend .btn, .btn-primary;
&:before {
font-family: "FontAwesome";
@extend .fa-pencil;
padding-right: 0.5em;
}
}
a.delete {
@extend .btn, .btn-danger;
&:before {
font-family: "FontAwesome";
@extend .fa-trash;
padding-right: 0.5em;
}
}
If we go to our project’s page once again, we’ll see the "Edit Project" and "Delete Project" links have stayed the same:

We’re not done yet though, there’s a bit of duplication happening in this file, which we can clean up to just this:
new
, edit
and delete
stylesa.new,
a.edit,
a.delete {
@extend .btn;
&:before {
font-family: "FontAwesome";
padding-right: 0.5em;
}
}
a.new {
@extend .btn-success;
&:before {
@extend .fa-plus;
}
}
a.edit {
@extend .btn-primary;
&:before {
@extend .fa-pencil;
}
}
a.delete {
@extend .btn-danger;
&:before {
@extend .fa-trash;
}
}
The links with new
, edit
and delete
classes will now be styled
identically as buttons that use the "FontAwesome" font. From there, each
different class has its button type and icon specified in different rules.
Styling headers
Where else can we apply this semantic styling? Well, we could replace this
<div class="page-header">
tag with something more meaningful, like a
<header>
tag. After all, if we want to use more than one of the page headers
on a single page, it’s not really a page header is it! Let’s make that change
on all of the changes we’ve made it on, so far:
<header>
<h1>Projects</h1>
...
</header>
<header>
<h1>New Project</h1>
</header>
<header>
<h1>Edit Project</h1>
</header>
<header>
<h1><%= @project.name %></h1>
...
</header>
...
<header>
<h2>Tickets</h2>
</header>
Now we can style our new header
tags, by changing our .page-header
CSS selector to header
:
header {
@extend .pb-2, .mt-4, .mb-2, .border-bottom, .row;
h1,
h2,
h3,
h4,
h5,
h6 {
@extend .col-sm-12, .col-md-6;
}
ul.actions {
@extend .col-sm-12, .col-md-6;
@extend .list-unstyled, .list-inline;
li {
@extend .list-inline-item;
}
text-align: right;
}
}
If we refresh all our pages, they should look exactly the same as they did
before - we haven’t changed any styles with our header
tag, we’ve just made
them more semantic. Using generic <div>
elements to represent headers is bad practice — we should use <header>
elements because they more clearly explain what the element does.
Styling flash messages
The next thing that we can style is our flash messages that appear. If you create another project in the Ticketee application, you can see how plain they are:

To make these stand out more, we’re going to apply Bootstrap’s alert styling
to it. Let’s open app/views/layouts/application.html.erb
and change this
code:
<% flash.each do |key, message| %>
<div><%= message %></div>
<% end %>
To this:
<% flash.each do |key, message| %>
<div class="alert alert-<%= key %>">
<%= message %>
</div>
<% end %>
The alert
class for each piece of the flash
will make this object stand
out more, and the alert-<%= key %>
will use another class called alert-notice
or alert-alert
to color the flash message box a different color. If
we look at Bootstrap’s documentation for their alerts
(http://getbootstrap.com/components/#alerts), we can see that alert-notice
and alert-alert
are not available:

In that example, we can see two that look like the kind of thing we want:
alert-success
and alert-danger
. We can make our alert-notice
and alert-alert
classes use the stylings of these two other classes with this code added to
app/assets/stylesheets/application.css.scss
:
.alert-notice {
@extend .alert-success;
}
.alert-alert {
@extend .alert-danger;
}
When an alert with the class
attribute of alert-notice
is displayed, it
will be styled as if it had a class
attribute of alert-success
. This is
thanks to the @extend
directive in Sass, which we used earlier with the
"New", "Edit" and "Delete" links.
When we create a project again, we should see a much nicer styled flash messages:

That’s much nicer! Let’s see what it looks like when we force a validation error by not entering a name for a new project:

That’s looking good too! That’s all for our flash stylings. We’re making some really good progress.
The next thing we’ll do is re-style our projects form to take it from this:

To this:

Using Bootstrap Form
To change the form to its new styling, we’re going to enlist the help of another gem called bootstrap_form
, which provides not only a shorter syntax for rendering forms, but also has Bootstrap integration. Let’s add this gem now:
bundle add bootstrap_form -v 4.5.0
Next, you’ll need add the import line for this gem to application.scss
:
@import "rails_bootstrap_forms";
Next, we can change our forms to use this new gem. We’ll do both the projects and tickets form.
Updating the styling of the project form
Over in app/views/projects/_form.html.erb
, let’s change this code:
<%= form_with(model: project, local: true) do |form| %>
To this:
<%= bootstrap_form_with(model: project, local: true) do |form| %>
This will tell the view to use the form builder from Bootstrap Form, rather than the one that’s built into Rails. Next, we can simplify the code used for rendering the name
and description
fields for our project form, turning it from this:
<div>
<%= form.label :name %>
<%= form.text_field :name %>
</div>
<div>
<%= form.label :description %>
<%= form.text_field :description %>
</div>
<%= form.submit %>
To this:
<%= form.text_field :name %>
<%= form.text_field :description %>
<%= form.primary %>
We don’t need to use the surrounding <div>
tags any more or to provide the
labels for the fields. Bootstrap Form takes care of all of that for us with the
input
helper. It also generates a button for us using its primary
helper
which operates similarly to the old f.submit
method, but styles it using
Bootstrap’s default styles — adding a btn
and btn-primary
class.
If you refresh the form page now, the form will now look like this:

It’s looking good. But if you submit the form with some validation errors, you’ll get a nasty shock:

We now have two sets of error messages, one styled, and one not! We only put one
set of errors on the page - the top "1 error prohibited this message from being
saved" set. The second set of errors, next to the fields they relate to, are
provided by the bootstrap_form
gem. They’re easier for users to understand -
if you had a long form and you wanted to know which fields you have errors for,
it might require scrolling up and down. So we can just use the errors from
bootstrap_form
, and delete the error messages we added.
Open up app/views/projects/_form.html.erb
, and delete the whole errors block
so that it just looks like this:
<%= bootstrap_form_with(model: project, local: true) do |form| %>
<%= form.text_field :name %>
<%= form.text_field :description %>
<%= form.primary %>
<% end %>
The code is much simpler than the original form partial we started off with, and the unsightly double errors are now gone!
This has an unfortunate side effect though, as you’ll see if you run the spec
for creating projects, with bundle exec rspec spec/features/creating_projects_spec.rb
:
1) Users can create new projects when providing invalid attributes Failure/Error: expect(page).to have_content "Name can't be blank" expected to find text "Name can't be blank" in "Project has not been created. New Project * Namecan't be blank Description"
Oops. We were checking for the presence of an error message that we just
deleted, and by default bootstrap_form
displays shortened error messages, such
as "can’t be blank". Luckily, it’s easy to configure Bootstrap Form to use full
error messages that include the name of the field.
We can do this by changing our form to this:
<%= bootstrap_form_with(model: project, local: true, label_errors: true) do |form| %>
<%= form.text_field :name %>
<%= form.text_field :description %>
<%= form.primary %>
<% end %>
This will make the error now appear in the label instead of underneath the input:

This will also fix the broken spec - you can verify this by running
bundle exec rspec spec/features/creating_projects_spec.rb
:
2 examples, 0 failures
Great! Now all of the project pages in our application are looking good.
Updating the styling of the ticket form
Our ticket form is currently not styled the same way as our project form:

To fix this, we’ll change the ticket form partial from this:
<%= form_with(model: [project, ticket], local: true) do |form| %>
<% if ticket.errors.any? %>
<div id="error_explanation">
<h2><%= pluralize(ticket.errors.count, "error") %>
prohibited this project from being saved:</h2>
<ul>
<% ticket.errors.full_messages.each do |msg| %>
<li><%= msg %></li>
<% end %>
</ul>
</div>
<% end %>
<p>
<%= form.label :name %><br>
<%= form.text_field :name %>
</p>
<p>
<%= form.label :description %><br>
<%= form.text_area :description %>
</p>
<%= form.submit %>
<% end %>
To this:
<%= bootstrap_form_with(model: [project, ticket], local: true, label_errors: true) do |form| %>
<%= form.text_field :name %>
<%= form.text_area :description %>
<%= form.primary %>
<% end %>
This form will now look like this:

Great!
Adding a navigation bar
We’ll do one more thing and then wrap up here: add a navigation bar to the top of our application’s layout. It will look like this:

This is by far the easiest part of the Bootstrap work that we’ve done so far;
all we need to do is to add this content above the flash messages (above the
<div class="container-fluid">
in our application’s layout):
<nav class="navbar navbar-expand-lg navbar-light bg-light">
<%= link_to "Ticketee", root_path, class: "navbar-brand" %>
<button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#collapse" aria-controls="collapse" aria-expanded="false" aria-label="Toggle navigation">
<span class="navbar-toggler-icon"></span>
</button>
<div class="collapse navbar-collapse" id="collapse">
<ul class="navbar-nav mr-auto">
<li class="nav-item <%= "active" if current_page?("/") %>">
<%= link_to "Home", root_path, class: "nav-link" %>
</li>
</ul>
</div>
</nav>
The navbar contains two different parts: a header and the navigation itself.
The header contains a link which shows the "Ticketee" text, and when that
link is clicked it will take the user back to the homepage. In the navigation,
we add a "Home" link, just in case people don’t realise that clicking
"Ticketee" will take them back to the homepage. If the user is currently at
the /
path, then an extra class called active
is added to the li
for
that link, turning it a different colour.
If you go to the application’s homepage, you’ll now see the navbar:

We’ve also elected to use the responsive navbar, meaning that it will display nicely on both large and small screens. You can test this out by resizing your browser, wider and smaller. When the window gets below 990px wide, the navbar will automatically switch to its "small" display:

It looks good! But the button at the top-right, which is supposed to cause the menu to expand, doesn’t work yet - it uses JavaScript to show and hide the menu contents, and we haven’t included Bootstrap’s JavaScript into our application yet. If you click that button, nothing will happen at the moment.
Like we included Bootstrap’s CSS into our application.css.scss
file, we also
need to include its JavaScript. Open up app/javascript/packs/application.js
,
and check out what it looks like. We haven’t modified it yet, so at the bottom
it will have four important lines:
application.js
filerequire("@rails/ujs").start();
require("turbolinks").start();
require("@rails/activestorage").start();
require("channels");
These lines require all the main JavaScript features that Rails uses. We won’t concern ourselves with these yet. We’ll look into some of them later on.
All that we need to do here is to add Bootstrap’s JavaScript:
require("bootstrap");
Next up, we will need to add Bootstrap’s JavaScript dependencies, which we do by running this command in our terminal:
yarn add bootstrap popper.js jquery
The popper.js
and jquery
JavaScript packages are other packages that
Bootstrap relies on for its functionality. When these packages are installed, we will need to add require
lines for these to application.js
too:
import 'jquery'
import 'popper.js'
import 'bootstrap'
Now when you refresh your browser, and press the little three-bar icon[7] the menu should expand and contract, like in the following screenshot:

While the navbar is a little bare at the moment, we will be filling it out in future chapters.
More responsive styling
We’ve fixed the top navigation bar to be fully responsive, but it would be great if our whole app looked great on-the-go, from a mobile phone, tablet, or any other device. Let’s look at implementing that now.
The first thing we need to do is include a special meta
tag in our document, to
ensure that responsive styles get picked up by mobile devices. Without this tag,
you’d see the same layout as you do on a desktop, just very very small to fit
on the smaller screens.
Add the following line of text just inside the <head>
section of the page, in
app/views/layouts/application.html.erb
:
<meta name="viewport" content="width=device-width, initial-scale=1">
Some background information on this meta
tag can be found on the Mozilla
Developer Network[8].
For now, we don’t have to know exactly how it works, but now we know that it
does work - our mobile browser won’t artificially pretend it has more pixels
than it has, to render larger layouts in smaller spaces.
Now our pages will use properly responsive styles on mobile. The way this works is with a CSS feature called media queries. Bootstrap uses media queries internally for lots of things, like the navbar styles we looked at earlier. If the screen is larger, the links in the bar appear in a line; if its smaller, they appear hidden, but then on their own line when revealed. We can do a similar sort of thing, using Bootstrap’s own defined styles.
Here’s what our project page will look like on an iPhone:

Whew, that was a lot of styling work! That completes all of our Bootstrap additions for the time being. Throughout the remainder of the book, we’ll be using features of Bootstrap as they’re needed to improve the design of our application, but we’ve got the basic foundation in place.
Let’s commit and push our recent changes now:
$ git add . $ git commit -m "Added Bootstrap for styling" $ git push