April 24, 2020

2 responses

How to install Tailwind CSS using Ruby on Rails

In this guide, you'll learn how to install Tailwind CSS using Ruby on Rails

It occurred to me recently that I never actually recorded a screencast about installing Tailwind CSS within the Ruby on Rails framework.

For most screencasts, I tend to lean for my kickoff_tailwind application template as a quick way to get a new app up and running. Inside the template, I have the process of installing Tailwind CSS automated minus a few PostCSS configuration settings.

While it's relatively straight forward, there are some quirks to the process. I hope you find this guide useful. The written form below will be a bit brief while the video goes into more depth about the details.

I'd recommend referring the Tailwind documentation if you get hung up.

Create a new rails app

This is quite straight forward. At the time of recording/writing, I'm using Rails 6.0.2 and Ruby 2.6.6.

$ rails new tailwind_demo

Rails 6 gives us webpack configurations by default. We also get support for PostCSS which is a requirement to get Tailwind installed.

Installing Tailwind CSS

Within your new rails app run the following:

$ yarn add tailwindcss

Getting our app ready for Tailwind

There are a few tasks to do once the node package is installed in order for Rails to play nicely with the framework. I'll go into more details but here's a quick outline:

  1. Set up our postcss.config.js file to require the new node module and provide a relative path to a file called tailwind.config.js
  2. Create a tailwind.config.js file.
  3. Create the main CSS file where we import Tailwind CSS specific modules.
  4. Add our stylesheet pack tag to the main application layout view (or your view of choice).
  5. Consider using PurgeCSS to reduce the file size of our final CSS output.

PostCSS configuration.

At the root of your Rails project, you should find a postcss.config.js file. Inside this file will already be some code. We're going to append more to the file. I prefer the following format:

// postcss.config.js
let environment = {
  plugins: [
    require('tailwindcss')('./app/javascript/stylesheets/tailwind.config.js'),
    require('postcss-import'),
    require('postcss-flexbugs-fixes'),
    require('postcss-preset-env')({
      autoprefixer: {
        flexbox: 'no-2009'
      },
      stage: 3
    })
  ]
};

module.exports = environment;

I've added a new require('tailwindcss) line to tell our PostCSS configuration to use Tailwind CSS. Tailwind requires a configuration file that we pass in parenthesis following the require statement. You can put your configuration file anywhere in your project but I tend to group things inside app/stylesheets as it's all related to Tailwind. The Tailwind CSS documentation states to add autoprefixer as well. Luckily enough we already have that installed so we save a step.

Create a tailwind.config.js file.

The heart of Tailwind CSS is the config file. Here you define your own theme options and plugins. Tailwind takes an unopinionated approach so the possibilities here are endless. I'm going to make use of the default theme that ships with Tailwind CSS for brevity's sake. We can generate this all from the command line:

$ npx tailwindcss init --full

This command should create a tailwind.config.js file at the root of your project with the default theme configuration inside. As I stated before, I prefer to have my tailwind.config.js file live inside app/stylesheets. You'll need to create this folder as it won't exist initially.

Add Tailwind to your CSS

Inside app/stylesheets create a new file called application.scss (or whatever you prefer here). This file will be what we import inside our default app/javascript/packs/application.js file. Since this is all generated with JavaScript, this is all totally possible.

/* app/javascript/stylesheets/application.scss */
@import "tailwindcss/base";
@import "tailwindcss/components";

// You can add your own custom components/styles here

@import "tailwindcss/utilities";

Next, we need to import this file into our app/javascript/packs/application.js file. That's as simple as:

// app/javascript/packs/application.js

require("@rails/ujs").start()
require("turbolinks").start()
require("@rails/activestorage").start()
require("channels")

// Tailwind CSS
import "stylesheets/application" // ADD THIS LINE

Update layout template

We need a way for our HTML to absorb our work this far. With modern versions of Rails our work is nearly complete thanks to the addition of the javascript_pack_tag inside our app/views/layouts/application.html.erb file. We still need to add a stylesheet_pack_tag to that same template to get things to work.

Note the pre-existing stylesheet_link_tag. Legacy versions of Rails tended to use what is called the Asset Pipeline. This is still in use today but the idea is everything within your app/assets/stylesheets directly would get compiled down into one file for use here. The way it worked is an inspiration for a lot of build processes you find today. I definitely recommend studying up on it if you're interested.

Today's conventions assume you'll only use the Asset Pipeline for CSS and images and leave Webpack for your javascript. So everything inside app/javascript is all Webpack as a result.

<!-- app/views/layouts/application.html.erb -->

