Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
74 commits
Select commit Hold shift + click to select a range
4893949
docs: ADRs 0012–0017 — the design-system model
Sep 29, 2026
6a0782e
format: spec regions, styles table and <Repeat> (ADRs 0013–0017)
Sep 29, 2026
db87621
schema: derived variants, model bindings, <Repeat> expansion, design-…
Sep 29, 2026
1f82b5a
codegen: HTML/CSS and React rendered from identities; example design …
Sep 29, 2026
e1d0092
design-system: draw derived sets faithfully, prop samples, pass-throu…
Sep 29, 2026
10d0272
viewer: Contract tab binds components, parts and repeats to the headl…
Sep 30, 2026
2059fad
codegen, viewer: shadow parts (cssParts) beside element parts
Sep 30, 2026
749a575
codegen: a shadow part is not a finding
Sep 30, 2026
419fe7e
contract: booleans are states, models are shared, parts are described
Sep 30, 2026
7eb5c7a
lint: drop imports the contract move left unused
Sep 30, 2026
f6bea76
lint: the React emitter no longer imports ModelSpec
Sep 30, 2026
c5c28ab
headless: library profile, per-library bindings, and discovery
Sep 30, 2026
43ff858
viewer: design states on the canvas, a Repeat tool, and a Contract ta…
Sep 30, 2026
c2d9558
codegen from the viewer: uidx.json "codegen", a server route, a Gener…
Sep 30, 2026
49b96b1
example: resolve workspace packages to source when typechecking
Sep 30, 2026
0ccdb1a
deps: patch-level bumps for undici, brace-expansion and fast-uri advi…
Sep 30, 2026
0289c65
Repeat as an attribute on any layer (ADR 0017 §2)
Sep 30, 2026
223fc9e
Models face: declare and edit models in the viewer (ADR 0015 §1)
Sep 30, 2026
768c2d6
Drop the repeat count: the model's samples decide (ADR 0017 §2)
Sep 30, 2026
8dca71f
What an instance receives, and the tree walkthrough's fixes
Sep 30, 2026
ca50b75
A tree row with a turning chevron: what the second walkthrough fixed
Sep 30, 2026
f846d4b
For designers who never open the file: errors surface, any field binds
Sep 30, 2026
5109be4
A designer's first hour, measured against Builder, Plasmic, Framer, W…
Sep 30, 2026
fafa8a1
Token refactors see the styles table: rename, delete, uses and UIDX401
claude Sep 30, 2026
28c4ecf
Agents write the design-system model: contract, style, model and fiel…
claude Sep 30, 2026
970c2e0
A list's empty state: styled like hover, drawn on the canvas, set in …
claude Sep 30, 2026
fb8c104
A Docs face: each component's words, contract, behaviour and examples
claude Sep 30, 2026
2d06b54
codegen: a stories target — Storybook CSF from each identity
claude Sep 30, 2026
4771abf
Journeys: the first hour walked in a real browser on every canary run
claude Sep 30, 2026
9010b50
Canvas: a variant set names its columns and rows
claude Sep 30, 2026
8e0c44e
Inspector: a state says what it sets, and a value can go back to the …
claude Sep 30, 2026
8b714c6
Tokens in and out as DTCG 2025.10: `uidx tokens import|export`
claude Sep 30, 2026
07a6569
`uidx adopt`: draft identities from a headless library's manifest
claude Sep 30, 2026
a1af433
Onboarding: `uidx init --design-system`, and the overview says how to…
claude Sep 30, 2026
15a68f1
Docs face: the component as the generated code renders it
claude Sep 30, 2026
8946661
Agents building product get the design system: components, contracts,…
claude Sep 30, 2026
be828bd
`uidx lint`: application code checked against the design system
claude Sep 30, 2026
b326a62
Design review in pull requests: `uidx diff` and `uidx init --ci`
claude Sep 30, 2026
668c85e
codegen: map identities onto an existing React library; fix untagged …
claude Sep 30, 2026
53f365d
codegen: a cem target — a Custom Elements Manifest from the identities
claude Sep 30, 2026
ae8c67e
`uidx export fig`: the design system as Figma files (F2, first slice)
claude Sep 30, 2026
f7ff8fd
`uidx share`: a static, read-only site of the design system
claude Sep 30, 2026
a08dbaa
Changelog and README: the design-system workflow end to end
claude Sep 30, 2026
aa977f4
`uidx export design-md`: the design system as a DESIGN.md
claude Sep 30, 2026
c6fa1b4
Contract tab: choice properties and built-in states
claude Oct 1, 2026
37b8c25
Tokens editing: types, modes, collection names and tiers, description…
claude Oct 1, 2026
972775e
Editor: choice props on instances, component status, Duplicate, Renam…
claude Oct 1, 2026
f079b63
Docs face: write a component's description, behaviour rules and examples
claude Oct 1, 2026
e3b3925
Contract tab: accessibility, forms and composition
claude Oct 1, 2026
2119799
Pages: rename and delete from the pages rail
claude Oct 1, 2026
28b7b09
Contract tab: rename a prop and everything that reads it
claude Oct 1, 2026
4459163
Undo: one step per action across files, and renames that undo
claude Oct 1, 2026
74f6ccd
Detach instance, and a clear refusal when deleting a used component
claude Oct 1, 2026
9215900
Canvas: drop a PNG, JPEG, GIF, WebP or AVIF to place it
claude Oct 1, 2026
69f915f
Instance panel: fill a slot from a list, and empty it again
claude Oct 1, 2026
d8e2164
Styles rows in the Contract tab; rename models and fields with their …
claude Oct 1, 2026
aa2bc74
Editor chrome: a Builder-style top bar, an Insert panel, and a light …
claude Oct 1, 2026
e78d8bd
Figma-style panels with Builder-style code connection; dark by default
claude Oct 1, 2026
e2646bc
Connect tab: manage a component's code identity from the right panel
claude Oct 1, 2026
925c8c9
Add an end-to-end Shoelace example and the codegen fixes it needed
claude Oct 1, 2026
e36c76c
Fix what building a Switch in the editor, designer-style, ran into
claude Oct 1, 2026
dd10e03
Make the editor build a component the way a designer does
claude Oct 1, 2026
9cfb720
Validate injectable list items (render-prop) in the designer flow
claude Oct 1, 2026
555adfb
Make injecting list items a designer-grade flow
claude Oct 1, 2026
ef6e8e7
Set a slot up from the slot: repeat, model, default content, accepts
claude Oct 1, 2026
a0c00f0
Repeat as a template on any layer, from the Design tab
claude Oct 1, 2026
6bb8be2
Simple repeat: pick a model, bind fields like tokens
claude Oct 1, 2026
3202734
Read bindings as "This Person", bind the row as a component is placed
claude Oct 1, 2026
acd50ee
Guided tutorials: build a Button, a Switch and a List step by step
claude Oct 1, 2026
1b4e7ee
Resize an instance to the size it states; the tour waits for its popups
beharguy Oct 1, 2026
aedd262
Draw a resized instance live while its handle is held
beharguy Oct 1, 2026
587975c
Style a placed instance's outer box, insides stay a black box (ADR 0018)
beharguy Oct 2, 2026
1cebe98
Give the Contract, Connect and Code tabs one frame and short warnings
claude Oct 2, 2026
ac796cc
Fix what a review of the new code tabs found
claude Oct 2, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
6 changes: 6 additions & 0 deletions .github/workflows/canvas-canary.yml
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,12 @@ jobs:
- name: Canvas canary
run: pnpm test:canvas

