destroytoday.com

Favicon

I didn’t realize until a friend nudged me about it, but I’ve been using Nuxt’s default favicon this whole time. There’s not much to a favicon, and I didn’t want to overthink it, so I simply carried over the “transparent canvas” favicon I made from my existing site. I did set it up in Figma as its own artboard in an attempt to organize my assets better, so there’s that.

RSS feed location

Previously, I served the site’s RSS feed /feed.xml, but despite having a proper meta link tag pointing to it, some RSS apps still couldn’t discover it. This made me realize that /feed.xml might not be as intuitive as it could be, so I initially changed the feed location to /rss, which feels easier to check and maybe more standardized? Then, as I was browsing the website of my good friend, Chris Shiflett, I realized he publishes multiple RSS feeds, located at /feeds/blog and /feeds/links to separate blog posts from links. I love this idea because it immediately makes me think about how folks might want to only subscribe to certain parts of my site.

Instead of restricting myself to the one feed, I opened up the possibility of multiple feeds in the future by following suit. Since the current feed is essentially for the entire site, I’m now serving it from /feeds/all, but still redirecting /rss to that location. Then, once I add more content types, like long-form blog posts or new portfolio pieces, I’ll create feeds for those, but still list them in the catch-all feed, too.

Along with making the site more future-proof, this change also gave me the opportunity to set up redirects, since existing subscribers would need to land on the new endpoint. Setting up the redirect was pretty straightforward with the @nuxt-community/redirect-module middleware, but I don’t love installing yet another stack-specific dependency simply to handle redirects compared to a system-level config. It’ll do for now.

I’m starting to think about routes for the rest of the site, which is why I wanted to set up the system for redirects. I’d like to move on from only having articles, which I think will help expose other scenarios to consider, like navigation. Every small change sprouts an entirely new branch of considerations to make. Good thing I enjoy building websites.

TypeScript

TypeScript is one of those technologies that I missed the boat with early on, so I didn’t feel compelled to follow along with it. Since then, a large majority of the dev community went full-in, which makes it hard to ignore now, so I’d be foolish not to see what the fuss is about—at the very least, I’ll learn more about TypeScript, so I have more context and experience when people talk about it. Also, this site is still early enough in development that TypeScript is much easier to implement now, and either embrace or reject, than attempt a complex rewrite later.

For those who are unfamiliar with TypeScript, it is described as “JavaScript that scales ... a typed superset of JavaScript that compiles to plain JavaScript.” TypeScript brings static type-checking to JavaScript that will catch any type-related issues during compile time—saving you from runtime errors. This means instead of writing a function:

function fooToBar(str) {
  return str.replace(/foo/g, 'bar')
}

fooToBar(1337) // Runtime error: str.replace is not a function

...and running the risk of someone passing a non-string argument, I could write:

function fooToBar(str:string) {
  return str.replace(/foo/g, 'bar')
}

fooToBar(1337) // Compiler error: Argument of type '1337' is not assignable to parameter of type 'string'

...which would throw an error at compile time if you tried to pass something else. While this also makes it easier to understand unfamiliar code by documenting props and responses, TypeScript also enables code completion and IntelliSense for compatible IDEs, like VS Code. This lets you hover a method or argument to see more info about it, but also indicates the compiler error in the IDE as well.

I am cautiously approaching TypeScript with a strong bias. After CoffeeScript, I’m pretty scarred when it comes to writing invalid JavaScript and adding another layer to the build process. The beauty of a personal website, however, is that it’s the perfect opportunity to experiment with technologies like this. A lot of this site’s stack is overkill, but this site gives me the opportunity to learn new things in a safe environment. If an experiment doesn’t work out, I can easily remove it and move on—compared to an early stack decision with an app that might come back to haunt you forever. *shakes fist at Angular & CoffeeScript*

Sideways progress

