I finally took some time to add a somewhat-proper styleguide to this site. I’ve always had “hidden” styleguides for articles and notes which I use to check how things look, and for the last year or so I’ve also had a very janky styleguide-cum-component-library at /styleguide.
That’s now a much more useful document containing sections on:
Foundations – The fundamentals of my design system including colours, design tokens, type and spacing scales etc.
Typography – Visual reference and explanation of the typography & text styling.
Content Components – Visual reference and explanation for the components I use in my content.
UI Components – Visual reference for non-content UI components like footer or button.
HTML – Visual reference for native HTML elements which aren’t covered elsewhere.
This is mostly meant as a reference for me but I’m glad I took the time to polish it up enough that I’m happy to share it.
I write most of my content in Astro Editor, which is intentionally designed to leave all code-like things alone and just help me write prose. One of the most annoying things about this is that whenever I use a component like <Callout> in an MDX file I have to remember to import it at the top of the file before I publish.
I recently stumbled upon Chris Swithinbank’s astro-auto-import, which can automatically import named components into all astro files at build-time.
This works like a charm. The only negative side-effect so far is that when running the dev server, all MDX-generated pages include the CSS and client-side JS for all these components whether they’re used or not. This isn’t an issue with production builds though, so I can live with that.
My notes and articles live in Content Collections but I also have a few text-based pages which don’t – my Now page and my recently-added colophon, AI and Privacy pages. I was previously generating them from .astro files in the same way I would for any other “HTML Pages”:
pages
now
index.astro Imports the MDX file as Content
_now.mdx The actual text content
colophon
index.astro
_colophon.mdx
This is nice because I can still think of the page as a normal .astro page and can fully control the HTML and CSS for it. The only reason the _thing.mdx files exists is to make the content I change regularly nicer to edit. This is great for pages with their own design and layout, but all four of the pages I mentioned above essentially share the same layout and had almost identical index.astro’s.
This is precisely what Astro’s individual markdown support is for. And so now these pages are just MDX files:
pages
now.mdx
colophon.mdx
ai.mdx
privacy.mdx
Here’s what my now page looks like:
now.mdx
---
title:Now
subtitle:What I'm doing now
description:Danny Smith's Now Page
layout:'@layouts/Page.astro'
headingAlign:right
---
<Calloutemoji="💡">
This is a [Now Page](https://nownownow.com/about). Thanks to Derek Sivers for the [idea](https://sive.rs/nowff).
</Callout>
- [Consulting](https://betterat.work) on leadership, remote working and operations with a few companies.
- Working on [Taskdn](https://tdn.danny.is), a system for managing tasks and projects as plain markdown files. Includes a desktop app, a CLI, Claude plugin and Obsidian plugin, among other things.
- Working on [Astro Editor](https://astroeditor.danny.is), a markdown editor for Astro content collections.
- Learning a shit ton about how to work with AI to build stuff fast **and well**.
- Occasionally adding to my [collection of tools and frameworks](https://betterat.work/toolbox).
- Playing at Open Mic nights in my local pub.
- Living in a lovely little mews in Islington, North London.
Updated: 2025-10-04
To make this work I added a new Page.astro layout specifically for this kind of page. The title and description are passed through to BaseHead and the title is used for the Heading. If subtitle is present it’s shown under the heading (and slightly changes some styling). headingAlign does what it says on the tin. Thanks to astro-auto-import, I can use all the same components in these files as I would in articles and notes without needing to import them.
Considering the whole point of this is making it nicer to edit these pages, this felt all kinds of wrong. So I ended up with a tiny remark plugin which does it for me!
I’ve just added a few new pages to this site, linked from the footer.
An AI Statement which outlines where and how I use AI to work on this site. I suspect this will eventually expand into a more general statement on the topic.
A Privacy Policy which states very simply and in non-legal language what data I collect (which at the moment is none). It seems weird to me that very few people bother to include these on personal sites – not because there’s a strong legal reason – but because a simple explanation like this can go a long way towards reassuring visitors who care about privacy.
A Colophon page which briefly explains how this site is put together. I had one years ago and can’t remember why I removed it, but I absolutely love stumbling across other people’s /colophon pages. I love that we use the word Colophon for these, because history.
I gave this site’s OG Images a makeover a few weeks back. These are images that show up when you share a link to a post on Slack, Discord, Bluesky and the like. The old ones were hastily thrown together ages ago and looked pretty rubbish. The new ones include a title, my avatar, the post’s URL and my name. Here’s an example from a recent note…
I went with Tree → SVG → PNG because of the fonts. Satori converts all the text into vector paths, so by the time the image is rasterised there are no fonts to resolve. This avoids a whole set of font-rendering issues I ran into before when I was hand-writing SVGs and rasterising them directly with sharp.
The fiddly bit was the title because it needs to be as big as possible without overflowing, and Satori has no “shrink to fit”. It’s not perfect, but I ended up with some code which roughly estimates how wide each letter is, tries wrapping the title at the biggest size, and then stepping down until it fits its box:
functionglyphEm(ch:string):number {
if (ch ==='') return0.3;
if (`.,:;'!|iIlj`.includes(ch)) return0.3; // skinny
if ('MWmw@%'.includes(ch)) return0.92; // wide
return0.64; // everything else
}
// Try the biggest size, shrink by 2px until the lines fit.
for (let size =96; size >44; size -=2) {
if (titleFitsAt(size)) return size;
}
It’s deliberately rough and Satori still does the actual wrapping. The URL is easier: it’s set in a monospace font, so every character is the same width and I can work out the right size directly.
Here’s an example of a short title:
And here’s a stupidly long title:
And here’s the default, which is statically served but can be regenerated based on the same templates with a manual script.
The whole thing is now cached too, so runs much faster than it used to. This is a tiny little tweak, but I’m pleased with how they look – especially when shared on Bluesky with my new atproto records…