Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions .changeset/6773-aspect-ratio-demo-content.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,13 @@ nested `card` never read `content` either, so moving only the outer key would ha
an empty box for an empty card. The page's Schema block published `content` as contract
while omitting `image`/`alt`; it now documents what the renderer reads.

⚠️ **Dated note, 2026-09-28 — the child list is `children` alone — objectui#6771.** Since
this change, objectui#6771 dropped the `body` arm of that read, in `aspect-ratio.tsx` and in
the nested `card` alike: both read `children` and never `body`, both zod mirrors refuse an
authored `body` by name, and both TypeScript faces declare it `never`. The four card demos
already author `children` at both levels, so they are unaffected. The rest of this entry is
kept as the reading of this change.

Nothing publishes from this change — a docs page plus `@object-ui/example-schema-catalog`
fixtures, both outside the release — hence the empty frontmatter. The regression control is
`examples/schema-catalog/test/aspect-ratio-demo-content-6773.test.tsx`: category scope, not
Expand Down
7 changes: 7 additions & 0 deletions .changeset/6788-context-menu-demo-content.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,4 +18,11 @@ dialect, and objectui#6773 authored `children` in the four sibling
`aspect-ratio` card demos. The renderer was NOT widened to read `content` —
that would add a second dialect for one slot to a published surface.

⚠️ **Dated note, 2026-09-28 — `card` reads `children` alone — objectui#6771.** Since this
change, objectui#6771 retired `body` as a child-list spelling: the `children || body` read
above is now `children` alone, `CardSchema` declares `body` as `never` and its zod mirror
refuses it by name, and `BaseSchema.body` is `never` rather than a legacy member. So the
demo's `children` is the one spelling that renders, not the preferred one of two. The rest
of this entry is kept as the reading of this change.

No package source changed, so this declares no release.
8 changes: 8 additions & 0 deletions .changeset/6877-div-guidance-names-box.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,3 +15,11 @@ exactly the conversions objectui#3965 measured and rejected.
Both statements now name `box` first for the mechanical swap, keep the layout components for the
cases where their layout is actually wanted, and state the `body` → `children` move that every
option except `card` requires. `span`'s guidance is deliberately unchanged.

