Design systems
How 10 Public Design Systems Are Built, Compared From Their Repos
We read ten public design-system repositories instead of their landing pages. Token formats, package layouts and theming mechanisms differ more than the marketing suggests.
· · Diagram Studio Editorial
Ask five teams how many components their design system has and you will get five numbers, none of them comparable. The same goes for tokens: every system claims layers, and the layers carry different names. Landing pages make all ten look alike. The repositories do not.
We read ten public design-system repositories on one day, October 1, 2026, using shallow clones and three small scripts, and wrote down where each keeps its token values, how its themes reach the page, how its packages are cut and how its components can be counted fairly.
If you are choosing or auditing a design system, this comparison is for you. After reading, you should be able to tell what adopting each one entails and to run the same five checks on any system you are considering.
Direct answer
In ten public design-system repositories snapshotted on October 1, 2026, no two share a token pipeline. Token values are written as JSON (2 systems), TypeScript (5), CSS or Sass (2), or kept in a separate repository (1). Repository shape runs from one package to 249 workspace packages, and component counts depend so much on the counting rule that one "number of components" does not compare.
Ten systems, ten token pipelines
No two of the ten repositories build design tokens the same way, and the headline numbers hide most of what matters. Four findings came out of the files.
- Token values live in four kinds of place. JSON or JSON5 (Primer, Carbon), TypeScript (Fluent, Chakra, Mantine, Ant Design, shadcn/ui), CSS or Sass (Radix Themes, Material Web), or a separate repository (React Spectrum).
- Themes reach the page in three ways: shipped CSS, a runtime JavaScript theme, or a build-time macro. Which one a system uses decides whether you can change a theme at runtime.
- Package and component counts only compare under a written rule. Repository shape runs from one package (Ant Design) to 259 workspace packages (Fluent), and Carbon's React package alone gives 147 component folders against 100 for its web-components package.
- Status matters more than size. One system is in maintenance mode, one reference system is archived, and one ships code you copy instead of install.
A design token is a named design value, such as a colour, a spacing step or a duration, stored once so design tools and code read the same source. Jina Anne, who helped popularise the idea, described its origin this way:
this was a way to store our visual information as data and then, in an automated way, generate the variables or the XML data you needed or the JSON data
The Design Tokens Community Group published the first stable version of a shared token format, 2025.10, in October 2025. Two of the ten systems write tokens in that format's key style. The other eight do not.
What was cloned, and the counting rules
The ten systems are IBM Carbon, GitHub Primer, Material Web, Adobe React Spectrum, Fluent UI, Chakra UI, Mantine, Radix Themes, shadcn/ui and Ant Design. They solve different problems: a copy-and-own registry, a web-components library, an enterprise kit and a React toolkit are not competing for the same job. Shopify Polaris React was measured under the same rules but is archived, so it is a reference row.
Primer is the only system whose components and tokens sit in two repositories, so both are listed. The scripts and raw output are saved in the article's evidence folder, and the rules in each section are enough to reproduce the counts.
| System | Repository at commit | Last commit | License file | Note |
|---|---|---|---|---|
| Carbon | carbon-design-system/carbon @ c73d227 | 2026-10-01 | Apache-2.0 | Active |
| Primer (components) | primer/react @ c4189aa | 2026-09-30 | MIT | Tokens are in primer/primitives |
| Primer (tokens) | primer/primitives @ f48bc06 | 2026-09-09 | MIT | Token source and build |
| Material Web | material-components/material-web @ a6b2d26 | 2026-09-30 | Apache-2.0 | README says maintenance mode |
| React Spectrum | adobe/react-spectrum @ 57c56b8 | 2026-09-30 | Apache-2.0 | Includes Spectrum 2 package |
| Fluent UI | microsoft/fluentui @ 6116761 | 2026-10-01 | MIT | GitHub API reports no SPDX match; the file reads MIT |
| Chakra UI | chakra-ui/chakra-ui @ 9611614 | 2026-09-24 | MIT | Active |
| Mantine | mantinedev/mantine @ f38933c | 2026-09-26 | MIT | Active |
| Radix Themes | radix-ui/themes @ 1faff10 | 2026-04-11 | MIT | Last commit about five and a half months old |
| shadcn/ui | shadcn-ui/ui @ b0fcb58 | 2026-09-30 | MIT | Copy-and-own registry |
| Ant Design | ant-design/ant-design @ 820e1a8 | 2026-10-01 | MIT | Single package |
| Polaris React (reference) | Shopify/polaris-react-archive @ 3f7954a | 2026-09-09 | MIT-style text | Archived, deprecated |
One caveat: the Primer React clone warns on case-insensitive file systems because some Playwright screenshots differ only by letter case. Those are image files and do not affect any count.
Where token values are written
Token values sit in four kinds of place, so adopting a system also means adopting its build pipeline. The table gives the location, the format and one count per system as evidence that the files exist. The counts are file counts or text matches, so do not compare them across rows: a JSON5 $value and a TypeScript var(--x) line are different units.
| System | Where token values are written | Format | Evidence in the repo |
|---|---|---|---|
| Primer | primer/primitives, src/tokens | JSON5 with $value and $type keys, compiled with Style Dictionary | 61 JSON5 files, 3,453 $value matches; Style Dictionary 5 as dev dependency |
| Carbon | packages/themes/src/dtcg | JSON in the Design Tokens Community Group format; older TypeScript definitions remain | 6 JSON files with 312 $type nodes; 12 TypeScript token files still present |
| Fluent UI | packages/tokens/src (global, alias, themes) | TypeScript | 11 + 8 + 8 files; 467 tokens mapped to var(--…) names |
| Chakra UI | packages/react/src/theme/tokens and semantic-tokens | TypeScript | 18 token files, 3 semantic-token files; 107 _light value pairs in semantic colors |
| Mantine | MantineProvider/default-theme.ts and default-css-variables.css | TypeScript theme object plus a CSS file | 99 CSS Modules in @mantine/core |
| Ant Design | components/theme (seeds, maps, alias, algorithms) | TypeScript | 5 map files; 3 algorithm folders (default, dark, compact) |
| shadcn/ui | apps/v4/registry/themes.ts | TypeScript objects holding cssVars for light and dark | 24 registry:theme entries; 809 oklch() values |
| Radix Themes | src/styles/tokens | Hand-written CSS custom properties | 10 token CSS files plus 31 colour-scale files |
| Material Web | tokens/ | Sass | 2 reference, 6 system and 49 component token files |
| React Spectrum | Not in this repo: @adobe/spectrum-tokens 14.15.0 | JSON, imported by the Spectrum 2 package | One dependency; source in adobe/spectrum-design-data |
For a closer look at how JSON and Markdown token files serve AI agents, see the comparison of Markdown and JSON design tokens for agents. For lint rules over the public files in the newer DESIGN.md format, see the DESIGN.md linter write-up.
Three details matter more than the counts. Carbon's README says all themes and component tokens "have been migrated" to the Community Group format, yet twelve older TypeScript token files are still in the tree, so the repository is mid-transition.
Primer keeps tokens and components in different repositories, and its React package lists @primer/primitives as a dependency (10.x or 11.x). Radix Themes has colour-scale CSS in the repo but also depends on @radix-ui/colors, so the scales are partly sourced elsewhere.
Spectrum is the clearest case of tokens living outside the component repository. Its Spectrum 2 package takes @adobe/spectrum-tokens 14.15.0 as a development dependency. Adobe's styling documentation says the style macro runs at build time and returns a class name that applies Spectrum 2 design tokens, so token JSON is consumed when the app is built, not shipped as a theme object.
A couple years ago I designed the styling system for React Spectrum. It's inspired by StyleX and Vanilla Extract's Sprinkles, and built on bundler macros.
— Devon Govett (@devongovett) View on X
Token layers: who names them
Layer names are local conventions, not a standard. A token layer is a step of indirection: a raw value, then a meaning that points at it, then optionally a component-specific name. Four systems expose layers in their file names or docs, with four vocabularies.

