Loom Base: our own design system, built with our own engine

The best test of a design-system engine is a design system it built. Loom Base is Loom's own system. It is the base for Loom's own screens, which we are moving onto it now, and we offer it to teams that have no system yet. It was generated by the same engine that converts client systems, and it is held to the same checks. Partial

Status: version 1, beta. Every component is marked beta and does not count as stable until a person reviews it against the definition of ready. Loom Base is MIT-licensed and builds as an npm package and a shadcn-compatible registry; it is not yet published.

Loom Base covers the components most products need

  • 69 component contracts, with behaviour written as data: the 31 near-universal components found in our benchmark of product design systems, layout primitives, six AI interface patterns, and components for human approval of AI proposals.
  • React and React Native from the same contracts. On the web, components build on React Aria Components for accessibility. On native, they use React Native's own accessibility features.
  • Tokens in three tiers (primitive, semantic, component) in DTCG format, with colour in OKLCH and a hex fallback, and modes for light, dark and high contrast, density, reduced motion and Japanese typography.
  • Page templates, such as a list and detail view, a settings page, a wizard, sign-in, empty and error pages.
  • Docs for people and agents: one page per component, a typed component catalogue and an llms.txt file.

Every component is checked in every mode

These numbers are read from the coverage report (packages/base/COVERAGE.md) each time this page is built.

  • 453 of 453 parity tests pass, on web and native. Native forms: 65 full, 3 partial, 1 without one.
  • 1,113 accessibility runs with axe, 0 failing: all 371 Storybook stories, in every colour mode.
  • 2,436 of 2,436 declared colour pairs pass WCAG 2.2 AA contrast, in light, dark and high contrast. A brand layer that drops a pair below AA fails the build.
  • 26 of 26 keyboard scripts pass in a real browser.

Loom Base: 69 components by check

