Skip to main content

Bootstrap 5

This site uses the general Bootstrap layout (i.e. navbar at the top and, with Bootstrap 5, off-canvas navigation).

The site specific appearance is generated through Sass:

The site uses Bootstrap 5.3’s dark mode using CSS variables.

Changes to Bootstrap 5:

Configuring Bootstrap’s Scrollspy:

Development setup

For reference, I use the following for building Bootstrap/Fat-Free websites:

Windows PC – supports Chrome, Firefox, Edge, and Opera browsers. I borrow a Mac, iPhone, and iPad to test with Safari.

Microsoft Edge – I use Edge to review site operation with Internet Explorer.

To use Edge to review pages in Internet Explorer:

  1. Open the page in Edge
  2. Reload the page in Internet Explorer mode using Edge’s menu or button
  3. To open IE’s Developer Tools:
    1. Open the Run command window (hold down the Windows key and press R)
    2. Enter %systemroot%\system32\f12\IEChooser.exe
    3. Select which tab to use with Developer Tools

Samsung Galaxy smart­phone – for testing mobile browsers. I use SDK Platform-Tools on my PC, which includes the Android Debug Bridge (adb) that connects Chrome on my PC to Chrome on my phone for Remote Debugging.

I use Google’s TalkBack screen reader to ensure the pages (and especially the navigation) work for visually impaired users.

Note: The Android debug bridge needs to be started first by opening a command window, going to the platform-tools directory, and running adb devices.

WAMP software stack – localhost using Apache, MySQL, and PHP (I don’t use the database) – Apache & PHP are needed for Fat-Free.

Node.js / npm package manager – for installing packages (see the package.json file on GitHub for all of the packages):

Visual Studio Code – (A.K.A. VS Code) for writing the code.

Live Sass Compiler – Glenn Marks’ VS Code extension for compiling Sass code into CSS during development.

Webpack 5 – for bundling multiple JavaScript files into a single file and for compiling production versions of the Sass to CSS file (file size is smaller). The Webpack files are available on GitHub.

Development file structure

/
 ├── .vscode
 ├── app
 │    └── ui
 ├── css
 ├── dict
 ├── js
 ├── images
 ├── lib
 ├── node_modules
 ├── src
 │    ├── js
 │    └── sass
 └── tmp

Production file structure

/
 ├── app
 │    └── ui
 ├── css
 ├── dict
 ├── js
 ├── images
 └── tmp

Sass organization

I use a common CSS file across all three languages I use for the site as the text can be displayed using a system font-family. For Korean language pages, I set word-break: keep-all; using the lang attribute. I use only the Bootstrap Sass files that I need.

style.scss

    @import "../../node_modules/bootstrap/scss/functions";

    // 2. Include any default variable overrides here
    @import "../scss/site_variables";

    // 3. Include remainder of required Bootstrap stylesheets (including any 
        separate color mode stylesheets)
    @import "../../node_modules/bootstrap/scss/variables";
    @import "../../node_modules/bootstrap/scss/variables-dark";

    // 4. Include any default map overrides here - not applicable

    // 5. Include remainder of required parts
    @import "../../node_modules/bootstrap/scss/maps";
    @import "../../node_modules/bootstrap/scss/mixins";
    @import "../../node_modules/bootstrap/scss/utilities";

    // 6. Optionally include any other parts as needed
    @import "../../node_modules/bootstrap/scss/root";
    @import "../../node_modules/bootstrap/scss/reboot";
    @import "../../node_modules/bootstrap/scss/type";
    @import "../../node_modules/bootstrap/scss/images";
    @import "../../node_modules/bootstrap/scss/containers";
    @import "../../node_modules/bootstrap/scss/grid";
    @import "../../node_modules/bootstrap/scss/forms";
    @import "../../node_modules/bootstrap/scss/buttons";
    @import "../../node_modules/bootstrap/scss/transitions";
    @import "../../node_modules/bootstrap/scss/dropdown";
    @import "../../node_modules/bootstrap/scss/nav";
    @import "../../node_modules/bootstrap/scss/navbar";
    @import "../../node_modules/bootstrap/scss/card";
    @import "../../node_modules/bootstrap/scss/alert";
    @import "../../node_modules/bootstrap/scss/list-group";
    @import "../../node_modules/bootstrap/scss/close";
    @import "../../node_modules/bootstrap/scss/modal";
    @import "../../node_modules/bootstrap/scss/carousel";
    @import "../../node_modules/bootstrap/scss/offcanvas";
    @import "../../node_modules/bootstrap/scss/placeholders";

    // Helpers
    @import "../../node_modules/bootstrap/scss/helpers";

    // 7. Optionally include utilities API last to generate classes 
        based on the Sass map in `_utilities.scss`
    @import "../../node_modules/bootstrap/scss/utilities/api";

    // 8. Add additional custom code here
    @import "../scss/site_styles";
        @import "../scss/flag_icons";
        @import "../scss/header";
    @import "../scss/eu_cookie";
    @import "../scss/utilities";
    @import "../scss/ie_explorer";
    @import "../scss/github";
                    

Dark mode

Bootstrap 5.3 includes support for color modes (one of which can be dark mode) using either data-bs-theme="dark" attributes (Bootstrap’s primary method) by setting $color-mode-type: data; in the SCSS file, or using @media (prefers-color-scheme: dark) media queries by setting $color-mode-type: media-query; in the SCSS file.

$color-mode-type: media-query;

:root, [data-bs-theme=light] {
    --bs-body-color: #212529;
    --bs-body-bg: #fff;
}

