My new digital home
Read the original on dariuszwinkler.com ↗It's time for a dev blogger's favorite topic: blogging about my new blogging setup. After migrating my blog from Hexo to Eleventy, building a photography website and creating a personal one-pager, it was time to find a new digital home for all my projects.
If you notice any issue after the migration, please let me know. I redirect the RSS feed to the new domain, but you might want to update the feed URL in your reader.
Motivation
I've been maintaining three different websites under two different names: a mostly developer-oriented blog, a photography portfolio and a one-pager gluing everything together. When I started blogging about my sabbatical journey, I was thinking about making yet another page to create a clear separation. I've decided to do the opposite and merge all my websites into one instead. As I further reduce my dependency on social media and third-party services, it has become even more important to have my own space and practice POSSE.
Every decision I've made was driven by two main goals: simplicity and personality.
Setup
I've stopped chasing the latest and greatest. I want to keep the setup as clean as possible, which means pulling fewer dependencies. Every package adds complexity and maintainability costs, so I've only added what was necessary — no more Gulp, Sass or Tailwind.
Instead of taking one of my existing websites as a starting point, I created this project from scratch. I reused some of my previous code, but I didn't want to take over any technical debt (especially with the advancements in CSS).
The foundation of this website hasn't changed: Eleventy (Build Awesome). It covers the fundamentals of a static site and offers a great level of customization. I've made my project more and less dependent on Eleventy at the same time:
- I replaced Gulp as my build pipeline with pure Eleventy processing.
- I replaced most shortcodes with Markdown alternatives (and
markdown-itplugins), which are better supported by editors like Obsidian.
I've also replaced the Nunjucks template engine with Liquid, which comes with a few trade-offs. The main reason was to avoid creating a new collection for every set of posts I render. With Liquid, you can apply array functions like filtering or sorting in the template itself:
{% assign drafts = collections.all | where: "data.isDraft" | reverse %}
{% for draft in drafts %} … {% endfor %}Another advantage is having date formatting included, so I don't need another library or custom code. You can even set a default date format, so you don't have to provide it each time.
What I don't like is the verbosity of the template language, although I've gotten used to it by now.
- Nunjucks:
{{ "/tags/" + tag + "/" }}
{{ modified or created or date }}
{% set perspectives = ["dev", "photography", "life"] %}- Liquid:
{{ "/tags/" | append: tag | append: "/" }}
{{ modified | default: created | default: date }}
{% assign perspectives = "dev, photography, life" | split: ", " %}After migrating my existing content, I've noticed the build taking way too long, both in the dev server and the production build. I've found out that the Eleventy template cache is turned off for Liquid by default. This was useful during development, but when updating actual content, it caused every template and partial to be constantly recalculated. After turning the cache on, this issue was resolved:
eleventyConfig.setLiquidOptions({ cache: true });Check the colophon for the full tech stack overview.
Perspectives
The main challenge was finding a way to separate and present my main personas. The Venn diagram intersection of people interested in software development, photography and my personal life is probably small. I could use tags, but they don't offer the level of personalization I was looking for. Most personal blogs separate the content by post type (blog post, note, photo), but this doesn't fit my use case. Instead, I've decided to structure my website around three perspectives, a term I have borrowed from the Eclipse IDE. Each perspective comes with its own avatar, tagline and bio. If someone is interested in everything I share (hi Mom!), they can still view all my posts.
Design
Since building my photography website, I've made many changes, but I never got around to share an update. The biggest change was the design, which I have used as a blueprint for the new website. I've decided to mimic a social timeline/feed, which most people are familiar with. At the same time, it should be clear that it's a personal website and not yet another walled garden.
Typography is an important part of every design. The first step is to choose between a system font stack and a custom font, which comes with the usual performance vs. personality trade-offs. I've decided to go with a uniform look across all devices. I've settled on Source Sans 3 for copy text and Hack for code.
Another big change was introducing a sidebar. I always felt like the empty horizontal space was "wasted" on a desktop device, but getting the sidebar UX right is challenging. I'm happy with the result, especially with the last two panels adding a personal touch to the design.