Material Web's tokens/ folder holds 2 reference, 6 system and 49 component files, named _md-ref-*, _md-sys-* and _md-comp-*. Primer's token source splits into base (12 files), functional (22) and component (27), and its token documentation calls base tokens the lowest level. Ant Design's theme documentation names seed, map and alias tokens. Fluent's @fluentui/tokens has global, alias and themes folders with 11, 8 and 8 files.
Carbon and Chakra do not use the same triple. Carbon's DTCG folder separates theme tokens (one file covering four themes) from component tokens. Chakra separates base tokens, semantic tokens and recipes, which are component style definitions. Both are our reading of the folder structure.
The practical point is modest: three tiers is a common pattern, not a rule, and the names do not map one to one. What does match is the middle tier, the one that gives a value a meaning such as "surface" or "danger". That is where light and dark modes, high-contrast themes and brand overrides attach.
How a theme reaches the screen
The ten systems deliver themes in three ways, and the choice decides whether a theme can change at runtime, whether a provider component is required, and where theme values can go wrong.

Shipped CSS. The theme is a set of CSS custom properties in a stylesheet, switched by a class or attribute. Radix Themes writes .dark and .dark-theme selectors in all 31 of its colour files. Primer's React package reads data-color-mode in 5 files, and its token build defines 14 theme variants. Material Web's components reference --md-sys-color-* in 26 Sass files. shadcn/ui's theming guide describes CSS variables for semantic tokens such as background and primary.
Carbon's themes package emits custom properties for four themes: white, g10, g90 and g100. Putting Carbon, Primer and Material Web here is our reading; we did not trace every code path.
Runtime from a JavaScript theme. A provider turns a theme object into CSS variables when the app runs. Fluent's FluentProvider renders a <style> element with rules such as --colorNeutralForeground1: value. Mantine's provider injects a <style> element and sets data-mantine-color-scheme, which appears 16 times across 9 files in @mantine/core. Chakra mounts an Emotion <Global> and switches on a .dark class, with 107 _light and _dark pairs in its semantic colours. Ant Design registers styles through useStyleRegister and offers an optional cssVar mode.
Build time. Spectrum 2 is the only one of the ten that resolves tokens into atomic CSS during the build, through its style macro, with light-dark() expressions in its theme file. The trade-off, our reading, is that there is no theme object to inject at runtime, so arbitrary themes are harder to supply from application code.
Runtime generation makes the theme ordinary data, which suits per-tenant branding. Shipped CSS needs no provider and works with a plain stylesheet, but a new theme means new CSS. Neither is better in the abstract; they fit different products.
Repository shape: from one package to 259
Package counts say more about how code is cut up than about product size. We counted a workspace package as any directory matched by the workspace globs (negations honoured) that contains a package.json, plus a named, non-private root manifest. "Non-private" still includes internal tooling not marked private, such as Mantine's documentation packages, so read it as an upper bound on what is published.
| System | Workspace tooling at the root | Workspace packages (all / non-private) | Framework targets declared in this repo |
|---|---|---|---|
| Carbon | Yarn, Lerna, Nx | 38 / 28 | React, web components (Lit); also an icons-vue package |
| Primer components | npm, Turborepo | 13 / 4 | React; a styled-react compatibility package |
| Primer tokens | npm | 3 / 2 | None (tokens only) |
| Material Web | npm, one library plus a catalog site | 2 / 2 | Web components (Lit) |
| React Spectrum | Yarn, Lerna | 249 / 220 | React (205 packages declare it) |
| Fluent UI | Yarn, Nx | 259 / 131 | React (97 packages), web components (2 packages) |
| Chakra UI | pnpm | 20 / 6 | React; a Panda preset package |
| Mantine | Yarn | 32 / 30 | React; optional Emotion and vanilla-extract packages |
| Radix Themes | pnpm, Turborepo | 2 / 1 | React |
| shadcn/ui | pnpm, Turborepo | 5 / 3 | React registry components plus a CLI package |
| Ant Design | none (single package) | 1 / 1 | React |
The big numbers belong to systems that publish one package per component or concern. Fluent's React v9 components live in 60 separate react-* packages, and Spectrum splits state, accessibility hooks and styled components into separate families. That is our reading of the layout.
Framework reach is uneven too. Seven of the ten ship React only. Carbon, Fluent and Material Web include web components in the same repository. Other framework ports may exist in other repositories, which this study did not search.
Component counts and why they do not compare
One rule per source directory, applied the same way everywhere. For PascalCase layouts, count immediate subdirectories starting with a capital letter. For kebab-case layouts, count immediate subdirectories (or .tsx files where a component is a single file) not on a short deny list of helper folders such as utils, internal, hooks and theme. Material Web is counted as unique @customElement('md-…') tag names, because its folders group several elements. Fluent React counts react-* packages that contain a src/components folder.
| System | Source counted | Rule | Units |
|---|---|---|---|
| Carbon | @carbon/react components | PascalCase folders | 147 |
| Carbon | @carbon/web-components components | kebab-case folders | 100 |
| Primer | @primer/react source | PascalCase folders | 74 |
| Material Web | @material/web | unique md-* element tags | 62 |
| React Spectrum | @react-spectrum/s2 source | PascalCase .tsx files | 89 |
| React Spectrum | React Aria Components source | PascalCase .tsx files | 65 |
| Fluent UI | React v9 component packages | react-* with src/components | 60 |
| Fluent UI | Web components v3 | folders with a same-named .ts | 42 |
| Chakra UI | @chakra-ui/react components | kebab-case folders | 115 |
| Mantine | @mantine/core components | PascalCase folders | 116 |
| Radix Themes | @radix-ui/themes components | .tsx files, minus .props | 59 |
| shadcn/ui | Registry, Radix base | .tsx files in ui | 61 |
| shadcn/ui | Registry, Base UI base | .tsx files in ui | 62 |
| Ant Design | antd components | kebab-case folders | 77 |
| Polaris React (archived) | @shopify/polaris components | PascalCase folders | 121 |
Four things show why one number misleads. The same system gets two answers: Carbon's 147 against 100, Fluent's 60 against 42. Counts include parts that are not catalogue components: 6 of Carbon's 147 folders end in "Item", and Chakra's 115 include layout primitives such as box, flex and stack. Material Web counts md-filled-button and md-outlined-button as two elements. And shadcn/ui ships parallel registries whose ui folders differ by one file, toast.tsx, which exists only in the Base UI set.
If you need a comparable figure for your own shortlist, write the rule down first, count under it, and publish the rule beside the number.
Status and distribution change what you are adopting
Two systems here carry status notices that matter more than any count, and a third changes who owns the code. Nathan Curtis's line, quoted in Brad Frost's chapter on maintenance, explains why:
A system isn’t a project with an end, it’s the origin story of a living and evolving product that’ll serve other products.
MWC is in maintenance mode pending new maintainers
The notice sits in the README of a repository that had a commit on September 30, 2026. Maintenance mode is the maintainers' own description; we did not assess what it means for any release plan.
The Shopify Polaris React library is deprecated. We are no longer accepting contributions or feature requests in this repository.
The same README says Polaris Web Components were released on October 1, 2025, so a search for "Polaris design system" can return a deprecated React library. Distribution is the other hidden variable. Most systems here are packages you install and upgrade. shadcn/ui is not:
This is not a component library. It is how you build your component library.
In practice the registry components are copied into your project and become your code, with the theme as CSS variables in your stylesheet. That changes upgrade work, ownership and review, and no package count captures it.
Radix Themes shows a third pattern: its last commit was April 11, 2026, much older than the rest of the sample. A quiet repository is not by itself evidence of abandonment, and we did not examine its issues or releases. Check both before relying on it for new features.
Read the repository, not the component count
Ten public design systems answer the same question, how to keep an interface consistent, with ten different pipelines. What affects a team is where token values are written, how a theme reaches the page, who owns the code after adoption, and whether the project is still moving. The same five checks work on any system, public or internal.

- Find the packages. Read the root
package.jsonworkspaces orpnpm-workspace.yaml, list directories with apackage.json, and note which are markedprivate. - Find the token files. Search for
$value,--custom property declarations or atokensfolder, and note whether the source is in this repository or a dependency. - Find the theme switch. Search for
data-attributes and.darkselectors, and for providers that render a<style>element or call a CSS-in-JS registration function. - Count with a written rule. Pick one source directory per package, define a unit, and keep the rule next to the number.
- Check license and last commit. Read the license file, run
git log -1 --format=%cI, and look for archive or maintenance notices in the README.
Once you have those answers, draw them: a design-system diagram of packages, token tiers and the theme switch shows a team in one picture what the tables above spread across pages. The same files are also what an AI agent needs to follow a system's rules; the guide to Claude Design skills covers how that rule set is packaged, and the comparison of design-to-code mechanisms shows what each tool reads from a repository like these.
Everything above reflects October 1, 2026. The rules are meant to survive a re-run even when the numbers do not.