<!DOCTYPE html>
<html>
  <head>
    <title>TailwindDemo</title>
    <%= csrf_meta_tags %>
    <%= csp_meta_tag %>

    <%= stylesheet_link_tag 'application', media: 'all', 'data-turbolinks-track': 'reload' %>
    <%= stylesheet_pack_tag 'application', 'data-turbolinks-track': 'reload' %>
    <%= javascript_pack_tag 'application', 'data-turbolinks-track': 'reload' %>
  </head>

  <body>
    <%= yield %>
  </body>
</html>

Testing your work

Testing your work is pretty simple at this point. We need a way to see a basic template that isn't the default welcome screen Rails ships with.

$ rails generate controller static index

The command above will generate a static controller with an index action. The app/views/ folder should receive a new folder called static with a file called index.html.erb inside.

Update your routes

To test things out easily we can make our new static#index action our root path in our config/routes.rb file.

# config/routes.rb
Rails.application.routes.draw do
  root to: 'static#index'
end

Booting your server should get you to a functioning app with Tailwind CSS resetting all the text you see on the screen.

$ rails server

From here you can play around with classes on the body tag for example.

<!-- app/views/layouts/application.html.erb -->
<!DOCTYPE html>
<html>
  <head>
    <title>TailwindDemo</title>
    <%= csrf_meta_tags %>
    <%= csp_meta_tag %>

    <%= stylesheet_link_tag 'application', media: 'all', 'data-turbolinks-track': 'reload' %>
    <%= stylesheet_pack_tag 'application', 'data-turbolinks-track': 'reload' %>
    <%= javascript_pack_tag 'application', 'data-turbolinks-track': 'reload' %>
  </head>

  <body class="bg-black text-white font-sans">
    <%= yield %>
  </body>
</html>

Tips for reducing file size

When shipping Tailwind CSS to a production environment the file sizes can get massive thanks to all the classes that get generated. To reduce file sizes you can either clean up your configuration file a bit and/or use a tool like Purge-CSS.

This tool will find and compare any classes not in use in your app and remove them during compilation. It's super convenient and powerful but comes with a few quirks.

Installing is simple, just like we did Tailwind CSS

$ yarn add @fullhuman/postcss-purgecss

You'll want to save this as a regular dependency and not a dev dependency.

Setting it up gets a little more challenging. We need to modify our postcss.config.js file once again to account for the new module.

// postcss.config.js
let environment = {
  plugins: [
    require('tailwindcss')('./app/javascript/stylesheets/tailwind.config.js'),
    require('postcss-import'),
    require('postcss-flexbugs-fixes'),
    require('postcss-preset-env')({
      autoprefixer: {
        flexbox: 'no-2009'
      },
      stage: 3
    })
  ]
};

// Add everything below!
if (process.env.RAILS_ENV === 'production') {
  environment.plugins.push(
    require('@fullhuman/postcss-purgecss')({
      content: [
        './app/**/.html.erb',
        './app/helpers/**/*.rb',
        './app/javascript/**/*.js',
        './app/javascript/**/*.vue',
        './app/javascript/**/*.jsx',
      ],
      defaultExtractor: (content) => content.match(/[A-Za-z0-9-_:/]+/g) || []
    })
  )
}

module.exports = environment;

The biggest change to understand here is that we only want to run purgecss in our production environment. In our development environment it's not a big default to have larger file sizes.

We take our environment variable and push the new module onto the plugins array. From there we pass in an array called content which accepts relative paths that purgecss should look for when doing its work. Finally using the defualtExtractor method we can pass a RegEx pattern that instructs purgecss what to look for.

Gotchas

I've run into issues before where any custom CSS I add within app/javascript/stylesheets/application.scss gets removed. To cure this issue I found a way to whitelist that CSS using some handy commenting patterns from purgecss.

That might look like the following:

@import "tailwindcss/base";
@import "tailwindcss/components";

/*! purgecss start ignore */
@import "components/buttons";

body {
  @apply bg-blue-500 text-white;
}
/*! purgecss end ignore */

@import "tailwindcss/utilities";

Be sure to include the ! marks otherwise they will be stripped out. This code assumes you made a components folder inside app/javascript/stylesheets and a file called _buttons.scss.

Shameless plug time

I have a new course called Hello Rails. Hello Rails is a modern course designed to help you start using and understanding Ruby on Rails fast. If you're a novice when it comes to Ruby or Ruby on Rails I invite you to check out the site. The course will be much like these builds but a super more in-depth version with more realistic goals and deliverables. Download your copy today!!

Follow @hello_rails and myself @justalever on Twitter.

Leave a reply

Sign in or Sign up to leave a response.

2 responses

Nathan Roise
Icons/flag
Icons/link diagonal

Hi, I think you meant app/javascript/stylesheets instead of app/stylesheets above in these sections:
- PostCSS configuration
- Create a tailwind.config.js file
- Add Tailwind to your CSS
(That's what the video has, i.e. 5:06, anyway.)

Andy Leverenz
Icons/flag
Icons/link diagonal

Ah good spot. You're right!