Last night, in a moment fueled with creative energy, I sat down to making forward progress with the site. I didn’t know exactly what I wanted to work on, so I started looking for the next most valuable thing to work on. In doing so, my eye caught a seemingly small issue with the RSS feed—it was rendering the articles without any of my custom renderers, so any images or hyperlinks appeared as Contentful IDs. This definitely felt worth prioritizing first, and didn’t seem like a time sink. I’d simply extract my renderers out of the Article component, so they could be used by both the Article and the RSS feed, which was on my to-do list anyway.

I approached the refactor by keeping each custom renderer in a /lib/contentful/renderers folder, in their own JavaScript file, with each exporting a render function. Then, I mapped these methods to the Contentful documentToHtmlString options for a much cleaner, predictable design—rather than keeping all the render functions inline. This made the renderers easier to find, straightforward to build, and well-isolated to test if I wanted to.

The refactor felt great and didn’t take too long, so I pushed the change, and checked the deploy—not expecting anything out of the ordinary. Instead, I found that none of the articles in the production RSS feed rendered. The RSS feed looked fine locally, but on production, it only displayed the feed metadata—no error or anything in the logs. Something told me this was going to consume my night. It did.

Since the bug itself was in production, I could only test it by deploying, which took nearly a minute each time. I started by double-checking that there were articles in the feed—there were. Next, I checked the output of my extracted render method with all the custom renderers—empty. I then tried wrapping the render in a try { ... } catch to see if any errors were coming through, but being silenced for some reason—yes, but the returned “error” was an empty object.

I knew the issue was with my custom renderers, but couldn’t put my finger on it, so I took the most technically advanced approach I could think of—commenting out code and uncommenting it. I started with all the renderers, then re-added them one-by-one until I discovered the culprit—my <code> renderer. This was a surprise because there’s not much to it. The renderer simply checks for the syntax language using the regular expression I wrote about in a previous article, then uses Prism.js to highlight the code.

I continued to comment out code throughout the render function until I could easily toggle the bug, which existed in the following lines of code, which were responsible for extracting the specified language from a code block:

const match = code.match(/^(?<language>\w+):`(?<code>[\s\S]+)`$/)

if (match) {
  language = match.groups.language
  code = match.groups.code
}

I spent a few minutes scratching my head until it dawned on me—is there a chance my production environment differs from my local dev environment? *gasp* No... It couldn’t be... We live in an age where everything just works... I nervously added console.log(process.version) to check the Node version. Locally, it was latest recommended version, 12.13.1, but in production, my Now environment used Node 8, which doesn’t support named groups in regular expressions. I was relieved to find the issue, but any sort of clue would’ve saved me a couple hours.

To fix the issue, and maintain consistency between my local and production dev environments, I specified the target Node version in my package.json:

{
  "engines": {
    "node": "12.13.1"
  }
}

I also added a .nvmrc file to my codebase to specify the version, so I could run nvm use to use the right Node version locally. I’d love to have it specified in one place, but Now only seems to use the version from my package.json. I didn’t spend long looking for a way, but I’m sure there must be one.

I typically get down on myself for experiences like this because I often see it as lost time that could’ve been spent towards forward progress instead of troubleshooting an issue. At the same time, I accomplished a few things. I mirrored my RSS feed to the actual site; I refactored my renderers to be easier to navigate and test; and I synchronized my Node version between local dev and production—avoiding an inevitable head-scratcher down the road. I’m still a bit bummed that I spent so long troubleshooting, but that’s the life of a dev.

Pagination

Now that I’ve written more than a dozen articles, this page is starting to feel incredibly long, which makes it the perfect time to think about pagination. In Contentful, the API request I use to retrieve entries takes skip and limit parameters to paginate the results. In an effort to embrace the metric system (while biting my thumb at the imperial system), I’ve limited pages to 10 articles. I originally considered a classic sidebar of month-based categories (alongside a blogroll of my friends), but didn’t want to fall victim to the 1-article months that shame you for not writing as consistently as you thought you would.

