{
"path": "/3mkvmntsdy227",
"site": "at://did:plc:2ha7bym7sxhtpt3du2lasczt/site.standard.publication/3mjiak5m65c25",
"tags": [],
"$type": "site.standard.document",
"title": "Dev Diary 0001 - Prototype Archive",
"content": {
"$type": "pub.leaflet.content",
"pages": [
{
"id": "019dea63-0304-7cc4-8bbc-2b9fb6fe57b5",
"$type": "pub.leaflet.pages.linearDocument",
"blocks": [
{
"$type": "pub.leaflet.pages.linearDocument#block",
"block": {
"$type": "pub.leaflet.blocks.text",
"facets": [
{
"index": {
"byteEnd": 210,
"byteStart": 209
},
"features": [
{
"$type": "pub.leaflet.richtext.facet#footnote",
"footnoteId": "019dea67-a6b3-7556-a7e6-b78d7a78837e",
"contentPlaintext": "at the time of writing (2026-04-30) there's just a landing page but if you're from the future there might be more to see"
}
]
},
{
"index": {
"byteEnd": 242,
"byteStart": 214
},
"features": [
{
"uri": "https://prototypes.likes.fyi",
"$type": "pub.leaflet.richtext.facet#link"
}
]
},
{
"index": {
"byteEnd": 287,
"byteStart": 281
},
"features": [
{
"uri": "https://github.com/likes-fyi/prototypes",
"$type": "pub.leaflet.richtext.facet#link"
}
]
},
{
"index": {
"byteEnd": 299,
"byteStart": 292
},
"features": [
{
"uri": "https://tangled.org/likes.fyi/prototypes",
"$type": "pub.leaflet.richtext.facet#link"
}
]
}
],
"plaintext": "I've been working on prototyping for the past few days and I figured I'd stop to write up a milestone, even if it was sort of minor. My goal was to set up a website to house prototypes and demos. It's live now* at https://prototypes.likes.fyi and as usual the code is available on github and tangled. Read on for a quick tour of the interesting parts of getting an Astro website up on Cloudflare."
}
},
{
"$type": "pub.leaflet.pages.linearDocument#block",
"block": {
"$type": "pub.leaflet.blocks.text",
"plaintext": ""
}
},
{
"$type": "pub.leaflet.pages.linearDocument#block",
"block": {
"$type": "pub.leaflet.blocks.header",
"level": 2,
"plaintext": "Astro, Theme, Hosting"
}
},
{
"$type": "pub.leaflet.pages.linearDocument#block",
"block": {
"$type": "pub.leaflet.blocks.text",
"facets": [
{
"index": {
"byteEnd": 72,
"byteStart": 67
},
"features": [
{
"uri": "https://astro.build",
"$type": "pub.leaflet.richtext.facet#link"
}
]
},
{
"index": {
"byteEnd": 85,
"byteStart": 77
},
"features": [
{
"uri": "https://tailwindcss.com/",
"$type": "pub.leaflet.richtext.facet#link"
}
]
}
],
"plaintext": "The first step is to get a basic website up and running. I'm using Astro and Tailwind because they're what I know, not because they're necessarily the best tools for the job."
}
},
{
"$type": "pub.leaflet.pages.linearDocument#block",
"block": {
"$type": "pub.leaflet.blocks.text",
"facets": [
{
"index": {
"byteEnd": 37,
"byteStart": 16
},
"features": [
{
"uri": "https://astro.build/themes/1/?search=&technology%5B%5D=tailwind&price%5B%5D=free",
"$type": "pub.leaflet.richtext.facet#link"
}
]
},
{
"index": {
"byteEnd": 56,
"byteStart": 48
},
"features": [
{
"uri": "https://miniblog.nicholasly.com/",
"$type": "pub.leaflet.richtext.facet#link"
}
]
},
{
"index": {
"byteEnd": 64,
"byteStart": 58
},
"features": [
{
"uri": "https://github.com/nicholasdly/miniblog",
"$type": "pub.leaflet.richtext.facet#link"
}
]
}
],
"plaintext": "I dug around in Astro's theme gallery and found miniblog (github) which seemed like a good place to start. Astro doesn't have support for modular themes so you're supposed to clone the theme's repo and build on top of it. I don't like that so I did a bunch of extra work instead:"
}
},
{
"$type": "pub.leaflet.pages.linearDocument#block",
"block": {
"$type": "pub.leaflet.blocks.code",
"language": "shellscript",
"plaintext": "$ git init\n$ git commit --allow-empty -m 'git init'\n$ git add LICENSE README.md\n$ git commit -m 'add license and readme'\n$ git clone https://github.com/nicholasdly/miniblog\n$ rm -rf miniblog/.git miniblog/README.md miniblog/LICENSE\n$ mv miniblog/* .\n$ mv miniblog/.* .\n$ rmdir miniblog\n$ git add .\n$ git commit -m 'import nicholasdly/miniblog@65ba328'",
"syntaxHighlightingTheme": "rose-pine-dawn"
}
},
{
"$type": "pub.leaflet.pages.linearDocument#block",
"block": {
"$type": "pub.leaflet.blocks.text",
"facets": [
{
"index": {
"byteEnd": 57,
"byteStart": 53
},
"features": [
{
"$type": "pub.leaflet.richtext.facet#code"
}
]
},
{
"index": {
"byteEnd": 92,
"byteStart": 83
},
"features": [
{
"$type": "pub.leaflet.richtext.facet#code"
}
]
}
],
"plaintext": "Could you have cloned the template repo, removed its .git directory, rewritten its README.md, and accomplished essentially the same task? Yes. Would that be easier, cleaner, and safer than my method? Also yes. Is it what I did? No."
}
},
{
"$type": "pub.leaflet.pages.linearDocument#block",
"block": {
"$type": "pub.leaflet.blocks.text",
"plaintext": "Next up was some customization of the theme. Nothing particularly interesting here, just tailoring it to my needs. Take a look at the the commit history if you're curious."
}
},
{
"$type": "pub.leaflet.pages.linearDocument#block",
"block": {
"$type": "pub.leaflet.blocks.text",
"facets": [
{
"index": {
"byteEnd": 99,
"byteStart": 75
},
"features": [
{
"uri": "https://developers.cloudflare.com/pages/framework-guides/deploy-an-astro-site/#deploy-via-the-cloudflare-dashboard",
"$type": "pub.leaflet.richtext.facet#link"
}
]
},
{
"index": {
"byteEnd": 230,
"byteStart": 210
},
"features": [
{
"uri": "https://prototypes.likes.fyi",
"$type": "pub.leaflet.richtext.facet#link"
}
]
}
],
"plaintext": "Finally, hosting. I'm deploying on Cloudflare using their instructions for Astro + Cloudflare Pages. Nothing interesting here either, just followed that guide exactly and used the Cloudflare dashboard to point prototypes.likes.fyi at it."
}
},
{
"$type": "pub.leaflet.pages.linearDocument#block",
"block": {
"$type": "pub.leaflet.blocks.text",
"plaintext": "Now that we have a basic shell of a website, let's get some content into it."
}
},
{
"$type": "pub.leaflet.pages.linearDocument#block",
"block": {
"$type": "pub.leaflet.blocks.text",
"plaintext": ""
}
},
{
"$type": "pub.leaflet.pages.linearDocument#block",
"block": {
"$type": "pub.leaflet.blocks.header",
"level": 2,
"plaintext": "Content Collections"
}
},
{
"$type": "pub.leaflet.pages.linearDocument#block",
"block": {
"$type": "pub.leaflet.blocks.text",
"facets": [
{
"index": {
"byteEnd": 46,
"byteStart": 27
},
"features": [
{
"uri": "https://docs.astro.build/en/guides/content-collections/",
"$type": "pub.leaflet.richtext.facet#link"
}
]
}
],
"plaintext": "Astro has a feature called Content Collections which allows you to flexibly load data into your system for further processing. A common use case would be a directory full of blog posts written in markdown that get parsed and used to generate markup for each post, index pages, tag pages, etc."
}
},
{
"$type": "pub.leaflet.pages.linearDocument#block",
"block": {
"$type": "pub.leaflet.blocks.text",
"facets": [
{
"index": {
"byteEnd": 34,
"byteStart": 28
},
"features": [
{
"uri": "https://docs.astro.build/en/reference/content-loader-reference/",
"$type": "pub.leaflet.richtext.facet#link"
}
]
},
{
"index": {
"byteEnd": 75,
"byteStart": 74
},
"features": [
{
"$type": "pub.leaflet.richtext.facet#footnote",
"footnoteId": "019dea93-4763-7556-a82f-66980313d5c9",
"contentPlaintext": "\"caw,\" cries the crow of foreshadowing"
}
]
},
{
"index": {
"byteEnd": 127,
"byteStart": 116
},
"features": [
{
"uri": "https://docs.astro.build/en/reference/content-loader-reference/#glob-loader",
"$type": "pub.leaflet.richtext.facet#link"
}
]
},
{
"index": {
"byteEnd": 172,
"byteStart": 161
},
"features": [
{
"$type": "pub.leaflet.richtext.facet#code"
}
]
},
{
"index": {
"byteEnd": 251,
"byteStart": 229
},
"features": [
{
"$type": "pub.leaflet.richtext.facet#code"
}
]
},
{
"index": {
"byteEnd": 280,
"byteStart": 272
},
"features": [
{
"$type": "pub.leaflet.richtext.facet#code"
}
]
},
{
"index": {
"byteEnd": 300,
"byteStart": 289
},
"features": [
{
"$type": "pub.leaflet.richtext.facet#code"
}
]
},
{
"index": {
"byteEnd": 339,
"byteStart": 325
},
"features": [
{
"$type": "pub.leaflet.richtext.facet#code"
}
]
}
],
"plaintext": "To make this work you use a Loader to source your data. My content is just* files on disk so I picked the built-in glob loader. I want to keep my prototypes in src/content either as single files or as dated subdirectories, e.g. src/content/2026-04-29. I want to find any .md/.mdx file in src/content but I only want to find index.{md,mdx} files in the directories, and only one level deep. Here's the loader config I'm using:"
}
},
{
"$type": "pub.leaflet.pages.linearDocument#block",
"block": {
"$type": "pub.leaflet.blocks.code",
"language": "typescript",
"plaintext": "loader: glob({ base: \"./src/content\", pattern: [\n \"*/index.{md,mdx}\",\n \"*.{md,mdx}\",\n]})",
"syntaxHighlightingTheme": "rose-pine-dawn"
}
},
{
"$type": "pub.leaflet.pages.linearDocument#block",
"block": {
"$type": "pub.leaflet.blocks.text",
"facets": [
{
"index": {
"byteEnd": 82,
"byteStart": 79
},
"features": [
{
"uri": "https://zod.dev/",
"$type": "pub.leaflet.richtext.facet#link"
}
]
}
],
"plaintext": "Content Collections also lets you validate content against a schema written in Zod. I'll make better use of this later but let's at least get the basics in place now:"
}
},
{
"$type": "pub.leaflet.pages.linearDocument#block",
"block": {
"$type": "pub.leaflet.blocks.code",
"language": "typescript",
"plaintext": "schema: z.object({\n title: z.string(),\n subtitle: z.string(),\n})",
"syntaxHighlightingTheme": "rose-pine-dawn"
}
},
{
"$type": "pub.leaflet.pages.linearDocument#block",
"block": {
"$type": "pub.leaflet.blocks.text",
"facets": [
{
"index": {
"byteEnd": 62,
"byteStart": 41
},
"features": [
{
"$type": "pub.leaflet.richtext.facet#code"
}
]
}
],
"plaintext": "Putting it all together, here's the full src/content.config.ts:"
}
},
{
"$type": "pub.leaflet.pages.linearDocument#block",
"block": {
"$type": "pub.leaflet.blocks.code",
"language": "typescript",
"plaintext": "import { defineCollection } from \"astro:content\";\nimport { glob } from \"astro/loaders\";\nimport { z } from \"astro/zod\";\n\nconst pages = defineCollection({\n loader: glob({ base: \"./src/content\", pattern: [\n \"*/index.{md,mdx}\",\n \"*.{md,mdx}\",\n ]}),\n\n schema: z.object({\n title: z.string(),\n subtitle: z.string(),\n })\n});\n\nexport const collections = { pages };",
"syntaxHighlightingTheme": "rose-pine-dawn"
}
},
{
"$type": "pub.leaflet.pages.linearDocument#block",
"block": {
"$type": "pub.leaflet.blocks.text",
"facets": [
{
"index": {
"byteEnd": 102,
"byteStart": 91
},
"features": [
{
"uri": "https://docs.astro.build/en/guides/content-collections/#building-for-static-output-default",
"$type": "pub.leaflet.richtext.facet#link"
}
]
}
],
"plaintext": "That's the input side of Content Collections taken care of. The output side is pretty much by the book, nothing particularly interesting here."
}
},
{
"$type": "pub.leaflet.pages.linearDocument#block",
"block": {
"$type": "pub.leaflet.blocks.text",
"plaintext": ""
}
},
{
"$type": "pub.leaflet.pages.linearDocument#block",
"block": {
"$type": "pub.leaflet.blocks.header",
"level": 2,
"plaintext": "Other Stuff"
}
},
{
"$type": "pub.leaflet.pages.linearDocument#block",
"block": {
"$type": "pub.leaflet.blocks.text",
"plaintext": "Another few small things:"
}
},
{
"$type": "pub.leaflet.pages.linearDocument#block",
"block": {
"$type": "pub.leaflet.blocks.unorderedList",
"children": [
{
"$type": "pub.leaflet.blocks.unorderedList#listItem",
"content": {
"$type": "pub.leaflet.blocks.text",
"facets": [
{
"index": {
"byteEnd": 28,
"byteStart": 0
},
"features": [
{
"uri": "https://github.com/likes-fyi/prototypes/commit/e396d92245e513d3582fd0aea8c905651ce76258",
"$type": "pub.leaflet.richtext.facet#link"
}
]
},
{
"index": {
"byteEnd": 87,
"byteStart": 43
},
"features": [
{
"uri": "https://css-tricks.com/emoji-as-a-favicon/",
"$type": "pub.leaflet.richtext.facet#link"
}
]
}
],
"plaintext": "likes-fyi/prototypes@e396d92 – I'm using this post from at Chris Coyier at CSS Tricks to use the 💗 emoji as my favicon. "
}
},
{
"$type": "pub.leaflet.blocks.unorderedList#listItem",
"content": {
"$type": "pub.leaflet.blocks.text",
"facets": [
{
"index": {
"byteEnd": 28,
"byteStart": 0
},
"features": [
{
"uri": "https://github.com/likes-fyi/prototypes/commit/95b7dacbb98ac1eaa2c5221565f42da61dad94cf",
"$type": "pub.leaflet.richtext.facet#link"
}
]
},
{
"index": {
"byteEnd": 76,
"byteStart": 41
},
"features": [
{
"$type": "pub.leaflet.richtext.facet#code"
}
]
},
{
"index": {
"byteEnd": 88,
"byteStart": 84
},
"features": [
{
"$type": "pub.leaflet.richtext.facet#code"
}
]
},
{
"index": {
"byteEnd": 252,
"byteStart": 219
},
"features": [
{
"uri": "https://www.zachleat.com/web/stable-scrollbar-gutters/",
"$type": "pub.leaflet.richtext.facet#link"
}
]
},
{
"index": {
"byteEnd": 306,
"byteStart": 288
},
"features": [
{
"$type": "pub.leaflet.richtext.facet#code"
}
]
}
],
"plaintext": "likes-fyi/prototypes@95b7dac – setting scrollbar-gutter: stable both-edges on the html element prevents page content from shifting horizontally when a scrollbar appears. I'm not sure where I found this originally but Zach Leat-Herman has an explainer. I found it works fine without the overflow-y: scroll but try for yourself"
}
},
{
"$type": "pub.leaflet.blocks.unorderedList#listItem",
"content": {
"$type": "pub.leaflet.blocks.text",
"facets": [
{
"index": {
"byteEnd": 28,
"byteStart": 0
},
"features": [
{
"uri": "https://github.com/likes-fyi/prototypes/commit/cd78d07f6e7a98e99de823df246baf5c7a91a972",
"$type": "pub.leaflet.richtext.facet#link"
}
]
}
],
"plaintext": "likes-fyi/prototypes@cd78d07 – the miniblog theme ships with its config file exporting a series of constants. I changed that to export a constant object containing the settings and then changed it back when I realized I preferred importing specific config vs. the whole thing"
}
}
]
}
},
{
"$type": "pub.leaflet.pages.linearDocument#block",
"block": {
"$type": "pub.leaflet.blocks.text",
"plaintext": "That's it for now. Next time I'll be adding the first prototype to the site. Thanks for reading!"
}
}
]
}
]
},
"bskyPostRef": {
"cid": "bafyreiewyklxmwtb5slmpdhmvvxwivaaiswwglyhlixpypl2j3ehpvkm7m",
"uri": "at://did:plc:2ha7bym7sxhtpt3du2lasczt/app.bsky.feed.post/3mkvmo2tyok2t",
"commit": {
"cid": "bafyreie72ed6rlcgruz4be2o4p5tfupmen3hfs3bggugk6xmvzzf3lz7im",
"rev": "3mkvmo2yf772d"
},
"validationStatus": "valid"
},
"description": "let's make a website that doesn't do anything",
"publishedAt": "2026-04-30T21:48:00.000Z"
}