Skip to case study
Lego · 314e CorporationDesign system / Healthcare product platform / 2020–2022

Fifty-four components.
Almost none of them new.

One shared language for five healthcare products at 314e. Built on an existing library, with the care spent on clinical workflows, token architecture, and keeping five teams in sync.

Components
54
Healthcare products
5
Token tiers
2
The library sheet in Figma. Every component has its own artboard, its own states and its own name — and that name is the same word the engineer types in the editor.
Source material & project context

Captures come from the working Figma libraries and the shipped products. Two disclosures: the data grid in §08 is AG Grid's own public demo, recorded while we were evaluating it — it is not a Lego screen; and a collaborator's username has been cropped out of the repository capture in §09. Colour values shown here are read back from those captures. No adoption or performance metrics are claimed, because none were measured at the time.

01 / Where it starts

Five products. Five ways to say Save.

314e builds software for the unglamorous middle of a hospital — the record room, the training desk, the internal hiring pipeline, the integration layer. By 2020 there were five products in that portfolio, five teams, and five release calendars that had never once lined up.

None of them had done anything wrong. Each team had solved the same problems in isolation and arrived at reasonable, incompatible answers. A date picker in one product, a different date picker in the next. A destructive action confirmed in a modal here and in a popover there. Somewhere in the estate there were four spellings of the same empty state.

The tell was never how it looked. It was the arithmetic underneath. Every team paid to design a table, then paid again to build it, then paid a third time to test it. A designer moving between products lost a week to relearning. And the person who actually carried the cost was the one nobody in the room could see: the hospital staffer who used two of our products in the same shift and had to hold two grammars in their head to do one job.

The business case is throughput, not beauty. A system is worth building when the same decision is being made in more than one place. Consistency is the visible symptom; the saving is in decisions never made twice.

Healthcare compounds it. Clinical users are trained once and then left alone with the software, often at hours when nobody is available to ask. Every inconsistency between our products was a small tax collected at exactly the wrong moment.

Reconstruction, not a screenshot. Five plausible renderings of the same control, drawn to the kind of divergence the estate actually had. Each one is defensible on its own. That is the problem — there is no argument to win, only a decision nobody had the standing to make.

02 / The decision that shaped everything

We did not start from a blank canvas.

The seductive version of this project is a year of drawing: a button, then an input, then a select, then a date picker, until you have a library with your name on it. We had five products already in production and a team of five designers. That version of the project would have finished in 2024 and shipped nothing in the meantime.

So Lego was built on top of an existing enterprise library rather than beside one. Ant Design gave us the substrate: React components, a component taxonomy already argued over by people with more data than us, and — the part that actually decided it — the handful of controls that no sane team builds from scratch. A Transfer. A Cascader. A TreeSelect. A table with column pinning and grouping. Those are not components. They are small products.

What that bought was about a year. What it cost is less often admitted: when you adopt a substrate, you inherit a stranger's opinions. Their density. Their default blue. Their idea of how much air a form needs. The first real design job on Lego was not drawing anything — it was auditing decisions we had not made, and deciding which ones we were going to live with.

What we kept. The component taxonomy, the interaction behaviour, the accessibility groundwork, and every control whose complexity is in its edge cases rather than its surface.

What we overrode. Colour, type, density, iconography, and the semantics of the primary action — everything a user reads as “this is our software”, and nothing a user reads as “this is how a tree select behaves”.

What that implies. The system's own opinions had to live in tokens, not in forked components. A fork is a debt with interest; a token is a value you can change on a Tuesday.

A design system is not a drawing exercise. It is a set of decisions about which decisions are no longer yours.

03 / Token architecture

One tier says what exists. One says what it means here.

Colour was where the system either held or came apart, so it got split in two before anything was drawn.

The system tier is a closed vocabulary: the base ramps, the neutral ramp, and the data-visualisation set. It answers one question — which colours exist at all — and it is deliberately boring. Nothing in it means anything yet.

The product tier is where meaning gets assigned. colorPrimary is not a colour; it is a pointer to one step in a system ramp. Which step, and therefore what “primary” looks like, is a product's decision to make.

That indirection is the whole architecture, and it earns its keep the moment two products disagree. Jeeves resolves its primary to blue. Penknife resolves to violet. Neither forked a component to do it; both are the same Button, reading a different value from the same slot.

Why not just theme it? Because “theme” usually means a stylesheet that overrides the library, and stylesheets cannot be reasoned about. A token table can: you can diff it, review it, and hand it to an engineer without a conversation.

The test we used. If changing a colour requires opening a component file, the architecture has already failed — it just hasn't told you yet.

Tier one

