Back / GL Design Systems
B. Sivarami Reddy
Principal Designer & UX Design Manager
Profile

Design systems engineering · Great Learning

The design system AI agents actually follow.

Great Learning had three products, one loosely held design system, and a growing share of front-end code written by AI agents. I split it into three first-class systems, extracted every component value from Figma, and moved the specs into the files agents already read. Now people and agents build from the same source.

My role
Design engineer, end to end: Figma decoder, component specs, agent skills, handoff
Scope
3 design systems · 85 components · 1 repository
Built for
Engineers, PMs & vibe coders, designers, content, and AI agents
Shipped as
System docs, Claude skills, and a 14-slide handoff deck
One intent, a primary action, shown in each system's posture. The rules come from the audience, not from a shared template.
00

One system was quietly serving three worlds

Great Learning runs a learner app, a suite of internal staff tools, and a public marketing site. In practice, all three used Magna, the learner system, and the other two improvised around it. They don't share a runtime, a component vocabulary, or an audience, so each one drifted on its own.

Then AI coding agents arrived: Claude Code, Cursor, and PMs and designers generating UI from prompts. Without an authoritative source, an agent fills the gaps with values it makes up.

When a person guesses a hex value wrong, it's one bug. When an agent invents values every time, the system erodes at machine speed.

So the brief was never "make it prettier." It was: three surfaces, three first-class systems, one source of truth that humans and agents build from in the same way.

01

Equal, but distinct. Purpose sets the rules.

The founding decision: define each system by who it serves, not by a shared component list. No abstraction layer forcing them into a fake common API. Each system's audience decided its rules, down to the breakpoint.

Magna

Learners and students

Stack
Tamagui + React Native Web, Material 3
Posture
Warm, generous, touch-first

Mobile-first at 360dp. Bottom navigation exists only at compact widths.

Jedi

Staff, faculty and ops, all day on big monitors

Stack
MUI v6 + React
Posture
Dense, flat, keyboard-driven

Desktop-first, min 1024px. Tables scroll horizontally instead of silently reflowing.

GLDS-Web

Prospective learners, the public

Stack
Rails + HTML / CSS / jQuery
Posture
Scannable, persuasive

Mobile-first is mandatory. At 360px, without scrolling, the hero answers: what is this, what do I get, what do I do.

Shared shell showcase app navigation routing search publishing AI distribution
Magna/magna · self-contained
Jedi/jedi · self-contained
GLDS-Web/glds-web · self-contained
+ a 4th system is a new folder, not a rewrite

Shared infrastructure on the outside, independent engines inside. The libraries never share code with each other.

02

The changelog of one Switch

The deepest work started with blunt feedback: the component definitions "weren't working." There were two separate failures, found months apart, and each taught a different lesson. One component, the Switch, shows the whole path.

v0

Names without substance

The original definitions named components without defining them. "A button, primary variant." No border, no height, no radius, no padding.

Asked to build a Magna button, an agent had no choice but to invent values, and invented values are off-system by definition.

Failure 1 · nothing to build from
switch.jsondefined
{
  "name": "Switch",
  "variant": "default",
  "description": "A toggle switch"
}
// colour? size? states? the agent guesses.
v1

Go to the source and read it

The only honest source of a component's values is its Figma file, not the docs and not anyone's memory. A .fig file is a ZIP around a kiwi-encoded binary, so I built a decoder that unpacks it into a node tree: about 286,000 nodes for Magna alone.

Then came the real job: finding the component library in that tree and filtering out Android system kits, flag icons, glyphs, payment logos and archived canvases. Missing a real component and including a decoy break the output in the same way.

The first extractor read values off each component's outer frame. That was fine for Buttons and empty for Switch, Checkbox and Radio, whose colour lives deeper, on the track and thumb.

~⅓ of files came out hollow
switch.jsondefined
{
  "name": "Switch",
  "width": 52,
  "height": 32
}
// outer frame only. track, thumb and outline
// are three levels down, unread.
v1.1

Owning the gap instead of footnoting it

I shipped v1 anyway, with a politely worded "limitations" note. Stakeholders pushed back hard on quality, and they were right.

Instead of defending the work, I put the evidence side by side: the flagship Button was rich, a third of the library was hollow, and three dimensions (interaction states, dark mode and per-component type) had never been specced at all. Naming that plainly put the work back on track.

A limitations note is often a known defect with cover
audit.txtside by side
Button     colour  size  radius  padding  type
Switch     ——————  size  ——————  ———————  ————
Checkbox   ——————  size  ——————  ———————  ————
Radio      ——————  size  ——————  ———————  ————

never specced, for any component:
  interaction states · dark mode · typography
v2

Deep extraction: correct, and still ignored

The rewrite walks every descendant node, finds fills wherever they actually live, reads type from text nodes, and sorts variants into three groups: canonical, interaction state, and dark mode. It runs across all three systems: 85 components, including all 43 of Jedi's, decoded from its own Figma.

The JSON was now right, and agents still didn't use it. A JSON file in a folder is passive. Nothing prompts an LLM to open it mid-task, and nothing says its numbers are non-negotiable.

