Adding Bootstrap 4 and Purge CSS to Phoenix 1.5

I hear Tailwind is popular now, but I personally do not see any benefit. It just adds extra complexity. First I tried to get used to it but I feel a lot more productive using Bootstrap and I like simple Bootstrap styles better. So I decided to stick with Bootstrap. For setting up Tailwind, this Pragmatic Studio post was helpful, by the way.

Here is how I set it up.

1. Install Bootstrap

cd assets
npm install bootstrap purgecss-webpack-plugin glob-all --save-dev

2. Import Bootstrap CSS

Next we need to import Bootstrap's CSS into our assets/css/app.scss file.

  /* This file is for your main application css. */
  @import "../node_modules/nprogress/nprogress.css";
+ @import "../node_modules/bootstrap/scss/bootstrap.scss";

3. Configure Purge CSS

This is an optional step but we might as well get rid of unused CSS from Bootstrap since it is easy to set up. Open the assets/webpack.config.js file and modify a few locations:

  • Use glob-all instead of default glob because we need to specify multiple filenames to Purge CSS.
  • Require purgecss-webpack-plugin
  • Specify array of file paths that may reference any Bootstrap CSS class by name, which in Phoenix are all the view modules, template files, and JavaScript files.
  const path = require('path');
- const glob = require('glob');
+ const glob = require('glob-all');
  const HardSourceWebpackPlugin = require('hard-source-webpack-plugin');
  const MiniCssExtractPlugin = require('mini-css-extract-plugin');
  const TerserPlugin = require('terser-webpack-plugin');
  const OptimizeCSSAssetsPlugin = require('optimize-css-assets-webpack-plugin');
  const CopyWebpackPlugin = require('copy-webpack-plugin');
+ const PurgecssPlugin = require('purgecss-webpack-plugin');

  module.exports = (env, options) => {
    const devMode = options.mode !== 'production';

    return {

      # ...

      plugins: [
        new MiniCssExtractPlugin({ filename: '../css/app.css' }),
        new CopyWebpackPlugin([{ from: 'static/', to: '../' }]),
+       new PurgecssPlugin({
+         paths: glob.sync([
+           '../lib/**/*.ex',
+           '../lib/**/*.leex',
+           '../lib/**/*.eex',
+           './js/**/*.js'
+         ]),
+       }),
      ]
      .concat(devMode ? [new HardSourceWebpackPlugin()] : [])
    }
  };

Or if we want to use Purge CSS only in production mode

module.exports = (env, options) => {
  const devMode = options.mode !== 'production';

  return {

    # ...

    plugins: [
      new MiniCssExtractPlugin({ filename: '../css/app.css' }),
      new CopyWebpackPlugin([{ from: 'static/', to: '../' }]),
    ].concat(
      devMode
        ? [
            // development only
            new HardSourceWebpackPlugin(),
          ]
        : [
            // production only
            new PurgecssPlugin({
              paths: glob.sync([
                '../lib/**/*.ex',
                '../lib/**/*.leex',
                '../lib/**/*.eex',
                './js/**/*.js',
              ]),
            }),
          ]
    ),
  }
};

Now that we use Bootstrap CSS, we can remove Phoenix default CSS for the flash message, other than .alert:empty, which hides the flash message when there is nothing to show.

- .alert {
-   padding: 15px;
-   margin-bottom: 20px;
-   border: 1px solid transparent;
-   border-radius: 4px;
- }
- .alert-info {
-   color: #31708f;
-   background-color: #d9edf7;
-   border-color: #bce8f1;
- }
- .alert-warning {
-   color: #8a6d3b;
-   background-color: #fcf8e3;
-   border-color: #faebcc;
- }
- .alert-danger {
-   color: #a94442;
-   background-color: #f2dede;
-   border-color: #ebccd1;
- }
- .alert p {
-   margin-bottom: 0;
- }
  .alert:empty {
    display: none;
  }

That's it!