Contents
  1. 0. Opening
  2. 1. Same by default
  3. 2. The register
  4. 3. The method
  5. 4. Components, Cards and Pages
  6. 5. OmoCatalog
  7. 6. The reviewed copy, and the debt
  8. 7. What stays outside, and what the final brings into line
  9. Appendix A. The tokens
  10. Appendix B. The inventory
  11. The Card drawings
  12. The frame
  13. Controls
  14. Marks and states
  15. The interactions
  16. The thirteen views
  17. Appendix C. The differences register
  18. Appendix D. Glossary

Omo Design System#

The third document, after Omo Plan (1-Omo-Plan.md) and Omo Architecture (2-Omo-Architecture.md). Written by Native Canvas, Omo's design lead, through the studio's writing process (docs/canon/writing-process.md), from précis V4. Final, 2026-11-12. Ryan pre-approved its précis and its final as gates, and reads it after publication. Its working files and reviews are in docs/archive/2026-11/omo-design-system/.

0. Opening#

Why it exists. Omo Architecture made the things consistent: one Place that every list conforms to, one list contract that every Place keeps, one Item that a task, a letter and a decision all conform to. The design didn't follow. Each Space was designed afresh, so Mail grew its own row, its own open letter, its own list header and its own copy of the selection colour beside the project's. Ryan kept asking why something was different when nothing required it (12 November). This document is where the architecture reaches the design.

What it is:

Where it ranks:

What it isn't: a brand book, a token dump, or a fourth source of truth.

How to read it:

1. Same by default#

Ryan set the rule on 12 November, reviewing the desktop prototype, where Mail's lists had their own styling and clicks. He asked for everything to follow the pattern already established (design-principles.md, "Same by default").

Components. A component is one reusable piece of Omo's interface (a row, a list card, a button), built once in OmoKit, the code library every Omo screen is drawn from. It's used wherever its job appears.

Siblings. Two places are siblings when they draw the same construct in the same view, in different Spaces or on different devices.

Content and form. Siblings differ only in content.

Each component's form is written once, in its component specification, kept beside its code (§4).

Conditions. A component specification may make a part of a component depend on something, with its reason. That's a condition, and it applies the same way to every sibling. It may depend only on:

A condition is never worded by Space, by kind of Place, or by device name. "Mail's lists have no add row" is wrong; "the add row shows where the Place takes members typed in" is right, and it hides the add row on any Place like that.

Differences and defects. A sibling may depart from its component specification only as an intended difference:

Anything else is a defect, removed rather than defended. A missing implementation is always a defect, whatever is written about it (design-principles.md, "Required behavior").

The model case is where an Item is finished.

One rule is departed from ("an Item finishes from its row's glyph"), for one reason, and everything else is the same: the glide, the fold, the counts, the Undo. It's register entry d-002.

2. The register#

The differences register (appendix C) is the one list of intended differences.

A register entry has:

A register entry for a list also gives what the list contract asks of an exception: the exact collection, the feature left out, the structural reason, its replacement, and the independent review that accepted it.

What a register entry may depart from: this document, a component specification, design-principles.md or ux.md. A departure from the architecture isn't a register entry. It's a change to the architecture, proposed on its Omo item (c9fb2142) and made in docs/canon/architecture-source/.

Ryan approves every register entry, and every condition. A treatment's token values aren't on his list: they come from the reviewed copies, and they're form.

For lists, the register has a code form:

Both are still to be built.

3. The method#

Before every change, the designer answers five questions, in writing, on the work's Omo item.

  1. Which component is this? Name it, the construct it draws, its view and its Scale.

    • If no component fits, it's a new component: below.
  2. What one thing differs between siblings? If nothing does, or the variation is already a condition, go to 5.

  3. Why? Write the reason in one sentence. If you can't, the difference goes.

  4. Is it in the register? If not, propose the register entry (or the condition) as provisional, before it's built.

  5. Did the change reach every caller? This runs on every change, including one with no difference.

    • Every caller: each place the app uses the component, from the callers list, a list generated from the code that names each one with how it's configured (§5).
    • Every copy still on the debt list, the list of copies of a component still outside OmoKit (§6). A copy doesn't receive the change by itself, so each is changed or retired in the same commit.
    • Seen in OmoCatalog, the design-only target that shows each component in each of its callers' cases (§5), in light and dark, in each treatment, a treatment being a component's sizes and type for touch, a pointer or Vision Pro (§4). Until OmoCatalog is built, the app's development build stands in (§5).
    • Tried: clicks, keys, drag and Undo, in OmoCatalog and in the app's development build, with the interaction recorded.

    This is "One note covers every instance" (design-principles.md): Ryan never gives the same note twice.

