Restaurant Websites | Template-Driven Static Site Platform
One Astro codebase that builds and deploys independent static websites for restaurant clients from shared templates and per-site JSON content. Two themes, five live client sites, zero-JavaScript output, consent-gated ads, and a one-command deploy per site.
The problem
A restaurant needs a website that loads fast on a phone, shows the menu without a PDF, lists hours and directions, and does not cost them a monthly platform fee for the privilege. That is a solved problem exactly once. The second restaurant needs the same thing with different colors, a different menu, and a different city.
Build those one at a time and you get five codebases that drift apart, five sets of dependencies to patch, and a security fix you have to apply five times and will apply four times. Build them on a page builder and every client inherits a slow site and a subscription.
What I built
One Astro project that builds any number of completely independent static restaurant websites. A SITE environment variable picks which restaurant to build, the shared code renders it, and the output is a folder of static HTML ready to rsync to its own domain.
Five client sites run on it today: El Farolito in San Francisco, ABC Bakery in Sacramento, Adobo Taco Grill, All That BBQ in Irvine, and Anjin. Each is its own domain with its own look. None of them contain a line of code that is not shared.
Content is data, not code
Every restaurant-specific value lives in that restaurant’s folder, never in shared code. A site is a site.config.json plus five content files: menu.json, locations.json, testimonials.json, gallery.json, and an about.md.
The rule that keeps this honest is simple and absolute: if you are typing a restaurant name, a phone number, or a color into src/, you have made a mistake. Colors and fonts come out of the config as CSS custom properties, so a theme is a set of variables rather than a fork.
That rule is what makes the platform worth having. It means a shared improvement lands on all five sites at once, and it means adding a sixth restaurant is a content task rather than an engineering task.
Schema stability is the real constraint
The interesting engineering problem here is not rendering a menu. It is that five sites depend on the same JSON shapes, so the schemas are effectively a public API with clients I cannot force to upgrade.
Changing the shape of menu.json means updating every existing site’s content to match, in the same commit, or half the portfolio breaks. So the working discipline is to add optional fields rather than change existing ones, and to test any shared change against at least two different site configs before calling it done. A change that works on one restaurant and breaks another is the default failure mode of this architecture, and testing against two costs about thirty seconds and catches almost all of it.
Ads that ask first
These sites carry AdSense, and the consent handling is not the default. Ad scripts initialize in a denied consent state and only activate after the visitor grants consent through Google Funding Choices. Nothing loads unconditionally.
There is no GA4 on any of them. Traffic reporting comes from Cloudflare and cPanel logs, which answers the questions a restaurant owner actually asks without putting another tracker on the page.
Build and deploy
The build pipeline runs font generation, image optimization through Sharp, the Astro build, and an .htaccess copy, producing static HTML, CSS, fonts, optimized images, a sitemap, and robots.txt. Output is static with no SSR, no API routes, and no server runtime. There is nothing to patch at runtime because there is no runtime.
Deploy is a script per site, plus a deploy-changed.sh that ships only the sites touched by the last commit and a rollback-site.sh that can pull down the current live version before overwriting it.
All of it lands on the same cPanel account that hosts the MenuFindr directory as a live WordPress site. That shared account is the one genuine hazard in this setup, so the deploy tooling carries an explicit rule fencing the WordPress document root off from every rsync path. An accidental --delete against the wrong target would destroy a production site, and “be careful” is not a control.
Why this matters for a restaurant
The client gets a site that is static HTML on their own domain. It loads fast on a phone in a parking lot, it has no database to be breached, no plugin to go unpatched, and no monthly platform fee. When they change a price, I change one line of JSON and redeploy in under a minute.
If you run a restaurant and your current site is a slow page builder or a PDF menu, that is the conversation to have.
Like what you see?
I build tools that solve real problems. If you have an idea or a project that needs engineering, let's talk.
Get in Touch