Work
Design systems / Open source · Designed during tenure · Aug 2022–May 2026

Making the answer reusable.

Design-system work at Learning Equality was not about producing components; it was about reducing the number of product decisions each new Kolibri feature had to reinvent — under constraints most design systems never meet.

Role
Senior Product Designer
Timeline
Aug 2022 — May 2026
Company
Learning Equality
A system sheet headed “KDS tokens, used verbatim”: twelve named colour tokens with their hex values, and the type roles — Noto Sans for product text, Faculty Glyphic for statements — with the primary, secondary, flagged and approved action styles.
The tokens and type roles, used verbatim. Nothing is invented: every colour is a named KDS token, and the learner-activity and resource palettes stay reserved for their own meanings rather than being used decoratively.

Contributor to the open-source Kolibri Design System (500+ components); listed in Kolibri AUTHORS.md. I also designed a new 100+ component design system for learningequality.org and led the public website redesign with in-house engineering.

01

The constraint set

Kolibri runs in learning environments with old devices, small screens, shared computers, unreliable connections, multilingual content and Arabic RTL interfaces. A pattern that assumes fast networks or modern hardware fails quietly in exactly the places the product exists to serve. The design system had to encode those constraints so individual features did not have to rediscover them.

What follows is the system as a set of settled decisions: type, the card, direction, status and loaders, tables, the atoms built on top, and what all of that removed from feature work.

Interactive: select a constraint to see the rule it produced, the patterns that carry it and the design cost it introduced.
02

Type as named roles, not sizes

Noto Sans is the system face because of script coverage, not preference: the same ten roles have to carry Latin, Arabic and Devanagari. A role is chosen by what the text is doing — headline, heading, subheading, body, caption, action label, annotation — not by how large it should look. That is what makes a translated string safe: the role already states how much room it may take and what happens when it runs out.

The rules travel with the role. A headline is used once per screen and truncates to two lines, never one. An action label sizes to its content, so no button in the system is fixed-width, and stays at two words in English so translations hold one line. Annotation is the floor for contrast at its size and is never the sole carrier of a requirement. Ten named roles, one family, zero per-feature sizes.

The ten roles carrying Arabic, with the Body role’s rules open: a measure capped near sixty characters, the minimum size for any instruction, never used for a label that must fit a fixed space.
03

The side panel, composed rather than designed

Seven real panels ship from one library — publish a channel, select recipients, assign a course, filter users, submit to the Community Library, review a submission, change status — so the panel is the clearest case of the system replacing decisions rather than adding components. Its architecture is three zones: a header that owns the title, navigation and dismiss; a body that is the only scrollable region; and a footer that owns the actions and the summary.

A feature composes a panel from eight body blocks rather than designing one. That is what settles the questions a feature would otherwise answer differently each time: the spacing between unrelated block types, which element owns the scroll container, and whether a block may set its own margin. Below 600px the panel becomes a full-screen sheet with stacked footer actions, and direction mirrors as a property of the pattern.

The header and footer are stress-tested against real strings rather than English placeholders: Kolibri ships in dozens of languages, and a header has to hold a translated string two or three times longer than its source without pushing the dismiss control off the panel. One line, ellipsis, the full string in a tooltip, and the dismiss control never the element that moves.

Interactive: select a panel zone to see what it owns and which product questions it settles.
Interactive: tick body blocks to compose the panel and inspect how each block keeps its own label, spacing and empty state.
A header that cannot break is worth more than a shorter label. The variants are stress-tested with real translated strings, and the rule holds: one line, ellipsis, the full string in a tooltip, and the dismiss control is never the element that moves.
04

One card, five states, one content structure

The card does not change shape to change meaning. Five states ship in the product — default, update, update with search, draft, import — and each one adds or removes a single region: a version badge, an alert naming exactly what will be downloaded, a search-context bar, a progress region reporting finished groups rather than a percentage. Thumbnail, title, description and the four metadata figures hold their position in all five, so a channel stays recognisable whether it is installed, updatable, a draft or mid-import.

