What's new is new again
On 2026's site redesign

In 2025, your hostess said hopefully:

I've finally got myself a maintainable foundation that I can iterate on. Here's hoping this rewrite is the last one!

Reader, it was not. The posts were hard to read. My visual identity had evaporated. And the technical choices I'd made meant fixing either would be a slog. So, 2026 brings a new version, torn down to the studs and built anew. Rejoice— you may now listen to me navel-gaze for 2500 words about the choices I made along the way.

Inspirations and Principles

A number of different sources jostled around in my mind as I designed this new version. First, the works of Matthew Butterick, particularly his books Practical Typography and Beautiful Racket. These challenged me to improve the legibility and visual interest of my type-heavy pages. Second, Refactoring UI by Adam Wathan and Steve Schoger. I am a design-challenged, engineer-brained person; this book helped me understand some design principles that even someone like myself can grasp. Third, V.H. Belvadi's website, which might be the best fully-typographic personal website I've seen. Fourth, Tufte CSS, especially its sidenotes feature111 Like this one here! . Lastly, and most oddly, the lockscreens on my digital devices, which made me want to bring back images in some capacity.

After percolating on what I liked about these sources, I landed on a few principles for my visual identity:

On going monochrome

At one point, I dearly wanted a role for color on my website. But try as I might, over many attempts, I struggled to create something meaningful. Color was reserved for limited, and frankly careless uses, such as links and accents. And I have no eye for color harmony. So, I've gone monochrome. Instead of leaning on colors, I've focused on other visual elements to create interest and hierarchy. Frankly, this was a relief— I freed up so much mental space for other things. And I mostly dress in blacks and greys anyway, so you could call it a more authentic expression of myself.

Rather than treat monochromaticity as a limitation, I found it interesting to consider the richness of a monochrome palette. Take a look at this:

a slice from an image of a black sand beach

Look at the texture! The variation! The top and bottom half are both black, but such different black. This excites the eye in a way that a flat color field does not. I hoped to channel some of this into my design choices. Have I succeeded? Perhaps not yet, but I like to think I'm getting closer.

On typography

Like the previous design, I chose to focus on type as the primary visual element. I am a software engineer, not an artist. The words and code are the main focus of my site. I would gain little from adding purely decorative imagery. Better to highlight type above all.

However, I significantly changed the typefaces I chose. My previous choice of Orpheus Pro was too decorative, and harmed the legibility. I had also sized my headings like I was designing highway signage. This time, I chose two fonts from MB Type: Equity for serif body text and headings, and Concourse for sans-serif UI elements. I was particularly pleased with the legibility of Equity, and with the wide variety of OpenType features both support. These served as a great base to dial in a systematic typography system. More on that later.

Perhaps most importantly, I had to invest a lot of time into dialing in my MDX components222 That would be @vivshaw/basalt-mdx, my MDX expansion pack for Basalt. . Previously, the most common elements (paragraphs, first and second level headings) looked fine, but many things like lists and blockquotes were pretty wonky. I used a mixture of Butterick's rules and my own guess-and-check to get things looking a bit more legible. I also eliminated the smaller headings— if I'm actually nesting headings five levels deep, I am making a mistake! I found the best technique for improving my MDX rendering was to create a "kitchen sink" that used every component I had, amongst big sections of body copy and so on. This helped see how everything hangs together in practice.

On tasteful simplicity

It's regrettably easy to be simple in a tasteless way. My old design did this, with things like big whacks of fancy-serifs filling the whole page. But it's also possible to break from simplicity tastefully. The simplicity of a broader whole can benefit from contrast to carefully chosen details. One example of this would be my wiggly links333 Like this link here! Hover to see the underline wiggle, click for a surprise. . I wanted a way to call out links without using color, and I also wanted to explore motion. This felt like an opportunity to play with both. Another example would be my re-introduction of images in limited spots, such as my home page and 404 page. These are actually images from the collection I use for device backgrounds. I had already collected some great monochrome options, so it was easy to choose one for the dark theme444 This Icelandic black sand beach from Jeremy Bishop. , and one for the light theme555 This snow-capped and cloud-obscured Swiss mountain range from Matthias Kinsella. .

