← Journey Phase 0D — Web architecture

Dropping Sanity, shipping MDX

Replaced Sanity CMS with Astro content collections and MDX. Journey posts now live as files in the repo, generated automatically from MASTERPLAN checkboxes.

Sanity is out. The stack is simpler now.

Posts live as .mdx files in src/content/journey/. No external CMS, no API calls at build time, no dashboard to log into. The Astro content collection handles schema validation; the GitHub repo is the source of truth.

What changed

Removed
  • sanity
  • @sanity/client
  • @sanity/astro
  • @sanity/image-url
  • @portabletext/to-html
  • studio/ directory
  • sanity.config.ts
Added
  • @astrojs/mdx
  • src/content.config.ts
  • src/content/journey/*.mdx
  • src/content/work/*.mdx

Content schema

Two collections, defined once in src/content.config.ts:

const journey = defineCollection({
  loader: glob({ pattern: '**/*.mdx', base: './src/content/journey' }),
  schema: z.object({
    title: z.string(),
    phase: z.string(),
    publishedAt: z.string(),
    excerpt: z.string(),
    tags: z.array(z.string()).optional(),
    draft: z.boolean().default(false),
  }),
})

const work = defineCollection({
  loader: glob({ pattern: '**/*.mdx', base: './src/content/work' }),
  schema: z.object({
    title: z.string(),
    client: z.string(),
    year: z.number(),
    cover: z.string().optional(),
    featured: z.boolean().default(false),
    draft: z.boolean().default(false),
  }),
})

Images

Assets go to Cloudflare R2 via a CDN subdomain. Bucket: artupinad, folder structure: artupinad/journey/, artupinad/work/, artupinad/design/. Zero egress cost — images are served directly from the CDN, not proxied through Netlify.

Automation still works

The pipeline from MASTERPLAN to live post is unchanged from the outside:

tick checkbox in MASTERPLAN.md
  → push to dani-brain/main
  → GitHub Action triggers (path filter: MASTERPLAN.md)
  → parse-masterplan.py diffs newly ticked items
  → generate-update.py calls Claude API → writes MDX
  → commits to packages/web/src/content/journey/
  → opens PR in danidee/artupinad
  → Netlify preview URL sent via ntfy
  → merge → live on /journey

The difference: instead of committing a Sanity document via their write API, the script commits an .mdx file via GitHub’s API. One fewer external service in the chain.

Monorepo consolidation

danidee/artupinad is now a proper npm workspaces monorepo:

  • packages/web → artupinad.com (Astro + MDX)
  • packages/design-system → storybook.artupinad.com (Storybook)

Both deploy independently via Netlify with repo-scoped deploy keys. The old danidee/web and danidee/design-system repos are archived.