To persist the pagination, I’m using a 1-based page query parameter. Then, in my Contentful request, I set the skip parameter to ($route.query.page - 1) * limit. Since the site is universal, navigating between pages is lightning quick, which makes me wonder if it’s worth implementing a smoother transition at some point. I typically hate transitions between pages, but this might be on opportunity to improve the UX rather than snapping to the top. At the very least, this initial barebones implementation provides plenty of room for tweaking.

Links

In the previous article, I linked to another article for the first time. With Contentful, you can link to another entry by setting the link type to “entry” instead of “url”. This lets you select another entry in your account, but by default only renders the payload of the entry because Contentful relies on you to handle routing (and render the anchor element).

Similar to images, this required me to create a custom renderer for the link. For now, I know that any link within the site will go to another article, so I simply hard-coded the routing, but eventually, I’ll need proper logic to figure out the right path for a given entry. At that time, I’ll most likely route entries based on their content type—articles to /journal/:slug, portfolio items to /work/:slug, etc.

I do wonder if I’ll prefer a single blog stream for quick journal articles, traditional blog posts, and anything in-between. That would make it easier than checking several different spots, while still providing separate RSS feeds based on type.

Footnotes

After writing a few articles, a friend “Well, actually‘d” me about one of them. In the article, I suggested that Contentful should use a prefix for their API tokens, so you wouldn’t need to specify a different host between dev and production. I didn’t realize that Contentful’s “delivery” API, which is intended for production, is served through a CDN, while the “preview” API isn’t. I wanted to point this out on the original article, but I didn’t want to lose the original content. At first, I considered “highlights”, which could be hovered to show a tooltip, but it didn’t feel accessible enough, so I decided to lean on another pre-web writing element—the footnote.

In Contentful, there’s a “Rich Text” content model type that’s common for article bodies, like the text you’re reading now—potentially including bold, italic, and underlined text, or links, images, and code snippets. Within this rich text, you can embed assets, other entries, or inline entries.

Inline Entry

While regular embedded entries make up an entire block of content, an inline entry can live inline with your text—similar to the inline code snippets I’ve written in other articles, but existing as a referenced entry rather than embedded as text. With an inline entry, I was able to highlight the original words I needed to reference with a custom renderer that simply wraps the text with a <span> and adds a <sup> for the footnote number. Here is an example footnote reference.

*American Psycho voiceover* Oh, the background color? That’s moccasin.

The footnote number is tricky because I don’t have any context in the custom renderer. Typically, I would simply use the index number of the footnote if I had access to it. I still thought this was the most straightforward and least clever approach, so I cheated a bit to make it work. In my Vue component for the article, I created a mounted hook, looped through the <sup> elements, and set their textContent to one plus the index. Since I’m using Nuxt, and serving the site in “universal” mode, the number is still server-side rendered.

To display the footnotes below the article, I filtered the body payload for the footnote entries. Since these are inline, this means iterating through all content arrays within the body field, which definitely doesn’t feel performant, but it works, and I couldn’t easily find another way, so it’ll suffice for now. The request I get from Contentful does return an includes array with the footnote entries, but when retrieving multiple articles, I couldn’t find any references from the footnotes to the related articles, so there doesn’t seem to be a way to match them.