I will not claim to be an arbiter of good taste— indeed, I'm certain I've made many tacky choices here I will later come to regret. But compared to the previous design, it's night and day.

On systems

In my prior post, I had mentioned creating the bones of a design system666 That design system, @vivshaw/basalt, remains the design system for this site. . This time, I took it further into an actual usable system. Refactoring UI helped me greatly here. It introduced me to a simple, step-by-step process to adopt constraint-based design principles. Though that book is focused on app design and on greenfield work, the same underlying ideas applied. Choosing one design aspect at a time— color, typography, spacing, etc.— I would gather all the extant values for that aspect, make them into tokens, then tweak all the styles to use those tokens rather than bespoke values. Once I had done this everywhere, I looked back at the token system and tried to simplify it further, removing the least-used or least-differentiated values. For example, I didn't really need 5 different breakpoints, or 6 levels of heading size!

Once I had achieved a sensible system of base tokens, I added a layer of semantic tokens where applicable (things like --basalt-color-background-default or --basalt-font-size-body). These removed the cognitive overhead of remembering things like, "now which color was muted text supposed to be?" I also added a layer of PostCSS mixins, to group together CSS rules that ought to occur together. That way, I can set @mixin text-heading1; and @mixin font-serif; instead of needing to remember all the individual properties that should be paired.

The most challenging aspect was the color scale. Previously, my light and dark theme were simply two unrelated color palettes. This time, I munged them all together into one scale. But it didn't look very harmonious. So, I needed to fill in some intermediate values, remove others, and tweak the relationships between the colors a bit. I'm not sure what the right tools are for this, so I did a lot of guess and check. I still don't think I have it right. But, it's consistent and usable, which is a vast improvement from before.

I now have these tokens covering: color (including light/dark theme), typography (font stacks, sizing, type properties, OpenType features), motion, sizing & spacing, border radii, responsive breakpoints, and focus states.

Dialing this in took a long time, and involved some truly awkward in-between states. But constraint-based techniques get there in the end! By steadily ruling out more and more inconsistencies and horrible choices, I steadily approached a system that was useful to build with and pleasant to look at. I think the next step is to fill out my components— I only have four in the main system (<Heading>, <Text>, <Link>, <Pill>). I don't wanna go wild with it, but I could see a role for a few more- perhaps a <Surface>, or some layout components.

On progressive enhancement

I've always claimed to care about progressive enhancement, but in practice I accomplished it by avoiding runtime JS entirely. Really, I'd rather have a site that can leverage those features when they are available, and gracefully degrade when they are not.

This site now does that. I set a no-js class on the html element, and then added a script to swap it out for a js class when JS is available. The majority of my components are statically rendered. Those components that are dynamic watch this js class, and only if it's present do they do their fancy stuff. The fallback behavior differs from component to component, but generally it's either to not render at all, or render a static fallback UI777 For example, the showy/hidey sticky navbar becomes a stationary navbar at the top of the page. .

I made sure that my light and dark theme are fully functional without JS. I default to the system's prefers-color-scheme setting, and only set a specific theme if JS is active. I also made sure to use semantic elements whenever possible. So, even without JS and CSS, it should at least look like a 1990s website and still function. I see progressive enhancement as about making the site usable to everybody, regardless of device or person. So accessibility and responsivity are a part of it, too. I implemented prefers-reduced-motion and did some sizing and contrast tests to make sure text was legible. I also distilled my responsive code to a simpler three-breakpoint system, and baked some of that into the design system tokens, such as text sizes.

Alrighty, that's enough about the design. On to the architecture!

Abandoning CSS-in-JS

In my prior version, I had adopted Vanilla Extract for styling. My hope was for something that:

Vanilla Extract did accomplish these things. As I adopted it, though, I ran into a structural problem: it offers multiple ways to style, each of which has gaps that push you toward the others.

