A writing rule
became a test.
A bilingual site, no framework and no dependencies, where the editorial standard is not a recommendation in a doc. It is an assertion that breaks the build.
I had a portfolio to maintain in Portuguese and English. The two obvious options were bad. Keeping two files per page means a fix on one side forgets the other, and the drift shows up months later. Adopting an i18n framework means carrying a build, dependencies and security updates for a ten page site.
One file per page, both languages inside, marked by class. At deploy time a script strips the opposite language and writes two versions, PT at the root and EN under /en/, with correct canonical and hreflang and a bilingual sitemap. Editing means editing a single file, and it is impossible to fix one language and forget the other, because they sit on the same line.
Alongside the splitter came a validator that runs after the build and fails the deploy. It checks one language per URL, canonical, hreflang and og:locale per page, a single h1, shared assets on absolute paths, the form and the résumé in the right language, a bilingual sitemap. It also checks a writing rule of mine: em dashes are banned across the site. If one shows up, the build fails.
The system caught me out. A new page came in with a compound class, the splitter was matching an exact string and removed nothing, and the Portuguese version started showing the whole English text. The validator said nothing, because it was matching the same exact string. I fixed both to treat the class as a list of words, which is what it always was.
If the test and its target share the same blind spot, the test passes and the bug ships.
The complaint was subjective: the text felt small. Instead of bumping sizes by eye, I measured the whole site and built a table of sizes by text role. The diagnosis became objective: pure reading copy was correct at 16px, but the argumentative text inside the cases, where the reasoning actually lives, was running between 13.6px and 14.1px. It was typographically treated as a caption while being the main content of the page.
The fix raised reading text to roughly 15.7px and pulled source notes and footers out of the 9.6px range they were in, up to roughly 11.5px. The form field label, which is functional instruction and not decoration, came off 9.28px.
HTML, CSS and JavaScript written by hand, no framework, with CSS and JS inlined per page. The pipeline is a language splitter, a build script and a validator, all in plain Node. Version control in Git, automatic publishing on Netlify on every push to the main branch.
It was built in pair with a coding agent, Claude Code, and that is worth stating plainly because it is part of the method and not a footnote. The division that worked is the one I argue for in CRM: the model reads, proposes and writes; code calculates and verifies; the call on what goes live is mine. The validator exists precisely so that last part does not rest on trust, but on a check.