The system palette

Base ramps, neutrals, and the data-visualisation set. Defines the colours that exist. Assigns no meaning, and is not allowed to.

Tier two

The product palette

Binds roles — primary, success, warning, error, navigation — to steps in tier one, according to what the product does and who is using it at 3 a.m.

The primary ramp, tier one. Step 6 is the base; every other step is a derivation of it, which is why a product can move its primary one notch without redrawing anything. Values read back from the Figma variables capture below.

Tier one · raw Colors/Base/Blue/6 #1677FF
Tier two · role colorPrimary → Blue/6
Component · property colorPrimaryHover → Blue/5
Renders as

Three hops, and only the first one holds a literal value. An engineer changing colorPrimary changes every hover, border, focus ring and disabled state that derives from it — which is the point, and also the risk.

The variable table doing the work. Every row on the right resolves to Primary/primary-n, not to a hex — so the entire informational state of the system is seven bindings, and moving the ramp moves all of them at once.

04 / Typography

Ten sizes, and a leading that is a constant, not a ratio.

Inter, for reasons that are mostly unromantic: open licence, a real range of weights, unambiguous figures, and a tall x-height that survives being set at 12px in a dense table by someone reading it at arm's length.

The base size is 14px, and it is 14px for a stated reason rather than a habit. At a typical seated distance of 50cm, a 14px cap subtends roughly a third of a degree — which is comfortably above the threshold where sustained reading starts costing effort. Fix the base, and the rest of the scale has something to be derived from.

The detail worth stealing is in the second column. Line height is not a multiplier — it is the font size plus exactly eight pixels, at every single step. Which means the effective ratio slides from 1.67 at 12px down to 1.12 at 68px, all on its own.

That is the correct direction, and a fixed ratio gets it backwards. Small text set in long lines needs air to keep the eye on the right row; a 68px headline set with 1.5 leading falls apart into unrelated stripes. A constant does the optical thing automatically, and it has the side benefit of being arithmetic a developer can hold in their head.

Why it matters more here. Clinical screens are dense by necessity — a record is not a marketing page. Most of the estate lives in the bottom three steps of this scale, so the small end had to be the well-designed end.

The rule we wrote down. Steps are not decoration. If a screen needs a size that is not on this list, the screen is wrong before the scale is.

68px76  ·  +8Discharge summary
56px64  ·  +8Discharge summary
46px54  ·  +8Discharge summary
38px46  ·  +8Discharge summary
30px38  ·  +8Discharge summary
24px32  ·  +8Discharge summary, signed
20px28  ·  +8Discharge summary, signed 14:20
16px24  ·  +8Discharge summary, signed 14:20 by A. Rao
14px22  ·  +8Discharge summary, signed 14:20 by A. Rao — base size
12px20  ·  +8Discharge summary, signed 14:20 by A. Rao — table and caption size

Rendered live in Inter at the real values, not screenshotted. Read the middle column downward: the gap never changes.

05 / The library

Fifty-four components, in five families.

The inventory below is the whole of Lego's surface area. It is worth reading as a list, because the shape of it is the argument: almost every entry is a solved problem somewhere else, and the value we added was in the tailoring, the naming and the refusal to extend it.

Eight of them are marked. Those are the ones the build notes single out as re-specified for clinical work — the transfer, the complex lists, the tooled navigation, the data tables, the heavy modals, the forms. They are also, not coincidentally, the components where Dexit, Muspell and Jeeves diverge most: a document queue, a patient record and a training asset library ask very different things of the same table.

The rest were adopted, re-tokened and left alone. That was a policy, not laziness. Every component you take ownership of is one you maintain forever, and a five-person team that owns fifty-four components owns none of them well.

Naming was half the battle. The name in Figma, the name in Storybook and the name in the import statement are the same word. Three vocabularies for one thing is how systems quietly stop being used.

Every variant is a promise. A button with five types, three sizes, an icon slot, a ghost mode and four states is already 200-odd renderings that have to hold. Adding a sixth type is never one decision.

General 3

  • Icons
  • Button
  • Divider

Navigation 7

  • Affix
  • Breadcrumb
  • Dropdown
  • Menu
  • PageHeader
  • Pagination
  • Steps

Data entry 17

  • AutoComplete
  • Cascader
  • Checkbox
  • DatePicker
  • Form
  • Input
  • InputNumber
  • Mentions
  • Radio
  • Rate
  • Select
  • Slider
  • Switch
  • TimePicker
  • Transfer
  • TreeSelect
  • Upload

