At some point I realized that plain markdown was not visually or structurally distinct enough for the kind of work I wanted to publish.
Expanded the site into a denser technical publishing tool with notebook embedding, richer article framing, and better support for project artifacts that did not fit clean blog-post shapes.
A lot of my projects are not just “here is the final result.” They involve experiments, code, notebooks, logs, hardware iterations, and notes that make more sense when they are preserved as part of the article instead of flattened into a clean summary.
That was the problem this phase was trying to solve.
Around early 2020, the repo starts leaning hard into technical project writeups:
Embedding jupyter notebooks rather than creating pagesNew camera mount post, task-digest workPublishing task digestPublishing first hanging plotter articlers and docker straceUpdating esp articleDescribing spinning up a stepperConnecting esp32 to bluetooth postUpgrading deps, reorg generated typesAdding remark42 commenting system from forkThat is not just “more posts.” It is the repo bending toward technical publishing.
At the time I was also working on a headless CMS, which had me thinking more about algorithmic handling of content instead of pure 1:1 writing and display. I did not want the site to be a dumb markdown renderer.
The turning point in this phase was getting Jupyter notebooks embedded in markdown.
That mattered a lot to me because it changed the relationship I had with Gatsby. Up until then, a lot of the content pipeline felt like something I was negotiating with. Once notebooks worked the way I wanted, it felt more like Gatsby was under my control instead of me being under Gatsby’s control.
The relevant plugin code is refreshingly blunt:
const JUPYTER_REGEX = /^{% jupyter (.*) %}$/
visit(markdownAST, "text", (node) => {
const match = JUPYTER_REGEX.exec(node.value)
if (match) {
markdownJupyterNodes.push({ node, jupyterPath: match[1] })
}
})
and then:
const children = jupyterFileNode.children
.map(getNode)
.filter((node) => node.internal.type === "JupyterNotebook")
if (children.length < 1) {
return `Error: notebook not found for file ${jupyterPath}, is gatsby-transformer-ipynb installed?`
}
return jupyterNode.html
That is from plugins/gatsby-remark-jupyter/index.js.
There is a lot I like about this pattern. It is not magical. It is just: find the notebook reference, resolve the local file, get the transformed notebook node, and replace the marker with generated HTML.
In practice, the setup was: keep the notebook file beside the article assets, let the transformer generate the notebook HTML during Gatsby’s data pipeline, and then let the remark plugin swap a lightweight inline marker for the rendered notebook output at markdown-processing time.
Because I wanted more control over the chrome around them.
When notebooks were their own pages, I had less control over how they were framed. Embedding them in markdown let me preface them, contextualize them, and treat them as part of a larger article instead of a separate artifact awkwardly hanging off the side.
A typical rendered result was an article section that introduced the experiment in prose, embedded the notebook output directly underneath, and then resumed with conclusions or next steps. That made the notebook feel like evidence inside the story instead of a detached appendix.
That is an important distinction. I was not trying to show that I could technically render a notebook. I was trying to make notebooks useful inside a narrative.
This phase also includes a lot of plugin frustration.
Gatsby obscures the workflow behind several layers of indirection. Determining what inputs are available for a page, which transforms have already happened, and where types should come from is harder than it should be.
The plugin ecosystem made this worse. Small plugins would bit-rot, dependencies would drift, and debugging often meant spelunking through a chain of packages maintained at different levels of seriousness.
One of the most revealing external references from this phase is a Gatsby PR I had to care about directly:
That is not the whole notebook story, but it captures the broader pattern: sometimes the thing that is broken is not even your plugin. It is upstream Gatsby itself, or some transitive dependency interaction around it. In this case, the PR mattered because it was part of making notebook rendering behave again on a newer Gatsby stack instead of quietly rotting behind plugin boundaries.
I upstreamed the Gatsby change when it made sense. I did not always upstream plugin changes, mostly because a lot of those plugins had so little traffic that maintaining a private or vendored path was more practical than waiting around.
The comment-system work belongs in this phase for the same reason notebooks do: I wanted more useful technical publishing behavior without handing the whole experience over to a provider.
That is the connecting thread for the whole article: notebooks, plugin work, and comments were all attempts to make the site better at carrying live technical material instead of just polished summaries.
Later in 2020 I added Remark42, based in part on a simple Gatsby integration pattern inspired by posts like this one:
I chose Remark42 because it was minimal and self-hostable. The integration logic was simple enough that I could just add it directly instead of depending on a bloated abstraction layer. That fit the rest of the repo pretty well.
I do not love systems that are “easy” only as long as somebody else keeps the SaaS alive.
This was the point where the site became capable of carrying work that was messy, technical, and still in touch with its original artifacts.
That includes things like:
It also clarified one of my broader preferences: I care a lot about preserving the shape of the work.
I do not just want a polished summary. I want to keep enough of the notebook, experiment, code, and debugging trail that the project still feels real.
By the end of this phase, the repo was no longer just a custom site with a content model.
It was becoming a publishing environment for technical artifacts.
That is a more specific and more demanding job.
It also made the next set of problems inevitable. Once you depend on notebooks, transforms, comments, and a stack of plugins to publish dense work, dependency drift stops being background maintenance and starts becoming an existential threat to whether the site builds at all.