Diagram
ComponentWebNativeWeb testsNative testsStoriesaxeKeyboard
AILabelWeb: yes (complete)Native: yes (complete)Web tests: 3/3 (complete)Native tests: 2/2 (complete)Stories: 2 (complete)axe: pass (6 runs) (complete)Keyboard: focus ring pass (complete)
AIStatusWeb: yes (complete)Native: yes (complete)Web tests: 4/4 (complete)Native tests: 3/3 (complete)Stories: 8 (complete)axe: pass (24 runs) (complete)Keyboard: focus ring pass (complete)
AlertWeb: yes (complete)Native: yes (complete)Web tests: 4/4 (complete)Native tests: 3/3 (complete)Stories: 8 (complete)axe: pass (24 runs) (complete)Keyboard: focus ring pass (complete)
AppHeaderWeb: yes (complete)Native: yes (complete)Web tests: 2/2 (complete)Native tests: 1/1 (complete)Stories: 2 (complete)axe: pass (6 runs) (complete)Keyboard: focus ring pass (complete)
AppShellWeb: yes (complete)Native: partial (partial)Web tests: 2/2 (complete)Native tests: 1/1 (complete)Stories: 3 (complete)axe: pass (9 runs) (complete)Keyboard: focus ring pass (complete)
AvatarWeb: yes (complete)Native: yes (complete)Web tests: 4/4 (complete)Native tests: 3/3 (complete)Stories: 7 (complete)axe: pass (21 runs) (complete)Keyboard: – (none)
BadgeWeb: yes (complete)Native: yes (complete)Web tests: 3/3 (complete)Native tests: 2/2 (complete)Stories: 8 (complete)axe: pass (24 runs) (complete)Keyboard: – (none)
BandScaleWeb: yes (complete)Native: yes (complete)Web tests: 2/2 (complete)Native tests: 1/1 (complete)Stories: 2 (complete)axe: pass (6 runs) (complete)Keyboard: – (none)
BoxWeb: yes (complete)Native: yes (complete)Web tests: 2/2 (complete)Native tests: 1/1 (complete)Stories: 12 (complete)axe: pass (36 runs) (complete)Keyboard: – (none)
BreadcrumbsWeb: yes (complete)Native: yes (complete)Web tests: 2/2 (complete)Native tests: 1/1 (complete)Stories: 2 (complete)axe: pass (6 runs) (complete)Keyboard: focus ring pass (complete)
ButtonGroupWeb: yes (complete)Native: yes (complete)Web tests: 2/2 (complete)Native tests: 1/1 (complete)Stories: 5 (complete)axe: pass (15 runs) (complete)Keyboard: focus ring pass (complete)
ButtonWeb: yes (complete)Native: yes (complete)Web tests: 13/13 (complete)Native tests: 9/9 (complete)Stories: 15 (complete)axe: pass (45 runs) (complete)Keyboard: 4/4 scripts, focus ring pass (complete)
CardWeb: yes (complete)Native: yes (complete)Web tests: 3/3 (complete)Native tests: 2/2 (complete)Stories: 5 (complete)axe: pass (15 runs) (complete)Keyboard: – (none)
CheckboxGroupWeb: yes (complete)Native: yes (complete)Web tests: 6/6 (complete)Native tests: 4/4 (complete)Stories: 5 (complete)axe: pass (15 runs) (complete)Keyboard: 1/1 scripts, focus ring pass (complete)
CheckboxWeb: yes (complete)Native: yes (complete)Web tests: 8/8 (complete)Native tests: 7/7 (complete)Stories: 9 (complete)axe: pass (27 runs) (complete)Keyboard: 1/1 scripts, focus ring pass (complete)
CitationWeb: yes (complete)Native: yes (complete)Web tests: 2/2 (complete)Native tests: 1/1 (complete)Stories: 2 (complete)axe: pass (6 runs) (complete)Keyboard: focus ring pass (complete)
ClothWeb: yes (complete)Native: yes (complete)Web tests: 4/4 (complete)Native tests: 2/2 (complete)Stories: 4 (complete)axe: pass (12 runs) (complete)Keyboard: focus ring pass (complete)
ColourBlockWeb: yes (complete)Native: yes (complete)Web tests: 3/3 (complete)Native tests: 2/2 (complete)Stories: 6 (complete)axe: pass (18 runs) (complete)Keyboard: focus ring pass (complete)
ComboBoxWeb: yes (complete)Native: yes (complete)Web tests: 6/6 (complete)Native tests: 4/4 (complete)Stories: 4 (complete)axe: pass (12 runs) (complete)Keyboard: 1/1 scripts, focus ring pass (complete)
ConfirmActionWeb: yes (complete)Native: yes (complete)Web tests: 4/4 (complete)Native tests: 3/3 (complete)Stories: 7 (complete)axe: pass (21 runs) (complete)Keyboard: focus ring pass (complete)
DialogWeb: yes (complete)Native: yes (complete)Web tests: 11/11 (complete)Native tests: 7/7 (complete)Stories: 8 (complete)axe: pass (24 runs) (complete)Keyboard: 3/3 scripts, focus ring pass (complete)
DisclosureWeb: yes (complete)Native: yes (complete)Web tests: 6/6 (complete)Native tests: 4/4 (complete)Stories: 4 (complete)axe: pass (12 runs) (complete)Keyboard: 1/1 scripts, focus ring pass (complete)
DividerWeb: yes (complete)Native: yes (complete)Web tests: 3/3 (complete)Native tests: 2/2 (complete)Stories: 5 (complete)axe: pass (15 runs) (complete)Keyboard: – (none)
DrawerWeb: yes (complete)Native: yes (complete)Web tests: 6/6 (complete)Native tests: 5/5 (complete)Stories: 6 (complete)axe: pass (18 runs) (complete)Keyboard: 1/1 scripts, focus ring pass (complete)
EmptyStateWeb: yes (complete)Native: yes (complete)Web tests: 3/3 (complete)Native tests: 2/2 (complete)Stories: 3 (complete)axe: pass (9 runs) (complete)Keyboard: focus ring pass (complete)
ErrorSummaryWeb: yes (complete)Native: yes (complete)Web tests: 3/3 (complete)Native tests: 1/1 (complete)Stories: 3 (complete)axe: pass (9 runs) (complete)Keyboard: focus ring pass (complete)
FeedbackControlsWeb: yes (complete)Native: yes (complete)Web tests: 4/4 (complete)Native tests: 3/3 (complete)Stories: 5 (complete)axe: pass (15 runs) (complete)Keyboard: focus ring pass (complete)
FormFieldWeb: yes (complete)Native: yes (complete)Web tests: 4/4 (complete)Native tests: 2/2 (complete)Stories: 4 (complete)axe: pass (12 runs) (complete)Keyboard: – (none)
GridWeb: yes (complete)Native: partial (partial)Web tests: 2/2 (complete)Native tests: 1/1 (complete)Stories: 7 (complete)axe: pass (21 runs) (complete)Keyboard: – (none)
HeadingWeb: yes (complete)Native: yes (complete)Web tests: 3/3 (complete)Native tests: 2/2 (complete)Stories: 8 (complete)axe: pass (24 runs) (complete)Keyboard: – (none)
HealthBandWeb: yes (complete)Native: yes (complete)Web tests: 3/3 (complete)Native tests: 2/2 (complete)Stories: 6 (complete)axe: pass (18 runs) (complete)Keyboard: – (none)
IconWeb: yes (complete)Native: yes (complete)Web tests: 3/3 (complete)Native tests: 2/2 (complete)Stories: 11 (complete)axe: pass (33 runs) (complete)Keyboard: – (none)
LinkWeb: yes (complete)Native: yes (complete)Web tests: 6/6 (complete)Native tests: 4/4 (complete)Stories: 5 (complete)axe: pass (15 runs) (complete)Keyboard: 1/1 scripts, focus ring pass (complete)
LooseThreadsWeb: yes (complete)Native: yes (complete)Web tests: 2/2 (complete)Native tests: 1/1 (complete)Stories: 2 (complete)axe: pass (6 runs) (complete)Keyboard: focus ring pass (complete)
MastheadWeb: yes (complete)Native: yes (complete)Web tests: 3/3 (complete)Native tests: 2/2 (complete)Stories: 3 (complete)axe: pass (9 runs) (complete)Keyboard: – (none)
MenuWeb: yes (complete)Native: yes (complete)Web tests: 7/7 (complete)Native tests: 4/4 (complete)Stories: 6 (complete)axe: pass (18 runs) (complete)Keyboard: 2/2 scripts, focus ring pass (complete)
NumeralWeb: yes (complete)Native: yes (complete)Web tests: 3/3 (complete)Native tests: 2/2 (complete)Stories: 3 (complete)axe: pass (9 runs) (complete)Keyboard: – (none)
PageHeaderWeb: yes (complete)Native: yes (complete)Web tests: 2/2 (complete)Native tests: 1/1 (complete)Stories: 2 (complete)axe: pass (6 runs) (complete)Keyboard: – (none)
PaginationWeb: yes (complete)Native: yes (complete)Web tests: 7/7 (complete)Native tests: 5/5 (complete)Stories: 5 (complete)axe: pass (15 runs) (complete)Keyboard: 1/1 scripts, focus ring pass (complete)
PopoverWeb: yes (complete)Native: yes (complete)Web tests: 5/5 (complete)Native tests: 3/3 (complete)Stories: 8 (complete)axe: pass (24 runs) (complete)Keyboard: 1/1 scripts, focus ring pass (complete)
ProgressBarWeb: yes (complete)Native: yes (complete)Web tests: 4/4 (complete)Native tests: 3/3 (complete)Stories: 5 (complete)axe: pass (15 runs) (complete)Keyboard: – (none)
ProofDiffWeb: yes (complete)Native: yes (complete)Web tests: 2/2 (complete)Native tests: 1/1 (complete)Stories: 2 (complete)axe: pass (6 runs) (complete)Keyboard: – (none)
ProviderWeb: yes (complete)Native: yes (complete)Web tests: 3/3 (complete)Native tests: 2/2 (complete)Stories: 11 (complete)axe: pass (33 runs) (complete)Keyboard: – (none)
RadioGroupWeb: yes (complete)Native: yes (complete)Web tests: 7/7 (complete)Native tests: 5/5 (complete)Stories: 8 (complete)axe: pass (24 runs) (complete)Keyboard: 1/1 scripts, focus ring pass (complete)
SealWeb: yes (complete)Native: yes (complete)Web tests: 3/3 (complete)Native tests: 2/2 (complete)Stories: 4 (complete)axe: pass (12 runs) (complete)Keyboard: – (none)
SectionNavWeb: yes (complete)Native: yes (complete)Web tests: 2/2 (complete)Native tests: 1/1 (complete)Stories: 1 (complete)axe: pass (3 runs) (complete)Keyboard: focus ring pass (complete)
SelectWeb: yes (complete)Native: yes (complete)Web tests: 7/7 (complete)Native tests: 5/5 (complete)Stories: 5 (complete)axe: pass (15 runs) (complete)Keyboard: 1/1 scripts, focus ring pass (complete)
SideNavWeb: yes (complete)Native: yes (complete)Web tests: 2/2 (complete)Native tests: 1/1 (complete)Stories: 2 (complete)axe: pass (6 runs) (complete)Keyboard: focus ring pass (complete)
SidenoteWeb: yes (complete)Native: yes (complete)Web tests: 2/2 (complete)Native tests: 1/1 (complete)Stories: 1 (complete)axe: pass (3 runs) (complete)Keyboard: – (none)
SkeletonWeb: yes (complete)Native: yes (complete)Web tests: 3/3 (complete)Native tests: 2/2 (complete)Stories: 5 (complete)axe: pass (15 runs) (complete)Keyboard: – (none)
SkipLinkWeb: yes (complete)Native: none (none)Web tests: 2/2 (complete)Native tests: 1/1 (complete)Stories: 2 (complete)axe: pass (6 runs) (complete)Keyboard: 1/1 scripts, focus ring pass (complete)
SourcesListWeb: yes (complete)Native: yes (complete)Web tests: 2/2 (complete)Native tests: 1/1 (complete)Stories: 2 (complete)axe: pass (6 runs) (complete)Keyboard: focus ring pass (complete)
SpinnerWeb: yes (complete)Native: yes (complete)Web tests: 3/3 (complete)Native tests: 2/2 (complete)Stories: 5 (complete)axe: pass (15 runs) (complete)Keyboard: – (none)
SpreadWeb: yes (complete)Native: yes (complete)Web tests: 3/3 (complete)Native tests: 2/2 (complete)Stories: 5 (complete)axe: pass (15 runs) (complete)Keyboard: – (none)
StackWeb: yes (complete)Native: yes (complete)Web tests: 3/3 (complete)Native tests: 2/2 (complete)Stories: 17 (complete)axe: pass (51 runs) (complete)Keyboard: – (none)
StoryHeaderWeb: yes (complete)Native: yes (complete)Web tests: 3/3 (complete)Native tests: 2/2 (complete)Stories: 3 (complete)axe: pass (9 runs) (complete)Keyboard: – (none)
StreamingTextWeb: yes (complete)Native: yes (complete)Web tests: 4/4 (complete)Native tests: 3/3 (complete)Stories: 7 (complete)axe: pass (21 runs) (complete)Keyboard: focus ring pass (complete)
SwitchWeb: yes (complete)Native: yes (complete)Web tests: 10/10 (complete)Native tests: 8/8 (complete)Stories: 7 (complete)axe: pass (21 runs) (complete)Keyboard: 2/2 scripts, focus ring pass (complete)
TableWeb: yes (complete)Native: yes (complete)Web tests: 5/5 (complete)Native tests: 4/4 (complete)Stories: 8 (complete)axe: pass (24 runs) (complete)Keyboard: focus ring pass (complete)
TabsWeb: yes (complete)Native: yes (complete)Web tests: 5/5 (complete)Native tests: 3/3 (complete)Stories: 2 (complete)axe: pass (6 runs) (complete)Keyboard: 1/1 scripts, focus ring pass (complete)
TagWeb: yes (complete)Native: yes (complete)Web tests: 3/3 (complete)Native tests: 2/2 (complete)Stories: 6 (complete)axe: pass (18 runs) (complete)Keyboard: focus ring pass (complete)
TextAreaWeb: yes (complete)Native: yes (complete)Web tests: 5/5 (complete)Native tests: 4/4 (complete)Stories: 6 (complete)axe: pass (18 runs) (complete)Keyboard: focus ring pass (complete)
TextFieldWeb: yes (complete)Native: yes (complete)Web tests: 9/9 (complete)Native tests: 6/6 (complete)Stories: 15 (complete)axe: pass (45 runs) (complete)Keyboard: focus ring pass (complete)
TextWeb: yes (complete)Native: yes (complete)Web tests: 3/3 (complete)Native tests: 2/2 (complete)Stories: 10 (complete)axe: pass (30 runs) (complete)Keyboard: – (none)
ToastRegionWeb: yes (complete)Native: yes (complete)Web tests: 3/3 (complete)Native tests: 2/2 (complete)Stories: 2 (complete)axe: pass (6 runs) (complete)Keyboard: focus ring pass (complete)
TooltipWeb: yes (complete)Native: partial (partial)Web tests: 3/3 (complete)Native tests: 2/2 (complete)Stories: 5 (complete)axe: pass (15 runs) (complete)Keyboard: 2/2 scripts, focus ring pass (complete)
VisuallyHiddenWeb: yes (complete)Native: yes (complete)Web tests: 2/2 (complete)Native tests: 1/1 (complete)Stories: 1 (complete)axe: pass (3 runs) (complete)Keyboard: – (none)
WarpRailWeb: yes (complete)Native: yes (complete)Web tests: 3/3 (complete)Native tests: 1/1 (complete)Stories: 2 (complete)axe: pass (6 runs) (complete)Keyboard: 1/1 scripts, focus ring pass (complete)
WeaveDraftWeb: yes (complete)Native: yes (complete)Web tests: 2/2 (complete)Native tests: 1/1 (complete)Stories: 2 (complete)axe: pass (6 runs) (complete)Keyboard: focus ring pass (complete)
  • complete
  • partial
  • none or not applicable
