The sun is shining, I have plenty of time as I’m still looking for a job so I’ve poured a lot of work into this website to clean up and improve things. I want to share the highlights in this article.
New navigation
Except for some recent design tweaks in February, this website has remained virtually unchanged since as far back as 2020, which is when I ported it to Eleventy. I couldn’t find a Wayback Machine snapshot dated earlier than that, so I’m going to assume this is also when I redesigned it.
I’ve finally bitten the bullet and moved the navigation out of the content column. It took me a little while to come up with something I didn’t hate, and I’m still not enamored with it, but I feel it’s better than it used to be. It’s more anchored, it’s more usable, and breaks the centered column flow a little. Also, the skip link fits very nicely within it.
On the plus side, this enabled me to play with the Popover API for the first time, for the mobile version (thanks to John Dalesandro for his instructive article on the matter). I tried a few approaches where the whole menu was visible at all times before, but there just wasn’t enough room so I decided to switch to a popover menu. It looks nice, I’m happy with it.
I’ve also used @starting-style for the first time to animate the apparition of the menu.
.Navigation[popover] {
opacity: 0;
transform: translate(1rem, -1rem) scale(0.95);
transition: 300ms ease-out;
}
.Navigation[popover]:popover-open {
opacity: 1;
transform: translate(0) scale(1);
@starting-style {
opacity: 0;
transform: translate(1rem, -1rem) scale(0.95);
}
}
New post head
Moving the navigation out of the way had some unintended design side-effects, and led me to rework the header of blog posts. I’ve made the following changes:
- The title is now start-aligned, instead of being centered. It actually looks better this way (in my opinion that is). On large screens, it’s also pulled into the left margin like other embeds within blog posts, which gives it a nice emphasis that I find aesthetically pleasing.
- There is now an update date below the publication date (see Update dates below).
- The table of contents moved into that header instead of floating to the right of the content, which really helped solve a lot of weird layout quirks. It opens like a dropdown on large screens and I think works well enough.
Update dates
As mentioned above, I’ve added an update date to articles to help figure out how relevant the content still is.
To do that, I wanted to leverage Eleventy’s built-in support for git Last Modified, which tells Eleventy to extract the date from the last commit touching the file. It has two major downsides though:
-
It only works for a field named
date. If you want both a publication date and an update date, you end up withdatebeing the update date, and have to create apublication_date(or whatever) field. This is backward to me: the publication date is the truly important one, and as such should be held indate. Not the other way around. -
It comes with a significant performance hit, due to Eleventy spawning a
git logsubprocess for every single template being rendered, which starts to matter when you have hundreds of files. To be fair, Eleventy mentions that in the docs.
So I’ve shamelessly copied Jens Oliver Meiert’s approach. The idea is that you compute all dates at once in a single pass and store them in a data file so templates can do cheap reads. I’ve made two important changes to their code:
-
I’ve recently renamed the directory containing all my posts from
_poststoposts, and of course that caused all posts to be committed. To discard such change, I have used--diff-filter=M --name-statusand some normalization instead of--name-only. This basically ignores file moves which is nice. -
I’ve added caching, because every save in watch mode causes the data files to be recomputed, which meant re-running the git command, which seemed unnecessary.
I’m pretty happy with the outcome, except for the fact that I work so much on this website that most posts end up being marked as recently edited anyway. In principle, this should be helpful for the future. This change also made its way into the sitemap and the RSS feed.
Deprecation notices
I make a point not to delete content from this website, following the Don’t Break the Web directive. However, not all content ages the same way. So I’ve introduced deprecation notices to old articles. It looks like this:
This article is old and references content that may no longer be available, relevant or accurate. It remains online as to not break the web, but its content should be viewed in the context of its publication date in 2012.
Content improvement
To mark old articles as outdated, I’ve gone through all articles and took this opportunity to clean up a lot of things:
- Rescued a handful of articles that SitePoint silently removed.
- Fixed a bunch of old typos, broken Markdown and clumsy wording.
- Removed all broken links to the now defunct Sassmeister website and replaced them with links to GitHub Gists where the content was backed up.
- Cleaned up some old HTML still present in Markdown posts.
- Created proper Liquid partials for CodePen embeds and figure elements.
- Removed old Twitter embeds and replaced them with simple quotes.
And a few things that are a bit more stylistic:
- Normalized the casing of every article title to use title case capitalization.
- Used the & character where relevant in titles because it just looks gorgeous in Baskerville.
- Replaced a bunch of em dashes with commas or parentheses — apparently the em dash is no longer cool.
- Had some fun with smiley faces. :)
New lists
As shown in the previous section, I’ve updated the appearance of lists (mostly unordered but also ordered a bit) to be more spacious and provide more breathing room. I used to rely on the default browser styles, and decided to go for something custom instead.
I’ve added a small animation to them so they fade into the viewport as you scroll past them. It was really a pretext to use scroll-driven animations, based on an experiment from Adam Argyle.
@media (prefers-reduced-motion: no-preference) {
.Post li {
animation-fill-mode: both;
animation-name: fade-in;
animation-range: entry 25% cover 50%;
animation-timeline: --item-timeline;
view-timeline-name: --item-timeline;
}
}
@keyframes fade-in {
from {
opacity: 0;
transform: scale(0.95);
}
}
Animated theme switcher
I’ve found this other cool demo by Adam Argyle leveraging the View Transitions API to animate the change of theme. I’ve tweaked his code a bit to match the design of this website better, and I really love the transition effect. It basically swoops outward from the button in the bottom right corner. Try it!
Baseline widget
As you can see, I’ve introduced a widget for baseline support. It uses the baseline-status web component, which I integrated in Eleventy following Stuart Robson’s article.
It’s nice overall, but I’m particularly unhappy with the fact that it weighs 65Kb, even if I conditionally and asynchronously load it. For reference, my entire home page weighs 75.9kB, and that’s including a stupid 52.9kB apple-touch-icon.png file, so 23kB really. This is 3 times as big.
Also, I couldn’t find a way to tweak the focus styles of the summary element which lives inside the Shadow DOM. No big deal, the component looks very clean anyway.
Tabbed code blocks
I was browsing Roman Komarov’s fantastic website when I stumbled upon his write-up about responsive tab size within code blocks. I’ve been thinking about this problem in the past, wanting to have smaller indentation on mobile where there is less room.
Turns out he has some magic up his sleeve:
pre {
container-type: inline-size;
}
pre code {
tab-size: round(up, 100cqi / 20ch, 2);
}
This is lovely and it works like a charm, but none of my code blocks used tab indentation. I thought it would be a nightmare to convert everything, but Cursor absolutely nailed it out of the park. It quickly wrote a solid script, converted all 1,000 or so code blocks to tabs, and that was that.
Perk of that conversion: tabs are better for accessibility anyway, so I’m glad it’s finally done. The only thing I need to figure out is how to make sure I keep using tabs for new code blocks, because my editor defaults to spaces and I’m likely to forget.
Logical properties
More as a learning opportunity than anything else, I’ve updated all CSS to use logical properties and values. That means padding-left becomes padding-inline-start, margin-bottom becomes margin-block-end and so on. In the process, I’ve learned a few things:
- The
floatproperty acceptsinline-startandinline-endin lieu ofleftandright. Floats are largely obsolete, but I still use it on the home page for a controlled case. - Border logical properties exist:
border-<inline|block>-<start|end>. There are a few places where I use some specific borders, which I could convert to use these properties. - Rounded corners logical properties also exist, and they can be a bit wacky. Namely, the
border-start-start-radiuslooks funky but makes sense (startside on both axis). - Interestingly, the
linear-gradient()function does not accept logical properties for its angle argument. You cannot useto endin place ofto right. This is a bit of a shame I’d say? I wonder whether there is an open issue somewhere about it.
Adrian Roselli has a lovely article and diagram to illustrate the differences on CodePen:
See the Pen Logical Properties Mapping by @aardrian on CodePen.
Of course it doesn’t matter too much for this blog since I write almost exclusively in English, and the few pieces of content that are not in English are still not in languages written right-to-left, but still. It’s nice to know it would work flawlessly. Potentially it could be helpful if someone was to use a browser extension to translate content in, say, Persian or Arabic.
You can toggle RTL mode for the whole document with this button, if you just want to try it:
SEO improvements
While doom-scrolling LinkedIn, I stumbled upon this GEO/SEO guide for Claude. I didn’t want to blindly install it because I am being cautious with AI and live in fear of prompt injection, so I’ve manually set up some Claude agents the way that plugin does.
After running them on this website, I came across some opportunities to improve SEO:
- Introduced JSON-LD structured data. I was using microdata somewhat successfully so far, and now combine both approaches.
- Tweaked the robots.txt file to be more kind to our AI overlords. 🙄
- Made it so when an article displays an image that can be used for Open Graph purposes, it now does.
- Optimized the favicon mess.
Final Jekyll extinction
Since I edited most files anyway with all the aforementioned changes, I’ve decided to address one of the last remnants of the Jekyll era and renamed all folders using an underscore prefix (e.g. _includes now includes). It’s just unnecessary with Eleventy, where we have more granular control over what gets built and how.
I’ve also replaced the now long defunct Simple-Jekyll-Search script with a simple homemade one.
The last real testament of this blog having ever used Jekyll is the URL pattern for posts, which is /YYYY/MM/DD/slug/. Had I built this website today, I’d drop the date from the URL entirely. Too late now.
Wrapping up
Overall, I’m very pleased with how it all turned out. I know it’s not super significant, and most people won’t notice any of that, but it feels good, you know. It’s like when you deep-clean your kitchen, or go through your wardrobe. It’s good for the soul.