diff --git a/.changeset/6773-aspect-ratio-demo-content.md b/.changeset/6773-aspect-ratio-demo-content.md
new file mode 100644
index 000000000..67468c550
--- /dev/null
+++ b/.changeset/6773-aspect-ratio-demo-content.md
@@ -0,0 +1,21 @@
+---
+---
+
+The five `aspect-ratio` docs demos now author the keys the renderer reads, so every demo on
+the published page draws its content instead of an empty ratio box (objectui#6773).
+
+`packages/components/src/renderers/layout/aspect-ratio.tsx` reads `ratio`, `className`,
+`image` (with `alt`) and `children || body` — never `content`, which is what all five
+entries authored and nothing else. Measured on `c6732825d`, each rendered 3 elements, no
+text and no `img`: the Radix wrapper for the ratio itself, with nothing inside it. The
+photo demo now authors `image` + `alt`, the renderer's own declared inputs, whose ``
+is sized to fill the box; the four card demos author `children` at BOTH levels — the
+nested `card` never read `content` either, so moving only the outer key would have traded
+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.
+
+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
+a list of five ids, with a counter-probe that renders the pre-fix shape and proves the
+assertion can still fail.
diff --git a/content/docs/components/layout/aspect-ratio.mdx b/content/docs/components/layout/aspect-ratio.mdx
index 4ba8ec3d5..8e798d76d 100644
--- a/content/docs/components/layout/aspect-ratio.mdx
+++ b/content/docs/components/layout/aspect-ratio.mdx
@@ -21,11 +21,17 @@ The Aspect Ratio component maintains a consistent width to height ratio for cont
## Schema
+The box holds its content one of two ways, and the renderer reads exactly one of them:
+set `image` for a photo, which is drawn to fill the box; otherwise the box renders
+`children`.
+
```plaintext
interface AspectRatioSchema {
type: 'aspect-ratio';
- ratio: number; // Width / Height ratio
- content: ComponentSchema; // Content to display
+ ratio?: number; // Width / Height ratio (default: 16 / 9)
+ image?: string; // Image URL, drawn to fill the box
+ alt?: string; // Alt text for `image`
+ children?: SchemaNode | SchemaNode[]; // Content, when no `image` is set
className?: string;
}
```
diff --git a/examples/schema-catalog/src/schemas/components-layout-aspect-ratio/16-9-aspect-ratio.json b/examples/schema-catalog/src/schemas/components-layout-aspect-ratio/16-9-aspect-ratio.json
index f9ce39057..6fee34949 100644
--- a/examples/schema-catalog/src/schemas/components-layout-aspect-ratio/16-9-aspect-ratio.json
+++ b/examples/schema-catalog/src/schemas/components-layout-aspect-ratio/16-9-aspect-ratio.json
@@ -1,9 +1,6 @@
{
"type": "aspect-ratio",
"ratio": 1.7777777777777777,
- "content": {
- "type": "image",
- "src": "https://images.unsplash.com/photo-1588345921523-c2dcdb7f1dcd",
- "alt": "Photo"
- }
+ "image": "https://images.unsplash.com/photo-1588345921523-c2dcdb7f1dcd",
+ "alt": "Photo"
}
diff --git a/examples/schema-catalog/src/schemas/components-layout-aspect-ratio/4-3.json b/examples/schema-catalog/src/schemas/components-layout-aspect-ratio/4-3.json
index 000a066cb..d02ff1594 100644
--- a/examples/schema-catalog/src/schemas/components-layout-aspect-ratio/4-3.json
+++ b/examples/schema-catalog/src/schemas/components-layout-aspect-ratio/4-3.json
@@ -1,8 +1,9 @@
{
"type": "aspect-ratio",
"ratio": 1.3333333333333333,
- "content": {
+ "children": {
"type": "card",
- "content": "4:3 Ratio"
+ "className": "flex h-full items-center justify-center",
+ "children": "4:3 Ratio"
}
}
diff --git a/examples/schema-catalog/src/schemas/components-layout-aspect-ratio/square.json b/examples/schema-catalog/src/schemas/components-layout-aspect-ratio/square.json
index 1bf7a0ade..08807c616 100644
--- a/examples/schema-catalog/src/schemas/components-layout-aspect-ratio/square.json
+++ b/examples/schema-catalog/src/schemas/components-layout-aspect-ratio/square.json
@@ -1,8 +1,9 @@
{
"type": "aspect-ratio",
"ratio": 1,
- "content": {
+ "children": {
"type": "card",
- "content": "Square (1:1)"
+ "className": "flex h-full items-center justify-center",
+ "children": "Square (1:1)"
}
}
diff --git a/examples/schema-catalog/src/schemas/components-layout-aspect-ratio/ultrawide.json b/examples/schema-catalog/src/schemas/components-layout-aspect-ratio/ultrawide.json
index 3b97bf22e..881f2c009 100644
--- a/examples/schema-catalog/src/schemas/components-layout-aspect-ratio/ultrawide.json
+++ b/examples/schema-catalog/src/schemas/components-layout-aspect-ratio/ultrawide.json
@@ -1,8 +1,9 @@
{
"type": "aspect-ratio",
"ratio": 2.3333333333333335,
- "content": {
+ "children": {
"type": "card",
- "content": "21:9 Ratio"
+ "className": "flex h-full items-center justify-center",
+ "children": "21:9 Ratio"
}
}
diff --git a/examples/schema-catalog/src/schemas/components-layout-aspect-ratio/video-aspect-ratio.json b/examples/schema-catalog/src/schemas/components-layout-aspect-ratio/video-aspect-ratio.json
index a1b08c5e7..8cb10ed6b 100644
--- a/examples/schema-catalog/src/schemas/components-layout-aspect-ratio/video-aspect-ratio.json
+++ b/examples/schema-catalog/src/schemas/components-layout-aspect-ratio/video-aspect-ratio.json
@@ -1,9 +1,9 @@
{
"type": "aspect-ratio",
"ratio": 1.7777777777777777,
- "content": {
+ "children": {
"type": "card",
- "className": "bg-muted flex items-center justify-center",
- "content": "Video Player (16:9)"
+ "className": "bg-muted flex h-full items-center justify-center",
+ "children": "Video Player (16:9)"
}
}
diff --git a/examples/schema-catalog/test/aspect-ratio-demo-content-6773.test.tsx b/examples/schema-catalog/test/aspect-ratio-demo-content-6773.test.tsx
new file mode 100644
index 000000000..d74976f33
--- /dev/null
+++ b/examples/schema-catalog/test/aspect-ratio-demo-content-6773.test.tsx
@@ -0,0 +1,186 @@
+/**
+ * ObjectUI
+ * Copyright (c) 2024-present ObjectStack Inc.
+ *
+ * This source code is licensed under the MIT license found in the
+ * LICENSE file in the root directory of this source tree.
+ */
+
+/**
+ * objectui#6773 — every `components-layout-aspect-ratio` demo puts its own
+ * authored content inside the ratio box.
+ *
+ * ## The reading this file was written against
+ *
+ * All five shipped demos authored `content`, and
+ * `packages/components/src/renderers/layout/aspect-ratio.tsx` reads four keys —
+ * `ratio`, `className`, `image` (with `alt`), and `children || body` — none of
+ * them `content`. Measured on `origin/main` @ `c6732825d`, rendering each entry
+ * through the real `SchemaRenderer` the way the docs gallery does:
+ *
+ * entry elements text img
+ * components-layout-aspect-ratio/16-9-… 3 "" 0
+ * components-layout-aspect-ratio/square 3 "" 0
+ * components-layout-aspect-ratio/4-3 3 "" 0
+ * components-layout-aspect-ratio/ultrawide 3 "" 0
+ * components-layout-aspect-ratio/video-… 3 "" 0
+ *
+ * Three elements is the harness wrapper plus the two Radix AspectRatio emits.
+ * The authored `content` reached the DOM only as the leaked host attribute
+ * `content="[object Object]"` — the objectui#5574 class, and `ui:aspect-ratio`
+ * is already ledgered for it in
+ * `packages/app-shell/src/__tests__/widget-dom-leak-sweep.test.tsx`.
+ *
+ * ## Why nothing already red covered it
+ *
+ * `catalog-gallery-render.test.tsx` DOES render all five, and passes: its
+ * non-vacuity control is `drewSomething(elements > WRAPPER_ELEMENTS || text)`,
+ * and an empty ratio box clears it on the wrapper Radix draws for the ratio
+ * itself. Its stronger control — the entry's own authored strings on screen —
+ * is scoped to `NEWLY_REGISTERED_CATEGORIES`, which this family is not in.
+ * Nor could a parse have caught it: `BaseSchema` is `.passthrough()` and
+ * carries `[key: string]: any`, so `content` is accepted by zod and by tsc
+ * alike (the objectui#6157 class-3 shape). `check-doc-component-types.mjs`
+ * rules the question out by name — "NOT in scope, deliberately: whether the
+ * snippet's OTHER keys are read by the renderer the type resolves to".
+ *
+ * So the control that was missing is the one below, and it is asserted at
+ * CATEGORY scope rather than over a list of five ids: a sixth demo authoring
+ * the phantom key again fails here without anyone remembering to add it.
+ *
+ * Each assertion is paired with a counter-probe that proves it can still fail
+ * — the objectui#6157 discipline. The counter-probe renders the exact pre-fix
+ * shape, so what is pinned is not "the text is somewhere on screen" but "the
+ * key that carries it is one the renderer reads".
+ *
+ * Module-scope import of `@object-ui/components`, not `beforeAll` (AGENTS.md
+ * §测试纪律): registering the renderers is an unbounded module load and must
+ * not be billed to a bounded hook timeout.
+ */
+import { describe, it, expect } from 'vitest';
+import { render } from '@testing-library/react';
+import '@object-ui/components';
+import { SchemaRenderer, toRenderableSchema } from '@object-ui/react';
+import { allExamples } from '../src/index.js';
+
+const CATEGORY = 'components-layout-aspect-ratio';
+
+/**
+ * The keys `aspect-ratio.tsx` reads off the schema, plus the `type`
+ * discriminator. COPIED as literals rather than imported: they are the
+ * contract this file is about, and a renderer that starts reading a new key
+ * should turn this red for review rather than silently widen the set.
+ */
+const READ_KEYS = ['type', 'ratio', 'className', 'image', 'alt', 'children', 'body'];
+
+/** Keys that carry visible content in the nodes this family authors. */
+const TEXT_SLOTS = ['children', 'body', 'title', 'description'];
+
+/** Render one entry the way `SchemaThumbnail` does. */
+function draw(schema: unknown) {
+ const { container, unmount } = render(
+