The Quipu product illustration — a blue marker drawing across a dark classroom board, with a French listening lesson card, a class-score chart and chips labelling the teacher, primary pupil and secondary learner

Infinitas Learning

Client
Infinitas
Role
Design System Specialist
Timeline
2025
Deliverables
Token architecture, Component library, Design-system documentation

Infinitas builds learning software for classrooms. I joined the design-system team as a specialist to move the product off Pixel, the system it had grown up on, and onto Quipu — the platform design system we built to replace it.

I owned the token architecture: how tokens were structured, what they were allowed to be called, and what a name had to settle before a component could use it. Then I rebuilt the components against it, one at a time.

What we inherited

Pixel was assembled rather than designed. Most components wrapped MUI; a few came from other libraries; some had been drawn from scratch. That is a normal way for a product to reach its third year, and it is not a criticism of the people who built it — it ships features fast, and the bill arrives later.

The bill is a vocabulary problem. When a component wraps MUI, its props describe MUI's ideas. variant distinguishes a filled chip from an outlined one, because MUI's model of a chip is a box that is either filled or not. Nobody at Infinitas was ever asking that question. They were asking whether a chip sat directly on the page or nested inside another surface — which is what actually decides how it should look. The prop that mattered did not exist, and the prop that existed was answering a question nobody had.

Values had the same problem in the other direction. With no naming rule, a developer reaching for a colour picked whatever was closest, so intent lived in people's heads and drifted every time someone new joined.

Making the name carry the intent

The rule I set for Quipu was that a token's name has to settle where it belongs and what it is for, without anyone having to ask a designer. Values are defined in Token Studio and resolve down a chain, so a component never reaches past the tier above it.

  1. Tier 1Primitive

    Raw values. Named for what they are, never for where they are used.

    blue/500
    #0095e1
    grey/900
    #111111
  2. Tier 2Semantic

    Usage context, aliased from a primitive. Role, then intent, then state, then emphasis — in that order, always.

    color/background/interactive/supportive/defaultSubtle
    blue/50#def1ff
    color/background/interactive/supportive/hoverSubtle
    blue/100#bfe2fc
    color/background/interactive/neutral/pressed
    grey/700#363636
  3. Tier 3Component

    Scoped to a component, aliased from a semantic. One edit moves every variant that uses it.

    label/smEmphasis
    fontFamily/body · fontSize/label/sm · fontWeight/labelEmphasis · lineHeight/label/sm · letterSpacing/label/sm

    A composite: five type decisions applied as one token, so a label cannot be assembled two different ways in two different components.

The chain a Quipu chip resolves through. No component reaches past the tier above it.

Read the middle tier out loud and it says what it is: color/background/interactive/supportive/hoverSubtle is a background, on an interactive element, in the supportive intent, on hover, at the subtle emphasis. There is exactly one place that token can go, and a developer picking it does not need to know which blue it resolves to.

The part I am most pleased with is the spacing. A chip's padding is not one number — the side with an icon needs less than the side with text, or the shape reads as lopsided. Most systems handle that by nudging the component. Quipu names it, so the compensation is a decision the system records rather than something each designer rediscovers.

Padding named by what it separates, not by size
TokenValueWhat it settles
spacing/padding/edgetoicon/xs6Icon side of a small chip
spacing/padding/edgetotext/xs8Text side of the same chip
spacing/padding/edgetoicon/sm10Icon side of a medium chip
spacing/padding/edgetotext/sm12Text side of the same chip
icon/size/labelSm15Icon beside a 12px label
icon/size/labelMd17.5Icon beside a 14px label

The icon sizes work the same way: an icon is sized against the label it sits beside, not against a generic scale. 17.5 is not a number anyone would pick from a t-shirt scale. It is the size that makes an icon look the same weight as 14px text, and having it in the system means it is right everywhere by default.

Rebuilding, component by component

Nothing was migrated. Every component was rebuilt against the new tokens, which is slower than re-skinning and the only way to actually leave the old system — a wrapper with new colours is still MUI's component, with MUI's props and MUI's assumptions.

Rebuilding also meant re-deciding the API. Chip came out with seven props, none of which is variant:

Component

Choice Chip

Placement
SurfaceNested
Size
smmd
State
DefaultHoverActive
Selected
TrueFalse
Disabled
TrueFalse
Avatar
TrueFalse
Leading icon
TrueFalse

