[{"data":1,"prerenderedAt":773},["ShallowReactive",2],{"article-\u002Fblog\u002Fvibe-coded-ai-design-system-cleanup":3},{"id":4,"title":5,"aiSummary":6,"articleSection":7,"author":8,"body":12,"breadcrumbs":739,"cover":22,"cta":747,"date":748,"dateModified":748,"datePublished":748,"description":749,"extension":750,"externalUrl":751,"faq":751,"footerCta":6,"footerCtaLabel":718,"footerCtaText":752,"howToDescription":751,"howToName":751,"howToSteps":751,"howToTotalTime":751,"image":751,"imageHeight":751,"imageWidth":751,"keywords":753,"lang":763,"meta":764,"navigation":594,"path":765,"publisher":751,"readingTime":333,"seo":766,"source":751,"stem":767,"tags":768,"type":771,"wordCount":751,"__hash__":772},"blog\u002Fblog\u002Fvibe-coded-ai-design-system-cleanup.md","I vibe-coded my site with AI. Here's what the cleanup revealed.",false,"Design System",{"name":9,"url":10,"jobTitle":11},"Sophie Boulaaouli","https:\u002F\u002Fprodju.com","Product Designer & Fondatrice",{"type":13,"value":14,"toc":727},"minimark",[15,24,27,38,41,44,49,57,75,83,90,93,95,99,110,113,121,124,126,130,133,139,144,150,212,219,225,229,236,383,402,405,465,468,472,475,531,534,552,556,562,568,609,611,615,618,637,640,643,658,660,664,667,682,689,698,701,703,713,723],[16,17,18],"p",{},[19,20],"img",{"alt":21,"src":22,"style":23},"I vibe-coded my site with AI, design system cleanup","\u002Fimages\u002Fvibe-coded-ai-design-system.png","border-radius: 0.75rem; margin-bottom: 2rem",[16,25,26],{},"I was going fast. The CSS wasn't clean, hardcoded values scattered everywhere, z-indexes pulled from thin air, the same orange hex repeated in five files. I told myself I'd fix it later.",[16,28,29,30,37],{},"Then I came across ",[31,32,36],"a",{"href":33,"rel":34},"https:\u002F\u002Fhvpandya.com\u002Fllm-design-systems",[35],"nofollow","Hardik Pandya's article on exposing your design system to LLMs",". \"Later\" finally had a method.",[16,39,40],{},"Pandya's piece genuinely shifted how I think about design systems in an AI workflow. From \"documentation designers maintain\" to \"constraints AI agents read at session start\". Everything below is built on top of that shift. It's not a review of the prompt (which works exactly as described), but a look at the four things I had to fix manually after running it.",[42,43],"hr",{},[45,46,48],"h2",{"id":47},"the-problem-the-ai-doesnt-read-your-variables","The problem: the AI doesn't read your variables",[16,50,51,52,56],{},"My site is built with Nuxt + SCSS + Claude Code. I'd done the right things upfront: a spacing scale, named colors, a radius system. My ",[53,54,55],"code",{},"_variables.scss"," was tidy. None of it was being used.",[16,58,59,60,63,64,63,67,70,71,74],{},"When you describe components to an AI in natural language, the AI doesn't go look up your tokens. It writes what seems reasonable. ",[53,61,62],{},"z-index: 2",". ",[53,65,66],{},"padding: 14px",[53,68,69],{},"#ff6b35",", even though ",[53,72,73],{},"$color-primary"," is sitting right there in the variables file. Each choice looks fine in isolation. Together, it's silent inconsistency, the kind you feel before you can name it.",[16,76,77,78,82],{},"Running Pandya's audit across 27 files confirmed it: ",[79,80,81],"strong",{},"~350 hardcoded values",". My primary orange appearing 5 times as a raw hex. 55+ unique pixel spacing values. 30+ raw z-indexes, none referencing my defined scale.",[16,84,85,86,89],{},"I ran the same audit on a client project, Vue 3 + Vuetify with a Storybook design system: 257 violations across 23 files, the same gray palette copy-pasted into 8+ components, ",[53,87,88],{},"!important"," flags where the AI couldn't navigate the cascade.",[16,91,92],{},"The AI rewrites what it sees, not what you defined.",[42,94],{},[45,96,98],{"id":97},"the-fix-one-agent-session","The fix: one agent session",[16,100,101,102,105,106,109],{},"Pandya's prompt does the whole thing in one pass: audit, build a ",[53,103,104],{},"tokens.css"," with three-layer indirection, generate spec files for every component, write a CI-ready audit script, replace every hardcoded value, and produce a ",[53,107,108],{},"CLAUDE.md"," that future sessions will read at session start. It's all in his article, worth reading in full.",[16,111,112],{},"On my site: 20 files modified, 230+ custom properties, 8 component specs, zero violations on final audit. Under an hour. Visually identical, which is the point.",[16,114,115,116,120],{},"What changes is the ",[117,118,119],"em",{},"next"," session — the constraint is now structural, not willpower-based.",[16,122,123],{},"But the interesting part isn't the run. It's what I found in the output.",[42,125],{},[45,127,129],{"id":128},"what-the-cleanup-revealed-context-is-everything","What the cleanup revealed: context is everything",[16,131,132],{},"Here's where it gets interesting.",[16,134,135,136],{},"The prompt produced a clean, passing audit. Zero violations. But looking at the generated tokens, I found a different kind of problem: ",[79,137,138],{},"the AI had sometimes tokenized the wrong things.",[140,141,143],"h3",{"id":142},"watch-out-for-1-near-duplicate-tokens","Watch out for #1: Near-duplicate tokens.",[16,145,146,147,149],{},"In ",[53,148,55],{},", Claude Code added:",[151,152,157],"pre",{"className":153,"code":154,"language":155,"meta":156,"style":156},"language-scss shiki shiki-themes github-light github-dark","$color-bg-card-glass: rgba($color-white, 0.6);\n$color-bg-card-glass-header: rgba($color-white, 0.565);\n","scss","",[53,158,159,191],{"__ignoreMap":156},[160,161,164,168,172,176,179,182,185,188],"span",{"class":162,"line":163},"line",1,[160,165,167],{"class":166},"s4XuR","$color-bg-card-glass",[160,169,171],{"class":170},"sVt8B",": ",[160,173,175],{"class":174},"sj4cs","rgba",[160,177,178],{"class":170},"(",[160,180,181],{"class":166},"$color-white",[160,183,184],{"class":170},", ",[160,186,187],{"class":174},"0.6",[160,189,190],{"class":170},");\n",[160,192,194,197,199,201,203,205,207,210],{"class":162,"line":193},2,[160,195,196],{"class":166},"$color-bg-card-glass-header",[160,198,171],{"class":170},[160,200,175],{"class":174},[160,202,178],{"class":170},[160,204,181],{"class":166},[160,206,184],{"class":170},[160,208,209],{"class":174},"0.565",[160,211,190],{"class":170},[16,213,214,215,218],{},"A difference of ",[53,216,217],{},"0.035"," opacity, invisible to any human eye. Two separate variables, used in a single component each. The AI found two slightly different values in the original code and dutifully tokenized both instead of consolidating them. A designer rounds one to match the other and moves on.",[220,221,222],"blockquote",{},[16,223,224],{},"Rule of thumb: if a token is used in fewer than two places, it probably shouldn't be a token.",[140,226,228],{"id":227},"watch-out-for-2-tokenizing-local-hacks-as-global-system-decisions","Watch out for #2: Tokenizing local hacks as global system decisions.",[16,230,231,232,235],{},"The audit found 30+ raw ",[53,233,234],{},"z-index"," values, so Claude Code generated a full scale:",[151,237,239],{"className":153,"code":238,"language":155,"meta":156,"style":156},"$z-behind: -1;\n$z-below: 0;\n$z-base: 1;\n$z-raised: 2;\n$z-dropdown: 100;\n$z-sticky: 200;\n$z-fixed: 300;\n$z-modal-backdrop: 400;\n$z-modal: 500;\n$z-tooltip: 600;\n$z-lightbox: 9999;\n",[53,240,241,254,266,279,292,305,318,331,344,357,370],{"__ignoreMap":156},[160,242,243,246,248,251],{"class":162,"line":163},[160,244,245],{"class":166},"$z-behind",[160,247,171],{"class":170},[160,249,250],{"class":174},"-1",[160,252,253],{"class":170},";\n",[160,255,256,259,261,264],{"class":162,"line":193},[160,257,258],{"class":166},"$z-below",[160,260,171],{"class":170},[160,262,263],{"class":174},"0",[160,265,253],{"class":170},[160,267,269,272,274,277],{"class":162,"line":268},3,[160,270,271],{"class":166},"$z-base",[160,273,171],{"class":170},[160,275,276],{"class":174},"1",[160,278,253],{"class":170},[160,280,282,285,287,290],{"class":162,"line":281},4,[160,283,284],{"class":166},"$z-raised",[160,286,171],{"class":170},[160,288,289],{"class":174},"2",[160,291,253],{"class":170},[160,293,295,298,300,303],{"class":162,"line":294},5,[160,296,297],{"class":166},"$z-dropdown",[160,299,171],{"class":170},[160,301,302],{"class":174},"100",[160,304,253],{"class":170},[160,306,308,311,313,316],{"class":162,"line":307},6,[160,309,310],{"class":166},"$z-sticky",[160,312,171],{"class":170},[160,314,315],{"class":174},"200",[160,317,253],{"class":170},[160,319,321,324,326,329],{"class":162,"line":320},7,[160,322,323],{"class":166},"$z-fixed",[160,325,171],{"class":170},[160,327,328],{"class":174},"300",[160,330,253],{"class":170},[160,332,334,337,339,342],{"class":162,"line":333},8,[160,335,336],{"class":166},"$z-modal-backdrop",[160,338,171],{"class":170},[160,340,341],{"class":174},"400",[160,343,253],{"class":170},[160,345,347,350,352,355],{"class":162,"line":346},9,[160,348,349],{"class":166},"$z-modal",[160,351,171],{"class":170},[160,353,354],{"class":174},"500",[160,356,253],{"class":170},[160,358,360,363,365,368],{"class":162,"line":359},10,[160,361,362],{"class":166},"$z-tooltip",[160,364,171],{"class":170},[160,366,367],{"class":174},"600",[160,369,253],{"class":170},[160,371,373,376,378,381],{"class":162,"line":372},11,[160,374,375],{"class":166},"$z-lightbox",[160,377,171],{"class":170},[160,379,380],{"class":174},"9999",[160,382,253],{"class":170},[16,384,385,386,184,388,184,390,184,392,394,395,397,398,401],{},"Looks systematic. But it conflates two completely different things: intra-component stacking (",[53,387,250],{},[53,389,263],{},[53,391,276],{},[53,393,289],{},") and global UI layers (",[53,396,302],{},"+). The low values aren't design decisions, they're local implementation details. A developer writing ",[53,399,400],{},"z-index: 1"," inline is perfectly readable. Tokenizing it adds noise, not clarity.",[16,403,404],{},"For a landing page, four tokens are enough:",[151,406,408],{"className":153,"code":407,"language":155,"meta":156,"style":156},"$z-sticky: 100; \u002F\u002F sticky nav\n$z-dropdown: 200; \u002F\u002F menus, tooltips\n$z-modal: 400; \u002F\u002F modals\n$z-toast: 500; \u002F\u002F notifications\n",[53,409,410,425,438,451],{"__ignoreMap":156},[160,411,412,414,416,418,421],{"class":162,"line":163},[160,413,310],{"class":166},[160,415,171],{"class":170},[160,417,302],{"class":174},[160,419,420],{"class":170},"; ",[160,422,424],{"class":423},"sJ8bj","\u002F\u002F sticky nav\n",[160,426,427,429,431,433,435],{"class":162,"line":193},[160,428,297],{"class":166},[160,430,171],{"class":170},[160,432,315],{"class":174},[160,434,420],{"class":170},[160,436,437],{"class":423},"\u002F\u002F menus, tooltips\n",[160,439,440,442,444,446,448],{"class":162,"line":268},[160,441,349],{"class":166},[160,443,171],{"class":170},[160,445,341],{"class":174},[160,447,420],{"class":170},[160,449,450],{"class":423},"\u002F\u002F modals\n",[160,452,453,456,458,460,462],{"class":162,"line":281},[160,454,455],{"class":166},"$z-toast",[160,457,171],{"class":170},[160,459,354],{"class":174},[160,461,420],{"class":170},[160,463,464],{"class":423},"\u002F\u002F notifications\n",[16,466,467],{},"The AI generated 11 because it found 11 distinct values. It had no way to distinguish \"local stacking hack\" from \"meaningful UI layer.\" That distinction is design judgment.",[140,469,471],{"id":470},"watch-out-for-3-component-specific-values-promoted-to-global-tokens","Watch out for #3: Component-specific values promoted to global tokens.",[16,473,474],{},"The generated file mixed a clean generic scale with single-use contextual tokens:",[151,476,478],{"className":153,"code":477,"language":155,"meta":156,"style":156},"$color-dot-red: #ff5f57; \u002F\u002F macOS traffic light, used in 1 component\n$color-quote-icon: #f5f0e8; \u002F\u002F a single decorative element\n$shadow-lightbox: ...; \u002F\u002F one overlay\n$shadow-btn-glow: ...; \u002F\u002F one button state\n",[53,479,480,495,510,521],{"__ignoreMap":156},[160,481,482,485,487,490,492],{"class":162,"line":163},[160,483,484],{"class":166},"$color-dot-red",[160,486,171],{"class":170},[160,488,489],{"class":174},"#ff5f57",[160,491,420],{"class":170},[160,493,494],{"class":423},"\u002F\u002F macOS traffic light, used in 1 component\n",[160,496,497,500,502,505,507],{"class":162,"line":193},[160,498,499],{"class":166},"$color-quote-icon",[160,501,171],{"class":170},[160,503,504],{"class":174},"#f5f0e8",[160,506,420],{"class":170},[160,508,509],{"class":423},"\u002F\u002F a single decorative element\n",[160,511,512,515,518],{"class":162,"line":268},[160,513,514],{"class":166},"$shadow-lightbox",[160,516,517],{"class":170},": ...; ",[160,519,520],{"class":423},"\u002F\u002F one overlay\n",[160,522,523,526,528],{"class":162,"line":281},[160,524,525],{"class":166},"$shadow-btn-glow",[160,527,517],{"class":170},[160,529,530],{"class":423},"\u002F\u002F one button state\n",[16,532,533],{},"These don't belong in a global design system file. They belong in their component's own SCSS. A global token should be reusable across at least 3 different components, otherwise it's just a variable with extra steps.",[16,535,536,539,540,543,544,547,548,551],{},[79,537,538],{},"The same applies to hardcoded pixel values in complex components."," A ",[53,541,542],{},"top: -2px"," for optical icon alignment, a ",[53,545,546],{},"transform: translateY(-120%)"," in a CSS animation, a ",[53,549,550],{},"width: 1px"," separator, these are implementation details, not design decisions. Forcing them into tokens creates more confusion than clarity.",[140,553,555],{"id":554},"watch-out-for-4-the-prompt-assumes-one-specific-stack","Watch out for #4: The prompt assumes one specific stack.",[16,557,558,559,561],{},"The prompt creates a ",[53,560,104],{}," file with CSS custom properties, which is the right answer for a vanilla CSS or React+CSS project. But every modern frontend has its own token format: SCSS variables, Less, Tailwind config, CSS-in-JS theme objects, Vuetify theme, Material UI palette. These aren't interchangeable.",[16,563,564,565,567],{},"My site uses SCSS. I already had ",[53,566,55],{}," with a working set of variables. The prompt didn't migrate them, it built a second layer on top in a different format:",[151,569,571],{"className":153,"code":570,"language":155,"meta":156,"style":156},"\u002F* _variables.scss, what I already had *\u002F\n$color-gray-100: #f5f5f5;\n\n\u002F* tokens.css, what the prompt created *\u002F\n--primitive-gray-100: #f5f5f5;\n",[53,572,573,578,590,596,601],{"__ignoreMap":156},[160,574,575],{"class":162,"line":163},[160,576,577],{"class":423},"\u002F* _variables.scss, what I already had *\u002F\n",[160,579,580,583,585,588],{"class":162,"line":193},[160,581,582],{"class":166},"$color-gray-100",[160,584,171],{"class":170},[160,586,587],{"class":174},"#f5f5f5",[160,589,253],{"class":170},[160,591,592],{"class":162,"line":268},[160,593,595],{"emptyLinePlaceholder":594},true,"\n",[160,597,598],{"class":162,"line":281},[160,599,600],{"class":423},"\u002F* tokens.css, what the prompt created *\u002F\n",[160,602,603,606],{"class":162,"line":294},[160,604,605],{"class":166},"--primitive-gray-100",[160,607,608],{"class":170},": #f5f5f5;\n",[42,610],{},[45,612,614],{"id":613},"does-it-actually-work-for-the-next-session","Does it actually work for the next session?",[16,616,617],{},"To test it, I asked Claude Code to generate a brand new landing page from scratch, one prompt, no additional instructions.",[16,619,620,621,184,624,184,627,184,630,184,633,636],{},"Before writing a single line of code, it automatically read ",[53,622,623],{},"color.md",[53,625,626],{},"typography.md",[53,628,629],{},"button.md",[53,631,632],{},"card.md",[53,634,635],{},"badge.md",", the spec files generated by the prompt. It listed existing components, checked the patterns, then built.",[16,638,639],{},"The result used the right tokens, the right components, the right visual language. Same orange, same typography, same card style as the rest of the site. No drift. No guessing.",[16,641,642],{},"That's what the whole setup is for.",[644,645,647,648,647,653],"div",{"style":646},"display: flex; gap: 16px; margin: 24px 0;","\n  ",[19,649],{"src":650,"alt":651,"style":652},"\u002Fimages\u002Fclaude-agent-generating-page-following-ai-design-system.png","Claude agent reading the design system specs before generating a page","width: 33%; height:auto; border-radius: 8px;",[19,654],{"src":655,"alt":656,"style":657},"\u002Fimages\u002Fpage-generated-with-ai-folowing-design-system.png","Page generated by Claude following the design system","width: 67%; height:auto;border-radius: 8px;",[42,659],{},[45,661,663],{"id":662},"what-this-means","What this means",[16,665,666],{},"The prompt is excellent. It solved the real problem, inconsistency across sessions, and it works exactly as described. Run it, commit the result, and future AI sessions will produce consistent output.",[16,668,669,670,673,674,677,678,681],{},"A design system is encoded thinking: the decision to name something ",[53,671,672],{},"--color-link"," instead of ",[53,675,676],{},"--blue-500"," encodes ",[117,679,680],{},"intent",", not just value. The decision to have four z-index layers instead of eleven reflects an understanding of how the UI is actually structured. The decision to keep a one-off shadow inline rather than tokenizing it keeps the global file readable.",[16,683,684,685,688],{},"The AI tokenizes what it finds. It can't decide what ",[117,686,687],{},"should"," be tokenized.",[16,690,691,692,694,695,697],{},"That's where knowing both sides matters. A pure designer wouldn't catch the over-tokenized z-index scale. A pure developer might not question whether ",[53,693,209],{}," and ",[53,696,187],{}," opacity are the same design intent. Reading the generated output critically, as someone who understands both the design decision and the code it produces, is what makes the difference between a passing audit and an actual design system.",[16,699,700],{},"That's the designer-developer's job. And in an AI-assisted workflow, it turns out to be the most durable contribution you can make.",[42,702],{},[16,704,705],{},[117,706,707,708,712],{},"The prompt used in this article is from ",[31,709,711],{"href":33,"rel":710},[35],"Hardik Pandya's piece on LLM design systems",". Worth reading in full if you want the technical depth.",[714,715,720],"cta-block",{"external":716,"href":717,"label":718,"title":719},"true","https:\u002F\u002Fcal.com\u002Fprodju\u002F30min","Let's talk","Working on an AI-assisted product?",[16,721,722],{},"30 minutes to talk about design system structure, token strategy, or your product design challenges.",[724,725,726],"style",{},"html pre.shiki code .s4XuR, html code.shiki .s4XuR{--shiki-default:#E36209;--shiki-dark:#FFAB70}html pre.shiki code .sVt8B, html code.shiki .sVt8B{--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .sj4cs, html code.shiki .sj4cs{--shiki-default:#005CC5;--shiki-dark:#79B8FF}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .sJ8bj, html code.shiki .sJ8bj{--shiki-default:#6A737D;--shiki-dark:#6A737D}",{"title":156,"searchDepth":193,"depth":193,"links":728},[729,730,731,737,738],{"id":47,"depth":193,"text":48},{"id":97,"depth":193,"text":98},{"id":128,"depth":193,"text":129,"children":732},[733,734,735,736],{"id":142,"depth":268,"text":143},{"id":227,"depth":268,"text":228},{"id":470,"depth":268,"text":471},{"id":554,"depth":268,"text":555},{"id":613,"depth":193,"text":614},{"id":662,"depth":193,"text":663},[740,742,745],{"name":741,"url":10},"Accueil",{"name":743,"url":744},"Blog","https:\u002F\u002Fprodju.com\u002Fblog",{"name":5,"url":746},"https:\u002F\u002Fprodju.com\u002Fblog\u002Fvibe-coded-ai-design-system-cleanup",{"title":719,"description":722,"href":717,"label":718,"external":594},"2026-05-14","I built a site fast with Claude Code. The CSS was a mess of hardcoded values. Running an LLM design system audit revealed something more interesting than inconsistency.","md",null,"Working on a product with AI?",[754,755,756,757,758,759,760,761,762],"vibe coding","AI design system","Claude Code","design tokens","CSS tokens","token audit","SCSS variables","LLM design systems","hardcoded values","en",{},"\u002Fblog\u002Fvibe-coded-ai-design-system-cleanup",{"title":5,"description":749},"blog\u002Fvibe-coded-ai-design-system-cleanup",[769,7,770,756],"AI","CSS","BlogPosting","7K0dzqJ_cujQ11a6RMsKqKR63bHxIqtKNKxau_msifA",1780924826454]