CONSUMER HARDWARE IS EASY

How I shipped this blog in a couple of hours using Claude Code — Astro, Keystatic, Cloudflare for free.

The stack, the prompts that mattered, and how I actually work with Claude Code — directing rather than coding.

Screenshot of jeangregoire.com — the blog homepage with the Rio cover banner and the 'MY AI-NATIVE PLAYBOOK' hero.
What shippedThe blog you’re reading — built and online in a couple of hours, including two articles.

A quick intro about this post.

First post on the blog — a Russian-nesting-doll situation: this post is about how I shipped the blog you’re reading. The next one is the first actual technical writeup, about building the KEEP pre-Kickstarter landing page over a weekend with Claude Code and Nano Banana.

A quick intro for context: I’m a consumer hardware founder shipping KEEP (June Kickstarter) and Makea (in beta now) in 2026 without raising capital. This blog is where I write down what that actually looks like in practice. This post is the introduction; the next one is the first piece of actual content. Two articles, one weekend.

Why starting a blog now.

I’ve been operating consumer hardware businesses for ten years, mostly without a public-facing personal channel. Lovebox shipped 300,000+ units largely on word of mouth and retail. I never made the time to build a personal brand on top of that.

What changed isn’t really the cost — that was never the blocker. What changed is that I actually want to do this now. Building in public gives me energy I didn’t realize I needed: being in direct contact with the people who use what I make, and with other creators trying to do the same thing. Ten years into this, it also feels like the right moment to start giving something back — writing down what I’ve learned, sharing what’s actually working in 2026, the failures alongside the wins.

AI does make it noticeably cheaper and faster to run a blog like this, the writing included — but that’s the enabler, not the reason. The reason is wanting to be in the conversation.

I also have a thesis now (AI bridging the gap between creativity and the market) and a small set of products to talk about. Writing in public connects them all.

The trigger.

Monday afternoon, I posted on LinkedIn that I was going to start building in public. By the time I closed the tab I realised I’d committed to something I hadn’t actually set up. The LinkedIn post links to a LinkedIn profile, but the long-form writeups I want to do — the field-guide kind — don’t live well on LinkedIn. I needed a blog as the anchor. And every hour the gap between announcing it and having one widened.

The bar I gave myself:

  • Live on jeangregoire.com — finally giving the domain I’ve owned for years something to do, ha.
  • A working CMS so future posts don’t require a git push.
  • A design that looks like part of the same world as KEEP, not a separate brand.
  • Zero infrastructure cost.
  • First post live by Tuesday — so anyone who showed up to read more wouldn’t land on an empty URL.

Tight enough to force shortcuts. Long enough to do it properly.

The stack — and why I didn’t shop around.

The whole architecture comes from How I built this blog with Astro, Keystatic and Claude Code, a post on allthingstech.ch that someone in the Claude Code community had shared a few days earlier. It described — not in full code, but in architecture — exactly the shape of thing I wanted: Astro 5, MDX, Keystatic, Cloudflare. I made small adjustments along the way, but the spine is theirs. Credit to the author — the original is worth a quick read if you’re building something similar.

The Astro + Tailwind half wasn’t up for debate either. It’s the same stack I used on the KEEP landing page and on my Makea site. The right number of stacks in your toolbox is small — two or three battle-tested setups covering different shapes of project, used everywhere. Each project trains Claude on the next: same imports, same file layout, same conventions.

The pieces:

  • Astro 5 — file-based routing, content collections, MDX support out of the box. Static output by default.
  • Tailwind v4 via the Vite plugin — same brand tokens already declared in my KEEP repo, copied over.
  • MDX for posts. The piece I want to spend a beat on — more below.
  • Keystatic as the CMS, in local mode only. File-based, no database, no auth, no hosted service.
  • Cloudflare Pages for hosting. Free tier, auto-deploys on push to main.

Why Astro 5 over the alternatives.