Data display 18

  • Avatar
  • Badge
  • Calendar
  • Card
  • Carousel
  • Collapse
  • Comment
  • Descriptions
  • Empty
  • List
  • Popover
  • Statistic
  • Table
  • Tabs
  • Tag
  • Timeline
  • Tooltip
  • Tree

Feedback 9

  • Alert
  • Drawer
  • Message
  • Modal
  • Notification
  • Popconfirm
  • Progress
  • Result
  • Skeleton
54 components across five families ●  marked entries were re-specified for clinical use
The button, exhaustively. Type × size × icon × ghost × state, drawn out rather than described. Matrices like this are tedious to build and are the only honest way to find the combinations that do not work — a ghost dashed button in a disabled state has to be somebody's problem before it is a user's.

Button — rendered live from Lego's tokens

Inter · 32px control height

Data entry — seventeen components, the largest family and the one clinical work leans on hardest.
Feedback — nine components, every one of which has to work without colour. See §07.

06 / Dark mode

Not a preference. A night shift.

Dark mode arrives in most product roadmaps as a taste question. In a hospital it is a working condition. Wards run dim at night so people can sleep; the staff working through those hours are dark-adapted, and a full-white record screen at 3 a.m. is a flashbulb that costs them several seconds of usable vision every time they open it.

What made it affordable was the decision made three chapters earlier. Because no component held a literal colour, dark mode was not a re-skin — it was a second column in the variable table. Every row already existed. Each one just needed a value for the other lighting condition.

The one thing that does not survive the flip is the ramp itself. Inverting a palette does not work: a blue tuned to sit on white is too saturated and too bright against near-black, so the dark column is its own set of steps, chosen to hold the same relationships rather than the same values. Same role, same contrast behaviour, different number.

Try it. The switch below drives every specimen on this page. It is doing exactly what the system does — swapping one set of token values, touching no component.

Where it still leaks. Charts and third-party surfaces. Anything the system does not own has to be themed separately, and that is the honest cost of §08.

The same components, one token column apart

Pending review Signed Amended Overdue
This record has an unsigned amendment.

The icon carries the state as well as the colour — see §07.

Assign reviewer

Working days only.
Notify on sign-off

Review queue

RecordDepartmentStatusDays open
Discharge summaryCardiologySigned1
Operative noteOrthopaedicsIn review3
Consent formDay surgeryAmended6
Referral letterNeurologyOverdue11

Live components, not images. Flipping the switch rewrites nineteen custom properties; every control above re-renders from them without a single rule of its own changing.

07 / Accessibility

Colour is an indicator. Never the message.

Lego targets WCAG 2.1 AA, which in practice comes down to three habits: contrast that clears the bar, a keyboard path to everything, and semantics that survive being read aloud rather than looked at.

The contrast table below is computed in your browser, live, from the same palette the specimens use. It is worth publishing unedited, because the interesting rows are the ones that do not pass everything.

Read the semantic set — success, warning, error. Against white, not one of them clears 4.5:1, and only the error red clears 3:1 at all. That is not a flaw to be fixed by nudging the hex; it is the nature of a saturated mid-tone. It tells you what those colours are allowed to be. They can fill a shape, tint a background, or carry a border. They cannot be the text, and they cannot be the only thing distinguishing one state from another.

Which is why every component in the feedback family has an icon slot, and why showIcon is a documented property rather than a decoration. The variant sheet below draws both branches on purpose. A user who cannot separate the red from the green still gets a cross and a tick, and the state still reaches them.

Keyboard. Every interactive control is reachable and operable without a pointer, with a visible focus ring that is not the browser default and not removed.

Screen readers. State lives in ARIA, not in styling. A tag that means “overdue” says so; it does not merely turn red and hope.

The unglamorous one. Inter's figures are unambiguous at 12px. In a system where numbers are doses and dates, that is an accessibility decision wearing a typography hat.

Contrast ratios, computed live against #FFFFFF. Thresholds: 4.5:1 body text, 3:1 large text and UI boundaries.
Token Value Ratio Body 4.5:1 Large / UI 3:1 So it may be
Both branches, drawn. Info, error and success, crossed against showIcon and showButtons. The right-hand column is the reason the property exists: with the icon suppressed, colour is doing the work alone, and you can see for yourself how little is left.

08 / Dependencies

A system is defined by where it stops.

Four things sit inside our products that Lego does not own, and deciding that was as much design work as anything in the library.

The temptation, always, is to absorb them. A charting component feels like it belongs in a design system; so does a rich text editor. Both are traps. A production-grade data grid with virtualisation, column grouping, pinning and export is a multi-year product with a company behind it. Rendering one is not the hard part. Keeping it correct for a decade is.

