Reading List
The most recent articles from a list of feeds I subscribe to.
Local or session storage?
Local storage tends to be the obvious place to persist data locally in a web application. We tend to grab straight for localStorage, but it’s not the only tool in our workbox. Another option is sessionStorage. Let’s review their similarities and differences, and determine when to use which.
localStorage and sessionStorage are part of the Web Storage API have the same key/value mechanism. We can write with setItem, and read with getItem.
localStorage.setItem('scheme', 'dark');
const colorScheme = localStorage.getItem('scheme');
sessionStorage.setItem('scheme', 'dark');
const colorScheme = sessionStorage.getItem('scheme');
They both store data as strings, but can store objects, arrays, and other primitive values as JSON with JSON.stringify and JSON.parse.
localStorage.setItem(
'preferences',
JSON.stringify({ scheme: 'dark' })
);
const preferences = JSON.parse(
localStorage.getItem('preferences') || '{}'
);
These are pretty naive snippets. When dealing with storage, try/catch is advices to avoid storage limits and other caveats. This post explains it better than I ever could.
So when to use sessionStorage over localStorage?
window.localStorage
localStorage is long-living, and shared across all visits to the same domain. Unless the user explicitly clears their cache, data will remain persisted.
It’s a good fit for things you want to remember forever. It’s still a local cache, so forever means “as long as possible.”
- Critical application data, like a JWT to authenticate a user
- Long-lived application preferences, like a dark mode toggle
- Long-lived choices a user makes on a site, like a country picker
- Data in an app with offline support, either as a cache for server data or as a queue for commands to sync with the server when it’s back online
window.sessionStorage
sessionStorage has a short lifetime, and is shared across all visits to the same URL in that tab. When the user closes their tab, stored data is discarded.
It’s good fit for things you want persisted for a short time.
- Page state, like a datatable filter
- Draft content, like the contents of a textarea for a comment
- Scroll positions or dynamic elements on a page, so they’re restored when the user hits the back button
General guidelines
In my experience, localStorage vs sessionStorage generally boils down to:
- If you want to store something that applies to the entire site or app, use
localStorage - If you want to store something that applies to a specific page, use
sessionStorage
This all highly subjective and depends on context. Datatable filters or draft content make more sense in localStorage. It depends on whether users expect to return to the page with a clean slate, or be able to pick up exactly where they left off.
There are more ways to store data locally like HTTP cookies, History.state, or IndexedDB. They each have their own use, but none are as straightforward as the Web Storage API.
CSS states with attribute selectors
Elise Hein compiled a few arguments in favor of using CSS attribute selectors more often. Two examples stood our for me.
Consider existing attributes. Whenever we add state we should take a step back and consider if we can leverage a standard. I often add an .is-active class to links in navbars. However, there’s an ARIA attribute for that. In addition to using a standard, our site becomes more accessible.
a[aria-current="page"] { }
Make invalid states impossible with data attributes. In HTML, we could accidentally end up with a card that .is-small.is-large. Using a data attribute enforces a single value for the attribute.
.card.is-small { }
.card.is-large { }
// vs
.card[data-size="small"] { }
.card[data-size="large"] { }
Read the full post on Elise Hein’s blog.
↗ CSS states with attribute selectors
Elise Hein compiled a few arguments in favor of using CSS attribute selectors more often. Two examples stood our for me.
Consider existing attributes. Whenever we add state we should take a step back and consider if we can leverage a standard. I often add an .is-active class to links in navbars. However, there’s an ARIA attribute for that. In addition to using a standard, our site becomes more accessible.
a[aria-current="page"] { }
Make invalid states impossible with data attributes. In HTML, we could accidentally end up with a card that .is-small.is-large. Using a data attribute enforces a single value for the attribute.
.card.is-small { }
.card.is-large { }
// vs
.card[data-size="small"] { }
.card[data-size="large"] { }
Read the full post on Elise Hein’s blog.
2022 redesign
This blog’s design has remained roughly the same the past two years. I tweaked the style a lot, but changes were incremental and stay true to the neutral black and white style. Codebases rot over time, and small changes slowly but surely introduce technical debt. I started cleaning house, and before I knew it I was embarked in a full redesign.
Desk research & design inspiration
Before I design a website, I like to set up a primary goal to fall back to. For my blog, I went with “minimal enough to not get bored of it, but with enough personality”.
I’ve had designs with exotic typography or a bunch of flair. But after a few weeks, I tend to get bored of them and fall back to a minimal design. This time I wanted more than a neutral style. I wanted some elements that add personality to the site without overdoing it, without getting tired of them after a few weeks.
First I went through my bookmarks to dig up some inspiration. Here are some that stood out the most.
- Elise Hein — Love the typography here. I really like how some elements are pulled out of the single column layout. This turned out to be a big source of inspiration for this site’s grid.
- Rasmus — Great content structure, great typography, lovely colors. Completely different style than what I ended up with, but I love this design.
- Robin Rendle — Interesting home page, and I like the split between “notes” and “essays”. I didn’t end up splitting mine up, but I might in the future.
- Paco Coursey — Perfect example of a minimal design. Clean, good typography, and a few elements to keep it interesting. However, a bit too minimal for my redesign goal.
- Daring Fireball — I always liked the simplicity of Daring Fireball’s content structure. Just one big stream of content, some links, some posts.
- Dries Depoorter — Dries Depoorter is a digital artist. I recently saw him speak at Full Stack Europe, and he reignited a spark to be more creative in digital projects.
Now, on to design and implementation.
Still Hugo
This site is built on Hugo, and I have no reason to change that. I adore Hugo. I’m still at awe of how fast it is, the entire site builds from source in 300ms. That includes parsing 100 markdown files to generate over 350 pages and concatenating and minifying the CSS. Hugo doesn’t offer a lot of configuration and has no plugin system, which keeps me in check from overengineering things.
New typography
Since text is the most important aspect of a blog, I started shopping for fonts. My final choices were Karla and Berkeley Mono.
- Karla — I’ve used Karla here in the past, it strikes a great balance between readability and character.
- Berkeley Mono — A new discovery for me. What a lovely mono font! Works for both code snippets and a display font for titles.
The chaotic neutral grid
On first sight, it’s a typical sidebar + content layout. But there are few items that break out and make things interesting. Code blocks expand from the start of the sidebar to the end of the page. Blockquotes remain in line with the content, but run until the end of the page. Images take in as much space as possible.
This is all possible thanks to some fun with CSS grid. Named tracks make it easy to assign elements to columns.
.grid {
display: grid;
grid-template-columns:
[left-edge] minmax(calc(var(--spacing) * 2), 1fr)
[sidebar-start] auto
[sidebar-end content-start] minmax(20rem, 32rem)
[content-end] minmax(calc(var(--spacing) * 2), 1fr)
[right-edge];
column-gap: calc(var(--spacing) * 3);
}
.grid > .sidebar {
grid-column: sidebar-start / sidebar-end;
grid-row: 1 / 999;
}
Posts and page content are wrapped in an <article> tag, so adding display: contents allows me to specify which grid tracks the underlying content should snap to.
.grid > article {
display: contents;
}
.grid > *,
.grid > article > * {
grid-column: content-start / content-end;
}
.grid > article > pre {
grid-column: sidebar-start / right-edge;
}
.grid > article > blockquote {
grid-column: content-start / right-edge;
}
.grid > article > p:has(img) {
grid-column: left-edge / right-edge;
}
.grid > .footer {
grid-column: left-edge / right-edge;
}
I like the way the grid creates organized chaos with odd islands of whitespace like here.
The divider between the sidebar and the content also adds a randomness factor to the page. It stops running when it comes across a piece of breakout content. Sometimes it stops after the sidebar, sometimes it nearly reaches the end of the page.
This is achieved with a large end value for grid-row. The day I have a post with more than 999 blocks, the line will stop. I could increase the number, but why fight its quirkiness?
.grid > .sidebar {
grid-column: sidebar-start / sidebar-end;
grid-row: 1 / 999;
}
145 colors to my disposal
Limiting options is the best way to induce creativity. I challenged my self to only use named CSS colors. Browsers support 145 named colors, that’s a complementary bag of 145 variables. My final color pallet consists of blue, magenta, cyan, gray, gainsboro, black, and white.
I also dug up the old a:visited selector which makes the site more colorful for some.
There’s no dark mode support for now, but I’ll most likely set that up later.
Better syntax highlighting
I ditched Hugo’s native highlighting engine in favor of Torchlight. I’ll write a post on setting up Torchlight with a static site generator in the coming days.
The biggest tradeoff is build speed. Hugo is inhumanly fast (300ms, remember?). Adding a tool that relies on network request is going to slow things down a lot. A full build takes about 16 seconds now, but the improved syntax highlighting is worth it. I only run Torchlight in production, so it doesn’t slow down the writing and dev experience.
Besides highlighting, Torchlight has fun features like code collapsing. And I don’t need to start every PHP snippet with a <?php tag anymore!
class User
{ // [tl! collapse:start]
public function __construct(
public int $id,
public string $name,
public string $email,
) {
} // [tl! collapse:end]
}
Knowing myself, I’ll be tweaking the design every know and then the coming months. I still want to restore dark mode, and maybe reintroduce webmentions (I used to have them, but got rid of them in my crusade for minimalism in the previous design). I hope the new design will be a good baseline for the next few months or years, only time will tell. But for now, time to get writing again.
2022 redesign
This blog’s design has remained roughly the same the past two years. I tweaked the style a lot, but changes were incremental and stay true to the neutral black and white style. Codebases rot over time, and small changes slowly but surely introduce technical debt. I started cleaning house, and before I knew it I was embarked in a full redesign.
Desk research & design inspiration
Before I design a website, I like to set up a primary goal to fall back to. For my blog, I went with “minimal enough to not get bored of it, but with enough personality”.
I’ve had designs with exotic typography or a bunch of flair. But after a few weeks, I tend to get bored of them and fall back to a minimal design. This time I wanted more than a neutral style. I wanted some elements that add personality to the site without overdoing it, without getting tired of them after a few weeks.
First I went through my bookmarks to dig up some inspiration. Here are some that stood out the most.
- Elise Hein — Love the typography here. I really like how some elements are pulled out of the single column layout. This turned out to be a big source of inspiration for this site’s grid.
- Rasmus — Great content structure, great typography, lovely colors. Completely different style than what I ended up with, but I love this design.
- Robin Rendle — Interesting home page, and I like the split between “notes” and “essays”. I didn’t end up splitting mine up, but I might in the future.
- Paco Coursey — Perfect example of a minimal design. Clean, good typography, and a few elements to keep it interesting. However, a bit too minimal for my redesign goal.
- Daring Fireball — I always liked the simplicity of Daring Fireball’s content structure. Just one big stream of content, some links, some posts.
- Dries Depoorter — Dries Depoorter is a digital artist. I recently saw him speak at Full Stack Europe, and he reignited a spark to be more creative in digital projects.
Now, on to design and implementation.
Still Hugo
This site is built on Hugo, and I have no reason to change that. I adore Hugo. I’m still at awe of how fast it is, the entire site builds from source in 300ms. That includes parsing 100 markdown files to generate over 350 pages and concatenating and minifying the CSS. Hugo doesn’t offer a lot of configuration and has no plugin system, which keeps me in check from overengineering things.
New typography
Since text is the most important aspect of a blog, I started shopping for fonts. My final choices were Karla and Berkeley Mono.
- Karla — I’ve used Karla here in the past, it strikes a great balance between readability and character.
- Berkeley Mono — A new discovery for me. What a lovely mono font! Works for both code snippets and a display font for titles.
The chaotic neutral grid
On first sight, it’s a typical sidebar + content layout. But there are few items that break out and make things interesting. Code blocks expand from the start of the sidebar to the end of the page. Blockquotes remain in line with the content, but run until the end of the page. Images take in as much space as possible.
This is all possible thanks to some fun with CSS grid. Named tracks make it easy to assign elements to columns.
.grid {
display: grid;
grid-template-columns:
[left-edge] minmax(calc(var(--spacing) * 2), 1fr)
[sidebar-start] auto
[sidebar-end content-start] minmax(20rem, 32rem)
[content-end] minmax(calc(var(--spacing) * 2), 1fr)
[right-edge];
column-gap: calc(var(--spacing) * 3);
}
.grid > .sidebar {
grid-column: sidebar-start / sidebar-end;
grid-row: 1 / 999;
}
Posts and page content are wrapped in an <article> tag, so adding display: contents allows me to specify which grid tracks the underlying content should snap to.
.grid > article {
display: contents;
}
.grid > *,
.grid > article > * {
grid-column: content-start / content-end;
}
.grid > article > pre {
grid-column: sidebar-start / right-edge;
}
.grid > article > blockquote {
grid-column: content-start / right-edge;
}
.grid > article > p:has(img) {
grid-column: left-edge / right-edge;
}
.grid > .footer {
grid-column: left-edge / right-edge;
}
I like the way the grid creates organized chaos with odd islands of whitespace like here.
The divider between the sidebar and the content also adds a randomness factor to the page. It stops running when it comes across a piece of breakout content. Sometimes it stops after the sidebar, sometimes it nearly reaches the end of the page.
This is achieved with a large end value for grid-row. The day I have a post with more than 999 blocks, the line will stop. I could increase the number, but why fight its quirkiness?
.grid > .sidebar {
grid-column: sidebar-start / sidebar-end;
grid-row: 1 / 999;
}
145 colors to my disposal
Limiting options is the best way to induce creativity. I challenged my self to only use named CSS colors. Browsers support 145 named colors, that’s a complementary bag of 145 variables. My final color pallet consists of blue, magenta, cyan, gray, gainsboro, black, and white.
I also dug up the old a:visited selector which makes the site more colorful for some.
There’s no dark mode support for now, but I’ll most likely set that up later.
Better syntax highlighting
I ditched Hugo’s native highlighting engine in favor of Torchlight. I’ll write a post on setting up Torchlight with a static site generator in the coming days.
The biggest tradeoff is build speed. Hugo is inhumanly fast (300ms, remember?). Adding a tool that relies on network request is going to slow things down a lot. A full build takes about 16 seconds now, but the improved syntax highlighting is worth it. I only run Torchlight in production, so it doesn’t slow down the writing and dev experience.
Besides highlighting, Torchlight has fun features like code collapsing. And I don’t need to start every PHP snippet with a <?php tag anymore!
class User
{ // [tl! collapse:start]
public function __construct(
public int $id,
public string $name,
public string $email,
) {
} // [tl! collapse:end]
}
Knowing myself, I’ll be tweaking the design every know and then the coming months. I still want to restore dark mode, and maybe reintroduce webmentions (I used to have them, but got rid of them in my crusade for minimalism in the previous design). I hope the new design will be a good baseline for the next few months or years, only time will tell. But for now, time to get writing again.