Reading List

The most recent articles from a list of feeds I subscribe to.

Technology didn’t have to glow

Technology didn’t have to glow. […] There were devices that simply did what they were for, without demanding attention.

I've always had a soft-spot for e-ink screens. Tom MacWright puts it better than I ever could, we should be the ones demanding attention from technology, not vice-versa.

macwright.com

Stefan Zweifel on storing user preferences in a Laravel application

Stefan Zweifel explains how he stores user preferences in a Laravel application using spatie/laravel-data. With the data package, you can store user settings as a blog of JSON in your database—so you don't need to update your table schema for every change—and have a typed object to work with in code.

He mentions poor query performance as a possible tradeoff if you need to query the database for a specific value.

One thing to keep in mind is that querying for specific settings can lead to performance issues and should probably be avoided.

If your app regularly needs to query for users who have selected a particular date_format , it's better to promote this setting to its own column. This makes the work of your database and possible indexing much easier.

If this is something you need, you could solve it with a virtual column mapped to a JSON value of the settings object. There's a nice tutorial on that on the Kirschbaum blog

stefanzweifel.dev

Introducing Svelte by Example

This week, I released Svelte by Example, a tutorial/resource/whatever-you- want-to-call-it introduction to Svelte.

The goal of Svelte by Example is not to turn you into an expert Svelte consultant after reading it through, but to introduce you to the framework's main concepts, and hopefully peak your curiosity and have you dig deeper.

If you've been curious about Svelte but haven't had time to give it a look yet, it's for you!

Keep reading if you want to learn more about how the site is built.

Implementation details

Svelte by Example is built with SvelteKit. (Otherwise it would be awkward, right?) It only has a homepage and example page template, so the codebase isn't that large. You can browse the source code (and fix my typos) on GitHub.

The fun part was figuring out a way to author content and add display metadata. Each example is laid out in two columns: a description on the left side, and a code snippet on the right. I wanted to be able to place the description at the same height as the snippet.

Svelte by Example screenshot

Here's part of the content's source for that page:

Svelte uses `{#each}` and `{#if}` template directives to render lists and conditionally render content.
 
*In this example, we'll render a list of todos and display a message based on the remaining amount of todos.*
 
---
 
{12} Use an `{#each}…{/each}` block to iterate over an array in a template.
 
{16} Use an `{#if}…{/if}` block to conditionally render HTML.
 
{20} Use `{:else}` and `{:else if …}` directives to add more conditions.
 
```svelte
<!-- TodoList.svelte -->
<script>
import Todo from './Todo.svelte';
 
let todos = [
{ task: "Mow lawn", completed: false },
{ task: "Walk dog", completed: false },
{ task: "Read newspaper", completed: false },
];
</script>
 
{#each todos as todo}
<Todo {...todo} />
{/each}
 
{#if todos.length === 0}
<p>All done!</p>
{:else if todos.length === 1}
<p>Almost there!</p>
{:else}
<p>Keep going!</p>
{/if}
```

The content is set with a superset of Markdown. First, I generate HTML from Markdown, then use Cheerio (a jQuery alternative that runs in Node.js) to extract & transform content to data objects I can use to render in SvelteKit.

  1. With Cheerio, I split the page in blocks. Each "block" on the page is separated by a horizontal rule ---.
  2. I extract the code from the blocks so I can render the description and code in different columns. Code is also passed through Shiki for syntax highlighting.
  3. Some paragraphs in the descriptions are prefixed with a number in curly braces, like {5}. I extract those numbers and add them as metadata, so I can absolutely position the line when rendering. This gives me exact control of the description placement.

All this happens in a build-content.js script (source), which transforms all markdown files and writes them in a large JSON file, which I can read in SvelteKit. During development, I used Chokidar to add a file watcher so content changes trigger the build script, which triggers a change in SvelteKit and hot reloads in the browser.


I had a lot of fun building the project as it was a healthy mix of content, design, and development. Time for a break now that it's out, but I have more than enough ideas to expand on in the future.

Empty states with CSS and the `:only-child` selector

On Twitter I came across a great tip on displaying empty states with CSS.

When you're rendering a list, I often wrap a loop in an if/else statement to display an empty state when the list is empty.

<section>
@if($events->isNotEmpty())
@foreach($events as $event)
<article>
{{ $event->name }}
</article>
@endforeach
@else
<p>No events created yet!</p>
@endif
</section>

If you want to reduce nesting and drop the conditional, here's a smart trick with the :only-child selector to display the message when there are no events.

<section>
@foreach($events as $event)
<article>
{{ $event->name }}
</article>
@endforeach
<p>
No events created yet!
</p>
</section>
section > p {
display: none;
}
 
section > p:only-child {
display: block;
}

We hide the message, but when it's the only child in the section, we'll display it. Since there are no other children, we know there are no events.

Using Tailwind

Using Tailwind, we can use the only: variant to do the same.

<section>
@foreach($events as $event)
<article>
{{ $event->name }}
</article>
@endforeach
<p class="hidden only:block">
No events created yet!
</p>
</section>

PS: Extra goodie for Laravel devs: another way to do this is with the lesser-known @forelse Blade directive.

<section>
@forelse($events as $event)
<article>
{{ $event->name }}
</article>
@empty
<p class="hidden only:block">
No events created yet!
</p>
@endforelse
</section>

Go Is a Shop-built Jig

I love this description of the Go programming language from Rob Napier:

Go feels under-engineered because it only solves real problems. If you've ever worked in a wood shop, you've probably made a jig at some point. They're little pieces of wood that help you hold plywood while you cut it, or spacers that tell you where to put the guide bar for a specific tool, or hold-downs that keep a board in place while you're working on it. They're not always pretty. They often solve hyper-specific problems and work only with your specific tools. And when you look at ones that have been used a lot, they sometimes seem a little weird. There might be a random cutout in the middle. Or some little piece that sticks off at an angle. Or the corner might be missing a piece. And when you compare them to “real” tools, “general” tools like you'd buy from a catalog, they're pretty homey or homely depending on how you're thinking about it.

But when you use one of them in your shop, you learn that the random cutout is because you store it against the wall and it would block the light switch otherwise. And if you put your hand on that little extra piece that sticks out, then the board won't fall at the end of the cut. And the corner… well the corner is where you messed up when you were first making it and it's kind of ugly, but it never actually matters when you use it. And that's Go.

robnapier.net