The plateau of sad gray icons
Everyone wants to teach AI agents good taste. I just want them to stop filling every surface with icons in soft gray containers.
I’ve been building a Claude Code prototype for a payroll product using a barebones design system. Without strong, specific UX patterns to lean on, the agent started filling the gaps by inventing its own.
Calling this inventing is generous. The agent just repeated the same pattern over and over.
Large Lucide icon in a gray container next to a heading and subheading.
It didn’t matter if it was a home card for urgent tasks, an action menu, or a paycheck breakdown. The agent kept reaching for the same pattern.
Why agents land in the bland middle ground
These generic icons may seem like a “taste problem.” People say we need to teach AI good taste so it stops defaulting to gaudy choices like purple gradients. But to me, this happens when an agent doesn’t understand what it’s building.
Claude’s own interface has a clear point of view about iconography and illustration. Icons stay lightweight and subordinate to text inside menus and other operational surfaces. Hand-drawn illustrations appear where the experience can hold more personality. You never see every action advertised with a large generic icon in a gray container.
That coherence comes from product judgment: having enough context about the user, the work, and the product to know what shape the experience should take.
Most of my design systems work is about closing the gap between an interface that’s technically correct and one that actually solves the user’s problem. A standard form can technically collect paycheck information, but it’s the wrong shape for showing how earnings, deductions, and taxes relate.
I didn’t want to just teach agents which tokens or components were valid. I wanted to teach it how to build an experience that fits the work.
When I led Polaris, much of the work involved turning years of product judgment embedded across Shopify into components, patterns, and guidance that could be maintained over time.
This project let me start from scratch and treat the AI agent as a primary user of the design system. I wanted to see what the system needed to give the agent so it could build something specific instead of falling back on generic solutions.
Don’t assume an agent can exercise judgment
Before I started, I wanted to see how an agent handled design guidance that required judgment, not just rules.
I added one principle to the claude.md file:
A color or container has to carry meaning; otherwise it is just decoration.
I asked the agent to check the product against this rule.
The result wasn’t good. The agent flagged nearly everything, like an overzealous compliance officer. Status dots and colored backgrounds on badges were out of compliance because text worked just fine. Background colors that grouped information inside of cards were all labeled as decorative. The sole survivor was the background color on an Error badge because its severity warranted one extra level of delineation.
This kind of guidance was too open-ended. The agents couldn’t exercise judgment. They just pattern-matched. If something had color or a background, they flagged it as decorative.
I had to go back to why the agents kept adding these big icons and repetitive subheadings.
They didn’t understand the work.
Yes, the menus looked bland and generic. But more importantly, they were wrong for the tasks. Someone who opens a menu many times a week needs quick recognition, not a marketing block advertising the menu’s features.
A designer makes that call because their judgment is grounded in an understanding of who is using the product and why.
I had to make that understanding explicit.
Translate judgment into structure
My next experiment was a decision tree based on an element’s semantic role and the density of the surface around it, borrowing from guidance we had used in Polaris.
I started there because the agent was making decisions one element at a time. Each choice could be technically valid, but the screen as a whole became incoherent. A Chip can be a good way to show a static attribute. But if every attribute in a busy payroll grid turns into a Chip, the result is overwhelming and hard to scan.
So I gave the agent a sequence to follow.
First: What role is this element playing?
- Recognition marker— identifies what something is.
- Status indicator— communicates state, severity, exception, count, or condition.
- Control— provides an interaction target.
- Structural container— groups related content.
Next: What kind of surface is it sitting within?
- High-density surface— menus, tables, repeated rows, compact lists, and lists inside cards.
- Medium-density surface— card headings, summary cards, dashboards, profile sections, and compact summary units.
- Low-density surface— empty states, onboarding, launchers, and guided setup.
Finally: What treatment is appropriate for that role in that environment?
For high-density surfaces, I added rules like:
- Use small outline icons.
- Size the icon to match the surrounding text.
- Keep the icon close to the label.
- Align the icon to the primary text line, not the vertical midpoint of the title and metadata.
- Do not place the icon in a circle, square, tint, or decorative container.
- Render static attributes as plain text by default. Treat them as recognition markers, not status indicators.
- Use badge treatment only for status indicators: state, severity, exception, condition, or attention needed.
This worked well when the rule was specific and easy to verify. The agent could recognize an action menu as high density, size the icon correctly, align it with the text, and use the right color.
It struggled when the decision needed more judgment. On Home, where medium-density cards had high-density lists, it couldn’t figure out which rules to follow.
I had used density as a shortcut, but it wasn’t the real decision. Density comes from choices about the user’s task, mindset, device, frequency of use, and need for comparison.
A payroll grid isn’t dense because that’s its identity. It’s dense because the user is comparing and editing many values at once. A profile page is more spacious because the user is reading and editing one resource. A home page is more expressive because the user is orienting themselves.
Density was an output of the work. I had documented the output, but not the reasoning that led to it.
I needed to show that reasoning to the agent.
Teach the agent what it’s actually building
In the next version, I described the main types of work people did in the product, and what UI shapes and behaviors fit that work.
The first decision I asked the agent to make was to classify the region’s surface:
A surface describes the kind of work happening in a region of the product. It’s not a route or a component.
- Orienting— “What needs my attention right now?” → Overview
- Locating or comparingmany records to act on or open one → Index
- Mass-entering or mass-editingvalues → Bulk edit
- Reading and editingone record’s details → Resource detail
This was useful because it let me define behavioral rules, not just visual ones. We always hear about how visual inconsistency degrades a user experience, but structural incoherence can be even worse. That’s what happens when you click an edit button and don’t know if it will edit inline, open a modal, or launch a full-screen experience. By defining these surfaces, I could specify that Bulk edit was the only surface where editing happened inline. On a Resource detail surface, editing happened in a sheet while the resource stayed as the main context.
Those behavioral rules came with visual guidance and the token values to support them. For example, a Bulk edit surface used compact spacing, type tokens, stable row rhythm, aligned values, and functional color so users could compare and update many values efficiently.
By the end, I had given the agent a decision-making sequence:
- Classify the region’s surface.
- Classify the element’s semantic role.
- Apply the interaction, visual, and token rules for that surface.
- Match a canonical product reference when one existed.
This version worked better because it described not just how surfaces should look and behave, but also how the product should function.
One rule established that a resource has one canonical presentation. A sheet can peek at that resource or expand into it, but it should not become a second summary of a page the user can already open.
Without that rule, the agent invented exactly that: a side sheet containing a summary of the employee profile before sending the user to the real one.
Canonical product examples mattered most
By the end of the work, I had three layers of design system guidance:
- Tokens and primitive components
- Product solutions selected as canonical references
- The claude.md file
Then I tested different combinations of these layers to see which drove the best UI output.
The most surprising result was that real product examples had a bigger impact than any design principle or UI rule I wrote.
Without strong, concrete examples, the agent reverted to generic forms and basic tables the moment things got complex. Tokens and prompt rules worked fine for basic screens. Basically any place where a general SaaS software pattern was sufficient. But in areas where I had refined the experience into a domain-specific shape, like treating a paycheck as an editable set of ledgers instead of a standard form, the agent needed real references.
I’ll be honest: I expected the written guidance to be more effective. I’m used to designing systems for people, where prose communicates intent and gives designers room to interpret, adapt, and invent. You don’t want to hand people a set of prebuilt solutions and ask them to repeat them, because that constrains creativity. But with agents, the balance shifted. The vetted product solutions helped them produce higher-quality work.
The system needs a loop back from the product
Design systems usually focus on pushing materials out into the product. For AI agents, product solutions also need to flow back into the system.
These solutions aren’t always abstract enough to become formal patterns. Often, they’re trusted answers to specific recurring problems: showing progress for asynchronous work, representing nested domain relationships, or supporting a specialized editing workflow.
They emerge through iteration and product judgment, but usually remain in a local Figma file, in code, or in someone’s head. They don’t reach the threshold of broad applicability required to become a system component or pattern.
People can still find them though. They can ask a colleague, “Has anyone solved this before?” Someone can point them to the strongest example and explain why it became the preferred approach.
An agent has no colleague to tap on the shoulder. Unless these solutions are deliberately identified and exposed, it sees only a collection of plausible implementations, with no way to know which one reflects the product’s latest thinking.
The system needs to tell the agent: This is the trusted solution. This is the problem it solves. This is when to use it.
My experiment suggests this feedback loop matters more for agents than I expected. The sad gray icons looked like a taste problem, but the biggest improvement came from giving the agent access to product judgment I had already worked out.