# A designer's and an agent's first hour, walked in the same browser:
# overview, canvas, contract, docs, models, and an agent edit through
# the CLI that the viewer then shows.
- name: Journeys
run: pnpm test:journeys

# Uploaded whether the canary passed or failed, so a human can look at
# what it actually saw instead of trusting a byte-count heuristic blind.
- name: Upload canvas screenshot
Expand Down
2 changes: 2 additions & 0 deletions .prettierignore
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ dist
node_modules
pnpm-lock.yaml
packages/viewer/public
examples/*/generated
examples/*/vendor

.uidx
.uidx-agent
Expand Down
220 changes: 220 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,226 @@ User-visible changes are recorded here. Versions follow semantic versioning;
pre-1.0 releases may change the design format or public APIs. Release notes must
describe migrations when an existing document or integration is affected.

## Unreleased

- The inspector's Contract, Connect and Code tabs share one frame: the same
section header (title, faint meta, actions, an (i) for what the section is
for), one status line per tab that stays a single line until opened, and
one empty state with a short hint and at most one action. A fault reads the
same on every tab ("Library file not found"), offers the fix where it lives
(Choose library on Connect, Fix in Connect elsewhere) and keeps the
server's own words behind Details. Tab dots mark only real problems, the
read-only lock is one chip in the identity row, and the library chooser
and Write code moved from Contract to Connect and Code.
- **Restyle a placed component from outside (ADR 0018).** An `<Instance>` may
set its outer box (`fills`, `strokes` and their weights, `dashPattern`,
corner radii, `padding*`, `opacity`, `effects`) and `textFills`, which
colours every text the component draws, nested instances' too. The box lands
on the frame a wrapper-shaped component wraps, or on the instance a
composition holds, and on through any frame that only wraps another — so
the Shoelace example's Button takes it on its `base` part — together with a
stated size, and beneath the component's state rows, so a hover row still
wins. The Design tab shows the component's values dimmed until a use
changes one, with ↺ per row and per section and Reset all overrides; detach
bakes the restyle in; agents may set the same attributes and are refused
the inside, in a `set_style` cell as on the instance. `uidx check` warns on
an attribute of the component's inside written on an instance or onto one
by a `<Style>` row (UIDX154), and on a box value that does nothing or a
token collection named `uidx` (UIDX155); `textFills` anywhere but an
`<Instance>` is UIDX114. Generated CSS and React read the box through
`--uidx-*` custom properties (`--uidx-fill`, `--uidx-radius`,
`--uidx-padding-left`, `--uidx-text-color`, …), reset at every component
root except the text colour, and a component's stylesheet now styles what it
puts in the slots of the instances it holds, so slot text keeps the fills it
states and otherwise takes the use's colour; `uidx contract` gains
`instanceBox` and each component's `box` (with the `part` it binds) and
`laysOut`, and the `cem` target lists the hooks as `cssProperties`. Over a
shadow-DOM library, code reads only the hooks for what the design states on
the box node, so a use's box does nothing in code on the Shoelace
Checkbox, Switch and Input. **Migration:** box attributes written by hand
on an instance of a wrapper-shaped component (one with a styles table, or a
bare component around one frame or one instance) now land on what it wraps
instead of painting an invisible wrapper behind it, and layout, text and
vector attributes on an instance stop drawing. Generated stylesheets gain
the hooks and split each `border` shorthand into longhands; they draw as
before, since every fallback is the old value.
- **Fixed: a bare component with a styles table drew in a 100×100 box.** A
`<Component>` that states no layout or size hugs what it holds, but once it
declared a visual prop each of its variants sat in the engine's default
100×100 frame with the content in its corner, and so did every use of it.
The variants now hug as the component does.
- **Fixed: token refactors missed the styles table.** A token used only in
`<Style>` rows showed 0 uses, a rename left those rows dangling, a delete
did not inline them, and `uidx check` stayed green while codegen emitted an
undefined CSS variable. Dependents, rename/delete plans, usage counts and
UIDX401 now read `<Styles>`.
- **Agents write the design-system model.** `uidx apply`, MCP `uidx_apply` and
`eval` gain `set_style`, `declare`, `set_model` and `set_field` (eval:
`style`, `declare`, `undeclare`, `model`, `field`). The shipped skills teach
identities (contract, `<Styles>`, models, repeat, behaviour), with a new
`uidx-design-system` skill; UIDX153 warns when `<Variant>` trees share one
anatomy and differ only in values.
- **Agents building product use the design system.** MCP `uidx_components`,
`uidx_component`, `uidx_tokens` and `uidx components`, `uidx component
<Name>`, `uidx tokens list`: contracts, import and usage lines, resolved
tokens with CSS variables. `uidx init` writes a uidx block into AGENTS.md
(and CLAUDE.md when present).
- **`uidx lint`** checks application code against the design system: colour
literals a token names, raw elements a component stands for. Exits 1 on
findings.
- **`uidx diff --base <ref>`** reports design-system changes with breaking ones
marked; **`uidx init --ci`** writes a pull-request workflow that checks,
lints, posts the diff and uploads a review site.
- **`uidx share`** writes a static, read-only site: page pictures, component
docs, tokens.
- **Tokens as DTCG 2025.10:** `uidx tokens import|export`, resolver documents
for modes.
- **`uidx adopt <custom-elements.json>`** drafts an identity per element a
headless library ships; **`uidx init --design-system`** writes a starter
system (tiered tokens, a Button); the overview of a document with no
component shows how to start.
- **`uidx export fig`** writes one Figma file per page (first slice of F2).
- A list's **`empty` state**: styled like `hover`, drawn on the canvas with no
rows, `[data-empty]` in CSS, set by the React wrapper.
- A **Docs face** per component: intent, contract, behaviour, examples drawn by
the renderer, where it is used, and the component as the generated code
renders it (`/__uidx/preview`).
- The canvas **labels variant sets** (column and row values); the inspector
says **what a state sets** and offers Reset to the base; a state's root no
longer offers x and y.
- Codegen: a **`stories`** target (Storybook CSF from samples, visual values and
examples), a **`cem`** target (Custom Elements Manifest 2.1.0 with an
`x-uidx` extension), and **`codegen.react`** to map identities onto an
existing React library through typed adapters.
- Fixed in codegen: frames that hug their width emit `inline-flex` (a Button
no longer stretches); components with no `implements` render a `<div>` with
their class and data attributes and a matching ref type; `models.ts` is a
module when no model is declared; a state row targeting a `<Slot>` selects
`[data-slot]`.
- `pnpm test:journeys` walks the first hour in a real browser (overview,
canvas, contract, state reset, docs, code preview, models, an agent edit)
in the canvas canary job.

