Most of my career has been design systems inside large companies. Intuit, Meta, CIBC. They ended the same way every time. I would set the rules, document them properly, hand them across the boundary to engineering, and then watch them soften. A hardcoded hex here, a one-off radius there. Nobody was a villain about it. The rules simply had nothing holding them in place, and I usually found out months later with no idea why.
robr0 DS started in February 2026 as a way to fix that from the inside, on a project where the boundary did not exist. It is now 79 React components, a package on npm, and the site you are reading. The components turned out to be the easy part.
The one part I would not delegate
Day one was not a button. It was a palette. I hand-picked every colour, built the ramps, wrote the semantic names, and mapped all of it myself in Figma variables. Then three tiers, and nothing skips a tier.
--primitive-teal-07: #118AB2--color-action-primary-bg: var(--primitive-teal-07)background: var(--color-action-primary-bg)Because every colour token carries a light and a dark value, dark mode is one attribute flipping rather than a second set of components. And colour is only one category: the same treatment runs through type, spacing, radius, elevation, motion and icons. A component in this system does not invent anything. It assembles.
That foundation took weeks and it was supposed to. What I had underestimated is how much more it matters once an agent is doing the typing. Sloppy tokens do not just slow down a human developer any more. They slow the model down too, and it will cheerfully invent a value to fill the gap. Design debt became AI debt.
Then the payoff arrived faster than I expected. Once the token layer held, I stopped drawing components in Figma and started describing them in words, and five components became thirty-nine in about two weeks. I gave up strict Figma-to-code parity in the same stretch, which felt like heresy at the time and turned out to be scaffolding: useful while the structure was going up, a bottleneck once it was proven.
Then nothing happened for three months
Components in the library
Counted from git at each month-end, February to August 2026. The flat stretch is the interesting part
39 in February. The same number in March, April and May. From outside, that flat line looks like a project stalling. It is the stretch that made everything after it possible.
What I was building was the layer that tells an agent how to use the foundation. design.md owns how things look. content-design.md owns how sentences read, including the AI writing habits I kept catching in my own shipped copy. CLAUDE.md sits above both as the operating manual: where facts live, what to generate, what never to do. Alongside them came the first skills, written procedures for recurring work like scaffolding a component or auditing a page.
What makes those documents different from every design doc I had written before is the audience. Most design documentation is written to be agreed with. These get read and executed before a line of CSS exists, so vagueness that would sail through a review meeting produces garbage immediately. Writing for a machine turned out to be the hardest editing pass my own thinking has ever had.
In May the site changed shape too. It stopped being a component library with an about page and became my portfolio, with the system as the main exhibit. Six case studies went up in one push. Building pages was no longer the bottleneck. Having something to say was.
A document nothing enforces is a preference
By June I could see the hole in my own plan. I had written the rules down beautifully, and there was still nothing stopping me, or an agent working from my instructions, from quietly ignoring them. Drift is not a discipline problem. It is the default whenever the same fact lives in more than one place.
So every fact in the repo got exactly one home, and the home has a name: a registry. A small structured file holding the authoritative list for one collection. Components, tokens, skills, case studies. Nothing else in the project states those facts on its own authority, and every other surface is either generated from the registry or checked against it when the build runs.
The component registry is the clearest case. One file lists every component with its name, category and description. The navigation, the sidebar, the sitemap, the page titles, the card grid and the README count are all built from it. Register a new component and every surface updates itself. Forget to register one and the build fails and names the folder. There is nowhere in this project I can type a component count by hand, including on this page.
The same idea, pointed at CSS, is what finally made the rules bite:
A chain of validators runs before anything builds. One of them reads every line of component CSS looking for raw values: a hex code, a pixel number, anything that should have come from a token. There are exactly two ways past it. Replace the value with the right token, or write a comment directly above the line declaring the category and the reason for the exception. No override flag. No just this once.
I cannot cut a corner without leaving a signed note at the scene. Neither can anything I delegate to.
CI closed the loop in July. Every Storybook story now runs as a render test in headless Chromium, 620 of them at the time of writing, with accessibility checks that fail the build on a violation. This is the thing I never had in any system I shipped before. It is not mandated in a deck. It is mandated in the machinery.
Publishing it broke my assumptions
Late July I published the library to npm as @robr0/design-system, and it immediately exposed something being my own consumer had hidden. The whole thing was built for this site.
Of the 68 components at the time, four forwarded a ref and none extended their native element’s props. Nobody could attach a ref, pass a data attribute, or register a field with a form library. Perfectly good components that were unusable inside anyone else’s application. Eighteen were rebuilt. An accessibility pass in the same stretch fixed 49 violations, nine of which were real component bugs rather than markup slips: DatePicker claimed a grid role it had never implemented, and icon-only buttons had no accessible name at all.
That is the lesson I would hand to anyone building a system for a single product. Using your own system catches taste problems. It does not catch contract problems. Only a second consumer finds those, and publishing is the cheapest way to invent one.
Teaching the site to answer for itself
August’s project was a chat that answers questions about my work and this system, on the page it is describing. I was lead design architect on Intuit’s agent chat platform, so I had a build order I trusted: build the parts first, compose them on a scripted fake transport that emits the same events a real model will, and only then wire up a backend.
Designing against a fake model is the move I would repeat on any project. It let me judge streaming pace and scroll behaviour as pure design work, with no API cost and no latency variance muddying the read. Most of two days went into scrolling alone. My first attempt pushed the conversation up from the bottom as each turn landed, and it read as jumpy the moment a real answer streamed in at an uneven pace. What worked was inverting it: let a new turn float to the top of the viewport with a spacer sized to the exact shortfall, handed back as the answer fills it in. Nothing jumps, because the scroll range never moves.
The hard part turned out to be the corpus. I found that out by asking my own chat for my email address, and being told the site does not publish one while the address sat in plain text on the contact page. Page prose now flows into the corpus straight off the filesystem, so a new page reaches the chat on its next build with nothing to remember. Structured facts opt in explicitly. Nothing reaches the model that a page did not deliberately publish, and the build fails on anything that tries another way in.
Then I tested it like a product rather than a feature. A golden set of questions written from the seats of the people who actually visit, a recruiter, a designer, a developer, plus a few hostile ones, run end to end through the real route. The first pass scored 71 of 78. After the fixes, 77. The one still failing was its own best find: the corpus had been teaching the model about a page that no longer existed.
What it added up to
Page views by month
robertritacca.com, February to July 2026, from Google Analytics
Monthly page views went from a floor of 153 in April to 2,350 in July. The first thirteen days of August alone served 1,489. LinkedIn is the largest referrer at 297 sessions, ahead of Google organic at 189 and my own Substack at 84. The two flat months in that chart are the same two flat months in the component chart above, which is not a coincidence: I was writing documents nobody could see yet.
The package has shipped six versions since 26 July, 0.1.0 through 0.6.0, each published with provenance. This site installs it by name like any other consumer, so a packaging mistake breaks my own build before it reaches anyone else’s.
And the outcome I actually care about. In June I signed an offer as Principal Product Designer on CoreX AI at Gusto, defining how AI works, behaves and earns trust across their payroll, benefits and HR platform. I started in August. This site was the portfolio I submitted, and the system came up directly in the conversations. It is not a number I can put in a chart, but it is the one that mattered.
Everything below is a real component from the library, rendered from the same token layer as the rest of this page:
Both charts above are components too.
Same library, same tokens, installed from npm exactly the way a stranger would install it.
What changed in how I work
The outcome that surprised me was where my judgment moved. The hours that used to go into pushing pixels now go into writing specifications precise enough for an agent to execute. The thinking is the deliverable. I can try three directions in an afternoon and keep one. The handoff between design and engineering is not shortened, it is gone: the design language lives in one written spec, in version control, next to the code it governs.
None of that replaces design judgment. It moves where judgment gets applied, earlier, at the spec, before any code or visuals exist. And the rules hold now, which was the whole test. One rule nobody can break is worth more than fifty everybody agrees with.
Browse the system
Every layer described above has its own documented page. Each tile is the real reference I work from:
Foundations
The project journal keeps its own timeline current: a scheduled loop reads the git history every two weeks and consolidates it into entries. I have written up the parts that generalise beyond my repo in three pieces: Design still derisks dev, You’re not building what you think you’re building, and How to add a chat to your own site.