Generated from packages/base/COVERAGE.md at build time. unit parity tests from the run of 2026-10-25; browser axe from 2026-10-25; browser keyboard from 2026-10-25.

Every decision about it is a record

Loom Base is governed the way we govern client systems. Its decisions are files in the repository, each with its reason:

Decisions, as records

8 records
  1. 0001Loom ships its own base design systemDecided by the owner
  2. 0002The base system is called "Loom Base" for nowDecided by the owner
  3. 0003Every component has component tokensDecided by the owner
  4. 0004React Aria Components on the web, React Native core APIs on nativeApplied in v1; follows the research recommendation and the build brief
  5. 0005Kasuri is the default theme; Calm stays as an alternativeProposed, then rejected by the owner in review ("has almost no personality"). Kasuri is kept as an alternative theme; [0006](0006-atelier-press-default-theme.md) proposed Atelier Press as the default; [0007](0007-selvedge-default-theme.md) (accepted) makes Selvedge the default and supersedes this record
  6. 0006Atelier Press is the default theme and shell; Kasuri and Calm stay as alternatives (now an alternative theme)Superseded as the default by [0007](0007-selvedge-default-theme.md): the owner chose Selvedge (25 Oct 2026). Atelier Press is kept as an alternative theme (`tokens/themes/atelier-press.theme.json` with `motifs/press.css`); its nine components stay in Base
  7. 0007Selvedge is the default theme and shell; Atelier Press, Kasuri and Calm stay as alternativesAccepted by owner choice, 25 Oct 2026
  8. 0008Loom Base is MIT-licensed and ships as an npm package and a shadcn-compatible registryDecided by the owner