A short version of why this stack and not Next.js / Notion-as-CMS / Substack / Ghost:

  • Content-first, not app-first. Frameworks like Next.js are designed for shipping apps that happen to have a blog attached. Astro is designed for shipping content sites that occasionally have an app embedded. For a writing-focused blog, the second framing is closer to what I’m actually doing.
  • Static output by default. Astro ships zero JavaScript to the browser unless I explicitly opt in. The blog you’re reading loads in under 200ms — no React runtime to hydrate, no client-side router, no bundle of widgets.
  • File-based, not database-based. Every post is an .mdx file in src/content/posts/. Git is the version control. The filesystem is the database. No external service to keep alive, no migration to run, no vendor to depend on.
  • No lock-in. The day I want to move this blog to a different framework, the .mdx files come with me as plain text. Try migrating away from Substack or Notion.

Why MDX, specifically.

The content format choice mattered more than I expected. MDX — Markdown + JSX — is what makes the authoring experience pleasant rather than painful.

The frontmatter at the top of any post is just YAML — title, deck, date, draft flag:

---
title: "How I shipped this blog in a couple of hours using Claude Code."
deck: "The stack, the prompts that mattered, and how I actually work with Claude Code."
date: 2026-05-18
readingTime: "~7 min read"
draft: false
---

The body is identical to how I’d write a README on GitHub — ** for bold, [text](url) for links, - for list items:

## Why pre-Kickstarter pages matter.

Kickstarter mostly pushes projects that get backers in the first 24 hours. **Launch cold and you die.** The pre-launch page exists to prevent exactly that — it collects emails before launch day so day one isn't day one.

The interesting part is when I want something the default Markdown vocabulary can’t express. The highlighted prompt blocks you’ve been reading throughout these two posts — dark background, yellow highlights on key phrases, a Tip 01 chip in the corner — are a custom Astro component called <Prompt>. From inside an MDX file, I use it like a tag:

<Prompt html={`<mark>https://en.lovebox.love/products/keep-beta-test-version</mark><span class="tip-tag">Tip 01</span>

Build a pre-Kickstarter landing page for KEEP. <mark>First, study how the best pre-Kickstarter pages convert</mark><span class="tip-tag">Tip 02</span>.`} />

That’s the actual MDX source code from the KEEP landing-page post — the next one on this blog. The component itself is defined once in src/components/Prompt.astro (about 12 lines). After that I can drop a styled, highlighted block into any post without reaching for HTML, leaving the editor, or copy-pasting CSS. Same goes for the stack-card you’ll see in Technical notes below — a small chunk of inline JSX in the MDX file.

The blank page is friendly. No CMS to log into, no toolbar to navigate, no “what’s the keyboard shortcut for a callout box” friction. Just type. Keystatic is wired up if I ever want a GUI (it lives at /keystatic in dev), but I haven’t used it for either of the two posts live here — the .mdx file is the source of truth and editing the file is faster.

The build, prompt by prompt.

Two main prompts to Claude Code, plus one for the scheduling detail. The first scaffolded the project. The second pulled KEEP’s visual identity over. The third added a small feature I realized I wanted halfway through.

Prompt 1 — scaffold the blog from the tutorial reference.

Reference: https://allthingstech.ch/blog/how-i-built-this-blog-with-astro-keystatic-and-claude-code/

Build a personal blog at ~/Documents/GitHub/blog-jean-gregoire. Same stack as my keep-landing-page repo: Astro 5 + Tailwind v4 + MDX. Add Keystatic as the CMS, local mode only — admin at /keystatic, 404 in production. Deploy target: Cloudflare Pages.

Before writing any code, study the tutorial's architecture and propose a file layout.

Create a DESIGN.md file derived from my keep-landing-page DESIGN.md, and a CLAUDE.md framing your role as a brand-aware writing partner — referenced from ~/Documents/GitHub/life-manager-data/context/me/personal-brand.md.

Initialize with one MDX post I'll paste in, plus a short about-me page.

