Dark Mode

Chromastyles

Taking a stab at this in public.

Hugo uses Chroma for it’s code-syntax-highlighting feature. Chroma is a code-syntax-highlighting tool written in Go. Chroma is heavily influenced by Pygments, the code-syntax-highlighting tool written in Python. Both Chroma & Pygments work by identifying lexers out of the provided code and then categorized characters or words to a token. There are then style-presets that will then color each token according to it’s style.

These styles are not dynamic. Nor are they very custom.

You may have seen these style-presets in code-editors. “Monokai”, “catpuccin-mocha”, “solarized-dark”. Below are previews of these styles.

Chroma Styles Preview

Pygments Styles Preview

In the Pygments documentation, they acctually suggest not to use CSS in writting your own style, so it can be used in other contexts.

Pygments Docs - Write your own style

(Philosophy: the styles aren’t written in CSS syntax since this way they can be used for a variety of formatters.)

Both Chroma & Pygments have application areas other than HTML, such as in command-line applications & customization of CLI outputs. The static nature if these styles makes sense and is understandable.

The Chroma styles are defined in XML files in thier source code. Each element matches a lexer-token to a static style, which generally includes a static hex color and can include other formatting such as "underline".

When Chroma goes to generate code-blocks in formatted HTML, it defaults to putting the styles in-line of the HTML elements. It can be done WithClasses(), putting the lexer-token-id’s in the class attribute of the HTML elements. (By “lexer-token-id’s” I mean an abbreviated code of the lexer-token-name. I.e, KeywordConstant becomes kc as an HTML class) This would allow us to use CSS for formatting. This can be configured in Hugo by setting the config file, hugo.toml, or whatever it is named these days.

[markup.highlight]
  noClasses = false

Hugo uses Goldmark to render Markdown into HTML. When it parses out fenced-code-blocks, that text will go into Chroma and be parsed for it’s lexers to assign tokens. With noClasses = false, the formatted HTML elements will have the lexer-token-id in thier class attribute. We will then need a CSS stylesheet to format our HTML code blocks. Hugo provides a CSS export option for the Chroma styles with the hugo gen chromastyles command. See also thier documentation here.

# In your hugo project directory
mkdir static/css
hugo gen chromastyles > static/css/chroma.css
hugo gen chromastyles --style=monokai > static/css/monokai.css
hugo gen chromastyles --style=monokailight > static/css/monokailight.css

Now we’ll need to link these in either the ./layout/_default/baseof.html file (or where ever you have your <head> element written), or we can add them in the hugo.toml file, or we can import then in any existing CSS files too.

One dynamic light/dark mode solution is to toggle between two CSS files. It’s not bad, but then the style mis-matching with my webpage becomes really clear. I don’t want to pick my website theme around one of the predefined styles.

Having written my own chroma.css

I have made a chroma.css file that utilizes CSS variables which are dynamic to dark-mode/light-mode and an input --accent-hue variable which can be set in my Hugo template. I will now have to tackle the contrast-ratio problem in my CSS file which will hopefully solve my ugly-light-mode-problem.

Another issue I have is that the lexers for bash are not as specific as I’d like. Most of my notes here are running stuff in the command line so the code-blocks tend to inherit the --foreground-accent-color I have set as a default.

I want to give a shout out to Nathan at nathan.rs for his solution to fix the iOS web browser resizing code-block elements. I have implemented that fix.