We're a tiny startup with one primary job: build Divooka - the general-purpose visual programming platform we wish we'd had for apps, automation, data, and AI. This post is about why we rewrote our public website, how we did it with almost no tooling, and what we learned about balancing tech debt with progress.
Why the website mattered (and what it is)
The original methodox.io site was built by a friend using React. Since we host it statically, he set it up with serve-static
(or a similar npm module). He did a great job with styling and functionality, but maintenance turned into a hassle.
Methodox's site exists to do three things:
- Explain Divooka in plain language with screenshots/video.
- Point people to places that change more frequently than a landing page - our Wiki, Store (itch.io), and Blog.
- Get out of the way so we can ship the actual product.
Simple goals - but the first version made the site feel like a software project. It was a React app, nicely designed, bundled, and served as a static site. It looked good and worked fine, but for a team our size it was too much overhead.
What wasn't working
The React version was useful early on because it let us move fast - and that was the right decision at the time. But over time, friction piled up:
- Updates were slow. Editing copy or adding a card meant touching components, wrangling styles, and redeploying a bundle.
-
SEO suffered. Blog entries lived behind query params (
?blogId=agi
), which isn't great for discoverability or sharing. - Debugging was painful. Styled-components generated opaque class names, making CSS diffs and quick DevTools fixes harder than necessary.
- Iteration cost attention. The site competed with product time.
Here's an example of the styled-component we had to poke at just to tweak a header link:
const MyLink = styled(Link)`
color: white;
text-decoration: none;
font-size: 1rem;
font-weight: 700;
${props => props.theme.breakpoints.tablet} {
font-size: 1.5rem;
font-weight: inherit;
}
border-radius: ${props => props.theme.spacing.xsmall};
padding: ${props => props.theme.spacing.xsmall} ${props => props.theme.spacing.small};
&:hover { background-color: rgba(255, 255, 255, 0.2); }
`;
Which produced this mess in the generated HTML:
<style data-styled=active data-styled-version=6.1.9>
.lhcNgY { margin: 0 auto; max-width: min(100vw, 992px); padding: 2rem 1.5rem }
@media only screen and (min-width:992px) {
.lhcNgY { padding: 3rem 0 }
}
.hCnWMo { display: flex; flex-direction: column; gap: 1rem }
.bpPBGQ { color: inherit; text-decoration: none }
.bpPBGQ:hover { text-decoration: underline }
.eGCHEm { width: 100%; height: 200px; border-radius: 1rem; object-fit: cover }
</style>
A fine pattern in an app; disproportionate ceremony for a landing page.
A couple of dead ends
I first tried to refactor the whole thing, but knew I didn't want to turn it into an ASP.NET Core Razor app - requiring a server. What I did want was a way to dynamically generate lists (e.g. blog links).
The idea was to write a tiny Static Site Generator that could fill placeholders like this in a "compilation" pass:
<div class="sc-beqWNU sc-brKelq lhcNgY bviNbt">
<h2 class="sc-ktEKgv dkIGRI">Blogs</h2>
<div class="sc-hAtFlw llkcWk">
@@BlogsList@@
</div>
</div>
I tried two approaches before settling on the rewrite:
- "Just refactor it." Rebuilding as an ASP.NET Core Razor app would have simplified some things but forced us to run a server.
-
"Freeze and template it." Tools like the SingleFile Chrome extension can snapshot a page, then you replace
@@Placeholders@@
with a generator. Promising - until the dumped CSS was unreadable and I realized I was about to write a mini-framework.
Both would have been "correct." Both would also have delayed Divooka.
The decision: embrace simplicity, ship faster
We rewrote the site as a single HTML page with a couple of images and a few lines of vanilla JavaScript. Anything that needs frequent updates now lives on platforms built for content:
-
Blog →
blog.methodox.io
-
Wiki/Docs →
wiki.methodox.io
-
Store / Downloads →
methodox.itch.io
The marketing shell is "boring" on purpose:
-
No framework. Just HTML, CSS, and some tiny JS snippets:
- a FAQ accordion,
- an
IntersectionObserver
for fade-ins.
- Semantics and accessibility first. Real headings, ARIA attributes, descriptive alt text.
- System fonts with Google Fonts fallback. Fast, easy to swap later.
-
Clean SEO. Canonical URL, description, Open Graph tags,
theme-color
. - Static hosting + CDN. The page is static; heavier media sits behind a CDN.
- One deployment path. Edit HTML, commit, GitHub Actions publish.
Now our CSS looks like this - still responsive, much easier to debug:
.landing__link {
color: #fff;
text-decoration: none;
border-radius: .5rem;
padding: .5rem 1rem;
font-size: 1rem;
}
@media (min-width: 600px) {
.landing__link { font-size: 1.5rem; }
}
.landing__link:hover { background: rgba(255,255,255,.2); }
Same intent as the styled-components snippet - at a fraction of the complexity.
A few technical choices worth calling out
-
Critical CSS & minimal JS. Everything the page needs is inline in a
<style>
block. JavaScript is deferred and tiny (no dependencies). - Breakpoints as CSS custom properties. Easier to keep design tokens visible and tweak them centrally.
- Progressive enhancement. The page is usable without JS; animation and FAQ expanders are nice-to-haves.
- Externalized "dynamic" content. The landing page links to frequently changing content rather than trying to ingest it. We deliberately resisted adding a build step or SSG.
- Observability for a static site. We kept Google Analytics (gtag) and added a small chat widget, both as leaf integrations so we can remove them without touching the rest of the page.
- Media delivery. Screenshots and videos live behind a CDN; the HTML references those URLs directly. That keeps the repo tiny and pushes bandwidth to the edge.
Process notes: iterative by design (and honest about tech debt)
We didn't "solve" the website in one pass. We iterated:
- Phase 1 – Prove value (React): Ship something good-looking fast. Accept tech debt knowingly.
- Phase 2 – Observe friction: Updates, SEO, debugging became recurring pain points.
- Phase 3 – Reduce surface area: Replace the framework with plain HTML/CSS/JS. Push volatile content elsewhere.
- Phase 4 – Optimize for reality: A site one person can maintain in minutes, not hours.
Tech debt isn't failure - it's a tool. The first version gave us speed, feedback, and a way to talk about Divooka. When its costs outweighed its benefits, we paid it down with a simpler architecture.
Results
- Edits are trivial. Open VS Code, change text, commit.
- SEO improved. Clean metadata and stable, shareable links.
- Debugging is simple. No generated class names - what you see is what's in the file.
- Operational load is near zero. No Node runtime, no bundler, no server. Just static hosting + CDN.
- More time for Divooka. Which was the point.
What we'd recommend if you're in a similar spot
- Ask what your site really needs. If it's a brochure with a few CTAs, frameworks may be overkill.
- Treat iteration as strategy. Start with what gets you moving; rewrite when friction accumulates.
- Prefer boring architecture at the edges. Save complexity for your product.
- Use existing platforms. If your blog, docs, or store can live elsewhere, let them.
Closing
The old site was a mix of love and frustration. It gave us speed when we needed it. The rewrite gives us clarity and low maintenance - so we can focus on Divooka, the part that is exciting.
If you spot rough edges or want to chat about the approach, we're listening:
General: Contact@Methodox.io · Sales: Sales@Methodox.io · Careers: Career@Methodox.io
– Charles, Founder & Lead Developer @ Methodox
Top comments (0)