Learning to <code/>

This is a coding blog, so it makes sense to start out by making sure we can represent code. My site:developer.mozilla.org-enhanced googling skills tell me that <pre /> + <code /> is all the rage, so let's give that a try.

The Basics

if ( window.navigator ) {
  console.log("Hello, user!")
} else {
  console.error("There's nobody to read me :(")
}

That looks good for vanilla Javascript, but what about JSX? The block above actually looks like

<pre>
  <code>{ stripIndent`
    if ( window.navigator ) {
      console.log("Hello, user!")
    } else {
      console.error("There's nobody to read me :(")
    }
  `}</code>
</pre>

stripIndent`...` is a Tagged Template Literal and allows us to write our code at the current indent level without the spacing making it into the final doc, while still maintaining its internal indentation. Super neat!

In a similar vein, that block actually looks like

<pre>
  <code>{ html`
    ...
  `}</code>
</pre>

Where html`...` is a similar tagged template which helps us automatically encode our alligators into their html-escaped equivalent. Looking alright so far...

Syntax Highlighting

I'm a strong believer in good web fundamentals, so any feature enhancement we add must have reasonable fallback behavior for people who choose to turn off Javascript or even who use browsers (such as links) who don't even support the notion. But I also can't stand reading un-highlighted code, and I'm sure you all would appreciate some color in your life as well. Let's see what we can do about that.

The first result for "react syntax highlighter" is, unsurprisingly, react-syntax-highlighter, which seems to support progressively highlighting if the client has JS enabled.

if ( window.navigator ) {
  console.log("Hello, user!")
} else {
  console.error("There's nobody to read me :(")
}

Not bad! In JSX, the above looks like

/* Contains our 2 styles behind a @media prefers-color-scheme selector */
import '../highlight.css'

// Then, anywhere
import { PrismAsyncLight as SyntaxHighlighter } from 'react-syntax-highlighter';

<SyntaxHighlighter language="javascript" useInlineStyles={false}>{ stripIndent`
    if ( window.navigator ) {
      console.log("Hello, user!")
    } else {
      console.error("There's nobody to read me :(")
    }
`}</SyntaxHighlighter>

Both of which render from the server as completely valid <pre><code> blocks with JS only being required to tag and color them.

Thinking outside the box

That scrollbar doesn't looks very good, considering how much free real estate we're wasting in our gutters. Let's see if we can't get something akin to 'full-bleed' working.

import { PrismAsyncLight as SyntaxHighlighter } from 'react-syntax-highlighter';

<SyntaxHighlighter language="javascript" useInlineStyles={false}>{ stripIndent`
    if ( window.navigator ) {
      console.log("Hello, user!")
    } else {
      console.error("There's nobody to read me :(")
    }
`}</SyntaxHighlighter>

And, just to test, if our content is really long...

if(you_use_really_long_variable_names_and_hate_newlines_because_youre_a_l33t_hacker_man){console.log("what a cool guy")}

This all works because of the amazing CSS Grid, which is nice enough to let us break out of the column we're constrained to as will. In its entirety, the code that allows all of this to work is as follows

// styles.css
main {
  display: grid;
  grid-template-columns:
    1fr
    min(65ch, 100%)
    1fr;
}

main > * {
  grid-column: 2;
}

// blog.module.css
.article {
  text-align: justify;
  display: contents;
}
.article > * {
  grid-column: 2;
}

.full_bleed {
  width: 100%;
  grid-column: 1 / -1;

  display: flex;
  justify-content: center;
}

This is almost entirely inspired by CSS Tricks's Full Bleed article with the tricky bit being that we want to declare our layout at the main tag but our semantic HTML-ness means we are surrounded by an article before we get to the actual content. By declaring the article as display: contents, we are able to hide the article tag from the DOM and promote its children to be in the grid instead.