Thanks to a quite overdue update of Hugo on our build system1, our website can now harness the full power of Hugo code highlighting for Markdown-based content.
What’s code highlighting apart from the reason behind a tongue-twister in this post title?
In this post we shall explain how Hugo’s code highlighter, Chroma, helps you prettify your code (i.e. syntax highlighting), and accentuate parts of your code (i.e. line highlighting).
Make your code look pretty
If you notice and appreciate the difference between
Syntax highlighting! ????️ Just do it. Life is better when things are colourful.
Syntax highlighting means some elements of code blocks, like functions, operators, comments, etc. get styled differently: they could be colored or in italic.
Now, how do the colors of the second block appear?
First of all, it’s a code block with language information, in this case R (note the r after the backticks),
```r
a <- c(1:7, NA)
mean(a, na.rm = TRUE)
```
as opposed to
```
a <- c(1:7, NA)
mean(a, na.rm = TRUE)
```
without language information, that won’t get highlighted – although some syntax highlighting tools, not Hugo Chroma, do some guessing.
There are in general two ways in which colors are added to code blocks, client-side syntax highlighting and server-side syntax highlighting.
The latter is what Hugo supports nowadays but let’s dive into both for the sake of completeness (or because I’m proud I now get it2).
Client-side syntax highlighting
In this sub-section I’ll mostly refer to highlight.js but principles probably apply to other client-side syntax highlighting tools.
The “client-side” part of this phrase is that the html that is served by your website host does not have styling for the code.
In highlight.js case, styling appears after a JS script is loaded and applied.
<pre class="r"><code>pal_a <- extract_colours("https://i.imgur.com/FyEALqr.jpg", num_col = 8)
par(mfrow = c(1,2))
pie(rep(1, 8), col = pal_a, main = "Palette based on Archer Poster")
hist(Nile, breaks = 8, col = pal_a, main = "Palette based on Archer Poster")</code>
Now, using Firefox Developer Console,
we see colors come from CSS classes starting with “hljs”.
And in the head of that page (examined via “View source”), there’s
A big downside of client-side syntax highlighting is loading time:
it appears quite fast if your internet connection isn’t poor, but you might have noticed code blocks changing aspect when loading a web page (first not styled, then styled).
Moreover, Hugo now supports, and uses by default, an alternative that we’ll describe in the following subsection and take advantage of in this post’s second section.
Server-side syntax highlighting
In server-side syntax highlighting, with say Pygments or Chroma (Hugo default), your website html as served already has styling information.
and it goes hand in hand with having styling for different “.chroma” classes in our website CSS.
.chroma .s { color: #a3be8c }
To have this behaviour, in our website config there’s
pygmentsUseClasses=true
which confusingly enough uses the name “Pygments”, not Chroma, for historical reasons.
You’d use CSS like we do if none of Chroma default styles suited you, if you wanted to make sure the style colors respect WCAG color contrast guidelines (see last section), or if you want to add a button switching the CSS applied to the classes, which we did for this note using a dev.to post by Alberto Montalesi.4
Click the button below!
It will also let you switch back to light mode.
How does Chroma know what parts of code is of the string class for instance?
Once again, regular expressions help, in this case in what is called a lexer.
Chroma is inspired by Pygments, and in Pygments docs it is explained that “A lexer splits the source into tokens, fragments of the source that have a token type that determines what the text represents semantically (e.g., keyword, string, or comment)."
In R lexer, ported from Pygments to Chroma by Chroma maintainer Alec Thomas, for strings we e.g. see
```{r name-your-chunks, hlopts=list(linenos="table")}
a <- 1+1
b <- 1+2
c <- 1+3
a + b + c
```
is rendered as
1
2
3
4
a <- 1+1
b <- 1+2
c <- 1+3
a + b + c
[1] 9
PSA! Note that if you’re after line highlighting, or function highlighting, for R Markdown documents in general, you should check out Kelly Bodwin’s flair package!
Produce line-highlighted code blocks with glue/paste0
What Chroma highlights are code blocks with code fences, which you might as well generate from R Markdown using some string manipulation and knitr results="asis" chunk option. E.g.
This is a rather uninteresting toy example since we used randomly drawn line numbers to be highlighted, but you might find use cases for this.
We used such an approach in the recent blog post about Rclean, actually!
Accessibility
Since highlighting syntax and lines changes the color of things, it might make it harder for some people to read your content, so the choice color is a bit more than about cosmetics.
Disclaimer: I am not an accessibility expert. Our efforts were focused on contrast only, not differences between say green and red, since these do not endanger legibility of code.
For instance, comments could be lighter or darker than code, but it is crucial to pay attention to the contrast between comments and code background!
Like Max Chadwick, we darkened colors of a default Chroma style, friendly, until it passed on an online tool.
Interestingly, this online tool can only work with a stylesheet: for a website with colors written in-line (Hugo default of pygmentsUseClasses=false), it won’t pick up color contrast problems.
We chose friendly as a basis because its background can stand out a bit against white, without being a dark theme, which might be bad on a mobile device in direct sunglight.
Comments are moreover in italic which helps distinguish them from other code parts.
A further aspect of contrast when using Chroma is that when highlighting a line, its background will have a different color than normal code.
This color also needs to not endanger the contrast between code and code background, so if your code highlighting is “dark mode”, yellow highlighting is probably a bad idea: in this post, for the dark mode, we used the “fruity” Chroma style but with #301934 as background color for the highlighted lines.
It would also be a bad idea to only rely on line highlighting, as opposed to commenting code blocks, since some readers might not be able to differentiate highlighted lines.
Commenting code blocks is probably a good practice in general anyway, explaining what it does instead of just sharing the code like you’d share a gist.
In this post we’ve explained some concepts around code highlighting: both client-side and server-side syntax highlighting; and line highlighting with Chroma.
We’ve even included a button for switching to dark mode and back as a proof-of-concept.
Being able to properly decorate code might make your content more attractive to your readers, or motivate you to write more documentation, which is great.
Now, how much time to fiddle with code appearance is probably a question of taste.