⚠️ **Dated note, 2026-09-28 — every option, and `div` itself, reads `children` only —
objectui#6771.** Later in this same release objectui#6771 retired `body` as a child-list
spelling, and `card` and `div` dropped their `body` arms with it. So the `body` → `children`
move is one every option requires, `card` included. A retype no longer drops `body` content
either: `div` does not draw it, and validation refuses the key by name. The notice's `body`
bullet now says that (objectui#8284); `deprecated.replacement` is unchanged. The rest of this
entry is kept as the reading of this change.
13 changes: 13 additions & 0 deletions .changeset/8284-content-channel-per-component.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,19 @@ that names the channel to write instead:
| `children` | `box`, `span`, `container`, `flex`, `stack`, `grid`, `scroll-area`, `form`, `toggle` | `body` |
| `body` (at the time of this change; objectui#6771 has since retired that spelling and these three read `children`) | `alert`, `badge`, `tooltip` (which reads `content` first, the child list as its fallback) | the other channel |

⚠️ **Dated note, 2026-09-28 — `BaseSchema` declares one content channel, and the
`alert` / `badge` / `tooltip` row is inverted at release — objectui#6771.** Later in this same
release objectui#6771 retired `body` as a child-list spelling: `BaseSchema.body` is `never` on
the TypeScript face and refused by name on the zod mirror, and `children` is the one
child-list key. So the paragraph that opens "`BaseSchema` declares two optional content
channels" no longer describes `BaseSchema`, and for all twelve components in the table the
renderer reads `children` and the refused channel is `body`. That includes `alert`, `badge`
and `tooltip`, which accept `children` and refuse `body`, not the other way round. For those
three the Migration paragraph below does not hold as written: in the previous release their
renderers drew an authored `body`, and this release refuses it. objectui#6771's entry states
that migration: author `children`. The rest of this entry is kept as the reading of this
change.

Which channel each renderer reads was measured with the TypeScript type checker over
every `ComponentRegistry.register(...)` call in `packages/components` — a read site is a
property access filed under the type of the object it is read from, so a docblock mention
Expand Down
23 changes: 23 additions & 0 deletions .changeset/8284-refusal-and-notice-text.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
---
'@object-ui/types': patch
'@object-ui/components': patch
---

Correct author-facing text that objectui#6771's retirement of the `body` child-list spelling
left false (objectui#8284). Only wording moves; no key, value or rendered result does.

- `@object-ui/types` — the `body` refusal on `box`, `span`, `container`, `flex`, `stack`,
`grid`, `scroll-area`, `form` and `toggle` explained that `body` "is inherited from
`BaseSchema`, so an authored `body` parsed green here". Since objectui#6771, `BaseSchema`
refuses `body` by name itself, so that explanation no longer holds. The message now says that
`body` is the child-list spelling objectui#6771 retired, refused by name, and that `children`
is the one child-list key. The same refusal on `alert` and `badge` said that an authored
`body` "now parses green through `.passthrough()`", which that refusal itself contradicts;
like the other nine, it now says that `body` is refused here by name and that `children` is
the one child-list key. The refused key, the replacement it names, the issue code and the
issue path are unchanged; the same string is still the `.describe()` metadata.
- `@object-ui/components` — the `div` deprecation notice said that every replacement except
`card` reads `children` only, so a blind retype drops `body` content. Since objectui#6771,
`card` and `div` itself read `children` only as well. The notice now says that validation
refuses `body` by name and that neither `div` nor any replacement draws it. The replacements
it offers are unchanged.
16 changes: 8 additions & 8 deletions content/docs/components/basic/div.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,9 @@ description: "Generic container element - use Shadcn components instead"
<li>When you want their layout: use <code>card</code>, <code>flex</code>, <code>container</code>, <code>stack</code>, or <code>grid</code> — each injects classes of its own, and <code>card</code> also moves children into an extra element</li>
</ul>
<p className="mt-2 text-sm text-yellow-700 dark:text-yellow-300">
Move any content authored under the legacy <code>body</code> key into <code>children</code> first: every
replacement listed above except <code>card</code> reads <code>children</code> only, so a blind retype drops
that content silently, at an unchanged element count.
Move any content authored under the retired <code>body</code> key into <code>children</code> first:
objectui#6771 retired that child-list spelling, so validation refuses it by name, and neither
<code>div</code> nor any replacement listed above draws it.
</p>
</div>

Expand Down Expand Up @@ -47,9 +47,10 @@ the page; a `div` → `box` swap does not.

One caveat for a node still authored in the retired `body` spelling: every
option reads `children` and never `body` (objectui#6771 retired it across the
protocol, `card` included). Such a node must move that content into `children`
as part of the swap, or it disappears from the page without changing the element
count — silently.
protocol, `card` included), and so does `div` itself. Such a node must move that
content into `children` as part of the swap. Until it does, validation refuses
the key by name, and the content is missing from the page under `div` and under
every replacement alike.

### For a Plain Wrapper → Use `box`

Expand Down Expand Up @@ -83,8 +84,7 @@ interface DivSchema {
type: 'div';

// Content
children?: SchemaNode | SchemaNode[]; // Child components
body?: SchemaNode[]; // Alternative content prop
children?: SchemaNode | SchemaNode[]; // Child components (the child key this component reads)

// Styling
className?: string; // Tailwind CSS classes
Expand Down
3 changes: 1 addition & 2 deletions content/docs/components/layout/page.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,7 @@ interface PageSchema {
description?: string; // Page description

// Content
body?: SchemaNode | SchemaNode[]; // Main content — one node or a list
children?: SchemaNode | SchemaNode[]; // Alternative content prop
children?: SchemaNode | SchemaNode[]; // Main content — one node or a list

// Styling
className?: string; // Tailwind CSS classes
Expand Down
7 changes: 5 additions & 2 deletions packages/components/src/renderers/basic/div.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,10 @@ function warnDeprecatedOnce(type: string, message: string): void {
* and four of those five — `flex`, `container`, `stack`, `grid` — read
* `children` ONLY, so a node that authored `body` loses its content SILENTLY,
* at an unchanged element count. `box` is the one class-transparent swap, and
* the old text never named it.
* the old text never named it. (That was the measurement at the time. Since
* objectui#6771 retired the `body` spelling, `card` and `div` itself read
* `children` only as well, so `body` content is drawn by none of them and the
* notice's `body` bullet says so.)
*
* That is why this is worth a re-ruling rather than a nice-to-have. Deprecation
* guidance is followed LITERALLY, by humans and by generating models reading
Expand Down Expand Up @@ -116,7 +119,7 @@ const DIV_DEPRECATION_NOTICE =
'[ObjectUI] The "div" component is deprecated on every authoring surface. Please use Shadcn components instead:\n' +
' - For a plain wrapper the drop-in swap is "box": same element, your `className` verbatim, no layout of its own.\n' +
' - Reach for "card", "flex", "container", "stack", or "grid" only when you want their layout — each injects classes of its own, and "card" also moves children into an extra element.\n' +
' - Move any `body` content into `children` first: every replacement above except "card" reads `children` only, so a blind retype drops it silently at an unchanged element count.\n' +
' - Move any `body` content into `children` first: `body` is the child-list spelling objectui#6771 retired, so validation refuses it by name, and neither this component nor any replacement above draws it.\n' +
' This applies to JSON-authored nodes and to kind:\'html\' pages alike: an html page refuses the\n' +
' tag when it compiles, naming the same replacement.\n' +
'See documentation at https://www.objectui.org/docs/components for alternatives.';
Expand Down
17 changes: 10 additions & 7 deletions packages/components/src/renderers/layout/card.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -62,13 +62,16 @@ const CardRenderer = forwardRef<HTMLDivElement, { schema: CardSchema; className?
{header}
</CardHeader>
)}
{/* `||` here is ALIAS RESOLUTION — `body` is the legacy spelling of
`children` — and ⛔ it is no longer the guard. That distinction is
the whole point: `children: 0` used to be converted away by the
accident of `0 || undefined === undefined`, which protected nothing,
because the sibling `body: 0` went through `undefined || 0 === 0`
and leaked. `renderNodeSlot` covers both; the alias order is
unchanged. */}
{/* `children` is the one child-list key: this slot used to fall back
from `children` to `body` through an `||`, and objectui#6771 dropped
that `body` arm when it retired the spelling (the callback's `body`
below is only a local name for the slot's content). ⛔ No `||` is
the guard here. That distinction was the point of objectui#9162:
`children: 0` used to be converted away by the accident of
`0 || undefined === undefined`, which protected nothing, because
the sibling `body: 0` went through `undefined || 0 === 0` and
leaked. `renderNodeSlot` is the guard, so an empty slot, `0`
included, renders no `CardContent` at all. */}
{renderNodeSlot(schema.children, (body) => (
<CardContent>{renderChildren(body)}</CardContent>
))}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,37 +7,45 @@
*/

/**
* objectui#8284 — the `body` / `children` duality on `BaseSchema` is resolved
* PER COMPONENT: each component schema narrows to the channel its renderer
* actually reads and TOMBSTONES the other, on both published faces
* objectui#8284 — each component schema narrows to the content channel its
* renderer actually reads and TOMBSTONES the other, on both published faces
* (maintainer ruling, summon #17 decision batch #2, 2026-09-07, verbatim
* 「同意」).
* 「同意」). This file pins that rule on the twelve dedicated declarations
* listed in `ROWS`.
*
* ## The defect this pins closed
*
* `BaseSchema` declares TWO optional content channels and its own docblock
* admits that "some components use `children` instead of `body`" WITHOUT
* saying which. The zod base is `.passthrough()` and both keys are optional,
* `BaseSchema` used to declare TWO optional content channels, and its docblock
* admitted that "some components use `children` instead of `body`" WITHOUT
* saying which. The zod base is `.passthrough()` and both keys were optional,
* so a node carrying the WRONG channel type-checked, parsed green, was
* PRESERVED by the parse, and then rendered an EMPTY element — no error at
* authoring time, none at validation time, none at render time. Seven cards
* repaired one page of that each (#5027 · #3900 · #6773 · #6806 · #8197 ·
* #8234 · #6939) before the declaration itself was named.
*
* objectui#6771 has since retired `body` as a child-list spelling on every
* node: `BaseSchema.body` is `never` on the TypeScript face and refused by name
* on the zod mirror, and `children` is the one child-list key. So on every row
* below the renderer reads `children` and the refused channel is `body`. What
* this file still pins is objectui#8284's own refusal on each row's DEDICATED
* declaration: it is installed there, it names both keys and this card, it is
* the `.describe()` metadata too, and it reaches a nested node.
*
* ## The population here is FAMILY A + B of the measured table, not "all"
*
* The table posted on objectui#8284 is derived from every
* `ComponentRegistry.register(...)` call under
* `packages/components/src/renderers/**` (114 registrations) and the read side
* is measured with the TypeScript TYPE CHECKER — each `body` / `children`
* property access is filed under the TYPE of the object it is read from, so a
* docblock mention cannot score.
* `packages/components/src/renderers/**` (114 registrations when it was taken)
* and the read side is measured with the TypeScript TYPE CHECKER — each
* `body` / `children` property access is filed under the TYPE of the object it
* is read from, so a docblock mention cannot score.
*
* ⚠️ THE CONTROL MOVED, because objectui#6771 SPENT the one that stood here.
* It read: the `BoxSchema` docblock in `renderers/layout/box.tsx` SAYS
* `schema.body` in prose and `grep -l` counts it, the checker does not — and
* the card's own `17 / 17` figure came from that query. This card rewrote that
* docblock, so `box.tsx` now answers 0 to BOTH halves and a reader replaying it
* the card's own `17 / 17` figure came from that query. objectui#6771 rewrote
* that docblock, so `box.tsx` now answers 0 to BOTH halves and a reader replaying it
* gets no divergence at all. ⛔ A control that cannot fire is worse than none:
* it reads as evidence and is not.
*
Expand All @@ -51,24 +59,27 @@
* `renderers/action/action-icon.tsx` is a REAL property access and survives
* stripping at 1, so the 0 above is a reading and not a blanked file.
*
* The twelve rows below are the ones where the renderer reads EXACTLY ONE
* channel, the component owns a dedicated declaration, and exactly one
* registration claims its `type`. `sidebar` is measured into family B and held
* BACK from it, because two registrations claim `sidebar` and the second types
* its schema prop `any`. Families C (reads both — a live `children || body`
* fallback), D (reads neither) and E (no dedicated declaration) are named on
* the card with the measurement each still needs.
* The twelve rows below are the ones that table measured as reading EXACTLY ONE
* channel, owning a dedicated declaration, and claimed by exactly one
* registration. `sidebar` was measured into family B and held BACK, because two
* registrations claim `sidebar` and the second types its schema prop `any`; it
* is not a row here. The other families are not pinned in this file:
* family C (read both, through a live `children || body` fallback) lost its
* `body` arm to objectui#6771 and reads the `children` channel only; family D
* (reads neither) is pinned by `content-channel-family-d-9256.test.ts`
* (objectui#9256); family E (no dedicated declaration) was ruled done or
* superseded by objectui#6771 and objectui#9910, with its residual moved to
* objectui#9256 (ruling 5861449497 on objectui#8284).
*
* ## What is NOT pinned here, and why
*
* ⛔ Not the renderers. Nothing about rendering changes: a document that
* authors the channel its renderer reads is byte-identical through both faces,
* and a document that authors the other one rendered nothing before and
* renders nothing now — it is merely REFUSED first. The counter-probes in
* ⛔ Not the renderers. This file pins the two authoring faces only: a
* document that authors the channel its renderer reads parses and compiles,
* and a document that authors `body` is refused by both.
* The render side is pinned elsewhere, by counter-probes that deliberately
* author the dead channel (`body`) and assert the empty render:
* `examples/schema-catalog/test/badge-demo-label-6829.test.tsx` and
* `packages/components/src/__tests__/span-children-rendering.test.tsx`, which
* deliberately author the dead channel and assert the empty render, therefore
* keep passing.
* `packages/components/src/__tests__/span-children-rendering.test.tsx`.
*
* ## ⚠️ Half of this file is a COMPILE-TIME assertion and vitest CANNOT read it
*
Expand Down
8 changes: 4 additions & 4 deletions packages/types/src/zod/data-display.zod.ts
Original file line number Diff line number Diff line change
Expand Up @@ -77,8 +77,8 @@ export const AlertSchema = BaseSchema.extend({
'children',
'this alert node',
'`alert` reads `children`, never `body` (READ SITE, measured with the TypeScript type checker: `packages/components/src/renderers/data-display/alert.tsx`). '
+ '`body` was this node\'s only child-list key until objectui#6771 retired the spelling; an authored `body` now parses green through '
+ '`.passthrough()` and renders an EMPTY element — no error, no warning. objectui#8284.',
+ '`body` was this node\'s only child-list key until objectui#6771 retired the spelling — one concept, one spelling — so it is '
+ 'refused here by name; write the content under `children`, the one child-list key. objectui#8284.',
),
});

Expand Down Expand Up @@ -124,8 +124,8 @@ export const BadgeSchema = BaseSchema.extend({
'children',
'this badge node',
'`badge` reads `children`, never `body` (READ SITE, measured with the TypeScript type checker: `packages/components/src/renderers/data-display/badge.tsx`). '
+ '`body` was this node\'s only child-list key until objectui#6771 retired the spelling; an authored `body` now parses green through '
+ '`.passthrough()` and renders an EMPTY element — no error, no warning. objectui#8284.',
+ '`body` was this node\'s only child-list key until objectui#6771 retired the spelling — one concept, one spelling — so it is '
+ 'refused here by name; write the content under `children`, the one child-list key. objectui#8284.',
),
});

Expand Down
Loading
Loading