Adding Bootstrap 4 and PurgeCSS 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 path/to/my/phoenix/app
npm install --prefix assets --save-dev bootstrap@4 purgecss-webpack-plugin glob-all
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 defaultglob
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',
+ './node_modules/some_library/**/*.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',
'./node_modules/some_library/**/*.js',
]),
}),
]
),
}
};
I personally prefer to apply it to both development and production so that I can prevent surprise at deployment.
4. Remove unnecessay Phoenix default CSS
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;
}
5. Override Bootstrap variables (optional)
Optionally, we can override Bootstrap variables so that we can customize the styling. You can find the full list of Bootstrap variables in the library's scss/_variables.scss
file.
I personally make a file at assets/css/_variables.scss
of my Phoenix project and copy some sections of Bootstrap variables that I intend to override. It may look like below.
@import "../node_modules/bootstrap/scss/functions";
// Custom Variables
// Override Bootstrap variables here.
// https://github.com/twbs/bootstrap/blob/v4-dev/scss/_variables.scss
// Color system
$white: #fff;
$gray-100: #eceff1;
$gray-200: #cfd8dc;
$gray-300: #b0bec5;
$gray-400: #90a4ae;
$gray-500: #78909c;
$gray-600: #607d8b;
$gray-700: #546e7a;
$gray-800: #455a64;
$gray-900: #37474f;
$black: #263238;
$grays: ();
// stylelint-disable-next-line scss/dollar-variable-default
$grays: map-merge(
(
"100": $gray-100,
"200": $gray-200,
"300": $gray-300,
"400": $gray-400,
"500": $gray-500,
"600": $gray-600,
"700": $gray-700,
"800": $gray-800,
"900": $gray-900
),
$grays
);
// https://material.io/resources/color
$blue: #2962FF;
$indigo: #304FFE;
$purple: #AA00FF;
$pink: #C51162;
$red: #D50000;
$orange: #FF6D00;
$yellow: #FFD600;
$green: #00C853;
$teal: #00BFA5;
$cyan: #00B8D4;
One important thing is we should remove !default
when we copy Bootstrap variable from the library's file because our custom values will be final, not default.
Then I import my custom variables in my assets/css/app.scss
file.
/* This file is for your main application css. */
+ @import 'variables';
@import '../node_modules/nprogress/nprogress.css';
@import '../node_modules/bootstrap/scss/bootstrap.scss';
6. Add custom CSS (optional)
/* This file is for your main application css. */
@import 'variables';
@import '../node_modules/nprogress/nprogress.css';
@import '../node_modules/bootstrap/scss/bootstrap.scss';
+ @import 'my_custom_syles';
That's it!