Building an AI-first design system for Moodle

PROJECT OVERVIEW
An open-source design system for a learning platform with over 500 million users, built so people and AI agents can use it without guessing.
↑ 95% accuracy in AI prototyping, 88% adoption across Moodle designers, and high-fidelity work 3x faster.
IMPACT SO FAR
95% accuracy in AI prototyping.
88% adoption across Moodle designers. That’s the median share of interface work done with MDS.
High-fidelity work 3x faster. Based on a survey of the design team.
About 30% of each designer’s year saved.
Capacity equal to about 7 extra designers, without adding headcount.
Handoff over 67% faster using the Figma MCP.
Defined a new, uplifted design language that users, theme developers and leadership have embraced, leading to new investment and partnerships.
THE STARTING POINT
Moodle has been around since 2002, built over the years by Moodle HQ as well as a huge global community of contributors. My expectation was that I’d find many different implementations of the same component across the codebase, and I was right. 54 versions of a checkbox, 48 versions of a dropdown, and I was only scratching the surface.
And unlike most products, we can’t just tell everyone to stop using the old ones. Moodle is open source. Community plugins live outside our control, so we can’t mandate deprecation. The system had to be good enough, and easy enough to find, that people would actually want to use it.
On top of that, people were starting to build with AI. So we didn’t just need a design system. We needed one AI could use too.
BUILDING AN AI-FIRST DESIGN SYSTEM, WHAT DOES THAT MEAN?
When we started work on MDS, we wanted it to be “AI-first”. But what did that actually mean in our context?
It came down to two goals. First, enable AI-generated UI, built on something underneath that’s actually worth generating from: our design system. Second, enable AI-assisted handoff, by designing components in a way that’s AI-consumable, with parity between design and code so agents can map between Figma and the codebase without guessing.
To get there, I went on a research journey to understand how components needed to be designed and built for them to be AI-legible. Did they need to be designed differently? What else, besides best practice, did I need to do?
At the time, mid 2025, there was very little out there on this, so there wasn’t much to take inspiration from. So I started testing. I gave agents our components and docs, asked them to build things, and watched where they went wrong.
A pattern showed up quickly. Agents kept mixing up components that look similar but behave differently, like Switch and Toggle, Alert and Toast, or Search Input, Combobox and Text Input. Moodle-specific components like Choicebox got squashed into standard patterns. The structure was there, but without the intent, agents just guessed.
After a lot of testing, I put together 15 principles, grouped around two clusters:
Clarity: a component should have one unambiguous interpretation.
Parity: design and code should speak the same language at all times.
I landed on clarity and parity because they address the two places ambiguity enters a system. Clarity is about making sure there’s enough information for a component to be understood correctly. Parity is about making sure that information stays consistent as it moves between design, code, documentation and tooling.
What I found was that many of these principles weren’t completely new. They were design system practices we already believed in, aimed at removing ambiguity. The difference was the standard they needed to be held to.

Structure and intent
To give agents the full picture, we split context across two layers. Figma and the npm package carry the structure: anatomy, token bindings and variant logic. zeroheight carries the intent: usage guidance, accessibility contracts and semantic token references right inside each component’s States section. That’s what makes our documentation machine-actionable, not just machine-readable.

DIFFERENT TYPES OF AGENTIC CONSUMPTION
We’ve built parseable components and context. What now?
AI can now accurately consume our components and their intent, but different workflows and teams consume it differently. Internally, we have direct access to our tools. But there are developers all over the world building for Moodle without that same access.
A Moodle HQ designer working in Figma can use the Figma agent to generate UI, with the local component library and the zeroheight MCP for context. If they’re prototyping in code with something like Claude Code, they use the zeroheight MCP for context and the npm package for components.
A member of the community doesn’t have that access, so we came up with what we like to call “cold retrieval”. Someone using Claude Code or Codex can simply prompt their agent to build something with the Moodle Design System, and it finds the open npm package or GitHub repo on its own. The components and their context live there, so either one works as a single source of truth, no MCP needed.

