I’m a big fan of Hugo for building static sites but wanted to port this blog over to Quarto as it’s a framework I’m using more consistently. This post describes some of the extra styling and javascript used to build the blog.
Index Page
The landing page contains a little javascript typewriter effect along with a couple of navigation buttons. The typewriter effect is generated using the Typewriterjs
library (https://github.com/tameemsafi/typewriterjs), built as a shortcode extension (see https://github.com/harveyl888/typewriter). The links are simply styled buttons that use Lord icons through the Quarto extension (https://github.com/jmgirard/lordicon).
Light/Dark Theme
It’s fairly simple to set a light/dark theme in Quarto and add additional styling. The _quarto.yaml
file includes the following lines which specifies an override order. Light and dark themes use a default (flatly, darkly) which is replaced with some general styling in custom.scss
. Finally, specific light and dark styles are applied using custom-light.scss
and custom-dark.scss
.
format:
html:
theme:
light: [flatly, custom.scss, custom-light.scss]
dark: [darkly, custom.scss, custom-dark.scss]
Light and Dark Swtiching
By default, Quarto includes a switch to change from light to dark mode. The switch is, in fact, two icons (switch to the left and switch to the right) from Bootstrap icons. This discussion pointed to the code used to define the light and dark icons, which can be changed by simply copying the svg code from https://icons.getbootstrap.com/. The folllowing has been added to custom.scss
to replace the switch with sun and moon icons.
.navbar .quarto-color-scheme-toggle:not(.alternate) .bi::before {
background-image: url('data:image/svg+xml,<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="#{colorToRGBA($navbar-light-color)}" class="bi bi-sun" viewBox="0 0 16 16"><path d="M8 11a3 3 0 1 1 0-6 3 3 0 0 1 0 6m0 1a4 4 0 1 0 0-8 4 4 0 0 0 0 8M8 0a.5.5 0 0 1 .5.5v2a.5.5 0 0 1-1 0v-2A.5.5 0 0 1 8 0m0 13a.5.5 0 0 1 .5.5v2a.5.5 0 0 1-1 0v-2A.5.5 0 0 1 8 13m8-5a.5.5 0 0 1-.5.5h-2a.5.5 0 0 1 0-1h2a.5.5 0 0 1 .5.5M3 8a.5.5 0 0 1-.5.5h-2a.5.5 0 0 1 0-1h2A.5.5 0 0 1 3 8m10.657-5.657a.5.5 0 0 1 0 .707l-1.414 1.415a.5.5 0 1 1-.707-.708l1.414-1.414a.5.5 0 0 1 .707 0m-9.193 9.193a.5.5 0 0 1 0 .707L3.05 13.657a.5.5 0 0 1-.707-.707l1.414-1.414a.5.5 0 0 1 .707 0m9.193 2.121a.5.5 0 0 1-.707 0l-1.414-1.414a.5.5 0 0 1 .707-.707l1.414 1.414a.5.5 0 0 1 0 .707M4.464 4.465a.5.5 0 0 1-.707 0L2.343 3.05a.5.5 0 1 1 .707-.707l1.414 1.414a.5.5 0 0 1 0 .708"/></svg>');
}
.sidebar-navigation .quarto-color-scheme-toggle:not(.alternate) .bi::before {
background-image: url('data:image/svg+xml,<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="#{colorToRGBA(theme-dim($body-color, 10%))}" class="bi bi-sun" viewBox="0 0 16 16"><path d="M8 11a3 3 0 1 1 0-6 3 3 0 0 1 0 6m0 1a4 4 0 1 0 0-8 4 4 0 0 0 0 8M8 0a.5.5 0 0 1 .5.5v2a.5.5 0 0 1-1 0v-2A.5.5 0 0 1 8 0m0 13a.5.5 0 0 1 .5.5v2a.5.5 0 0 1-1 0v-2A.5.5 0 0 1 8 13m8-5a.5.5 0 0 1-.5.5h-2a.5.5 0 0 1 0-1h2a.5.5 0 0 1 .5.5M3 8a.5.5 0 0 1-.5.5h-2a.5.5 0 0 1 0-1h2A.5.5 0 0 1 3 8m10.657-5.657a.5.5 0 0 1 0 .707l-1.414 1.415a.5.5 0 1 1-.707-.708l1.414-1.414a.5.5 0 0 1 .707 0m-9.193 9.193a.5.5 0 0 1 0 .707L3.05 13.657a.5.5 0 0 1-.707-.707l1.414-1.414a.5.5 0 0 1 .707 0m9.193 2.121a.5.5 0 0 1-.707 0l-1.414-1.414a.5.5 0 0 1 .707-.707l1.414 1.414a.5.5 0 0 1 0 .707M4.464 4.465a.5.5 0 0 1-.707 0L2.343 3.05a.5.5 0 1 1 .707-.707l1.414 1.414a.5.5 0 0 1 0 .708"/></svg>');
}
.navbar .quarto-color-scheme-toggle.alternate .bi::before {
background-image: url('data:image/svg+xml,<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="#{colorToRGBA($navbar-light-color)}" class="bi bi-moon" viewBox="0 0 16 16"><path d="M6 .278a.77.77 0 0 1 .08.858 7.2 7.2 0 0 0-.878 3.46c0 4.021 3.278 7.277 7.318 7.277q.792-.001 1.533-.16a.79.79 0 0 1 .81.316.73.73 0 0 1-.031.893A8.35 8.35 0 0 1 8.344 16C3.734 16 0 12.286 0 7.71 0 4.266 2.114 1.312 5.124.06A.75.75 0 0 1 6 .278M4.858 1.311A7.27 7.27 0 0 0 1.025 7.71c0 4.02 3.279 7.276 7.319 7.276a7.32 7.32 0 0 0 5.205-2.162q-.506.063-1.029.063c-4.61 0-8.343-3.714-8.343-8.29 0-1.167.242-2.278.681-3.286"/></svg>');
}
.sidebar-navigation .quarto-color-scheme-toggle.alternate .bi::before {
background-image: url('data:image/svg+xml,<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="#{colorToRGBA(theme-dim($body-color, 10%))}" class="bi bi-moon" viewBox="0 0 16 16"><path d="M6 .278a.77.77 0 0 1 .08.858 7.2 7.2 0 0 0-.878 3.46c0 4.021 3.278 7.277 7.318 7.277q.792-.001 1.533-.16a.79.79 0 0 1 .81.316.73.73 0 0 1-.031.893A8.35 8.35 0 0 1 8.344 16C3.734 16 0 12.286 0 7.71 0 4.266 2.114 1.312 5.124.06A.75.75 0 0 1 6 .278M4.858 1.311A7.27 7.27 0 0 0 1.025 7.71c0 4.02 3.279 7.276 7.319 7.276a7.32 7.32 0 0 0 5.205-2.162q-.506.063-1.029.063c-4.61 0-8.343-3.714-8.343-8.29 0-1.167.242-2.278.681-3.286"/></svg>');
}
Listing Layout
Quarto has a few listing formats built in but also allows you to create your own by scripting in ejs. Initially I tried to build a cool looking listing for blog entries using ejs but realized that some of the interativity, related to clicking on categories, would require to include additional javascript (copied from the Quarto repo). An alternate approach is to keep a standard listing and style it using scss. This was how this site was built.
Listing Page Background Color
To set body properties for a single page you can add a <style>
tag at the top of the quarto qmd file. For example, to set a body background color to grey you could insert the following code just after the yaml heading:
<style>
body {background-color: #eeeeee;
}</style>
Since we have a dark/light mode, I would like the body background color on the listing page to change with the theme of the site. Our themes are built with scss but the <style>
tag takes css. In order to make the body background change according to the theme setting I’ve added a css variable to the top of the scss custom-light.scss
and custom-dark.scss
files. The variable is added before the first section commment.
custom-light.scss starts with:
:root {
--post-page-background: #eeeeee;
}
custom-dark.scss starts with:
:root {
--post-page-background: #444444;
}
and posts/index.qmd contains the following code just after the yaml header:
<style>
body {background-color: var(--post-page-background);
}</style>
When the posts listing page is built the background color will be set to the css variable --post-page-background
which is defined in the light and dark scss files.
Blog Categories
Blog categories are listed to the side of the posts. They are styled using scss with a little javascript to remove the parentheses around the counts. Javascript is shown below. The code works by finding and looping over all instances of the quarto-category-count
class and removing the first and last character. The document.addEventListener("DOMContentLoaded", function() {})
code waits for the page to load before executing.
document.addEventListener("DOMContentLoaded", function() {
const counts = document.querySelectorAll('.quarto-category-count');
.forEach(count => {
countslet text = count.textContent;
if (text.length > 1) {
.textContent = text.slice(1, -1);
countelse {
} .textContent = '';
count
};
}); })
The javascript script file is added to the bottom of each page by adding the following to _quarto.yml
:
format:
html:
include-after-body:
- text: |
<script type="text/javascript" src="/_resources/js/scripts.js"></script>
Notes
Porting from Hugo to Quarto was simple and I can now execute R and python as blog entries are built. In addition, shinylive will allow shiny to be run from within Quarto pages.