The case the method exists for. Ryan changes the row's hover. Projects get it through the row. Mail's own rows are on the debt list, so question 5 names them, and the change retires or changes each. It isn't done until both Spaces show it.

Other cases, in a line each:

A new component follows design-process.md, "Inside Omo":

4. Components, Cards and Pages#

The layers follow design-process.md's layer cake:

A component is a view (the row), a style (the button's ButtonStyle) or a modifier (the glass modifier). Supporting types, such as an enum of widths or a value model, belong to the component they serve.

Its component specification is written by Native Canvas, in OmoKit beside the code, as the component enters OmoCatalog (Omo item 64ae294b). The row's, the open item's and the list card's come first, before the first merge. It follows ux.md's format:

To that format it adds two fields:

A style or a modifier fills only the fields that apply to it.

Card and Page. Architecture 9.2 declares one Card(node:scale:), one Page(node:scale:) and Presenter.present(_:mode:) in OmoKit.

Card(node:scale:) draws, as 9.2 has it:

The presenter's other cards. The Presenter chooses a card from a result's type and returns "the Card to show" (9.2).

Not in V1: the day's CheckIn is an Item, and comes with Core Memory in Omo V2, which is paused. Its card isn't built now.

Page shows one Node in full: a Place's whole screen, or one Node opened on its own. A Space's Kit fills Pages and Cards with value models, and the app's Spaces/ folder only wires them (architecture chapter 5, "The package layout").

The seam. Every component takes one shape of input and output, as OmoKit's CONTRACT.md says:

Who owns the state. Components draw and never write. They read three things:

This is architecture 9.1's "presentation state lives in the graph". Until the cutover (the move from today's store to the Kernel's graph, Omo Plan's stage 1), the app's store holds the list's state app-wide: the Item open in place (drawerItemID, which becomes openInPlaceID), the selection, the cursor, the Item being renamed, the open menu, the scroll target, the finishing hold. The first merge adds one interim OmoKit observable, KitListState, owned by the store.

The debt list's first merge places each field.

An interaction is shared, and its effect belongs to the Space.

Selecting and opening (architecture 9.1 and 9.3; design-principles.md, "Lists work the same way").

Each Manifold case has one view, drawn by OmoKit and used by every Place shown in that case (appendix B).

The list menu has one order: Rename, Order, Colour, Collapse, then what the list is for. Ryan approved it with the audit on 12 November. Each action is a condition on a capability:

Values live in code. A token's value is in OmoKit and nowhere else. Appendix A names each token's code home, and the ones still in the app are debt.

5. OmoCatalog#

Ryan noticed that a web prototype isn't built the way the native app is, so moving a design between them costs more than it saves (12 November). Design for Omo now happens in SwiftUI.

OmoCatalog is a design-only target beside OmoKit. It isn't built yet, and nor are its checks. Native Canvas builds it (the target, its hosts, the source check and the callers-list script) on its own omo-apple branch, as Omo item 64ae294b, and the iPhone builder reviews it. It lands before or with the first merge.

Three checks hold "same by default" where memory couldn't:

What Ryan approves. For a visible step, the plan's release order needs the prototype approved first, and the prototype is now the working OmoCatalog case. On the step's Omo item, Ryan sees light and dark renders of every caller's case, and a recording of it working, and approves there.

Every builder's branch is gated side by side (Ryan, 12 November, through the chief of staff). Native Canvas compares the app with the approved prototype screen by screen, in light and dark: on the Mac, the desktop prototype (dist-45, served at http://127.0.0.1:4325); on touch, the phone prototype. Any difference that isn't a register entry or a condition fails the gate, whatever the branch was about. A builder's own description of what it built is never enough.

A design change is a code change, made on a branch in OmoKit and merged like any code. The app draws OmoKit's components, so it reaches every caller in one merge.

6. The reviewed copy, and the debt#

The parts the app ships, the ones Ryan has reviewed, mostly live in the app target, not OmoKit: the row and the open item, five list headers, a menu system, and the sidebar. OmoKit holds unused earlier originals of most of them, and Mail adds copies of its own.

The reviewed copy wins. Where a component has several copies, the one Ryan last reviewed wins, as long as it meets the current canon and accessibility rules.

The debt list is Omo Plan 1.31's worklist. It lives in docs/working/apps/omo/design-system-debt.md, because it changes as 1.31 lands. 1.31 has two parts: it moves the surfaces into OmoKit, in omo-apple, and then moves OmoKit into Mindplex.

