RSSAmplifier

Blog

oli's rss feed

weird web person

oliverjam.comRSS feed ↗41 posts

Latest posts

Subtley better borders using transparency

Here's an input. You can tell because it's got a border. Your name This border is just a solid grey colour: hsl(0 0 75) . I think it looks pretty nice as is, but there are some situations where it will struggle. For example on a gradient background: Your name Now the grey border looks muddy and hard to see. That light grey doesn't stand out from the background at all. We'd need to make the border…

Better JS bundle caching with vendor chunks

Browser caching is an important part of web performance. You can tell the browser to keep a copy of a file around for later with the cache-control HTTP header. For example a response with this header set: cache-control: max-age=3600 will be cached for one minute (3600 seconds) A one minute cache is not going to improve performance much though, since most users will visit the site again more than…

Testing React components from scratch

Although React components seem like simple functions, they actually hide a lot of complexity that makes them difficult to test. You can't just call a component function and assert about its return value. Components usually have behaviour like state, event handlers and side effects that should be verified. We're going to implement a very simplified version of a testing library for React that is…

Speed up your SPA by preloading data requests

Quick answer If you just want the tl;dr solution: use <link rel="preload"> to make the browser kick off fetch requests before it even starts downloading your JS files. That way the responses are ready by the time your app code runs. The problem One of the major downsides to a single-page app is that data fetching doesn't begin until your app's JavaScript runs. Depending on the size and complexity…

Writing a simple JSX to HTML renderer

I decided to write my own JSX renderer to better understand how it works (and to have a simple library I could use in side projects). You can skip all my rambling about why I like JSX and jump straight to the code . Update (May 2026): I published an improved version of this JSX renderer as part of my server-side app library . What is JSX? JSX is an extension to JavaScript that lets you write…

Simple progressively enhanced web apps

This is a follow-up to my last article on building simple server-rendered web apps . We're going to enhance the user-experience of these apps with a little bit of client-side JS. If you want to skip the history lesson you can jump to the fun code . Update (May 2026): I have published an improved version of what we built in this post as Transclusion . The lost art of progressive enhancement…

Build simpler web apps using Bun

