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:
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:
Samsung Galaxy smartphone – 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.
/
├── .vscode
├── app
│ └── ui
├── css
├── dict
├── js
├── images
├── lib
├── node_modules
├── src
│ ├── js
│ └── sass
└── tmp
/
├── app
│ └── ui
├── css
├── dict
├── js
├── images
└── tmp
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";
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 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).
On large displays, I’m using position: fixed
for the hero image. Bootstrap, to prevent the page from scrolling while the modal is showing, sets the page to overflow: hidden
, which removes the scrollbar. Bootstrap adds padding to the body and the navbar to fill the space left by no scrollbar, but initially, my fixed image was expanding to fill the space.
By adding the class is-fixed
to the img, Bootstrap will also add padding to the image. I’m not sure why Bootstrap has this option as it’s not in the documentation, but it works.
From the scrollbar.js file:
const SELECTOR_FIXED_CONTENT = '.fixed-top, .fixed-bottom, .is-fixed, .sticky-top'
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.
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.
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.
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.
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.
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.
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.