BUILDING A NEW DESIGN LANGUAGE FOR A PLATFORM THAT’S BEEN AROUND SINCE 2002
Most of Moodle’s UI was legacy and fragmented. So how could I set a new design language without breaking the experience and familiarity millions of people rely on? Short answer: best practices and familiar patterns.
Redesigning something that already looks and performs great is hard. You need a lot of research just to find the gaps. That wasn’t Moodle’s situation. The gaps were obvious and the patterns were outdated. So rather than invent something new, I made a deliberate call to lean on modern, familiar patterns people already recognise, and apply them consistently.



And the response has been great. Community members told us they’d waited a long time for this. Theme developers said the new patterns make their lives much easier. Users have praised the new experience as something they value, and leadership got on board with new investment and partnerships off the back of the uplift.
“It’s still clearly Moodle and you can still see the structure’s the same, but it’s got a fresh appearance and I think that’s nice.”
“…it doesn’t seem like a different product, it just seems like additional features or refined features and a fresher look to something which is already understood. And that’s important.”
“I get more immediate info at a glance: not only the name of the activity, but also the type. It looks somewhat prettier as well.”
THE PROCESS: BUILDING A NEW DESIGN SYSTEM FROM SCRATCH
We had our AI-first principles and our design language north star. Now we needed our ways of working to reflect them.
From fragmentation to a single source of truth
So how do you design a single source of truth when there are 50 versions of the same thing?
The MSE, or Minimum Standard[s] of Evaluation as we like to call it. It’s our discovery process. Every component we audit has to go through the MSE before we start design, where we answer questions across three categories, split into must haves and nice to haves:
Accessibility: Does it meet WCAG AA contrast? Does it rely on colour alone for meaning?
Usability: Are the interactions predictable? Is it responsive?
Consistency: Does it align with the new visual language? Does it look and behave the same across different use cases?
Each component also gets a disambiguation table, so agents (and people) know exactly what it is and what it isn’t. Once all that’s answered and we understand the legacy, we know exactly how to design a single source of truth that covers every existing use case.


Our definition of done
What does done actually look like for us? We have a checklist for design and one for development. Both build on our AI-first principles, e.g. component property metadata, tokens throughout, semantic and unambiguous layer names, every state as a variant.
How do we make sure they’re met? I built a Figma plugin, MDS Lint. It lints each component against our definition of done and suggests fixes whenever it doesn’t hit a score of 100%. It’s powered by the Claude API and a skill that encodes our definition of done, and it also measures how readable a component’s metadata is to AI.

HOW DO WE ENABLE ADOPTION THROUGH CULTURAL CHANGE?
By getting people involved. We can’t mandate adoption across an open-source community, so we have to earn it.
We host Product Advisory Group meetings where community members and Moodle staff can see what we’ve been up to, ask questions, make requests and discuss ideas in a welcoming environment. We also run migration workshops and share our strategy across different teams. Their thoughts and suggestions have shaped a lot of how we display our documentation and prioritise our work.
Guidance right where designers work
Documentation only helps if people actually read it, so I built MDS-buddy, a Figma plugin that brings our docs into the canvas. It’s powered by an AI model through an API, with the zeroheight MCP connecting it to our documentation, so every answer is grounded in how we actually say components should be used. Designers can select a frame and get usage guidance based on what they’ve designed, or ask it any usage question without leaving Figma.

How do we measure adoption?
We built an adoption dashboard where we can actively see coverage and the number of component and token instances being used across the codebase. We also track the components and tokens being used in Figma internally, plus documentation traffic.

WHAT I LEARNED
A few things stuck with me along the way:
AI-first doesn’t mean AI-only. It means building every component so AI can understand it without someone filling in the gaps. People still make the calls.
If it’s clear enough for an agent, it’s clear enough for anyone. Our community can’t rely on tribal knowledge, and neither can AI. The work that made MDS legible to agents made it easier for every new contributor too.
Structure isn’t enough without intent. Agents could read our components just fine. Where they struggled was knowing when and why to use them, especially components that look alike. That’s why intent became its own layer.
Where context lives matters as much as what it says. Our docs site looked complete to people but was invisible to agents. Putting context in the package and the repo, where agents already look, is what made cold retrieval work.
In open source, you earn adoption. You can’t enforce it. Coverage isn’t about listing every use case, it’s about making the system easy enough to find and extend that people reach for it.
This will hide itself!