Bun is a new JavaScript runtime that aims to be more fully-featured and compliant with web standards than Node has historically been. For example it comes with a test runner , JSX & TypeScript support, an HTTP server based on fetch , SQLite storage , password hashing , and other convenient tools. It's worth noting that although I'm focusing on Bun here, Deno has most of these features (as does…

Styling an element nested within itself in Tailwind

tl;dr here's the magic: [&_&] . Isn't he great? I'm gonna call him Gill. This "arbitrary variant" lets you apply styles when this element is nested inside another element with this class. For example: < ul class = " [&_&]:pl-4 " > ... </ ul > This list will only have padding-left applied if it inside another such list. Why would you do this I've been building a simple app for reading Reddit…

Redirect your log output to files for easier debugging

I was recently scraping hundreds of URLs at once (backing up my saved Reddit posts). This was a bit annoying to debug as I worked on the code, as logging each success/failure meant a ton of output in my terminal. Sometimes a network failure was buried way back in the logs, making it easy to miss. Terminal problems Most terminal emulators limit the number of lines you can scroll back (my iTerm2…

Custom site search with DuckDuckGo

My site's new design has a searchbar front-and-centre. I don't have a ton of articles published, but as I start to add more shortform notes and bookmarks it will get harder to track down specific blog posts, so a search feature felt helpful. I didn't want to spend much time on this, and I definitely didn't want to change my site's architecture or goals (fully static HTML/CSS with as little JS as…

Simple icon systems using SVG sprites

I recently rebuilt both my own site and my designer buddy Jared's . Both required a few icons, which meant I had to decide how to handle lots of small images. There are quite a few options, but I think SVG sprites are the best solution for most projects. I'll start with the solution, then talk about alternatives afterwards. Building a sprite sheet Historically a "sprite sheet" was a single file…

Deploying web apps to Fly.io

Key features I'm looking for something quick and easy to setup, that doesn't require my code to be written a certain way, and has a free tier . That last one has gotten harder to find (probably blame cryptocurrency shenanigans ). It's important to me because most of my random side-projects average less than one user a month—paying a nominal $5 or something for that is pretty silly. What is Fly?…

Frontend testing in Node with jsdom

The problem The browser and Node might share the JavaScript language, but they are very different environments. This means it can be awkward to wrangle things when you have some code written for the browser (accessing browser APIs like document.querySelector ), but you want to execute that code in a more convenient Node environment (e.g. to run some tests in your terminal). You cannot simply…

Using JSDoc to check your code

Documenting your functions It's often useful to document what our functions do. For example, if we had a function that created a new task object (for an imaginary to-do list app): function createTask ( title , owner_id ) { let created_at = Date . now ( ) ; return { title , owner_id , created_at } ; } It's easy to accidentally mix up the arguments, especially working in a different file: let task =…

Making Git push a bit friendlier

Here's an approximation of what happens when a beginner tries to use Git branches for the first time. After committing some changes on the local branch they try to push to GitHub: $ git switch --create example Switched to a new branch 'example' $ git push fatal: The current branch example has no upstream branch. To push the current branch and set the remote as upstream, use git push --set-upstream…

Building boring websites with Next.js

If you're just interested in how to use Next.js for simple forms you can jump to that section . First I'm going to rant a little about the current state of web development. If you want a recap on how forms work you should check out my HTML forms intro workshop . What are we doing A significant percentage of websites are effectively forms connected to a database. This architecture is what the web…

Avoiding 404 errors with Single-Page Apps

Client-side routing Single-Page Apps differ from traditional server-rendered applications in that they only ever load one "page" from the server. This means your server only knows about a single route: the home index.html . The server is just there to deliver your client-side JS to the browser. Routing is usually handled client-side—when a user clicks a link some JavaScript intercepts it, prevents…

Better dev environments with npm workspaces

For example you may have a React frontend and an Express backend for your app that you want to manage as a single repository on GitHub. However this makes development awkward, as you constantly have to cd into the right directory to install dependencies or run npm scripts. npm setup To use workspaces you need to be on the latest version of npm. Although the feature was added in npm 7 some of the…

How to make your own Git hooks

What is a Git hook? Git hooks are programs that automatically run at different points in the lifecycle of a Git repository. The most common one I see in JavaScript projects is the "pre-commit" hook. As the name suggests this runs right before a commit is made (i.e. after a developer runs git commit in the repo). This allows you to "hook" into the Git process to run your own code, and potentially…

Responsive CSS For Beginners

Responsive Design history In the early days of the web layouts were mostly single columns. CSS didn't have any way to control where things went on the page. Eventually people began abusing the <table> element to achieve complex layouts. This let them control exactly where things went on the page. However tables had a big downside: they required hard-coding everything with absolute pixel values.…

How to make your terminal nicer to use

It can be overwhelming looking at power users' configurations since they tend to have years of accumulated stuff, and little explanation for what everything does. This guide will keep things as minimal as possible—just setting up a few useful things. If you dump 200 cool aliases and plugins into your setup you won't even remember most of them are there and they'll never get used. A little…

Better native form validation

Quick summary If you just want the code here's the final CodePen . I turn off default validation with the form's novalidate attribute, then manually validate all fields with form.checkValidity() . This triggers an invalid event on each field, allowing me to mark each field as invalid and show the default browser validation message in a div. Native isn't always better I sometimes catch myself…

Build your own analytics with Netlify Functions

DIY, not Google It would however be hypocritical of me to have privacy-violating user tracking (like Google Analytics) on my own site when I block all tracking scripts in my personal web browser. Instead I figured I could create my own basic analytics without handing my users' data over to Google. A bare minimum analytics implementation counts each request to the server, records what page was…

Pitfalls of async functions

Async functions An async function is defined with the async keyword at the start. It works for function declarations and arrow functions: async function getData ( ) { } const fetchData = async ( ) => { } ; This keyword makes the function always return a promise, even if all the code inside of it is synchronous: async function one ( ) { return 1 ; } const result = one ( ) ; console . log ( result )…

Schedule Netlify deploys using GitHub Actions

Scheduling Netlify deploys Netlify is a fantastic place to deploy static sites, but they have no built-in way to schedule a deploy. They do however provide "build hooks". These are URLs that trigger a build when they receive a POST request. Previously I had played with Zapier , setting up a scheduled task that hit the Netlify build hook. This never felt right to me though: this one (crucial!) part…

Where we're going, we don't need servers

Maybe we do need a server However you'll quickly hit a point where you need just a tiny taste of a server. This usually happens when you have an API secret that you don't want to leak—if all your code is client-side there's no way to hide anything. Have we lost all the nice benefits of our static app? Do we have to maintain and deploy a separate backend repo (or try and deploy both from one repo)?…

Setting up a project from scratch

I'm going to assume you aren't using a project-generator like Create React App or Vue CLI and are starting totally from scratch. I'll also be doing everything from the command line since that's how I typically work. I find it's faster and easier (after a bit of practice) than fiddling with Finder and other GUIs. Creating the directory First I create a new directory for the project. I keep all my…

First-class functions in JavaScript

Functions are variables When you create a function in JS you are creating a normal variable: function returnsOne ( ) { return 1 ; } // we now have a variable named returnsOne This is still true (and perhaps more obvious) for arrow functions: const returnsOne = ( ) => 1 ; You can reference this variable the same way you would any other: console . log ( returnsOne ) ; // function returnsOne() You…

A complete guide to making web pages from scratch

I'm going to explain all the required bits you need to get a functioning page, using nothing but a text editor. Before you start I'm assuming you're working on a desktop computer or laptop. Whilst it is possible to code on a smartphone or tablet it will be much more difficult to set up. You'll need a program for editing text (ideally one designed for writing code). I'd recommend VS Code from…

Minimum viable GraphQL: the client

I'm going to demonstrate how to make basic GraphQL queries directly from the browser using vanilla JavaScript. If you know how to make POST requests using fetch then you're 90% of the way there already. If you've never encountered GraphQL before or you're confused by any of the terminology take a look at my previous post on GraphQL concepts . The query GraphQL queries can be represented as strings…

Minimum viable GraphQL: the concepts

Unique selling points GraphQL's main selling points are (in my opinion): Clients can query exactly the data they need (less over-fetching) The data and queries are strongly typed via a schema (fewer typos) There's a well-defined "right way to do it" (fewer opinions) Tooling GraphQL's strictly spec'd and strongly typed nature lends itself to nice automated tooling. For example most APIs using…

Testing your UI with React Testing Library

Philosophy We're aiming to test our components like a real user using React Testing Library ]. That means rendering a React component to a real DOM, rather than shallow rendering. We should also avoid calling event handler methods directly—all interactions should happen via actual DOM events as they would when a user browses the site. Vanilla example Here's a simple test for a React component…

Stop exporting things, I'm begging you

Even if you aren't using this rule yourself it seems to have spread its sinister tentacles out into the brains of the community. In my experience most people default to creating a new file whenever they need a new component. I think this is a bad default. Modules are modules, not organisers Most people seem to think of ES Modules as a mechanism for modularising and organising their code. Whilst…

Gatsby is the future

Why WordPress was so successful I think WordPress has dominated the web for a few reasons. Free and open-source WordPress is free and open-source, but backed by a private company called Automattic. This makes it an obvious default to build on, especially for small to mid-sized sites with limited budgets. If you're an agency charging under £10k for a basic marketing site plus blog you can't really…

Static typing in JavaScript with Flow

Disclaimer This was written a year ago as an introduction to static typing and the Flow library to help onboard new developers into my team at Ticketmaster. It's very likely Flow has published new major versions with new features since then; I apologise in advance for any out-of-date information. What are types? A type is something that tells the language what a piece of data is and how it's…

Introduction to the DOM

This is my attempt to bridge the gap between JavaScript the language and the bits of it you need to know to make things happen on a web page. It will assume you have a basic understanding of HTML and JS, but are new to actually using JS on a web page. What is the Document Object Model? The Document Object Model (DOM) is the JavaScript representation of the elements on a webpage. It's implemented…

Introduction to Redux

Principles Redux has three Principles : Your state has a single source of truth (one top-level object) Your state is read-only (can only be updated by dispatching actions) Your state is only altered by pure functions These principles are designed to help state management remain predictable even as an app or team grows much larger. Core Concepts State Your single source of truth for state is by…

Create a flickering image effect using CSS sprites

Primitive came to my attention a while ago as a nice tool for creating vectorised images. I was enamoured with the flickering effect achieved by animating between multiple versions of the same photo (Primitive produces different versions each time it is run). Animation There are a few different ways to achieve this effect — the Primitive pencil example uses JavaScript to repeatedly switch each…

How to create a Ken Burns hero image effect

The design process The first iteration of this hero area featured several looping videos, but we quickly ran into performance issues. There was no way to get multiple videos at a high enough quality to do justice to Lick's work without forcing users to download unfeasibly large files. We also struggled to provide a good user experience for mobile browsers that either refused to autoplay video or…

Why I donate 10% of my income to charity

Imagine you’re crossing a bridge on your way to work. As you reach the middle you hear splashing and a cry for help. There’s a young child in the water! Glancing around you realise that you can’t see anybody else — if you don’t jump in and pull the child out they’ll drown for sure. This is going to inconvenience you, as you’ll have to go home and change your wet clothes. As soon as this thought…

A readable guide to writing more readable content

Writing informative articles for the web is not like writing for other mediums. People read differently online — they open lots of links at once and flick from tab to tab, scanning up and down each page to find the information they want as quickly as possible. The easier to read and absorb your writing is, the more people you will reach. Sentences and paragraphs Line Length Limit your sentences to…