Now that I had the footnotes listed below the article and the references highlighted within the article, I could’ve stopped there, but I didn’t. While searching through footnote implementations, I came across this Daring Fireball article from 2005 that describes John Gruber’s approach of linking references to their footnotes and vice versa. Linking the reference is easy, by simply linking the footnote reference number, but for the footnote itself, John suggests using a “leftwards arrow with a hook” (↩, or &#x21A9;) that links back to the reference.

He also prefixes the anchors with “fn1” and “fnref1” for the footnote and reference, respectively, then includes the footnote number. I didn’t love the abbreviations or the use of the footnote numbers (in case they changed), so I prefix with “footnote” and “reference”, then append the Contentful UUID for the footnote entry. I don’t love using such a long ID string, but it’s unique to the footnote, which is worth more to me.


  1. Here is an example footnote.

Line-height

While I’m tweaking typography, I figured I’d take the opportunity to further improve readability with more vertically-spaced type. Prior to adding a max-width, I didn’t immediately recognize the sub-par leading because there was so much else “wrong” with the readability, but as soon as I capped the width of the paragraphs, the tight leading stood out like a sore thumb.

By default, line-height is set to normal, which is roughly 1.2. This gives a 16px font a line-height of ~19.2—pretty tight for body copy. Before doing any research, like I did with the max-width, I tried increasing the line-height a tenth until it felt right, which was 1.5. This felt spacious enough, and resulted in a 24px line-height, which satisfies my bias towards base-8 numbers.

To back up my intuition, I did a quick search, and found that the answers vary, but typically land between 1.3 to 1.5. I also asked my studio mate, Frank, who specializes in type. He said, “It looks like you’re happy with 1.5, so go with that.” Forever wise, that Frank. The change meant updating single value, but the result makes the world of difference. Slowly but surely, the site is starting to look real.

Max-width

I’ve been holding off on applying a max-width to the site, but I figured now that I have 10 articles under my belt, the time is right. In the past, I’d honestly pick article width by choosing an ideal image width. This time, however, I’m focusing on readability. I remember learning in a typography class that there’s a rule of thumb for the ideal number of words on a line of text, which (I believe) was roughly 12 words. I received a tweet from @trey suggesting a max-width using the ch unit, which made me realize there’s a ch unit in CSS—I had no idea. Mozilla describes it as:

“The advance measure (width) of the glyph "0" of the element's font.”

MDN web docs - CSS values and units

Perfect. Now, I simply need to find the ideal number of characters on a line of text. A quick google search ..led me to this gem—The Elements of Typographic Style Applied to the Web. It quotes the book that the website is based on with our answer:

“The 66-character line (counting both letters and spaces) is widely regarded as ideal.”

The Elements of Typographic Style by Robert Bringhurst

Combining my newfound knowledge of the ch unit and the golden number 66, I set the site’s max-width to 66ch. It feels good, but the default <body> margin feels tight beyond mobile. I doubled the margin once it reaches the max-width, which feels much better. I don’t expect to keep this margin long-term, but this entire site is a series of small steps in the name of progress.

Syntax highlighting

In the previous article about WebP fallback, I wrote my first multiline code snippet on the site. In order to get the lines and spaces to work as expected, I added white-space: pre; to the <code> blocks that was generated from Contentful. It looked fine on desktop, so I hit “publish”, and went about my day. Later on, when showing a friend the site on my phone, I realized that the multiline code blocks caused a horizontal overflow on smaller viewports—how embarrassing!

Foreseeing future articles with multiline code snippets, I figured I should look into syntax highlighting. I didn’t spend too much time researching libraries before landing on Prism.js. I wrote a custom Contentful node renderer to intercept the <code> tag and reformat it using Prism.js. First, I detected if the code snippet is multiline or inline by checking for any newlines. If it’s multiline, I wrap the <code> block with a <pre> tag.

Prism supports about 200 programming languages, so I can highlight pretty much any language by specifying it. By default, I use markup as the language to handle generic code snippets, but I built a simple syntax for specifying the language, which looks like javascript:`const foo = 'bar';`. It’s not entirely standard, but works for now. Here’s an example of a multiline code block, which uses the default markup language:

<section>
  <h1 class='title'>Hello world!</h1>
  <p class='body'>This is my code snippet.</p>
</section>

And an example using a specified language:

import Foo from 'bar';

const foo = new Foo({ el: document.querySelector('#foo') });
document.documentElement.appendChild(foo.el);

Syntax highlighting is great, but it comes at a cost. Already, my Lighthouse score has dipped, so I’ll be looking at ways to improve it. The main issues involve color contrast for accessibility—an easy fix—and first meaningful paint, which might require some work. I imagine I’ll need to pull syntax highlighting out of the server-side rendering, so it executes asynchronously. I’m not too worried, but I am happy with this progress.