← Back to work
Private client project 2026·Full-stack Engineer
Solid
Corporate site with a custom headless CMS — and a multi-client store platform.
Overview
The project
Solid is the studio's own corporate site: a marketing front end driven by a bespoke admin CMS for blog, portfolio, services, and careers. Alongside it sits a reusable WordPress + WooCommerce platform that spins up branded multi-client stores from a shared codebase.
Screens
Screenshots
Tap any image to enlarge. Use arrow keys to navigate.
Stack
What I built it with
Laravel 12Inertia v2React 19TypeScriptTailwind v4WordPressWooCommerceMySQL
My role
What I owned
- Built the corporate marketing site (home, work, services, blog, careers) on Inertia + React.
- Built the admin CMS: blog with translations, portfolio, services, jobs, and newsletter campaigns.
- Implemented 2FA, email verification, and role-based access with Fortify + spatie/permission.
- Engineered a multi-client WordPress/WooCommerce platform with a shared plugin and vendor theme.
- Added bilingual storefronts, seeded catalogs, and Wompi/Tilopay payment gateways.
Architecture
How it's wired
- 01Laravel 12 monolith serving an Inertia v2 + React 19 front end, no separate API layer.
- 02Headless-style admin CMS with translation tables driving public content.
- 03Separate WordPress + WooCommerce platform split across plugin, theme, and per-client repos.
- 04Dockerized store deploys that auto-install WP, seed a catalog, and wire commerce config from env.
Hard parts
What was challenging
The decisions worth talking about in an interview.
One store codebase, many branded clients
The WooCommerce platform had to stay reusable while every client looks bespoke. A shared platform plugin plus an isolated vendor theme and a per-client repo kept client-specific code from leaking into the shared base, with hooks as the only extension surface.
Bilingual CMS without a translation plugin
Rather than pull in WPML/Polylang, the storefront does EN/ES through a gettext filter map plus a cookie — which means every new string has to be registered in both the gettext and ngettext maps to stay translated.