@media (prefers-color-scheme: dark)
:root {
    --bs-body-color: #adb5bd;
    --bs-body-bg: #212529;
}

@include color-mode(dark) {
    .element {
        color: var(--bs-body-color);
        background-color: var(--bs-body-bg);
    }
}

I use the media queries method for setting the color mode as:

  • I use media queries in picture / source tags (I’m not sure how to select an image based on a parent’s data attribute)
  • I don’t anticipate needing more than two themes (light & dark)
  • I want to use a visitor’s preferred theme (light or dark), so being able to switch to a theme isn’t necessary.

CSS variables

I override Bootstrap’s standard colors by redefining the CSS variables:

var Bootstrap Site Construction
Mode Light Dark Light Dark
--bs-body-color #212529; #ADB5BD; #212529; rgba(255, 255, 255, 0.6);
--bs-body-bg #FFFFFF; #212529; #FFFFFF; #16191C;
--bs-secondary-bg #E9ECEF; #343A40; #E9ECEF; #2F3337;
--bs-tertiary-bg #F8F9FA; #2B3035; #F8F9FA; #414549;
--bs-link-color #0D6EFD #6EA8FE; #0000EE; #00D2FC;
--bs-link-hover-color #0A58CA; #8BB9FE; #0000EE; #00D2FC;
--sc-link-visited-color Not defined #551A8B; #D8B1FA;

When hovering on a link, Bootstrap switches to a lighter color for the link text and underline – I keep the color the same while removing the underline.Bootstrap does not use a different color for links that have previously been visited (I do).

Bootstrap scrollspy configuration

Scrollspy’s normal configuration is to have the navigation linked to the headings for each section rather than to each complete section. This results in the navigation links being active when the heading is within the root element’s bounding box as set by the rootMargin. If there is no heading within the root element, there is no active navigation link.

My preference is to have some navigation link active at all times so the user will have a point of reference as to where they are within the content.

Rather than applying the id attribute to a heading tag, I apply it to the entire div or section. Scrollspy is okay with this when scrolling down the page — as each div or section crosses the first threshold value for the root, scrollspy applies the active class to the associated navigation link. There is a complication when scrolling back up the page. As a section leaves the root area (is no longer intersecting), scrollspy removes the active class from the associated navigation link, but scrollspy does not automatically apply the active class to the prior navigation link. That only happens when the prior section crosses a threshold point.

To ensure that the next section does cross a threshold point after the prior section leaves the root area, I configure the threshold setting to have 41 values from 1% to 100% in increments of 0.25% (Bootstrap’s default threshold setting is three values, 10%, 50%, and 100%).

I do set the rootMargin bottom value to -67% so there is a narrower window towards the top of the root area for switching to the next navigation link. The rootMargin values are illustrated in the examples below by the red lines.

Bootstrap method

First heading

Lorem ipsum dolor sit amet, consectetur adipiscing elit. Maecenas purus nibh, ultrices eu nunc eu, elementum cursus velit. Aenean pellentesque leo id rhoncus condimentum. Vivamus dignissim posuere lectus, venenatis cursus enim tempor vitae.

Second heading

Praesent hendrerit tempor vehicula. In vel vehicula mauris. Ut molestie tortor mi, in interdum mi semper id. Duis malesuada lorem mollis nulla lobortis, ac auctor purus vehicula. Aenean et risus diam. Nulla facilisi. Nunc facilisis efficitur commodo. Sed tempus lacus porta augue feugiat, sit amet vehicula massa accumsan.

Third heading

Nunc nec metus porttitor, interdum lacus id, varius diam. Donec nunc magna, feugiat nec eros ac, tempor vulputate leo. Maecenas odio justo, volutpat in nunc at, fermentum venenatis nulla. Cras non luctus enim, eget tristique nibh. Cras sed semper lorem. Mauris tempus eu quam nec placerat. Sed aliquam blandit sapien, et tempor lectus euismod id. Nunc efficitur vitae magna eget faucibus. Donec rhoncus, ipsum nec vehicula porta, lorem enim auctor dolor, sit amet porta magna ligula sed nulla. Nullam ornare condimentum volutpat. Etiam eleifend hendrerit auctor. Vivamus lobortis ornare diam vel porttitor.

My method

First heading

Lorem ipsum dolor sit amet, consectetur adipiscing elit. Maecenas purus nibh, ultrices eu nunc eu, elementum cursus velit. Aenean pellentesque leo id rhoncus condimentum. Vivamus dignissim posuere lectus, venenatis cursus enim tempor vitae.

Second heading

Praesent hendrerit tempor vehicula. In vel vehicula mauris. Ut molestie tortor mi, in interdum mi semper id. Duis malesuada lorem mollis nulla lobortis, ac auctor purus vehicula. Aenean et risus diam. Nulla facilisi. Nunc facilisis efficitur commodo. Sed tempus lacus porta augue feugiat, sit amet vehicula massa accumsan.

Third heading

Nunc nec metus porttitor, interdum lacus id, varius diam. Donec nunc magna, feugiat nec eros ac, tempor vulputate leo. Maecenas odio justo, volutpat in nunc at, fermentum venenatis nulla. Cras non luctus enim, eget tristique nibh. Cras sed semper lorem. Mauris tempus eu quam nec placerat. Sed aliquam blandit sapien, et tempor lectus euismod id. Nunc efficitur vitae magna eget faucibus. Donec rhoncus, ipsum nec vehicula porta, lorem enim auctor dolor, sit amet porta magna ligula sed nulla. Nullam ornare condimentum volutpat. Etiam eleifend hendrerit auctor. Vivamus lobortis ornare diam vel porttitor.