Images
I've made several changes to the handling of image files. To make them compatible with Markdown editors, I had to replace my custom picture shortcode. The official Eleventy image plugin is a popular choice, but it violates my simplicity goal and doesn't support everything I needed. I've decided to go with a plain Markdown image markup and a single image file (no format fallback or 2x version for retina displays). Additionally, I wrote a markdown-it plugin to turn the optional title parameter into a caption. As for the file format, both WebP and AVIF have excellent browser support. For each image, I select the format based on the quality and file size. For my photos, I'm using WebP for the timeline thumbnails and JPG for the full-size version.
Theme picker
There have been some recent discussions about dark mode toggles, and I considered going for a light/dark solution based on the user's OS preference. Driven by the personality goal, I knew I had to add at least one more theme, so a proper theme picker was necessary.
Similar to navigation, people like to hide the theme selection on mobile devices behind a burger menu or a dropdown. I prefer the solution by Sarah L. Fossheim, where all available themes are displayed above the page footer. A friend was scrolling through my new website, and their first question was "Why is the theme picker in the footer?". This tells me the solution was a good idea — I don't think he would have noticed a typical theme button in the header.
The implementation follows standard practices:
- Use the light or dark theme as default based on the user's OS or browser preference.
- Store a selected theme in the local storage and use it as the single source of truth.
- Prevent a flash of inaccurate color theme.
- Make the theme play nicely with bfcache.
I might have slightly gone overboard with my third theme, but the nostalgia kept me adding more and more features. Enjoy going back to the 90s:
Feeds
As browser vendors decided to remove XSLT support, I didn't bother to style my feed. Instead, I've created a new web feeds page, listing all available feeds and a short explanation. I provide one feed for every perspective and one with all my posts combined.
I once offered tag-based RSS feeds. This was useful for my sabbatical, but even there I had only a single subscriber. Now, all tag feeds redirect to their main perspective feed.
I've also dropped the JSON feed, despite having at least 16 subscribers. Given that every web feed reader supports Atom feeds, I don't see the value of providing a JSON alternative. A JSON feed doesn't add much complexity, but handling different media types (post, photo) and properly escaping the content is one more thing to implement and maintain.
Quality assurance
I've put a lot of effort into my new digital home, and I want to make sure it stays in good shape. I've included several tools, most running automatically as part of the CI pipeline:
- eleventyDataSchema — lint Eleventy data values like titles or dates
- markdownlint — format/lint Markdown files
- Vale — lint content (grammar, writing style, formatting)
- Lychee — find broken links (internal and external)
- Evaluatory — check accessibility and validate HTML
- Oxlint — lint JavaScript files
Eleventy linters are another option to analyze a template's output, but I haven't found a use case for it yet.
Not everything can be automated, so I also did the following manually:
- Test with Chrome, Firefox and Safari
- Test with Polypane
- Test with Google Lighthouse
- Test with a screen reader
- Test with JavaScript disabled
- Test with CSS disabled
- Test at 200% zoom
- Test with a bigger browser font size
- Test with custom fonts disabled
- Test with delayed assets
Performance
It's difficult to compare the performance of two separate websites with that of a combined one. Some parts got worse, e.g. bigger font files and more CSS. At the same time, I've noticed that gzip compression was turned off for anything but HTML for a few years now (I must have missed it when upgrading nginx) 🙈.
One improvement was to implement on-demand assets. For example, the lite-youtube-embed CSS and JavaScript is only loaded if a <lite-youtube> element is present on the page.
As before, JavaScript is fully optional (except for interactive demos in individual posts). I've been using a mix of <noscript> and CSS to hide elements that require JS (like the theme picker).
I don't like the user experience of pagination, so I avoided it for years. But now that I have almost 300 posts, I've decided to finally add it. I display 30 posts per page, with images further down still being lazy-loaded for better performance. I haven't applied pagination to tag pages, as that's more complex with Eleventy.
Bits and pieces
Here are some other noteworthy changes:
- I've removed post-specific social share images and use a single WebP Open Graph image instead.
- I've dropped JSON-LD. Open Graph and Microformats are sufficient.
- "Naming is hard" is a common expression in software development, but it also applies to photograph titles. I've decided to use a counter as the title, e.g. #0100. Sometimes I do come up with a title that I love, like the Castle in the Sky, so I might mention it in the description.
- On a photo page, you can now open the previous/next photo using the left/right arrow keys (in addition to the existing navigation buttons).
- It's now possible to filter photos by date, gear, or location. Check the explore page for an overview.
- I've learned about and used some experimental CSS features (with progressive enhancement), like scroll-triggered animations or squircle corners.
- I've added an AI statement on my LLM usage.
- I finally have a proper about page.
- Cool URIs don't change, but it's inevitable when moving to a new domain. All my previous URLs are redirected, except for some assets that are no longer used. I will keep the old domain, so the redirects will continue to work for a foreseeable future.
- I've kept my previous Easter egg and added a few more. Have a happy hunt! 🕵️♂️
Inspiration
I visited hundreds of personal websites looking for inspiration. Here are a few that I kept coming back to:
- Emma Goto. I love Emma's approach to presenting the variety of her interests, even though I went a different route.
- Brennan Kenneth Brown. This website has so much to offer. It's a great example of what the open web looks like.
- Rex Barrett. The sentence "Collecting hobbies is my hobby." hit home — so much that I had to
stealborrow it for my short life bio.
Conclusion
This project was mostly about merging the content of two websites into one. I knew this would take a considerable amount of time, but I still greatly underestimated the effort. In the end, it was all about the journey, not the destination. I loved fine-tuning the design and tweaking little details that probably no one will ever notice. I still have many more ideas left in my backlog.
How do you like my new digital home? If you have an opinion, let me know!