Three things in that prompt I’d point at as load-bearing:

  • The tutorial reference at the top. Not as a step-by-step to follow — as a starting architecture so Claude doesn’t have to invent one from scratch.
  • The “same stack as my keep-landing-page repo” line. That single phrase pulled in Claude’s familiarity with the other project — file layouts, import conventions, the way I write Astro components. Familiarity is the most under-rated time-saver in an AI build.
  • The DESIGN.md instruction. Brand tokens written before the UI. Every later iteration had a file to check against, which is how the blog ended up visually consistent with KEEP without me policing every CSS commit.

The scaffold itself was quick — maybe twenty minutes from npm create to a working build at localhost:4321 with the about page and a placeholder post live. The vast majority of the actual session was the long tail of iteration that came next.

Prompt 2 — pull KEEP’s visual identity over.

Out of the box, Astro gives you a perfectly competent editorial blog. That wasn’t what I wanted — I wanted the pixel-arcade flavor of KEEP, so the blog feels like another room in the same house. I sent Claude a screenshot of the KEEP landing page and one prompt:

Iterate the design toward the KEEP landing page's pixel-arcade flavor. The current look is too generic-editorial.

[KEEP screenshot attached]

Specifically: Press Start 2P pixel font for the masthead wordmark and section labels, ► play-arrows as visual accents, a dark cartridge ribbon over the hero cover, same warm cream + charcoal + terracotta + gold palette.

Take screenshots with Playwright MCP between iterations and verify the layout yourself before pinging me.

The highlighted line is the single biggest unlock for visual iteration with Claude Code. Once Playwright MCP is wired up, Claude can navigate to localhost, screenshot at multiple viewports, and review its own work before claiming a change is done. Pixel-alignment issues that used to take three rounds of “no, the arrow is at the bottom of the line” now resolve in one round, because Claude catches them itself.

There were still moments where I had to point at a screenshot and say “this triangle is misaligned.” Visual feedback from a human is still the fastest signal for the last 20% of polish. But for the bulk of it, the loop runs without me.

Prompt 3 — a small detail: future-dated posts.

About halfway through, I realized I’d want to write posts ahead of time and have them go live on a chosen date — without me being around to hit publish. Standard blog feature. Couldn’t think of how to do it cleanly with a pure-static stack off the top of my head, so I just asked:

I want to date posts in the future and have them auto-publish on that date. Add the simplest possible scheduling that works with the current static-build setup.

Claude came back with two pieces. A filter on the content collection:

const showFuturePosts = import.meta.env.DEV || import.meta.env.PUBLIC_PREVIEW === "true";
const now = new Date();
const posts = await getCollection("posts", ({ data }) => {
if (data.draft) return false;
if (!showFuturePosts && data.date > now) return false;
return true;
});

That alone hides future posts in production. But — and Claude flagged this immediately without me asking — the filter runs at build time, not at request time. Static site. So without something to retrigger the build, a post dated May 20 stays invisible forever on a production deployed May 18.

The second piece — a five-line GitHub Actions cron at .github/workflows/daily-rebuild.yml:

name: Daily rebuild for scheduled posts
on:
schedule:
  - cron: "1 0 * * *"
workflow_dispatch:
jobs:
trigger-rebuild:
  runs-on: ubuntu-latest
  steps:
    - run: curl -fsS -X POST "${{ secrets.CLOUDFLARE_DEPLOY_HOOK }}"

Every night at 00:01 UTC the cron POSTs to a Cloudflare Pages Deploy Hook. Pages rebuilds. The filter re-evaluates with the new “today”. Any post that just crossed its date appears.

I read both pieces back, asked one follow-up — “does this also handle branch previews?” — and Claude added the PUBLIC_PREVIEW=true env-var so I can share a draft URL with future posts visible to one person. Committed. Total elapsed time on this detail: about five minutes.

