Conversation
Identity, contract, behaviour guidelines, models and examples, variants as renders, collections and code targets. Theming beyond modes is recorded as a future task. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
A .uidx file may carry ## Contract, ## Behavior, ## Models and ## Examples after the visual contract, and a <Styles> table beside the root. They are lowered into doc.spec as declarations, never scene nodes; the tree the viewer draws is unchanged. <Repeat slot count> multiplies one instance for the design tools. The emitter prints the regions back verbatim so uidx fmt keeps them. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…system audit, uidx contract
A component with a <Styles> table is drawn as the variant set the table
derives, one synthetic tree per combination, unlinked from the file. A
contract prop with a model resolves {item.field} to the model's samples, and
a <Repeat> multiplies its instance at successive sample indexes. The audit
checks parts, slots, rows, axes, models, bindings and repeats against each
other, and uidx check runs it. uidx contract prints the spec as JSON.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…system over @hwc/components @uidx/codegen renders each component identity to code (ADR 0017): a stylesheet keyed by the headless root's attributes and states, a markup fragment with sample content, a typed React component with props and events from the contract — a plain slot as a ReactNode prop, a repeating slot as items plus renderItem generic over the model, text parts as compound sub-components, compositions as the instance they hold — plus models.ts, runtime.ts, elements.d.ts and the contract as JSON. With a custom-elements.json manifest, each contract is checked against its element. `uidx codegen` drives it; `--check` fails on drift. examples/design-system carries Checkbox, Field, CheckboxField, Button, ContactOption and ContactList as .uidx identities over the vendored @hwc/components build, with generated/ committed and tested for drift, a Vite page mounting the HTML fragments beside the React components, and the React output type-checked against React 19. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…gh props
Canvas fixes found by rendering the example end to end:
- A whole-attribute stroke alias (`strokes="{border#default}"`) skipped the
stroke composition, so the stroke kept its colour and lost its weight and
drew as nothing. Both paths now share `wholePaintAlias`.
- The derived component set no longer carries the component's own paint and
layout; those live on each variant's root (ADR 0005 §5).
- A `<Slot>` that says nothing about its size hugs its placeholder instead of
the engine's 100x100 box.
- `<Prop sample>`: a demonstration value for the canvas and generated markup
(ADR 0013 amended); a text bound to a prop with neither sample nor default
renders empty.
- An instance's `props={{ label: '{label}' }}` resolves in the consuming
component's scope before entering the definition's, and a component
declared by its contract alone accepts passed values. The HTML target does
the same for compositions.
- `layoutAlign` maps to `align-self` in the CSS target.
Example: hug sizing declared on the hugging components, per-variant hover
rows for the button, samples on text props, light background for the demo.
Agent's insertable-element test now lists `Repeat`.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…ess library The inspector gains a Contract tab beside Design (ADR 0013 §3 amended). Figma's precedent: bindings live in the properties panel, declared on the component and applied from the layer, with choices from a curated list. - A component chooses the element it implements from the library's roots; a text field only when the document names no library. - Each declared part is a row: bound rows name their layer and select it, unbound rows are a picker over the layers that could draw it. Binding from a new layer unbinds the old one in the same patch. A layer's own row offers the same parts, naming who holds the taken ones. - A <Repeat> picks a declared repeating slot (reselecting itself at its new address) and its count. Slots show whether the tree provides them. - The file's contract (props, events) is shown beside the bindings; the tab badge counts parts still to bind. uidx.json gains "headless": the library's custom-elements.json, served on /__uidx/headless and read on every document:opened; uidx codegen uses it when --manifest is absent. Roots and parts follow the <root>-<part> naming convention (plus cssParts), with a leaf test so hwc-text-input stays a root beside hwc-text. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
The manifest's own vocabulary for parts is `cssParts`: parts inside the root's shadow tree, styled from outside through `::part()`. Until now every part had to be a `<root>-<part>` element, which is one library's convention. - The component model records each part's kind. A shadow part is styled as `root::part(name)` in the CSS target, emits no element in HTML or React, and gets no compound sub-component. - Conformance accepts either kind. A shadow part with design content under it warns rather than fails: the library draws the part, so that content is styled but not rendered. - The Contract tab marks shadow parts with a pill on the component's list and in the layer's picker, so an author knows it can be styled, not filled. ADR 0017 §3 records the rule. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
The conformance warning for design content under a shadow part is gone. The library drawing the part, and the design drawing its own for the canvas and Figma, is how a shadow-part library works; a warning nobody can act on is noise. Conformance accepts a cssParts entry exactly as it accepts an element. ADR 0017 §3 reworded. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
The contract API, simplified to what each renderer actually reads:
- `<States structural styling>` is gone. A boolean prop marked `visual` is a
state; `hover`, `focus` and `active` are the browser's and need no
declaration; `<States><State name>` remains only for states the element
produces itself, rendered as `:state(name)`. The state axis is default,
the visual booleans in prop order, the interaction states in table order,
then the declared ones.
- `<Parts>` is optional and written like every other list, `<Part name>`
with a description. The tree's `part=` bindings are the declaration.
- A model is named by a prop's type — `Contact`, `Contact[]` — and shared
across pages: declared once, on the page of the component that shows it.
`model="{models#…}"` is gone; a repeating slot names the list prop it
iterates, `of="items"`, so the model is written once. The example no
longer declares Contact twice.
- The parser names each old spelling and says what replaced it.
The contract-only helpers (axes, state kinds) move to @uidx/format so the
server's symbol lint can read them: `componentProps` now includes contract
props, a `{label}` passed through an instance is not judged at the use, a
`{item.name}` binding resolves through the model prop, and a derived set
draws every combination. `uidx check` passes on the whole example for the
first time; the audit runs after every page is parsed with a shared model
index. The Contract tab marks visual booleans as states and lists declared
element states.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
The identity never spells a library. This makes that true end to end: - uidx.json's `headless` may be an object: `manifest`, a `profile` (how the library reflects props, its own states and parts — attributes, data attributes, classes, `::part()` or `data-part`) and `bindings` (its tag, part, event and attribute names per component). The code target and conformance read both, so the checkbox renders as `sl-checkbox`, `sl-checkbox[checked]::part(control)` and `sl-change` from the same file that renders over @hwc/components. Defaults are the example's conventions. - When nothing is named, the server discovers libraries the project's dependencies ship (the `customElements` field of package.json) and the Contract tab offers them, or any path; the choice is written into uidx.json through the headless route. The tab says which tag a binding renders a component as. - `uidx codegen` passes the profile and bindings through from uidx.json. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…b that edits the contract Phase 3 — a state is designed the Figma way. Derived variants are addressable, so selecting the hover checkbox on the canvas shows its root frame in the inspector with a "state=hover of Checkbox" chip, and a change to it becomes a cell of the `state="hover"` row through the new `style` patch op (format), rather than a patch to a node with no source. An edit to the default combination lands on the base tree; a position, a name or structure is refused with the reason. Undo inverts the cell. The canvas' own scene-write path resolves against the derived document too. The toolbar gains a Repeat tool that wraps the selected instance in a `<Repeat>` on the first open repeating slot; the patcher leaves a Repeat unnamed, as it does a Variant. Phase 4 — the Contract tab edits the contract. The new `contract` patch op writes one `<Prop>`, `<Event>`, `<Slot>`, `<State>` or `<Part>` in canonical form, creating its list and the `## Contract` region when absent and removing a list its last item leaves; it inverts to the previous declaration. In the tab, each declaration opens into a form (description, type, default, sample, visual and controllable; a slot's repeats, `of` and `accepts`), a row adds one, and "Fill from library" declares what the implemented element exposes and the contract lacks, with the manifest's descriptions or placeholders the tab marks. The viewer's library model carries the manifest's types and descriptions for that. Docs: ADR 0013 §3, ADR 0016 §4, ADR 0017 §2, a design-system walkthrough in the README, the example README, the changelog. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…ate button uidx.json gains an optional `codegen` — `out` (relative to it) and `targets` — that `uidx codegen` reads when `--out` is absent and that the server's /__uidx/codegen route runs for the Contract tab's Generate button: the same generator over the same pages with the same library, so the panel and the command line write the same files. A page that does not parse or a contract the library contradicts stops the run before anything is written. The example names ../generated and the button reproduces its 36 committed files byte for byte. Issue #37 and the PR description now record everything delivered. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
CI typechecks before it builds, and the example's tsconfig did not carry the base config's `customConditions: ["development"]`, so `@uidx/format` and friends resolved to a dist that did not exist yet. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…sories Advisories published after main's last green run; every fix is a patch release inside the existing ranges. The release audit passes again. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This branch has not been deployed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Closes #37
Summary
A
.uidxfile becomes the identity of a component: intent, anatomy, layout and styles, plus a written contract, behaviour guidelines and view models. Components are renders of that identity — the viewer canvas, HTML/CSS and React here; Figma unchanged. Nothing in the file computes.Documented in six ADRs, implemented end to end, with an example package rendering six identities over the headless
@hwc/componentslibrary, and an inspector that edits all of it.Decisions (docs/decisions)
## Contract: props, events, slots, form, accessibility, composes.implementsbinds a headless root,partbinds its parts.<Prop sample>for demonstration values.<State>only for states the element produces itself;<Part>descriptions optional. The identity never spells a library:uidx.jsonmay carry aprofileand per-componentbindings.## Behavior: short- id: sentencebullets. Nowhen, no code.## Modelsand## Examples: view models declared once, shared across pages, named by a prop's type; every field explained; samples for demonstration only.<Styles>table keyed by visual enum props and states derives the set. Enum props are axes, visual booleans are states,hover/focus/activeneed no declaration. Derived variants are addressable: a state is designed on the canvas and its row is written.<Repeat>on a repeating slot (of="items"), slots and render props in React, compositions render the instance they hold, shadow parts through::part(),codegenconfig shared by CLI and viewer.Packages
<Styles>table,<Repeat>, spec types, diagnostics UIDX140–152, thestyleandcontractpatch ops with inverses; contract-only helpers (axes, state kinds).derivedTarget,contractJson,auditDesignSystemwith a shared model index; pass-through instance props resolve in the consumer's scope; slots hug; whole-attribute paint aliases compose strokes.custom-elements.json, shadow parts, library profiles and bindings.uidx.jsongainsheadless(path or{ manifest, profile, bindings }) andcodegen(out,targets); routes serve the library, discover libraries from dependencies'customElements, write the choice, and generate code; the symbol lint reads contract props.uidx checkaudits the whole document;uidx contract;uidx codegenreadsheadlessandcodegenfromuidx.json.@hwc/components, generated output committed with a drift test, a Vite demo page;uidx checkpasses on the whole document.Verification
root:opacityinto its style row; editing a prop's description from the Contract tab rewrote the<Prop>; the Generate button wrote the same 36 files the CLI writes.🤖 Generated with Claude Code