AI and Theming: Stopping the Invented Hex Code In short: Ask for "a green approve button" and you get . It looks right, passes review, and silently opts that component out of dark mode. This is the easiest AI failure to catch automatically and the easiest to miss by eye. CSS is where AI-generated code goes wrong most quietly. A wrong TypeScript type fails to compile. A wrong colour renders beautifully — in the one theme you happened to be looking at. The four failures Ask a model to style a component and you will see these, in roughly this order of frequency. 1. Hardcoded values. Every value is plausible and every value is wrong. is not , so it will not change for a tenant. is not on the spacing scale. And in dark mode this button keeps its light-mode green while everything around it changes. 2. to solve any cross-component problem. It is heavily represented in older Angular content, and it works, so it is the model's first answer whenever styles need to cross a boundary. 3. Media queries for component layout. Training data is overwhelmingly media queries; container queries are recent by comparison. 4. Utility classes on library components. inside a shared library couples it to a CSS framework the consumer may not have — and bypasses your token system completely. The rules block Add this to : The first block is the highest-value one because it is mechanical. "Use tokens" is a judgement call; "no hex codes outside primitives.css" is a rule a model can follow exactly and a linter can check exactly. The lint rules that make it real This is one of the few places where a linter can catch essentially the whole category. Use Stylelint: Add one more rule for the margin boundary, which is easy to state and easy to break: And wire it into CI alongside the ESLint job from Chapter 1: Give it the token list A model cannot use tokens it has never seen. The single highest-value context you can provide is the actual list, in the library's own context file from Chapter 4: Without this, "use tokens" is an instruction to guess at names. With it, the model is choosing from a list — the same shift from invention to translation as Chapter 3. Review checklist Six greps, in order of how often they hit: And the one check that is not a grep: look at the component in every theme. Storybook's theme toolbar makes this a click. A hardcoded colour is invisible in the theme it was written for and obvious in any other. "But I thought I could just review the CSS" You can, and CSS is short — a component stylesheet is thirty lines, not three hundred. It feels reviewable in a way a large TypeScript diff does not. The trouble is that this class of mistake is invisible in a diff and only visible in a state you are not looking at. Read this in a pull request: Nothing looks wrong. The colours are tasteful, the contrast between them is fine, the spacing is sensible. If you render the story, it looks correct. You approve it, because there is nothing to object to in what you can see . What you cannot see in the diff: In dark mode this badge is a pale green block on a near-black card — and on is fine, but the badge as a whole no longer belongs to the page. For the green-branded cooperative it stays the default green, so it is now the same colour as their primary and reads as a button. is not on the spacing scale, so it is 1px off every other badge and nobody will ever notice why the row looks slightly ragged. When the design team changes the success colour, this badge does not change, and it becomes the one inconsistent element on the page. Every one of those is a state that exists in production and not in your review window. Human review Stylelint --- --- --- Catches a hex code Only if looking for it Always Catches off-scale spacing ( vs ) Almost never Always Catches Usually Always Catches a media query used for layout Rarely Always Catches a utility class Sometimes Always Judges whether the design is good Yes No Judges whether the token chosen is the right one Yes No The bottom two rows are why you still review. on a "save" button passes every lint rule and is the wrong design decision — a human catches that and a linter never will. So the split is the same one from Chapter 2, applied to CSS: let the linter judge whether a value is legal, and spend your review on whether it is right. Reviewing CSS by eye for hardcoded values is spending human attention on the half a machine does perfectly. Recap The four failures are hardcoded values, , media queries for layout, and utility classes. State the rule mechanically: no raw values outside . Stylelint catches nearly the whole category — , , , and allowed-value lists for spacing and radius. Publish the token list in your library's context file. A model cannot use names it has not seen. Six greps cover the review , plus one thing no grep can do: look at it in every theme. A hardcoded colour is invisible in a diff and only wrong in a state you are not looking at. Linters judge legality; you judge whether it i…