The first merge is the first move of 1.31, ahead of the rest of that step. It moves the row, the open item and the list card into today's OmoKit in omo-apple, because the proof (phone Mail redone from shared components) needs them.

The rest of 1.31 is its stage-1 builder's, the builder Omo Plan names for stage 1, checked by Native Canvas.

Ownership is by owner, not by file.

7. What stays outside, and what the final brings into line#

What stays in its own document:

Apps outside Omo keep their own design systems.

Proposed changes to the architecture, pending Ryan on c9fb2142:

The plan's amendment, made at the final (as the chief of staff decided). Omo Plan 1.31 now says:

  1. its first merge (the row, the open item, the list card) is the iPhone builder's, ahead of the rest of 1.31;
  2. that merge needs only 0.11's open, finish and list-card gates, not the whole of 0.11;
  3. its device-branch rewrite covers only those three components, and 1.32's inventory, per-family changes and static check still follow.

The canon's index, made at the final: docs/README.md and the opening of AGENTS.md rank this document third, after the plan and the architecture.

Changes made below at the final (12 November):


Appendix A. The tokens#

Each token's value lives in code, and this appendix names its home. A value is given here only where none is in code yet. Where the older sources disagree (docs/archive/2026-11/apps/omo/design-system-facts.md, omo-apple/docs/design-handover.md, the phone's design notes, 12 November's decisions), the latest reviewed decision wins.

Colour (KitColor, Foundations/Colour.swift). Each is designed separately for light and dark, and dark is pure black.

Colour in the app, to move: the open tone (Color.openItemTone, Interface/ItemDrawer.swift), the menu's values (Interface/Floating.swift), tintedGlassFill and GlassTuning (Interface/Theme.swift).

List hues (KitHue): terracotta, saffron, moss, jade, lagoon, lapis, amethyst, rose, slate.

Type (KitFont, KitFonts, Foundations/Type.swift): Geist and Geist Mono, relative to text styles.

Layout (KitMetrics, Foundations/Layout.swift, with its extension in Foundations/Themes.swift).

Touch (KitTouch).

Materials (.kitGlass; KitGlassTuning in Patterns/Glass.swift). Liquid Glass for floating chrome and the frame; content stays solid.

Motion (KitMotion, Foundations/Motion.swift; KitMotion.land in Patterns/Drag.swift).

Haptics. One per physical act, paired with something seen, none while the person reads (architecture 9.3).

Icons and avatars (KitGlyph, Foundations/Icons.swift; KitAvatarView, Foundations/Avatars.swift). 15 to 16 pt line icons in an 18 pt box.

Words. As design-principles.md, "Words and case".

Appendix B. The inventory#

Every component has the same entry:

A line that doesn't apply to a component is left out. Its component specification is written as it enters OmoCatalog. The debt list holds its other copies.

The Card drawings#

Row

Open item

Focus card

List card

List header

Add row

Ask card, consent card, alert card

Hover card

The frame#

Page header

View switcher

Sidebar (and the drawer, its treatment where there's no room for a column)

Command bar

Bottom bar

Modal

Columns

Resize edge

Controls#

Button

Running stroke

Chip

Field

Menu

Selection bar

Segmented control

Form row

Marks and states#

Avatar

Glyph

List mark

Conversation line

Empty state, loading line, failure note, undo toast

The interactions#

Select and open: as in §4.

Finish: a task from its circle, a letter from Done (d-002), a Focus card from its Done. Either way the row and its open item fade, slip and fold together, the counts update in place, and nothing re-lays the list. On the Mac too.

Drag (KitDragLift, KitDragSlot): its effect comes from the Space's adapter, shown before the drop.

Resize: at a resize edge; widths are the viewer's.

Turn the view: the pager's spring between a Place's views. A sideways swipe turns one view per gesture, leaning at the ends, and gives way only to something that can still scroll that way.

Arrive: the lists rise one after another, only for a move the person made.

Keys: as in §4.

The thirteen views#

Each is named Kit<Case>View.

Appendix C. The differences register#

Every register entry is provisional, for Ryan's list at this document's final. The conditions in appendix B go to him in the same list.

d-002 · open item · a task's open item and a letter's open item

d-004 · modal · Settings on the Mac and on iPhone (designed, not yet built)

d-014 · withdrawn. The design lead proposes, for the Newsletters design, that This Week is a Place of Paper Items in the Board view, a list card per topic, with the paper mark as the glyph's condition. Then it departs from nothing. The proposal goes to Ryan with that design.

d-015 · row · a letter's row and a task's row

d-019 · keys · a letter and every other Item

Open questions, each an Omo decision item for Ryan:

Appendix D. Glossary#