Kanzo UI

Metadata form

Posting a contract to the board — grouped Cards, repeatable and nested fields, live serialisation, and a rule engine that only ever hands the library a boolean and a node.

Open full size

A realistic contract-posting screen at full viewport, the form a quartermaster fills before work and, like the discovery showcase, built as a Workspace on the Shell regions. ShellHeader carries ONE row: the title, the Contract switcher that says which posting this is, and then Share / Source / Output / validation / Preferences. The standing-orders switcher is not up there — it lives in the Source panel's own header, against the document it replaces. Below it a three-column Resizable workspace reads left→right exactly like the breadcrumb: the hall's standing orders (a leading ShellAside) → the editable form (ShellMain, the single <main>, groups The work · The posting · The terms · The party) → generated Output (a trailing ShellAside). Each side column toggles independently from its header button and is drag-resizable.

It exists to answer the question a single field never can: does a whole form hold together — required markers, repeatable rows, nested objects, dates, AI assistance and validation — without the library growing a domain. Switch the Data selector from Empty to Q-1041 to watch the header badge flip from 3 issues to Valid and the Output column reserialise live; open Source and Output together and drag the splitters to rebalance the three columns.

What it composes

Almost nothing here is new — it is existing parts, arranged:

  • ShellRoot / ShellHeader / ShellBody / ShellMain / ShellAside + Resizable — the Workspace frame. Exactly one <main>; the Source and Output columns are <aside> landmarks (ShellAside side="start" / side="end", logical and RTL-safe) rendered only when open, sized by a drag-resizable Resizable splitter keyed on the open-set.
  • Field · FieldLabel · FieldError — every row. A required field shows a red * (FieldRequiredIndicator); an invalid one gets Field's own error channel.
  • FieldArray — the repeatable fields (what to expect, who to ask, the party), each row keyed by a stable id so editing never steals focus.
  • DatePicker — the posting and due dates. The ISO-string adapter the form wants is written out locally in the showcase, which is where that seam belongs.
  • SuggestRoot / SuggestMark / SuggestList — the ✨ beside Tags, streaming deduped candidates into a strip under the field while it has focus.
  • CompleteRoot + CompleteGhost — the title and the notice, with Tab-to-accept inline completion over a plain Input and Textarea. CodeEditor has no completion prop and never had one; the ghost is a compound composed over a pure primitive.
  • Steps / Tabs — the form re-lays-out live as sequential Cards, Tabs, or a Steps wizard.
  • Preferences (extended) — the library's own PreferencesRoot + PreferencesPanel (its "Preferences [P]" drawer) is reused as-is and given custom children: a Layout SegmentGroup and display Switches that drive the ShellMain form live, above the library's own theme axes. Flat exports, per the RSC boundary.
  • HoverCard + Badge — the header validation summary, listing every failing field. The Output column is the generated writ and record, kept distinct from validation.

Where the domain lives

The library is domain-free, so the entire rule engine lives in data.tsx — a plain validate(values) that walks a posting and returns Issue[] with resolved, tri-state (violation / warning / info) messages. That is the forms decision made visible: the library models validation as a boolean + a ReactNode and nothing more, so a violation flows through Field's invalid + FieldError, while the non-blocking severities the library deliberately does not model are product-rendered nodes. Where the boolean came from — here a fake engine, in the product a real one over the whole archive — is never the library's business.

Source

docs/showcases/metadata-form/
├── default.tsx   — the Workspace: Shell chrome, the resizable Source/form/Output columns, the
│                   four groups, and the three live layouts driven by the Preferences drawer
└── data.tsx      — the rule engine, three seeded postings, the world's vocabularies,
                    the writ / record serialisers, and the faked AI streams

Imports come from two entry points: @kanzo-tech/ui for the form and chrome, and @kanzo-tech/ui/editor for CodeEditor — a subpath, so the base bundle never carries CodeMirror for consumers who do not use it.