There were four options:

I'd hoped to do everything through <Box> and ignore the lower layers. That didn't last. My light/dark theme utils only worked in raw VE stylesheets, so theming pushed me back out of Sprinkles when I needed to write theme-aware components. Pseudo-selectors like :focus and :hover cause a combinatorial explosion of styles in Sprinkles, so interactive states pushed me out too. Sprinkles shorthands were great for grouping related properties, but they only worked in the Sprinkles layer, which pushed me back into Sprinkles. Recipes were lovely for defining variants simply and cleanly, but they only worked in the stylesheet. The result was that my styles were scattered across four techniques, each with its own tradeoffs and no clear path toward unity.

I decided to go back to basics here, and rip it out. CSS is quite powerful nowadays. With a smidge of CSS Modules and PostCSS to grease the wheels, I could accomplish almost everything I'd wanted Vanilla Extract to do. I would lose the type-safety and the <Box>. But in exchange, I could do everything in one place. Things I enjoyed along the way:

Custom rehype plugins

I had a couple features I wanted in my Markdown rendering pipeline that I couldn't find existing Rehype plugins for. So, I applied some elbow grease and wrote my own. The first and simplest was a plugin to apply custom styles to the first few words of each post, so that I could small-caps them. The second was a plugin to insert Tufte-style sidenotes. This was a bit more involved999 I found this post by Keith McClary a helpful start, though my approach diverged. , as I needed to do some complex parsing to extract the footnotes and rebuild them as sidenotes, yet also wanted to preserve the original footnote section on mobile.

This was relatively painless overall. Once you get a sense of how the parsing and tree-traversal works, you can build pretty much anything you'd want. The only thing that annoyed me was that I couldn't easily bundle the styles with the plugins. But that's not so bad, and in fact I can imagine situations where a "headless" plugin is actually a good thing.

MDX-driven pages

In addition to writing my posts with MDX, I created a top-level pages/ directory that I could use to stand up non-blog pages. It's way less annoying to write out a page of Markdown than a page of JSX, plus it keeps my styles consistent. I used to have this feature in the very first version of my site, a decade ago! Glad to have it back.

I also chose to use less JSX in my MDX— ideally, none. The less I have to worry about JavaScript while writing prose, the better. I accomplished this largely through expanding the selection of Rehype plugins I use.

Code highlighting with Shiki

I left Prism behind in favor of Shiki. There were a number of subtle bugs and janky bits that made Prism never feel quite 100% functional for everything I wanted to do. Shiki, on the other hand, is incredibly polished. It's a build-time tool, which is a nice fit for my static site. It doesn't need to import a separate CSS blob for theming. And I can easily get a number of nice features like line highlighting:

interface User {
  id: number
  name: string
  email: string
}

function greet(user: User) {
  return `Hello, ${user.name}!`
}

and diff notation:

def calculate_total(items):
    total = 0
    total = sum(item.price for item in items)
    for item in items:
        total += item.price 
    return total

I know many of these fancy features exist as Prism plugins, but my experience was that they were mildly buggy. I also found that Shiki's TextMate grammars resulted in highlighting that just... looked better to me.

What's next

What's next is to actually use the dratted thing. I've got a backlog of posts I'd like to write, as well as a small TODO list of features I'd love to add (tags, or perhaps ToCs). Await them with bated breath, I suppose.

Footnotes

  1. Like this one here!

  2. That would be @vivshaw/basalt-mdx, my MDX expansion pack for Basalt.

  3. Like this link here! Hover to see the underline wiggle, click for a surprise.

  4. This Icelandic black sand beach from Jeremy Bishop.

  5. This snow-capped and cloud-obscured Swiss mountain range from Matthias Kinsella.

  6. That design system, @vivshaw/basalt, remains the design system for this site.

  7. For example, the showy/hidey sticky navbar becomes a stationary navbar at the top of the page.

  8. Seek's Braid Design System uses this approach quite well.

  9. I found this post by Keith McClary a helpful start, though my approach diverged.