← Articles

remi.works updated

remi.works has had another rebuild. I kept the two-color theme, the tiles, and static hosting, and moved the application code to SvelteKit. This is how the site works now. The original project post covers the earlier Eleventy version.

Still just files on GitHub Pages

SvelteKit's static adapter builds the site into _site. GitHub Pages serves those files; there's no Node process running behind the live site. You get the article text in the initial HTML, and Svelte takes over the interactive parts once it loads.

Filesystem routes have replaced my custom router. The shared layout holds the navbar, theme controls, and search dialog. Individual pages own their content and any effects that need to stop when you leave. That division makes it much easier to see where a feature belongs.

Content without extra copies

I still write the article bodies as HTML fragments and keep their metadata in JSON. The module at src/lib/server/content/index.js reads and validates those files during the build. Articles, Work, search, and the sitemap all get their data from that model. A bad content reference is a build error, which is a better time to find it than after publishing.

The article route at src/routes/articles/[slug]/+page.server.js is small:

import { error } from "@sveltejs/kit";
import { createContentSnapshot } from "$lib/server/content/index.js";

export const trailingSlash = "always";

export function entries() {
  return createContentSnapshot().getArticleEntries();
}

export function load({ params }) {
  const articlePage = createContentSnapshot().getArticle(params.slug);
  if (!articlePage) {
    error(404, "Article not found");
  }
  return articlePage;
}

Articles and Work use the same reading layout. Text, code samples, and media get room across the page, and previous/next controls let you move through each collection. The archive is now called Articles; the old News links still redirect to the right places.

Two colors, everywhere

The theme starts with a generated dark/light pair. CSS properties carry those colors through the text, tiles, controls, and canvas effects. Employer logos use masks so they can take the same colors instead of bringing a fixed black logo into every palette.

A small script runs before the stylesheet paints. It restores the session's pair and day/night setting on a normal visit, or generates a fresh pair on a page refresh. The Svelte theme controller picks up that result rather than generating a second palette when it starts. Moving between pages keeps the current colors, and night mode swaps the two roles.

Muted text needs its own contrast check because lowering opacity mixes the foreground with the background. The theme computes a minimum opacity for each direction of the pair. I've written more about that in Automating color theory, and put the generator in the palette tool if you'd rather try it.

Moving between pages

SvelteKit handles internal navigation, metadata, focus, and scroll restoration. I've added a transition controller around its route lifecycle to keep the movement consistent with the tiles. Moving across the navbar follows its left-to-right order. Opening a post moves into the collection, and returning to its list reverses that direction.

The pieces enter and leave one after another: tiles on an archive, the header and sections on a post. Filtering and sorting use a similar cascade vertically. Reduced-motion preferences remove the animated movement. The links still work as ordinary links when JavaScript is unavailable.

Search and the things under Extras

Search uses a generated index of articles and work entries. The browser loads it when search is needed, then handles matching locally. It normalizes case and accents before ranking results. Tag buttons open the same search dialog with a tag filter, so there's one place to handle keyboard input, closing, and restoring focus.

The About page has its own animated identity tile. The interactive simulations live on Toys under Extras, alongside Tools and its palette generator. Each toy is loaded on demand. Its Svelte component owns the canvas host, loading state, and cleanup; the simulation and rendering code can stay separate from the page components.

The shared toy runtime runs simulation steps at a fixed cadence and lets rendering interpolate between states. It also handles pausing and teardown, so those details don't need to be rewritten for every experiment.

Publishing

A push to the publishing branch runs the GitHub Actions workflow. It validates the content, checks the Svelte source, runs the tests, builds the site, and verifies the output's routes and assets before uploading _site to GitHub Pages.

Hosting is still the easy part. For why I made the switch, see Transitioning remi.works to SvelteKit.

Find content