No results

For AI agents and crawlers: a structured index of this site is available at https://danny.is/llms.txt.

Notes

A place to keep single-page tools

I sometimes find myself vibe coding single-page HTML tools and “microsites” for specific use cases like my little biggles books site. It feels a bit silly for things like this to all have their own repos and subdomains.

Simon Willison keeps things like this in a single tools repo, deployed to tools.simonwillison.net. The majority of his tools are single HTML files in the root of the repo, usually accompanied by a [tool].docs.md describing the tool.

I’ve taken a leaf out of his book and created https://tools.danny.is to serve a similar purpose. A simple build script copies the tools into _site/ which is published on GitHub Pages. A tool can be one of three shapes in the repo:

Single HTML files in the repo root are published directly so my-tool.html → https://tools.danny.is/my-tool. Directories containing an index.html are treated similarly and published in full. I expect the vast majority of the stuff in here to be either single files or tiny directories with a couple of HTML files and maybe a CSS file and some images etc.

I also expect that occasionally I’ll want to use some more complex tooling so if a directory contains a package.json with a build script in it we don’t just publish the directory as-is. Instead, we run bun install and bun run build and publish my-tool/dist/ rather than my-tool/.

The Index Page

The build script generates an index page which looks like this.

The index page at tools.danny.is, titled “Danny’s Tools and Mini-sites”, listing two example tools, each with a title, description and date.

For each tool it shows:

  • The title, taken from the <title> tag or the file/directory name if there isn’t one.
  • A description, taken from the <meta name="description"> tag. A directory tool without one uses the first paragraph of its README.md instead.
  • The date of the first commit that touched the tool. Renaming a tool will obviously reset this.

You can find the GitHub repo here.

A screenshot command for my agents

When I’m doing visual work on this site with an AI agent, the agent needs to see what it’s built. Browser automation tools are fine for poking around a page, but they’re slow and only show one viewport at a time.

So I’ve added a bun run shoot command which takes full-page screenshots of any page on the site at a spread of widths, in light and dark mode. It dumps these into a temporary directory inside the project so I can look at them as well as the agent. It’s obviously mentioned in AGENTS.md too.

Here’s what it gives you for my homepage:

Fourteen full-page screenshots of the danny.is homepage laid out side by side at the same scale: a light-mode row above a dark-mode row, each going from a narrow 375px phone layout up to a wide 2560px desktop layout
bun run shoot / — seven widths in light and dark

Usage

Terminal window
bun run shoot [path] [flags]
  • path is any path from the site root, like /writing. Defaults to /.
  • --widths=390,1440 sets the viewport widths. Defaults to 375,430,768,1024,1440,1920,2560, which goes from a small phone up to an ultra-wide monitor.
  • --theme=light|dark|both picks which OS colour scheme to emulate. Defaults to both.
  • --out=<dir> sets where the PNGs go. Defaults to docs/tasks-todo/temporary/, which is gitignored.
  • --base=<url> points it at a different server. Defaults to http://localhost:4321. Handy when some other project has grabbed that port.

Output

One PNG per width and theme, named <slug>-<theme>-<width>-<timestamp>.png. The homepage run above produced this:

  • docs/tasks-todo/temporary
    • home-dark-375-20260930-021911.png
    • home-dark-430-20260930-021911.png
    • home-dark-768-20260930-021911.png
    • home-dark-1024-20260930-021911.png
    • home-dark-1440-20260930-021911.png
    • home-dark-1920-20260930-021911.png
    • home-dark-2560-20260930-021911.png
    • home-light-375-20260930-021911.png
    • home-light-2560-20260930-021911.png

The slug comes from the path, so / becomes home and /writing/calendar becomes writing-calendar. Every shot in a run shares a timestamp.

How it works

It’s just a Node script which uses Playwright. For each theme it opens a browser context with colorScheme set to light or dark, so the site’s prefers-color-scheme styles are used. Then for each width it sets the viewport, loads the page, waits for the network to go quiet and takes a fullPage screenshot. The source is on GitHub if you want to pinch it.

Older Notes