My app includes content areas that expand and collapse. A lot like accordions, except they take up the whole page and can be huge.

When you open one, whatever's open gets closed, and that makes whatever you just clicked on jump around as the previous content area stops taking up space on the page.

Here's a code snippet I put together that keeps whatever you just clicked at the same spot in the viewport after the layout shift:

 * This ensures that the element is at the same position within the viewport
 * after a layout shift.
 * MUST be called BEFORE triggering the layout shift.
 * IT can only do so much - if the layout shift cuts off enough content, the
 * element will still wind up positioned higher in the viewport than before.
export const retainScrollPosition = (el: Element) => {
  const targetViewportPosition = el.getBoundingClientRect().top;

  requestAnimationFrame(() => {
    const newPagePositon = el.getBoundingClientRect().top + window.scrollY;
    window.scrollTo({ top: newPagePositon - targetViewportPosition });

We have to pick a new health insurance plan this month, and we've had a tough time making the decision.

You can't just add up what you'll spend – what each thing costs depends on how much you've already spent!

And some things are inherently probabilistic – will I go through procedure X this year? How many visits will I need for condition Y? How many urgent care visits?

So complex and uncertain!

Inspired by vaguely recalling that I read Lucas F. Costa's blog post some time ago, I applied the Monte Carlo method to my health insurance decision.

I have a simplistic understanding of Monte Carlo simulations:

  1. Assign probabilities to everything that can happen in your scenario
  2. Randomly selecting outcomes for each possible event, then repeat the calculations a gazillion times
  3. Measure how things typically play out

It can get much fancier (hello, MCMC!) but I think that's the gist of it.

I put together a simple TypeScript file with some arithmetic operations and calls to Math.random() and ran it with Bun. I punched in all the reasons my wife and I will or might spend on healthcare, added in the premiums, and took the average result.

Surprisingly, the expensive plan will save us a couple thousand dollars this year, even accounting for the higher premiums.

I feel better about the decision since I did something resembling rigorous calculation of which plan is best. Usually I just guesstimate and anxiously hope for the best.

I give 100% effort. But I have no sense of moderation – I'm on or off.

I can do great work when I care about something. But if I don't, I can barely work at all.

I don't take credit for work that wasn't mine. But I can't feel work satisfaction just because I'm in the same team or company as the person who did something cool.

I can understand some things very clearly and deeply. But I can't believe something or change my mind just because someone insists I should.

I can do excellent work when I understand the assignment in detail. But when I don't, I can't even get started.

I have very broad interests, and can get excited about many subjects. But I can't stay focused on any one subject very long.

I aspire for my work to be excellent. But I'm inflexible, opinionated, stubborn, and the pickiest eater you'll ever meet.

Every neurodivergent trait that makes me stronger also makes me weaker. A coin with two sides. But sometimes people don't understand that; they imagine that their own capabilities are a baseline that I can just add my own strengths on top of.

My weaknesses are an integral part of me. You can't have half of the coin.

Dokku has a Let's Encrypt plugin which works behind Cloudflare. There's just a little bit of chicken-and-egg setup involved.

Let's Encrypt needs to connect back to your server to validate ownership of your domain. You can't have Cloudflare's “full” TLS mode enabled when you're doing first-time validation, because in “full” mode Cloudflare will error out, failing to establish a TLS connection to your not-yet-TLS backend server.

You could disable “full (strict)” TLS mode in Cloudflare, but then you'll take all your sites down: Dokku does HTTP –> HTTPS redirects on all sites configured with TLS, and will thus reject the non-TLS inbound connections from Cloudflare's networks. Or more accurately, it'll receive an inbound HTTP request from Cloudflare's servers, return a redirect to HTTPS, which Cloudflare will pass on to the client, but the client is already at an HTTPS URL, so the client will enter an infinite redirect loop.

You can get around all this during first-time setup by disabling Cloudflare's proxying behavior on your domain while you get Let's Encrypt set up on Dokku. After it's set up, you can turn Cloudflare proxying on, and cert renewals should work fine, since Let's Encrypt validation checks routed through Cloudflare can still establish end-to-end TLS while your certs remain valid.

While at work I was upgrading graphql-code-generator, and found that the generated code no longer exports the type for a query node. Instead, the type of the whole query response was exported. But I had code that relied on having the node type available.

To solve this, I created a new type by reading attributes from deep in the response type.

interface Foo {
    bar: {
        baz: {
            zap: {
                zoop: number;

type zoop = Foo['bar']['baz']['zap'][number]['zoop'];

If some of your properties are optional, you can use Required<T>. If they're possibly undefined, use Exclude<T, undefined>.

Recently, while building a simple Reddit clone, I wanted to lazy-load images and comments. That is, rather that loading all of the images and comments the instant I added a component to the DOM, I wanted to wait until the component was actually visible. This spreads out the impact of loading a page, both for the client and the server.


I needed to create a bottom nav (like what's used in many mobile apps) in CSS. Here's how I did it.


What are Sapper and Netlify CMS?


Sapper is Svelte's answer to Next.js/Nuxt.js. It's a way of rendering Svelte code on the server so your site is compatible with JavaScript-free devices, and so it renders immediately instead of waiting for a JS blob to download, parse, and run.

Sapper ordinarily runs as a full server application, but using the sapper export command we can generate a static version of our site that we can host on Github Pages or, in this case, Netlify. That's a great way to have a very fast site that's free for small-to-medium traffic numbers.

Netlify CMS

Netlify CMS is as open-source content management system, meaning it's a way to create blog posts and web pages through a web page. Since it's from Netlify, the static site host, it's designed to work with static site generators like Hugo and Jekyll. We'll be adapting it to work with Sapper.


I've been working on a project that uses GraphQL via Hasura. Using Subscriptions (real-time updates to queries) is a key feature for my project, but it's one that most of the simpler GraphQL client libraries don't support. That leaves me looking at heavyweight tools Apollo. Apollo is pretty complicated to configure (I'm paying for a lot of features I don't use), and it _strongly_ steers you towards React hooks for accessing data; I had to dig pretty hard to find the documentation for their language-agnostic client library. I've really come to believe data fetches shouldn't be represented declaratively, at least not the way React hooks handles it. The ergonomics are poor and the flexibility is low every time I try it.

So I went looking for a GraphQL client that could handle GraphQL subscriptions, which was simple to set up and worked great when used imperatively.


Here is a minimal router for Svelte v3 using Page.js.

Whenever the route changes, Page.js sets a variable that holds the component that should be rendered for the route.

  import page from 'page';

  import Home from './views/Home.svelte';

  let route;
  let routeParams;

  function setRoute(r) {
    return function({ params }) {
      route = r;
      routeParams = params;

  page("/", setRoute(Home));
  page({ hashbang: true });

<svelte:component this={route} bind:params={routeParams} />