I'm available for part-time work!

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} />