01What is ARS?
ARS is the platform Aucto runs its sales on. Aucto is a marketplace for used industrial equipment, and ARS is where the staff work: the orders, the assets, the auctions, the money. Everything a buyer never sees.
It was built on Ant Design. That was a reasonable choice when the platform was small. It stopped being one.
02Overview
I designed and built the design system the platform is now made out of: 89 components, 63 design tokens, 11 text styles, two themes, across 47 screens. It runs as a live preview you can open, resize and switch to dark, not as a slide deck about a design system.
Outcomes
- 89 components across seven tiers, from a checkbox to a finished screen
- 63 tokens, every one with a light and a dark value. No component holds a hex
- 47 screens checked in both themes and at four widths
- Two shadows in the entire system, and both are for things that float
Full process
The problem
The version of Ant Design underneath ARS was old, and the platform was growing faster than that structure could absorb. This is the part that matters: it was not that the screens looked dated. It was that there was no room left to add anything.
The clearest symptom was the smallest one. Every row in the orders table had exactly two actions, edit and add media, sitting side by side. No overflow menu, no third slot. So when the business needed a new action on an order, there was nowhere to put it. Not "it would look crowded". Nowhere.
Underneath that, the cost people actually paid every day:
- Ten to thirty seconds to render a screen, with the page completely blank. No skeleton, no spinner, no layout. Just white, for up to half a minute, before anything appeared.
- Selecting rows did nothing. You could tick every checkbox in the table and no action bar appeared, no counter, no response at all.
- A form that looked like it threw away your work. People reported the asset modal closing and losing everything they had typed. It happened three times before I understood it: with the browser tab in the background, Ant's animations freeze, and the modal stays in the DOM at opacity 0 and scale 0.2. It never closed. It never finished drawing. From the chair, those are the same thing.
- A column header that said userId, in a table the client reads.
None of that is a visual problem. You cannot fix any of it by changing a colour.
Why patching the library was not the fix
The obvious move was to upgrade Ant and move on. I argued against it, and the argument was not about taste.
Upgrading buys you the newest version of somebody else's decisions. It does not tell you what a row can hold, how dense a table is allowed to be, which colour means "the machine wrote this", or where a new action goes when the business invents one next quarter. Those are the questions that were actually breaking, and a version number does not answer any of them.
So the system was rebuilt from the structures up, and the library stopped being the thing that decides.
03The decision
Seven tiers under eight headings, each one answering to the one below it: Start, Foundations, Atoms, Molecules, Organisms, Templates, Screens, Staff screens. The order is itself a rule, written into the system: "Smallest decision first." And every heading carries its count, with one constraint that tells you what kind of person built this: "No count over one item — 'Start 1' is the same disease as 0 / 125 printed on fourteen empty fields."
Foundations are the colour, type, icons and motion everything else is made of. Change one thing there and it changes everywhere. Atoms are one control on its own. Molecules are a few of those doing one job, like a field with its label, its hint and its error. Organisms are a whole region of a screen, the table or the drawer. Templates are the shape a surface has before anything is in it. Screens are the finished pages.
Every card in the system also says what the thing refuses to do. That turned out to be the useful half. A component that only shows what it can do gets used for things it should not be; one that says where it stops ends the argument before it starts.
04The colour
The logo does not get touched. The ramp is built around it. That was the constraint I set before picking anything, because a design system that quietly redraws the brand is a design system nobody adopts.
Then the numbers decided the rest:
- The navy of the logo, #092846, became the ink. As text it is where the brand reads loudest.
- It could not be the button. That navy sits at luminance 0.02, so filled with it a button reads black, not branded. The action colour is the same hue lifted to where an action lives: #0f5fa8, 6.5:1 against white text.
- The orange of the logo stays exactly as it is, and is used only for the brand, for deadlines and for things about to expire. Never as a button fill.
- The orange could not be text. On white it gives 2.7:1, which fails AA and is not a close call. The text cut of it, #a85008, gives 5.5:1.
- The orange has exactly one job, and it is not decoration. In the system's own words: "Orange is Aucto: it marks what a machine produced, and it appears nowhere else." So the question "did a person write this or did the model?" is answered by looking, not by asking.
Every colour in the system is a token with a note attached saying where it goes, and the notes are the part that survives. "A border is earned" is why there are four border tokens and not eleven. "Three weights of attention. Nothing below tertiary exists, because a fourth would be unreadable and would get used anyway" is why the text ramp stops where it does, with the quietest readable text held at 4.7:1 as the floor.
05The rules
Three, and none of them are about looks.
1. The content is the protagonist. The sidebar steps back a shade in both themes. It is navigation; it is not what anybody came to read.
2. Separate with borders, not shadows. There are two shadows in the whole system and both belong to things that float. Shadows on static blocks turn a dense table into a pile of cards, and a pile of cards shows fewer rows.
3. Density is a function, not a mood. Three heights, 32, 36 and 44. The test is blunt: a screen that shows fewer rows than the one it replaces has failed. That rule killed several layouts I liked.
06Before and after
The same job, in both systems. On the left of each is the thing a warehouse operator opens every morning: a list of assets, with their state and where they are.
It is not ugly. That is worth saying, because it is the reason this kind of platform never gets fixed: nothing here is bad enough to justify a rewrite on its own. The cost is structural. Two actions per row and no overflow. A header row that competes with the content under it. Six tabs, a banner, a second banner, and the actual work starting two thirds of the way down the screen.
Dark is not a skin over the light theme. Every token carries its own dark value, which is the only reason the dark theme holds the same contrast ratios instead of inverting the light one and hoping.
07The evidence
The claim this case rests on is that the colour was decided on measured contrast, not on taste. So here is the measurement, for every token that carries text, in both themes. These are computed from the shipped values, not quoted from memory.
| Token | Light | Dark | Minimum |
|---|---|---|---|
| text-primary | 14.96:1 | 15.29:1 | 4.5:1 |
| text-secondary | 6.20:1 | 8.12:1 | 4.5:1 |
| text-tertiary | 5.47:1 | 6.17:1 | 4.5:1 |
| brand-text | 7.96:1 | 7.83:1 | 4.5:1 |
| accent-text | 5.50:1 | 8.52:1 | 4.5:1 |
| success-text | 6.36:1 | 7.61:1 | 4.5:1 |
| warning-text | 5.27:1 | 8.71:1 | 4.5:1 |
| danger-text | 6.41:1 | 7.07:1 | 4.5:1 |
| info-text | 6.85:1 | 7.34:1 | 4.5:1 |
| border-focus | 6.52:1 | 6.24:1 | 3:1 |
Every text token clears 4.5:1 in both themes, and the focus ring clears 3:1 in both. The lowest number in the table is the one I care about most: text-tertiary at 5.47:1 is the quietest text the system allows, and it is deliberately held above the floor rather than at it, because a timestamp that only just passes in a lab fails on a warehouse laptop in daylight.
The two numbers that did the most work are the ones that changed a decision. The logo orange gives 2.7:1 on white, so it never became text; the darkened cut gives 5.5:1 and did. The logo navy sits at luminance 0.02, so as a button fill it reads black rather than branded; the same hue lifted to an action luminance gives 6.5:1 with white text, and that is the blue every primary button in the platform now uses.
And here is the foundation itself, running in this page. Every token, its note, and its value in both themes. Scroll it.
The colour foundation, live from the system. Not a screenshot: this is the same page the team reads.
08The type
Eleven styles on a 13px base, in four groups: Display, Text, UI and Data. The number that matters is 13, and it is small on purpose. This is a platform where somebody compares forty rows at once; a comfortable reading size would mean fewer rows on screen, and fewer rows is the thing the whole system is built to prevent.
Every numeric column is set in tabular figures. It is one line of CSS and it is the difference between a column of money you can scan and one you have to read.
The type scale, live. Four groups, eleven styles, 13px base.
09The parts
Two of the eighty-nine, live and real, loaded from the system rather than drawn for this page.
Buttons. Five variants, three sizes, and the states that are usually an afterthought: hover, focus, disabled, loading. The focus ring is "always 2px, always visible, never removed for looks", which is written on the token itself so nobody has to argue it in a review.
Buttons, live. Variants, sizes and states, straight from the system.
Fields. The part that decides whether a dense tool is bearable. A field is not an input: it is a label, a hint, a value, an error and the space between them, and the system ships them as one thing so the error can never land somewhere different from screen to screen.
Fields, live. Label, hint, value, error and every state they go through.
10See it running
The system is not a document. It is a running preview with every component in it, resizable to four widths and switchable between light and dark. Eighty-nine components, every one of them operable.
Open the ARS design systemOpen the colour page first. Every token there carries a note saying where it goes, and that note is the part that survives a handover.
11What is measured, not estimated
Across 47 screens, both themes and four widths: no wrapped table cells, no sideways scroll. Those are checks, run on the system, not claims about it.
Numbers in the preview are invented and the companies are made up, on purpose. It is a design system, not a data dump, and it should be openable without leaking a client.
12What I learned
The strongest part of a design system is the part that says no. I started by documenting what each component does. The cards only became useful when they also said what it refuses to do, and why, in a sentence somebody can argue with.
Accessibility is cheaper as a constraint than as a fix. The orange failing AA at 2.7:1 was a five minute decision at the start. Found a year in, after it is painted across a platform, it is a migration.
And a design system is not a library upgrade. The library was never the thing that was broken. What was broken was that nobody had written down what the platform is allowed to do, so every new requirement arrived as an argument instead of a lookup.