### Earlier in this release

- The design-system model (ADRs 0012–0017). A `.uidx` file may now carry a
`## Contract` (props, events, slots, form, accessibility; `<State>` only for
states the element produces itself, since a visual boolean prop is a state
and `hover`/`focus`/`active` are the browser's; `<Part>` descriptions are
optional, the tree's `part=` bindings being the declaration),
`## Behavior` guidelines, `## Models` and `## Examples` after its visual
contract, and a `<Styles>` table beside the root. Models are shared across
pages and named by a prop's type (`Contact`, `Contact[]`). Any layer of a
component repeats over a list with `repeat="{items}"` — or `{person.tags}`
inside an outer repeat, which makes a tree — naming its item with `as`; the
model's samples decide how many rows the canvas draws; a repeat on a
`<Slot>` is the one consumers fill. Declarations only: nothing
in the file computes. The viewer draws a styles table as the variant set it
derives, resolves `{item.field}` bindings to a model's samples and `{prop}`
to the prop's `sample` or default, expands a repeat into sample rows (a
model declared on another page included), and resolves an instance's
`props={{ label: '{label}' }}` in the consuming component's scope. A `<Slot>` that states no size hugs its placeholder. `uidx check` audits the regions against each
other and the tree; `uidx contract` prints them as JSON.
- `@uidx/codegen` and `uidx codegen`: HTML/CSS and React rendered from the
identities over headless custom elements, with props, events and slots from
the contract — a plain slot as a `ReactNode` prop, a repeating slot as its
list prop plus a render prop named after it, a repeat elsewhere as a `map`
in place — and each contract checked against the headless library's
`custom-elements.json`.
- `examples/design-system`: Checkbox, Field, CheckboxField, Button and a
contact list rendered end to end over `@hwc/components`, with the generated
output committed and checked for drift.
- A state is designed on the canvas: the derived variants a styles table draws
are selectable, and a change to one writes the matching cell of its style
row (the new `style` patch op), while a change to the default combination
edits the base tree. The inspector names the state it is editing. A base
layer chosen in the rail is drawn by the default state, so that is what the
canvas highlights, and the default state chosen on the canvas is the base
layer in the inspector. The toolbar gains a Repeat tool that repeats the
selected layer over the first list its component's contract can place; the
first row of a repeat is the layer itself, the rows after it its echoes.
- A **Models** face beside Tokens and Fonts: every model of the document
with the page that declares it and the components that receive it, edited
in place (description; fields with type, key, optional, sample and words),
a new model declared on a chosen page — a `models` page being the shared
one — and models a contract names but nobody declares offered for
declaring. The new `model` and `field` patch ops write one `<Model>` back
canonically and invert. A sample changed there redraws every repeat of it,
and a repeat's row in the Contract tab links to its model.
- An instance inside a repeat shows what it receives: the Contract tab lists
its definition's contract props with the item each one receives, inferred
by type from the enclosing repeats the way the code target passes it, and
lets the use say otherwise (`props={{ node: '{child}' }}`). A text binds to
an item's field or a component prop from a picker beside its content, and
a `{…}` typed there is written as a binding rather than resolved away. A
slot the tree has and the contract lacks is listed with a Declare button;
a repeat on a container holding one row offers to move onto the row. Make
component warns when the page already has one, since the two would share
its contract. A new slot hugs what fills it; placing an instance with a
slot selected fills that slot, and the picker leaves out the component
the instance would sit inside. Removing the last declaration removes the
empty `## Contract` heading; `uidx codegen` takes directories as `check`
does.
- Reviewed against Builder.io, Plasmic, Framer, Webflow, Figma and Penpot,
for a designer's first hour: a text is typed where it is drawn (the Text
tool opens it for typing, a double-click reopens it; Escape or ⌘Enter
keeps the words); a right-click menu offers the toolbar's actions with
their shortcuts; the status strip says "Saving…" and "All changes saved";
an empty page says how to start; the "Inside …" breadcrumb names the
container by its current name; the Contract tab leads with properties
and slots and folds the code binding and code library under "for
developers"; the overview counts pages "with content" and "problems"
instead of "drawable" and "parse"; the file-error overlay leads with what
happened in plain words; the Models face explains itself without jargon.
- For designers who never open the file: a page that cannot be drawn keeps
its last frame and says why in a banner instead of going blank; a number
field commits on Enter as well as on leaving it; declaring a prop offers
its type, and a boolean comes with `default={false}`; a checkbox, a
number or the visibility pill binds to an item's field or a prop from a
picker beside it, as a text does, and any `{…}` committed from the inspector is
written as the binding it is.
- From a second walkthrough, a tree with a disclosure chevron: a visual prop
draws its state set at once, rows or no rows, so the first style row is
written by selecting the state on the canvas rather than by hand; rotation
is a look a state may set (a chevron turns when a row opens), rendered as a
transform by the CSS target; a use's bound value chooses the state on the
canvas, so rows repeat open or closed as their samples say; a text bound to
a boolean or a number is spelled out rather than crashing the layout; the
canvas receives the shared model index it was meant to (a list's model on
another page now draws its samples on the canvas, not only in thumbnails);
a pen-drawn vector stays stroked in the code targets; the Repeat tool names
a nested repeat's item after its list (`child` for `{item.children}`) and
offers the enclosing item's lists first; a sized component's layout reads
Free, so choosing Column writes it; an instance's bound prop is no longer
called mistyped.
- Fixed: a component edited structurally kept its contract (the incremental
patch path dropped the spec until a full parse), and `uidx check` no longer
reports `repeat="{items}"` or `{item.field}` as unknown component
properties.
- The Contract tab edits the contract: each prop, event, slot, state and
part opens into a small form (description, type, default, sample, flags;
a slot's `accepts`), a row adds one, and "Fill from library"
declares what the implemented element exposes and the contract lacks. The
new `contract` patch op writes one declaration in canonical form.
- `uidx.json` gains an optional `codegen` (`out`, `targets`): `uidx codegen`
needs no `--out`, and the Code tab's Write code button renders the code
targets from the server into that folder, reporting what it wrote.
- The inspector gains a **Contract** tab beside Design. A component chooses
the headless element it implements from the library; its parts are bound to
layers from the component's list or from the layer's own row, and any layer
picks the list it repeats over and its item's name. The tab shows the
file's contract beside the bindings and counts the parts still to bind.
`uidx.json` gains an optional `"headless"` path to the library's
`custom-elements.json`, served to the viewer and used by `uidx codegen` when
`--manifest` is absent. Parts a library exposes as shadow parts (`cssParts`)
are offered beside element parts and marked; the code target styles them
through `::part()`. `headless` may also be an object with a `profile` (how
the library reflects props, its own states and parts) and `bindings` (its
names per component), so one design renders over libraries that spell
things differently; when nothing is named, the Connect tab's Project
section offers the libraries the project's dependencies ship and writes the
choice.

## 0.1.6 — 2026-09-25

- The first `uidx dev` sets up the workspace itself when the install script did
Expand Down
Loading
Loading