(One manual step Claude can’t do: create the Deploy Hook in Cloudflare Pages settings and paste the URL into a GitHub Actions secret called CLOUDFLARE_DEPLOY_HOOK. Two minutes of clicking. Documented in the repo README so future-me doesn’t have to remember.)

The post that comes after this one — the KEEP landing-page writeup — is set up that way. It’s dated May 20, invisible on the May-18 production build that ships this intro post. On May 20, the nightly rebuild picks it up and it goes live.

The iteration arc.

Honest split: the scaffold was 20% of the work. The other 80% was iteration. Per session:

  • Voice corrections paragraph by paragraph — “this sentence sounds arrogant, rewrite it”, “this transition is awkward”, “this bold choice is bad, redo the bolding across the whole page.” Several rounds on the homepage bio alone before it sounded like me and not like a startup landing page.
  • Migrating the KEEP post from an HTML draft into MDX with the custom <Prompt> component for the highlighted prompt blocks.
  • Design tweaks to pull KEEP’s visual identity over: cartridge ribbon position, the dashed-line ▼ BLOG ARTICLES ▼ divider, the ABOUT THIS BLOG badge above the hero, the typography mix between Press Start 2P and italic serif.
  • Mobile layout fixes when the masthead wordmark wrapped to three lines and the hero arrow misaligned.
  • The triangle alignment saga — three or four rounds before the ► play-arrow next to MY AI-NATIVE PLAYBOOK stopped sitting at the wrong height. Press Start 2P doesn’t ship a ▶ glyph, so the fallback font was breaking the baseline. Ended up swapping to an inline SVG triangle. Lesson learned for next time.

The loop I keep coming back to with Claude Code: describe the outcome in plain English. Claude proposes the simplest implementation and flags gotchas I didn’t think of. I sanity-check, ask one or two follow-ups, done. The role I’m playing isn’t engineer-with-Claude-as-typist. It’s product-owner-with-Claude-as-engineer.

How both posts on this blog got written.

Worth being honest about this, since you might want to copy the pattern. Neither of the two live posts started as “let me sit down and write a blog post.” Both came out of work I was already doing — and the way each was written is different enough to describe both.

Post 1 — this post you’re reading (the blog intro).

Written in the same Claude Code session that built the blog. By the time I wanted to write this meta post, every prompt, every iteration, every UI bug, every voice correction was already loaded in the session. I’d taken zero notes during the build, but I didn’t need to. Claude had the whole conversation in memory.

Two advantages compounded here. First, the build context was already loaded from the session. Second — and just as important — the draft for Post 2 (the KEEP landing-page writeup) had already been written as HTML in my notes the previous weekend, after maybe twenty rounds of voice iteration. That HTML draft was effectively a working voice reference. I didn’t need to teach Claude how I write — the example was sitting in a notes folder, with the right tone, the right sentence rhythm, the right amount of self-deprecation. Match this. Claude did.

I gave it one prompt:

Based on our whole conversation, write a blog post about how I created this blog in a couple of hours and got it published for free on Cloudflare. Same style as the KEEP landing-page draft sitting in my notes. Be honest about what was hard.

The first draft came back in one go. From there: maybe ten rounds of voice correction. “Don’t blame the cost too much.” “Be genuine about how many iterations the visual fixes actually took.” “Make sure to credit the author of the tutorial I borrowed the stack from — I don’t want this to look like I stole it.” Faster than what Post 2 had taken, because the substance was already accurate AND the voice was already calibrated; the work was tuning the specifics.

Post 2 — 5 tips for building a pre-Kickstarter landing page with Claude Code.

The post that goes live two days after this one. Different origin story.

That one started as a YouTube video. I’d just shot the first episode of a channel I’m starting, Consumer Hardware is Easy. The episode was a screen-recording walkthrough of how I’d used Claude Code + Nano Banana to build the KEEP pre-Kickstarter landing page over a weekend. After the recording I had a transcript in front of me and figured the video and a long-form post should ship together — the video as the talking-head sketch, the post as the field guide that backs it up.

