In Part 2, I introduced the idea of defining "invariants" to prevent design degradation (drift). A state in which all invariants are satisfied — that's architecture-compliant code, and it shows up as zero lint errors. But defining invariants alone isn't enough. Detecting violations, communicating how to fix them, and driving corrections — only when this loop runs does the design hold.
Two Approaches: Upfront Teaching vs. Post-Hoc Feedback
There are broadly two ways to maintain CSS architecture.
Upfront teaching: Write guidelines, educate the team, verify through reviews. For AI agents, communicate rules through instruction files (llms.txt, AGENTS.md).
Post-hoc feedback: Verify what's written, return errors with fix instructions. Humans see errors in the editor and fix them; AI parses lint output and self-corrects.
Part 1 covered the limits of upfront teaching. The more rules there are, the higher the memorization cost; decisions become subjective; AI accuracy drops as context grows.
Post-hoc feedback doesn't require the author to know the rules beforehand. Write, see the error, fix it. Starting from zero knowledge, the code converges on architecture compliance simply by running through the loop.
Learning from TypeScript's Feedback Design
TypeScript's type system is a successful example of post-hoc feedback.
// TypeScript error message
// Type 'string' is not assignable to type 'number'.
const count: number = "hello";
What makes this error effective is that it contains three pieces of information:
- What's wrong: The types don't match
-
Where: Assignment to
count -
How to fix it: Pass a
numbervalue
Developers don't need to memorize all of TypeScript's type rules. The compiler tells them as they write code, so just following the feedback converges the code to type safety.
Architecture lint needs the same structure.
But here's the key distinction: traditional CSS lint checks what you wrote — naming patterns, property duplication. Architecture lint checks where you wrote it — whether the structure itself follows design rules. This is a fundamentally different level of verification.
Designing Error Messages for Architecture Lint
Traditional CSS lint tends to focus on surface-level checks.
// Traditional lint error (typical)
Expected indentation of 2 spaces (indentation)
The fix here is obvious — adjust the spaces. But this is surface-level formatting. It doesn't verify component structure or property placement — the architectural concerns that cause real design drift.
An error message that functions as feedback should look like this:
// Feedback-style lint error
"margin-top" is an item property — it must be specified
from the parent Block using the `> .child-name` selector,
not inside the child Block itself.
This message contains:
-
What's wrong:
margin-topis in the wrong place - Why it's wrong: Item properties must be specified from the parent
-
How to fix it: Write it in the parent Block's
> .child-nameselector
Here's what the Stylelint output would look like:
src/components/feature-card/feature-card.scss
4:3 ✖ "margin-top" is an item property — it must be spiracss/property-placement
specified from the parent Block using
"> .child-name" selector, not inside the
child Block itself.
File path, line number, error message, rule name — all the information an AI agent needs to parse. The correction flow looks like this:
1. Extract from lint output: "feature-card.scss line 4, move margin-top to parent"
2. Remove the margin-top declaration from feature-card.scss
3. In each parent Block that uses feature-card, add under its selector (SCSS nesting):
.parent-block {
> .feature-card { margin-top: ... }
}
4. Re-run lint → confirm zero errors
Note that violation detection is always local — lint spots margin-top in the wrong file. The fix may touch another file (the parent), but the error message tells you exactly where, with no project-wide scanning required.
Even humans with no CSS architecture experience can understand the fix from this message.
The Structure of a Feedback Loop
Whether the author is human or AI, the loop structure is the same:
Write → Run lint → Detect errors → Fix → Run lint → ... (until zero errors)
For humans:
- Write SCSS
- Stylelint shows errors in real-time in the editor
- Read error messages and fix
- Save → re-validate → fix complete
For AI agents:
- Generate SCSS
- Run
stylelint - Parse output and fix according to error messages
- Run
stylelintagain → repeat until zero errors
The key point is that this loop has the same exit condition. Zero errors — that's architecture-compliant code, and it converges to the same standard regardless of who wrote it.
Error Message Design Principles
For the feedback loop to function, error messages must meet these conditions:
-
Be specific: Not "invalid naming" but "Blocks must use a two-word kebab-case name (e.g.,
hero-container)" - Include fix instructions: Communicate not just what's wrong, but how to fix it
- Be self-contained: Understandable without referencing external documentation
- Be machine-readable: Structured so AI can parse and convert to actionable fixes
Error messages are both "documentation" and a "teacher." They must be comprehensible to a human encountering the rule for the first time and convertible to executable fixes when read by AI — that's the quality bar.
Feedback Effectiveness in Practice
Whether this design works hinges on: "Can you really fix issues just from lint errors?"
I've implemented and used this approach in production with SpiraCSS. Out of the box, traditional CSS lint mainly covers naming conventions and property duplication. SpiraCSS's Stylelint plugin goes further — architecture-aware lint that extends verification to component structure and property placement. As of 2025, when SpiraCSS was first released, I'm not aware of any other CSS architecture methodology that performs this level of structural verification through lint — integrated into an AI agent's autonomous correction loop.
This level of test coverage — over 650 patterns — would have been impractical to develop by hand. AI-assisted development made it possible.
From practical experience, I can say this: if invariants are properly designed, fixes are usually possible from error messages alone. Because invariants are "binary" and "locally verifiable," the location and method of correction are determined with little ambiguity.
With ambiguous conventions, you get hesitation: "Should I do this? Or that?" But an invariant violation almost always has one clear answer: "Fix it like this." That's why the feedback loop converges.
The next part looks at the concrete design principles and structure of SpiraCSS, which implements this approach. How many rules can be derived from a single principle?
→ Next (this weekend or next week): Part 4 — Parent Owns Layout — A New CSS Architecture for the AI Era — Drift-Resistant by Design
SpiraCSS's design specs, tools, and source code are all open source.
- SpiraCSS: https://spiracss.jp
- GitHub: https://github.com/zetsubo-dev/spiracss
Top comments (0)