How to make a Bootstrap 5 theme: typography
Now that our basic setup is done, we can start to customise the typography of our theme.
The scope of this tutorial is to start using the sass
variables in scss/_variables.scss
so we can change the look and feel of our typography while expanding our current setup.
The general idea
Import a custom font, use a modular scale, and extend Bootstrap 5 using scss/_variables.scss
to customise displays, headings and inline text elements.
In the meanwhile, we’ll have a small webserver running with some live reload and an index.html
to see how our typography is going on.
Extending the current setup: index.html and webserver
Let’s start creating our index.html
with some markup. We’ll import css/styles.css
as well, which you should have from the end of the last tutorial.
touch index.html
And now, we should have:
bootstrap5-theme
├── node_modules
├── .nvmrc
├── .gitignore
├── package-lock.json
├── package.json
├── css
│ └── styles.css
├── scss
│ ├── _variables.scss
│ └── styles.scss
└── index.html
Let’s edit index.html
and add the following:
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="stylesheet" href="css/styles.css">
<title>Bootstrap 5 Theme</title>
</head>
<body>
<div class="container">
<div class="row">
<div class="col-12 col-lg-8 col-xl-6 mx-auto py-4">
<h1>Displays</h1>
<div class="border p-3 mb-4">
<h1 class="display-1">Display 1</h1>
<h1 class="display-2">Display 2</h1>
<h1 class="display-3">Display 3</h1>
<h1 class="display-4">Display 4</h1>
<h1 class="display-5">Display 5</h1>
<h1 class="display-6">Display 6</h1>
</div>
<h1>Headings</h1>
<div class="border p-3 mb-4">
<h1>h1. Bootstrap heading</h1>
<h2>h2. Bootstrap heading</h2>
<h3>h3. Bootstrap heading</h3>
<h4>h4. Bootstrap heading</h4>
<h5>h5. Bootstrap heading</h5>
<h6>h6. Bootstrap heading</h6>
</div>
<h1>Inline text elements</h1>
<div class="border p-3 mb-4">
<p class="lead">lead text here</p>
<p>basic text here</p>
<p><a href="#">link</a></p>
</div>
</div>
</div>
</div>
</body>
</html>
Now, we can install a browser-sync which is a webserver that will also auto reload the page when we change our files.
npm i --save-dev browser-sync
And in devDependencies
you should have:
# in package.json you should have
"devDependencies": {
"browser-sync": "^2.27.9",
"node-sass": "^7.0.1"
}
Now, let’s add a serve
script to our package.json
in a way we can start browser-sync
with npm run serve
in cli
.
# In package.json
"scripts": {
"build": "node-sass scss/styles.scss css/styles.css",
"serve": "browser-sync start --server --files=\"css/*.css,index.html\""
},
As you can see browser-sync
has a --files
flag used to define which files to watch for changes to trigger the auto-reload.
In this case, we’re watching all the css files in the css
folder (css/*.css
) and index.html
.
When you npm run serve
you should have:
➜ npm run serve
> bootstrap5-theme@1.0.0 serve
> browser-sync start --server --files='css/*.css,index.html'
[Browsersync] Access URLs:
------------------------------------
Local: http://localhost:3001
External: http://192.168.0.6:3001
------------------------------------
UI: http://localhost:3002
UI External: http://localhost:3002
------------------------------------
[Browsersync] Serving files from: ./
[Browsersync] Watching files...
If so, it means that browser-sync
is up and running and you should be able to access index.html
from http://localhost:3002/
.
index.html
should be styled as per css/styles.css
Note: You can stop browser-sync
with ctrl+c
.
In addition, the page should automatically refresh every time we change index.html
or css/styles.css
(as it is, you’ll have to use npm run build
to change styles.css
).
Automate scss build
It’s the moment to automate the build of our styles.css
.
For this purpose will use chokidar, which will trigger npm run build
every time we change a scss
file.
npm i --save-dev chokidar-cli
In devDependencies
you should have:
# in package.json you should have
"devDependencies": {
"browser-sync": "^2.27.9",
"chokidar-cli": "^3.0.0",
"node-sass": "^7.0.1"
}
Let’s add a watch
script to our package.json
in a way we can build styles.css
when we change a scss
file. Use npm run watch
then to start watching the files.
# In package.json
"scripts": {
"build": "node-sass scss/styles.scss css/styles.css",
"serve": "browser-sync start --server --files=\"css/*css,index.html\"",
"watch": "chokidar \"scss/*.scss\" -c \"npm run build\""
},
Last touch, let’s install npm-run-all to polish the start script so we can run npm run serve
and npm run watch
on a single command (which is going to be npm start
).
npm i --save-dev npm-run-all
And in devDependencies
you should have:
# in package.json you should have
"devDependencies": {
"browser-sync": "^2.27.9",
"chokidar-cli": "^3.0.0",
"node-sass": "^7.0.1",
"npm-run-all": "^4.1.5"
}
The start
script now.
# In package.json
"scripts": {
"start": "npm-run-all --parallel watch serve",
"build": "node-sass scss/styles.scss css/styles.css",
"serve": "browser-sync start --server --files=\"css/*css,index.html\"",
"watch": "chokidar \"scss/*.scss\" -c \"npm run build\""
},
If you still here?! Congratulation! And if running npm start
and you got browser-sync
and the watch
task running you’re a legend!
We’re finally able to touch some scss
now!
Add a typeface
Ok! We’re ready to add our typeface. In this case, I’ll use Lato.
Download and place the woff
, woff2
fonts in a fonts
folder.
How to get woff and woff2 files?
Most of the free font providers (eg: fontsquirrel, googlefonts) provide ttf
file format which is not ideal for the web.
So you’ll need to convert the ttf
to woff2
.
To do that, you can use pages like Font Squirrel Webfont Generator or similar.
Let’s create scss/_fonts.scss
.
touch scss/_fonts.scss
Which has to be imported at the beginning of scss/styles.scss
.
After that you should have:
// scss/styles.scss
@import "fonts";
@import "./node_modules/bootstrap/scss/functions";
@import "variables";
.
.
.
Now, let’s add our @font-face
declaration in scss/_fonts.scss
/* lato-regular - latin */
@font-face {
font-family: 'Lato';
font-style: normal;
font-weight: 400;
src: local(''),
url('../fonts/lato-v22-latin-regular.woff2') format('woff2'),
url('../fonts/lato-v22-latin-regular.woff') format('woff');
font-display: swap;
}
/* lato-700 - latin */
@font-face {
font-family: 'Lato';
font-style: normal;
font-weight: 700;
src: local(''),
url('../fonts/lato-v22-latin-700.woff2') format('woff2'),
url('../fonts/lato-v22-latin-700.woff') format('woff');
font-display: swap;
}
@font-face {
font-family: 'Lato';
font-style: normal;
font-weight: 300;
src: local(''),
url('../fonts/lato-v22-latin-300.woff2') format('woff2'),
url('../fonts/lato-v22-latin-300.woff') format('woff');
font-display: swap;
}
/* lato-italic - latin */
@font-face {
font-family: 'Lato';
font-style: italic;
font-weight: 400;
src: local(''),
url('../fonts/lato-v22-latin-italic.woff2') format('woff2'),
url('../fonts/lato-v22-latin-italic.woff') format('woff');
font-display: swap;
}
Anti aliasing:
Currently antialiasing is out of specs for css3 but some browsers do implement it and sometimes you may want to boost font rendering.
html * {
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale
}
At this point you should have the following folder structure:
bootstrap5-theme
├── node_modules
├── .nvmrc
├── .gitignore
├── package-lock.json
├── package.json
├── css
│ └── styles.css
├── fonts
│ ├── lato-v22-latin-300.woff
│ ├── lato-v22-latin-300.woff2
│ ├── lato-v22-latin-regular.woff
│ ├── lato-v22-latin-regular.woff2
│ ├── lato-v22-latin-italic.woff
│ ├── lato-v22-latin-italic.woff2
│ ├── lato-v22-latin-700.woff
│ └── lato-v22-latin-700.woff2
├── scss
│ ├── _variables.scss
│ └── styles.scss
└── index.html
Last but not least, let’s edit scss/_variables.scss
and set the default font to Lato
changing $font-family-base
.
$font-family-base
it is used to setup a css variable --bs-font-family-base
in :root
within node_modules/bootstrap/scss/_root.scss
.
--bs-font-family-base
is then used within node_module/bootstrap/scss/_reboot.scss
in the body
.
$font-family-base: Lato, arial, sans-serif;
.
.
.
You should have your theme using Lato
. If that is so, congratulation! You finally changed a variable in Bootstrap 5 and built it!
woff2 and woff:
We used to have many formats to consume fonts on the web (ttf
, eot
, woff
, svg
) but nowadays woff2
and woff
will suffice.
See Why should we include ttf, eot, woff, svg,… in a font-face
Preload fonts for production:
If you were in production to speed up font loading you would use a <link rel="preload">
in your <head>
html tag.
<link
rel="preload"
href="fonts/lato-v22-latin-700.woff2"
as="font"
type="font/woff2"
crossorigin />
See Preload web fonts to improve loading speed for more details.
Use a modular scale
As the readme of Modular Scale state: A modular scale is a list of values that share the same relationship
.
Which it means that we’re going to use modular-scale
to calculate for us some ratios, some numbers in a way that our typography looks harmonious.
npm i modular-scale
And then let’s import at the beginning of scss/styles.scss
// scss/styles.scss
@import "./node_modules/modularscale-sass/stylesheets/modularscale";
@import "fonts";
@import "./node_modules/bootstrap/scss/functions";
.
.
.
In this case, I’ve opted to use 1.618
as ratio
and (1rem
, 1.25rem
, 1.5rem
, 1.75rem
) as base, see the scale.
Now, I’ll add the $modularscale
at the beginning of scss/_variables.scss
:
$modularscale: (
base: 1rem 1.25rem 1.5rem 1.75rem,
ratio: 1.618
);
So, in theory, we should be able to use the ms
function from modular-scale
.
Inline text elements
Let’s start with the inline text elements, which are the a tags
(links) and the lead text.
As a plus, we’ll set the body background color and body color. We’ll pick the colors from the ones provided by Bootstrap.
In scss/_variables.scss
let’s change the following:
Body settings: will have white background and $gray-900
text-color
$body-bg: $white;
$body-color: $gray-900;
Links: will use indigo color (respectively $indigo-500
and $indigo-300
on hover) for the rest will leave the default values.
$link-color: $indigo-500;
$link-decoration: underline;
$link-shade-percentage: 20%;
$link-hover-color: $indigo-300;
$link-hover-decoration: null ;
For the lead, will only change the font size, using the ms
function from our modular scale (In this case ms(3)
which is 1.5rem
).
$lead-font-size: ms(3);
$lead-font-weight: 300;
These variables, they going to be used in:
node_modules/bootstrap/scss/_root.scss
to define the css variablesnode_modules/bootstrap/scss/_reboot.scss
for body and linksnode_modules/bootstrap/scss/_type.scss
for the lead
!default
We did remove the !default
keyword. In theory, we could have left it there, it would have not changed the current behaviour.
But, as we are the one who is changing the default values to a defined value, I think it’s wiser and clear to state a) we did change that value (as the rest of the variables still have the !default
) b) we’re not expecting any extra override.
Also, we could have used a different _custom_variable.scss
on top of the current imports within styles.scss
and simply override the variables we wanted to use instead to copy _variables.scss
from Bootstrap.
I think that having the copy facilitates wandering around and curiosity (hopefully you’ll read and edit more variables than what we use in this tutorial).
Displays and headings
Ok, displays and headings at last!
In general, we’re going to change the font sizes using our modular scale and we’re going to use $purple-900
as heading color.
Note: The display color is not set on the .display
class but is inherited by the tag used.
Which it means: <div class="display-1">
will be $gray-900
, while <h1 class="display-1">
will be $purple-900
.
In scss/_variables.scss
let’s change the following:
Headings: font size / modular scale and color
$h1-font-size: ms(8);
$h2-font-size: ms(7);
$h3-font-size: ms(6);
$h4-font-size: ms(5);
$h5-font-size: ms(3);
$h6-font-size: ms(1);
$font-sizes: (
1: $h1-font-size,
2: $h2-font-size,
3: $h3-font-size,
4: $h4-font-size,
5: $h5-font-size,
6: $h6-font-size
);
$headings-color: $purple-900;
Displays: font size / modular scale.
$display-font-sizes: (
1: ms(14),
2: ms(13),
3: ms(12),
4: ms(11),
5: ms(10),
6: ms(9)
);
These variables will affect the following:
node_modules/bootstrap/scss/_reboot.scss
basic heading is therenode_modules/bootstrap/scss/_type.scss
heading classes and displays
Quick recap:
- we did extend our codebase with
browser-sync
,chokidar-cli
andnpm-run-all
- then we installed
modular-scale
and setup the font - we changed the variables for the inline text elements
- we changed the variables for the displays and heading
We started the customisation finally! We’ve got the base font, some inline text elements, headings and displays.
Congratulation! Our theme start to get shape!
But, most importantly we wondered around scss/_variables.scss
, _reboot.scss
and _type.scss
and hopefully you played a little bit more with other variables too.
As usual, curiosity is a must!
Notes
This post was partially inspired by bootstrap-npm-starter.