So the boundary was drawn explicitly, and the system's job at each edge changed from building to binding: take the dependency, feed it Lego's tokens, wrap it in Lego's spacing, and make it answer to the same names as everything else. A user should not be able to feel the seam. An engineer should always be able to.

The cost, stated plainly. Four dependencies are four theming surfaces the token flip has to reach separately, and four upgrade paths that are not ours to schedule. That is the bill for not spending a decade building grids.

Tables

AG Grid

Virtualised rows, column groups, pinning, grouping and export. The single largest thing we chose not to build.

Charts

AntV

Statistical charts through G2, relationship and network graphs through G6, and flow, DAG and ER diagrams through X6 — the estate needed all three shapes.

Rich text

Lexical

Selection, composition and IME behaviour in a content-editable surface. Correctness here is measured in years of edge cases.

Query building

React Query Builder

Nested boolean rule construction, themeable enough to accept Lego's controls rather than imposing its own.

AG Grid's own public demo, captured during evaluation — not a Lego screen, and the sample data is theirs. It is here to show the scope of what was being adopted: grouped headers, a filter row, pinned columns and a live column picker. That is the thing a five-person team would have spent a year approximating badly.

09 / Design to code

A library nobody imports is a mood board.

The failure mode for a design system is not that it is badly drawn. It is that it is beautifully drawn, published, admired, and then quietly not used, while five teams carry on shipping whatever they were shipping before.

So the second half of the work was distribution. Lego ships as React and Sass. Figma holds the source of truth for design; Storybook renders the built components; Git carries both, and every spec change and every code change lands as a reviewable diff. Four front-end engineers were on the project specifically to keep that chain connected — not to implement screens, but to make sure the thing designers changed and the thing engineers imported never drifted apart.

The rest is unglamorous plumbing that decides everything. One vocabulary across Figma, Storybook and the import statement. A Figma template preloaded with the styles and the library, so the cheapest path to a new screen is the correct one. Published libraries, so an update propagates instead of being announced.

Designers on the team learned enough CSS and JS to read what shipped rather than only what was drawn. That sounds like a nice-to-have. It is the difference between a handoff and a conversation.

Make the correct path the lazy path. Adoption is never won by advocacy. It is won by being the fastest way to start.

The weak link, honestly. Reference documentation lived in Google Docs, with a zeroheight site alongside it. It was collaborative and everyone could reach it — and it was unversioned, sitting next to a library that was versioned properly. Documentation that cannot tell you which release it describes will eventually lie to somebody.

Source of truth

Figma

Variables, styles and the component library. Published, so changes propagate rather than get announced.

Built reference

Storybook

Every component and every state, rendered from the real code. The place a disagreement gets settled.

Distribution

React + Sass, in Git

One package, reviewable diffs, shared naming. The import statement is the same word as the Figma layer.

Reference

Docs

Usage, rationale and do-not-do. Collaborative, accessible, and — the flaw we would fix — unversioned.

The other half of a design system. A file tree of feature folders, and beside it the shared editor configuration — linting, formatting, spell-checking — committed to the repository so the conventions apply to everyone by default rather than by reminder. A collaborator's username has been cropped from this capture.

10 / What it came to

Five products, one grammar.

Lego went into Dexit, Muspell, Jeeves, Penknife and Zsegment, and it is still in them. Products built years apart now open with the same controls, the same spacing and the same words for the same things, and a designer moving between them starts on the second day rather than the second week.

We did not instrument any of that, and it would be easy to write a number here that nobody could check. What can be said plainly is structural: no product forked a component to change its colour; dark mode shipped as a token column rather than a project; and the inventory has stayed at roughly its original size, which for a design system is the harder achievement.

Two of the products Lego serves have their own case studies on this site. It is worth reading one of them Jeeves or Penknife with this page in mind: everything in them is drawn in this vocabulary.

Five products. Fifty-four components. Two years, and one vocabulary left behind.

Bring forward

Buy the substrate. Spend the year on the delta.

The value we added was never in re-deriving a select menu. It was in the eight components clinical work actually bends, the token architecture, and the naming. Adopting a base library was the decision that made the rest of it possible, and it is the first thing I would do again.

Keep challenging

Indirection is a loaded weapon.

One token feeding four hundred surfaces is the architecture working and the risk in the same sentence. A one-step change to a ramp can quietly break a contrast ratio three components away. We caught those by eye. A system this leveraged should be catching them in CI.

Develop next

Version the documentation like the code.

The library had releases; the documentation did not. That gap is where a system starts telling people things that used to be true. Docs belong in the repository, built from the same source, shipped with the same version number.

Product screen

Scroll to inspect the screen at full size.