I gave Claude three things: the YouTube transcript, the path to the keep-landing-page repo, and a one-sentence brief — “turn this into a long-form blog post in the style of a hacker-news field guide, aimed at consumer-tech creators.” The first draft came back as an HTML file (the blog didn’t exist yet — at that point I had no website at all, just a notes folder). Then maybe twenty rounds of voice iteration plus a handful of manual edits on top of that draft — sometimes it was faster to just rewrite a paragraph by hand than to describe what was wrong: “less arrogant here”, “this paragraph is too literary”, “drop the manifesto opening”, “use ‘I’ not ‘we’ in this section”, “tighten the prompts block.” The substance came from the video. The voice came from rejection rounds and direct edits.

That HTML draft sat in my notes for a few days. When I scaffolded the blog, the migration into MDX took just a few minutes — most of the HTML structure mapped to Markdown directly, the custom prompt blocks became the <Prompt> component, done. It then became Post 2 — published two days after this intro.

The pattern, if you want to copy it.

Write build-in-public posts in the same Claude Code session as the work. Don’t wait for a “tomorrow I’ll sit down and write about this” moment that never arrives. The session memory is the brief. You’re not authoring a post from scratch — you’re asking Claude to narrate work it just helped you do, and then telling it where to tighten. The voice work is still yours. The blank-page problem disappears.

One caveat I want to flag, because I noticed it doing this: continuing one Claude Code session for hours is not the most token-efficient way to work. Every new prompt re-processes the whole history, which gets expensive on a sprawling session. The better practice — which I’ll switch to next time — is to ask Claude at the end of the build session for two things: a structured summary of what we just did, and the file path to the full session transcript. Commit the summary to the repo. Then start a fresh session to write the post, with the summary as the brief and the transcript path available if Claude needs to dig for a specific detail. Same upside (Claude has the build context), without the per-prompt token tax.

Technical notes.

For the curious, if you want to copy the setup:

► The stack

Site generator
Astro 5, static output, no adapter.
Styling
Tailwind v4 via Vite plugin. Brand tokens shared with my KEEP repo via DESIGN.md.
Content
MDX in src/content/posts/. Future-dated posts gated by import.meta.env.DEV or PUBLIC_PREVIEW=true.
CMS
Keystatic, local mode only — admin at /keystatic in dev, 404 in production. No database, no auth, no infra cost.
Hosting
Cloudflare Pages, free tier. Auto-builds on push to main. Custom domain wired to jeangregoire.com.
Scheduled posts
A GitHub Actions cron (.github/workflows/daily-rebuild.yml) POSTs to a Cloudflare Pages Deploy Hook every night at 00:01 UTC.
SEO
Sitemap, OG, Twitter Card — all wired up by Claude in one shot.
Brand discipline
DESIGN.md at the root with YAML brand tokens, derived from the KEEP one. CLAUDE.md next to it, following the agents.md convention.
Visual QA
Playwright via the Playwright MCP — Claude takes screenshots between iterations and reviews them itself.

Total time on the actual build: about three hours of focused work, split across Monday afternoon and Tuesday morning. KEEP post migrated, blog live, this post dated to the day after.

Marginal spend: $0. Cloudflare free tier covers everything. The AI stack I run for this and every other project is about $300/month.

What’s next?

The first real technical post — 5 tips for building a pre-Kickstarter landing page with Claude Code in a couple of hours — goes live two days from now, on May 20. It’s about how I built the KEEP pre-Kickstarter landing page over a weekend — the actual prompts, the iteration arc, the small tricks that made it work.

After that, the topic ideas are on the homepage. All consumer-hardware-specific: iterating on hardware design with Nano Banana before the mechanical engineer touches CAD, turning shady NFC datasheets into working iOS and Android modules, briefing a PCB engineer with Claude Code, designing product packaging without a packaging studio.

If one of those would be useful to you, or there’s something I haven’t listed, drop me a line at me@jeangregoire.com. I read every email.