Failure 2 · right data, wrong format
switch.jsondefined
{
  "track":  { "fill", "outline", "borderWidth" },
  "thumb":  { "fill", "size" },
  "states": { "hover", "pressed", "focused", "disabled" },
  "dark":   { …overrides },
  "keyboard", "contrast", "responsive"
}
// complete. sitting in /components. unopened.
v3

Change the delivery, not the data

The instinct was to add more data. That was the wrong move. From the validated JSON I generated a markdown reference: variant × size tables with hex, tokens, metrics, states, dark mode, keyboard behaviour and computed contrast.

Then I put a mandatory-read rule and a component picker inside each system's Claude skill, the file that auto-loads when an agent starts work. The JSON stays underneath as the schema-validated source that generates the markdown, so the two can't drift.

Format beat data
magna/SKILL.mdauto-loads
## Components — read before building UI
If a component is listed here, its values are law.
If it's missing, say so and flag a deviation.

| Component | Reference           |
|-----------|---------------------|
| Switch    | components.md#switch |
| Checkbox  | components.md#checkbox|
  … 1 hop from where the agent already is

Code panels are illustrative excerpts that show the shape of each definition; real values are omitted.

03

Put the truth where the agent already looks

Before the pivot, the right values were two hops away and nothing pointed to them. After it, they sit inside the one file every agent reads first, written as a rule rather than a suggestion.

v2 · data exists
Agent starts task
auto-loads
SKILL.mdDesign rules in prose
no pointer, never opened
/components/*.jsonCorrect, complete, invisible
fills the gap itself
Invented valuesOff-system UI
v3 · data is delivered
Agent starts task
auto-loads
SKILL.md Rules + "values are law" + component picker
components.md Variant × size tables, states, dark mode, keyboard, contrast ↑ generated from schema-validated JSON
builds from canon
On-system UI + coverage reportAssumptions and deviations listed, not hidden
04

Three skills, each shaped by its system

The skill is what gets the system to an agent. Every system ships one in two parts, design rules and content rules, but they aren't copies of one template. Each system's purpose decides what comes first, and what is forbidden is spelled out as carefully as what is allowed.

Magna Jedi GLDS-Web
Design rules lead with M3 role tokens, Lucide-only icons, 48dp touch targets Import from @gl/jedi, never @mui/material directly; flat, outlined surfaces; closed type scale CSS custom-property tokens; Freedom Blue reserved for CTAs; one filled CTA per band
Content rules Olympus product writing rules Olympus rules, plus student-vs-faculty labels and status language SEO and keyword-density rules: mandatory here, meaningless in an app
Never generate Hardcoded colours instead of tokens Colour-only status; happy-path-only components Fabricated urgency on marketing pages
  1. Enumerate every statebefore generating anything.
  2. Ask, assume, or buildand state which one, every time.
  3. Reuse before creatingwith existing components first.
  4. Close with a coverage reportthat lists assumptions and deviations.

The shared engagement protocol, embedded in all three skills.

05

If the system preaches it, encode it

The old docs described responsiveness and accessibility in prose, and nothing a machine could follow. Both became per-component fields. Contrast is computed rather than assumed, and AA failures are flagged inside the definition, which gave designers a concrete list to fix.

Before "Components should be accessible and responsive."
after · every componentmachine-readable
keyboard:
  Space   → toggle
  Tab     → move focus
touchTarget:  48dp minimum
contrast:     computed per variant · AA fails flagged
responsive:
  behavior:   per system rule (360dp / 1024px)

The same idea applies to content. A first attempt copied Great Learning's whole writing-review workflow into the docs, including intake questions and output formats. The fix was to keep only the rules, nested under the right section. Where a rule sits mattered as much as what it says.

06

What shipped

3first-class design systems in one repository
85components, every value traced to a Figma file
286kFigma nodes decoded for Magna alone
100%of definitions schema-valid

Each system now has a self-contained design-system doc covering colour and type, responsiveness, content rules, the AI protocol, the embedded component reference, and anti-patterns. Every definition holds exact dev values and plain-language guidance (what it is, when to use it, when not to) in the same file, so an engineer's spec and a PM's explanation can't fall out of sync.

EngineersReal, buildable values and a coverage report that turns "looks done" into a checklist.
PMs & vibe codersSkills that auto-load, so prompts produce UI in the right system without reading source.
DesignersAccessibility and behaviour decisions survive the handoff into generated code.
ContentWriting rules versioned and shipped next to the components.
AI agentsTreated as a real consumer with their own needs, which is why the format changed.
07

What I'd carry forward

01

Format beats data.

For an AI consumer, correct data in a file it never opens is worthless. The win came from putting the truth where the agent already looks and framing it as a rule.

02

Extract, never guess.

Every value traces back to a Figma file. Once a hex value is hand-waved, the system has started to drift.

03

Name the gap early.

The side-by-side of hollow and rich components moved the work further than any amount of polish. Owning a defect beats footnoting it.

Great Learning · Design systems engineering. Code excerpts are illustrative; real values are withheld. Related: Olympus (Magna) · Jedi Dashboard