The card got smaller over time by removing regions rather than by shrinking type. A draft shows its version as an em dash because an empty cell would read as a bug; an importing card keeps its place in the list because import never moves a channel to a separate screen.

The card pattern with its five states as tabs and the regions each one changes listed beside it. The constant is the channel’s identity; only the regions above it are added.
The same card in the product, as a channel list on a device. Installed content, available updates and drafts are separated by state, not by shape. Redrawn from the shipped product with representative data.
05

What mirrors, and what must not

Direction is a property of the pattern, not a final-pass correction. Reading order, alignment, panel placement, back navigation, action order and indentation all mirror for Arabic. Progress bars, time and media controls, numerals, file sizes and charts with a time axis do not, because they encode a direction of travel rather than a reading order.

A progress bar that mirrors tells an Arabic-reading educator that the download is going backwards. Keeping that distinction inside the pattern is the whole point: a feature team asked to decide it will decide it differently each time.

The side panel in Arabic, with the two lists that settle direction once: what mirrors and what must not. The progress bar and its numerals stay left to right.
The rule applied in the product: the channel row mirrored, chip order and button order flipped, the Latin title held left-to-right inside an Arabic line. Redrawn from the shipped product with representative data.
06

Status, loaders and confirmations

The status pattern has the widest reach in the system, because every surface in Kolibri waits on something. It settles what loading, success, warning and failure mean and that they are never conveyed by colour alone; what each state announces to assistive technology and whether it interrupts; what a long-running task shows when the connection drops mid-way; and that a failure must name its cause and offer exactly one recovery action.

Loaders follow from the hardware. Progress is reported as a count of finished items when the total is known, never as a fabricated percentage, and nothing in the system depends on animation to be legible, because a spinner on old hardware can drop to two frames a second. Confirmations follow the same discipline: built as a set, so the irreversible actions read as one pattern rather than four dialogs.

One pattern, traced from specification into two products. The same state carries the same meaning, the same announcement and the same behaviour when the connection drops, whether it is a channel downloading or a lesson in progress.
The status pattern carrying a long-running import: running, failed, retried from where it stopped, verified. The failed row keeps its transferred bytes on screen. Redrawn from the shipped product with representative data.
The confirmation pattern as a set, from the coach dashboard: destructive-timing actions are red and show the live completion count, starts carry reminder fields, every one names the unit.
07

Tables at two densities

The table pattern settles column behaviour when a translated header is twice its English width, what an empty cell means and that it is never left blank, and which columns survive at the narrowest supported width. The user table in device management and the courses table in the coach dashboard are the same pattern at two densities.

The dense end: bulk user administration with selection, permissions and pagination in one table. Redrawn from the shipped product with representative data.
The lighter end: the courses table, one row per course with status, a mastery bar and an in-row toggle. Redrawn from the shipped product with representative data.
08

Atoms built on the system

A product surface extends the system with atoms, not with exceptions. The coach dashboard added five on top of the tokens — the tri-colour mastery bar, the risk pill, the active-unit and active-test chips, and the test-readiness confirmation — and the mastery triad is the only place three colours sit together in the system. The bar is specified down to the width at which it becomes a chip without losing its headcount.

The mastery bar as a system atom: how it is built from the tokens, and how it degrades from full width to a chip.
09

What the system removed from feature work

Not components produced: decisions a feature no longer has to make. Each pattern settled something once, on behalf of every feature that came after it.

The patterns against the surfaces they serve, with what the status pattern settled. Measured outcomes for the period are reported in the content-import study, not re-attributed here.
10

Status and boundary

Designed during my Learning Equality tenure, Aug 2022 to May 2026, as shared infrastructure for the Kolibri product family. The measured outcomes attached to this period — the usability-testing results for content import — are reported in that case study, not re-attributed to the design system. Every channel, learner and count inside these frames is representative data drawn for the frame.

Design systemsAccessibilityOpen sourceLocalisation
Next case study
What happens when one successful product wants to become a category?