Listed from packages/base/decisions/ when this page is built. Each record has its reason and its consequences.

Fully themeable, with limits that protect people

A brand layer can change the look within lock levels. Some things are locked for every brand: the focus ring, the minimum target size and the colours that mark AI content. Status colours can be extended but not changed. Most colour, type and radius roles can be replaced, with a recorded reason. Spacing defaults can be tuned freely.

No system yet? Start from Loom Base

For a team without a design system, Loom forks Loom Base, themes it from your app's own colours and type in light and dark, prunes it to the components your app uses, and writes a migration plan. It also seeds a loom/ folder with governance sized to your team. You own the result from day one. Partial

What does not work yet: the starter has run on one pilot app so far.

Some of it is not finished, and we say which parts

We say this plainly. Some native forms are partial (3 components) and 1 component has no native form. There is no message catalogue yet, no visual regression baselines, no Figma library and no release tooling. On our own audit, Loom Base scores 73 out of 100, "Taking shape". The audit measures structure, governance and tooling; the gaps are mostly content tooling, release tooling, a docs site and Figma.

We audit ourselves with the same rubric

Loom's own control area was built before our standard existed, and it scores 26 out of 100 on our own audit, "Loose threads". We are rebuilding it on Loom Base. We publish both numbers because a strict standard has to apply to us as well. Working

We ask clients to trust our engine. Loom Base is how we earn that trust first.

Questions

Can we use Loom Base if we already have a design system? You keep your own system and your own look. Loom Base is for teams without a system, and for Loom's own screens.

Who owns a system started from Loom Base? You do, from day one. It lives in your repository.

Does every system started from Loom Base look the same? No. Loom Base has a default look, and a brand layer replaces it with yours, within the lock levels.

Start with a conversation about your system.

We read your system, show you what we found, and agree with your team what to fix first.