One content source, two sites: how mroot.co works
The portfolio you're reading. A Vite plugin turns Markdown into a typed data module, and both the regular site and the IDE mode render from it — no duplicated content.
Project: mroot.coopen →This is the case study for the site you're reading right now. mroot.co runs two completely different experiences off one codebase: a normal marketing site, and a working IDE — file explorer, tabs, terminal, command palette — that shows the exact same projects and writing as actual files. Writing a new project means writing one Markdown file, not maintaining two versions of the same content.
Content is compiled, not shipped as Markdown
A small Vite plugin reads everything under content/ at build time, parses the frontmatter, and emits a plain data module exposed as virtual:content. No Markdown parser ships to the browser; the site just imports typed arrays.
src/data.ts re-exports those, and every page — regular or IDE — reads from there. That's the whole reason there's no duplicated content: there's only one source, and it's typed.
The two modes share a URL space
Deep links work the same in both modes. The router pathname maps to the id the IDE already understands, so /projects/atlas-market opens the right file whether you're in website mode or IDE mode.
IDE mode is a preference stored in localStorage, so a visitor who flips into it stays there across pages and reloads.
Static all the way
The build prerenders every route to HTML with vite-react-ssg, then a postbuild step writes the sitemap and 404. It deploys to Cloudflare Pages on push. The dev server watches content/, so editing a Markdown file hot-reloads the explorer, the writing index, and the command palette at once.
What I'd improve
- The IDE code window on each project page is generated from frontmatter, not real source; pulling a genuine snippet per project would be more honest.
- The
App.tsxview-model is one large object; splitting it per surface would make it easier to follow. - There's no automated test around the content plugin, which is the load-bearing piece — a schema check on frontmatter would catch mistakes before they hit the build.
More on the design decisions behind the rest of the site is on the about page; the other write-ups under /writing are all built from this same pipeline.