Claude and ChatGPT will write you a working website in minutes, and left to their defaults they will write it the way the average GitHub repository is written, which is not how good websites are built.
The models are trained on the internet’s median code, so the median is what you get: Tailwind utility soup, div-heavy markup, a framework where a stylesheet would do. The fix isn’t better models, it’s better instructions. I’ve refined a standing set of rules I give AI assistants for front-end work, and with them the output goes from “works” to code I’d actually ship and maintain. Here’s the full set, and why each rule exists.
BEM SCSS and Clean HTML Over Tailwind
The first rule I give any model: no Tailwind, no utility classes, write BEM-structured SCSS against semantic HTML.
<section class="pricing">
<h2 class="pricing__title">Plans</h2>
<div class="pricing__grid">
<article class="pricing__plan pricing__plan--featured">...</article>
</div>
</section>
Utility-class markup entangles style with structure, so every design change is a find-and-replace across HTML, and the markup itself becomes unreadable noise. BEM keeps the HTML describing what things are and the SCSS describing how they look, in one place per component. Models are excellent at BEM when told to use it, because the naming convention is mechanical, and the resulting code is dramatically easier for both humans and future AI sessions to modify.
Class-Based JavaScript in a Single File
For behavior, I ask for class-based JavaScript organized in a single file: one class per UI concern, instantiated on DOM ready. jQuery is fine for straightforward interactive sites, and Vue is the step up when a UI is genuinely app-like, with multi-step flows, shared state, or heavy conditional rendering.
What I explicitly block is the default the models reach for: React plus a bundler plus a component tree for what is fundamentally a content page with a nav toggle and a couple of forms. The build tooling alone costs more maintenance than the entire feature. A single well-organized file with named classes gives you structure without a compile step, and any developer can open it and understand the whole site’s behavior in one read.
Keep the Markup-to-Content Ratio Low
Good HTML is mostly content. I instruct models to keep the text-to-HTML ratio as favorable as possible: no wrapper divs that exist only to hang styling on, no five-deep nesting where two levels work, no framework scaffolding around plain content.
Lean markup is faster to parse and render, easier to style, better for accessibility because the semantic structure is visible rather than buried, and better for SEO and AI extraction because crawlers and language models get content instead of wading through structure. Ask a model to reduce its own markup depth and it usually cuts a third of the elements with zero visual change.
Reusable Elements as Standalone Templates
Any element that appears on more than one page, the header, footer, cards, CTAs, forms, gets built as a separate standalone template file and included wherever it’s used, never copied into each page.
This is the same modularity argument I make everywhere, from ACF versus Gutenberg to schema markup in HubSpot: duplicated markup drifts, shared templates stay correct. It matters doubly with AI assistants, because a model asked to update a duplicated component will happily update the three copies it can see and miss the fourth. One file per component makes every future change, human or AI, a single edit.
Google Fonts, and One Typographic System
Typography rules I hand to the model:
- Use Google Fonts, self-hosted or loaded properly with
font-display: swap, with real fallback stacks - Define one typographic scale, heading sizes, body size, line heights, spacing, as SCSS variables at the top of the stylesheet
- Follow standard typographic structure, one h1 per page and headings in order, and never stray from the system page to page
The last point is the one models violate constantly when left alone: generate five pages in five prompts and you get five slightly different type treatments. Defining the scale once and referencing it everywhere keeps the site feeling like one designed thing instead of a collection of AI outputs.
Adapt to Any Screen Size, Not to Breakpoints
My responsive instruction is the one that most improves the output: make the layout look great and adapt fluidly to any screen size, avoiding fixed device breakpoints wherever possible.
Sizing scales continuously with clamp() and viewport units rather than jumping at 768px because a framework said so:
.hero__title {
font-size: clamp(1.75rem, 4vw + 1rem, 3.5rem);
}
Media queries still exist in this system, but they’re deployed at the specific viewport widths where a layout genuinely needs a structural change for ideal UX, a grid collapsing to one column at the width where the cards actually get cramped, not at a device width from 2015. Those adjustment points can vary from BEM module to module as each component needs, rather than the whole page snapping at once. The result works on every screen that exists and every screen that doesn’t yet.
Final Scans for Accessibility and SEO
The last rule: the job isn’t done when it renders. Every build ends with an explicit accessibility and SEO pass, and AI assistants are genuinely good at both when directly asked:
- Accessibility: heading hierarchy, landmarks, alt text, form labels, focus states, contrast, keyboard operability
- SEO: titles and meta descriptions, canonical URLs, Open Graph tags, Schema.org markup generated from the page’s real content, image dimensions and lazy loading
Ask the model to audit its own output against WCAG and an SEO checklist as a final step, and it will catch most of what it missed while generating. Skip the ask and none of it happens.
The Pattern Behind the Rules
Every rule here pushes the same direction: fewer moving parts, structure that lives in one place, and output a human can read. AI assistants remove the labor cost of doing things properly, which was always the main excuse for not doing them. Give the model the standards you’d hold a senior developer to, keep them in a standing prompt or a CLAUDE.md file so every session inherits them, and the code that comes back is the code you actually want, which is exactly how I run the builds behind my WordPress consulting work.