Variants drawn

84

Combinations

192

Choice Chip is one of four components on the Chip page — 104 variants in total, all bound to tokens.

The gap between the two figures is the interesting one. A disabled chip has no hover. A chip cannot carry an avatar and a leading icon at once. Those combinations are not missing — they were declined, and declining them is most of what designing a component API is.

Choice Chip's variant field. Every value on it resolves through the token chain above — nothing here is a hard-coded hex.

Search, and the two kinds of chip

Search is where a component library stops being a set of shapes. The field carries three jobs at once — a search affordance, a clear, and a submit — and only one of them is always available, so the clear is a state of the field rather than a control sitting beside it.

A misspelled query, corrected below the field rather than inside it.

The correction is the decision I would defend hardest. Spektrm Fsik returns results for Spektrum Fysik, and the system says so underneath the field, as part of the result — not as a suggestion inside the input. That keeps the input free of any state that depends on what the server thinks, which means the field does not need an error variant, a suggestion variant, or a dropdown that has to be dismissed.

The media types beside it are chips, and they are a different chip from the ones in the filter panel. A filter chip has a selected state, shown as a leading check so selection never rests on colour alone. An input chip does not — it is removable instead. Deciding that is what stops a library from growing one chip with a closable prop, a selectable prop, and an invalid combination of the two.

The filter accordion

The same filter state twice: chips to undo it, checkboxes to choose it.

The panel shows one state through two components on purpose. The checkboxes inside a section are how you choose a filter; the chips at the top are how you undo one, without hunting for the section it came from. Neither is redundant — they answer different questions, and the second only exists because a long facet list makes the first unfindable.

The section header keeps its full width and its chevron whether it is open or shut, so expanding is a change of state and never a change of layout. Show more options caps a long facet so the section below it stays reachable.

Card

Card as a set of slots: media, overline, title, meta, chips, actions.

Card is the component that had to stay a contract rather than a layout: media, overline, title, meta line, chips, actions. +2 is an overflow count, not a truncated third chip, so a book in five classes and a book in one occupy the same height. The favourite star sits outside the content flow, so a long title can never push it somewhere else.

That contract is what the next section is about — the same six slots hold a teacher's worklist and a nine-year-old's homepage.

What it composes into

Quipu serves three audiences out of one library: teachers, secondary learners and primary pupils. They do not want the same product. A teacher wants density and a lot of it on screen at once; a nine-year-old wants one obvious thing to press.

Teacher material search — every component from the previous section, on one page.
Bookshelf. The card from above, in the grid it was designed for.

A secondary learner gets the same vocabulary, loosened:

Secondary learner. Tabs carry icons and a disabled state; the card drops to one action.
The primary pupil homepage. Same slots, different register.

The cards are the card: media, overline chip, title, supporting line, progress. The nav is the nav. What changed is register — scale, roundness, weight, contrast — and register is exactly what a semantic tier is for. A product that needs to look this different from its sibling is the case that decides whether the middle tier was worth building, because the alternative is two libraries and two of every future fix.

Making it hold

A design system fails quietly. It ships, it looks complete, and then it drifts, because nothing records why anything is the way it is. So every Quipu component carries two things besides its variants: a documentation card stating what it is for and linking to its Storybook build, and a dated changelog. Every entry in the log names the change and the reasoning. Chip's runs from 9 May to 30 July 2025.

One entry reads: the closable chip had a Selected state of true, when it should be false — there is no selected state for an input chip, only a filter chip. Both of those chips are on the search page above: the removable ones under Selected filters, the selectable ones in the row of media types. The correction is small, and writing it down is the difference between a system that holds its shape and one that quietly accumulates exceptions. Six months later nobody has to reverse-engineer the intent from the file.

What this demonstrates

  • Token architecture

    Structure, naming rules, and the discipline to hold them

    Role, then intent, then state, then emphasis — defined in Token Studio and enforced by review rather than by hope.

  • Component API design

    Props that describe the product, not the library underneath

    Placement replaced variant because placement is the question the product was actually asking.

  • Migration under load

    Rebuilt component by component, with nothing frozen

    Each component left MUI on its own schedule, so the platform kept shipping throughout.

  • Governance

    Documentation and a changelog on every component

    The reasoning is recorded where the component lives, so intent survives the people who set it.