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
51 changes: 51 additions & 0 deletions .changeset/page-source-tailwind-prose-retraction-5469.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
---
'@object-ui/react-runtime': patch
'@object-ui/sdui-parser': patch
'@object-ui/components': patch
---

Documentation no longer teaches the "JSX/HTML + Tailwind" framing for a page's
`source`, which ADR-0080's own 2026-06-30 header amendment (under ADR-0065,
Accepted) retracted. objectui#5461 corrected three sites; a multiline census
found eight more, in three spellings a line-oriented grep could not see.

A page's `source` is *runtime metadata*. The console's Tailwind is compiled at
build time by scanning the console's own `src`, and there is no safelist, so it
never sees your page: an authored utility class produces CSS only by coincidence
(when objectui already ships that exact class) and otherwise produces nothing,
with no error anywhere. That is the ADR-0065 "works only by coincidence" failure
mode, and it is how a modal's `bg-black/50` backdrop reached production fully
transparent. `os validate` reports it as `page-source-className-tailwind`, a
warning on kinds `html`, `react` and `jsx`, shipped in `@objectstack/lint@11.5.0`.

The tiers themselves are unchanged and every load-bearing claim survives —
parse-never-execute, the untrusted-author safety argument for `html`, and the
deprecated `'jsx'` alias. Only the styling primitive is corrected, to the wording
`content/docs/guide/react-pages.md` §Styling already uses:

| `kind` | Style with |
|---|---|
| `"html"` | The blocks' own structured props (`` `<flex direction gap>` ``, `` `<grid columns>` ``) plus a JSON `style` object. |
| `"react"` | Inline `style` objects. |

Colors on both tiers come from the theme as `hsl(var(--token))`.

Why each package has an entry — each was measured against its built artefact, not
assumed:

- **`@object-ui/react-runtime`**: `README.md` is published to npm (npm includes
`README.md` in the tarball regardless of `files`). Its "no sandbox" callout is
the paragraph that routes untrusted-author work to the `html` tier, and it
carried the retracted framing line-wrapped across `:17-18`. It also gains the
§Styling section it was missing — the absence is why the framing survived here.
- **`@object-ui/sdui-parser`**: the corrected header of `src/types.ts` projects
verbatim into the published `dist/types.d.ts`.
- **`@object-ui/components`**: the corrected header of
`src/renderers/basic/html-elements.tsx` projects verbatim into the published
`dist/renderers/basic/html-elements.d.ts`. The `kind === 'html'` dispatch-arm
comment in `src/renderers/layout/page.tsx` does **not** project (it is inside a
function body) and is included here only because the same package already owes
an entry.

No behaviour change: this is prose only. `CHANGELOG.md` occurrences are
deliberately untouched — immutable release history.
7 changes: 6 additions & 1 deletion content/docs/components/basic/div.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -17,12 +17,17 @@ description: "Generic container element - use Shadcn components instead"
The Div component is a generic container element. **It is now deprecated in favor of semantic Shadcn-based components** that provide better accessibility, consistency, and design system integration.

**Scope of this deprecation: JSON-authored pages.** A `kind:'html'` page is written as
constrained JSX/Tailwind text that the engine compiles (parses, never executes) into
constrained JSX text that the engine compiles (parses, never executes) into
nodes, tag name straight through — there, the plain box tag is part of that tier's own
vocabulary and stays fully supported, because no other spelling of it exists for an
author to migrate to. The dev-build deprecation notice is therefore reported for
JSON-authored nodes only, and never for what the html tier compiled from its own source.

Styling on that tier is the blocks' own structured props (`<flex direction gap>`,
`<grid columns>`) plus a JSON `style` object — **not** Tailwind utility classes, which
produce no CSS at all when authored in page `source`. `os validate` reports that as
`page-source-className-tailwind`. See the React Pages guide, §Styling.

## Migration Guide

Instead of using `div`, use these Shadcn alternatives:
Expand Down
7 changes: 6 additions & 1 deletion content/docs/components/basic/span.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -19,12 +19,17 @@ import { DemoGrid } from '@/app/components/ComponentDemo';
The Span component is an inline container. **It is now deprecated in favor of semantic Shadcn-based components** that provide better accessibility and design system integration.

**Scope of this deprecation: JSON-authored pages.** A `kind:'html'` page is written as
constrained JSX/Tailwind text that the engine compiles (parses, never executes) into
constrained JSX text that the engine compiles (parses, never executes) into
nodes, tag name straight through — there, the plain inline tag is part of that tier's own
vocabulary and stays fully supported, because no other spelling of it exists for an
author to migrate to. The dev-build deprecation notice is therefore reported for
JSON-authored nodes only, and never for what the html tier compiled from its own source.

Styling on that tier is the blocks' own structured props (`<flex direction gap>`,
`<grid columns>`) plus a JSON `style` object — **not** Tailwind utility classes, which
produce no CSS at all when authored in page `source`. `os validate` reports that as
`page-source-className-tailwind`. See the React Pages guide, §Styling.

## Migration Guide

Instead of using `span`, use these Shadcn alternatives:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
/**
* The `div` deprecation notice is scoped BY PROVENANCE (objectui#4000).
*
* A `kind:'html'` page is authored as constrained JSX/Tailwind text that our own
* A `kind:'html'` page is authored as constrained JSX text that our own
* parser compiles (never executes) into SDUI nodes, tag name straight through.
* So an author writing the plain box tag in that tier gets a node the DEPRECATED
* renderer serves — and the notice fired at them, recommending replacements that
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
* The `span` deprecation notice is scoped BY PROVENANCE (objectui#4917,
* applying the ruling made for `div` in objectui#4000).
*
* A `kind:'html'` page is authored as constrained JSX/Tailwind text that our own
* A `kind:'html'` page is authored as constrained JSX text that our own
* parser compiles (never executes) into SDUI nodes, tag name straight through.
* So an author writing the plain inline tag in that tier gets a node the
* DEPRECATED renderer serves — and the notice fired at them, recommending
Expand Down
2 changes: 1 addition & 1 deletion packages/components/src/renderers/basic/html-elements.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
*
* Safe native HTML element passthrough renderers (ADR: kind:'html').
*
* A `kind:'html'` page is authored as a constrained JSX/Tailwind string that is
* A `kind:'html'` page is authored as a constrained JSX string that is
* PARSED (never executed) into the SDUI tree. For that tier to live up to its
* name, the everyday HTML tags an author reaches for — headings, paragraphs,
* lists, links, images, emphasis — must each resolve to a renderer (otherwise
Expand Down
12 changes: 9 additions & 3 deletions packages/components/src/renderers/layout/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -538,9 +538,15 @@ export const PageRenderer: React.FC<{
if (kind === 'react') {
return <ReactKindPage schema={schema} />;
}
// `kind:'html'` (formerly 'jsx') — author-written constrained JSX/HTML+Tailwind
// compiled (parsed, never executed) to a SchemaNode tree and rendered. The
// legacy 'jsx' value is still accepted as a deprecated alias.
// `kind:'html'` (formerly 'jsx') — author-written constrained JSX compiled
// (parsed, never executed) to a SchemaNode tree and rendered. The legacy
// 'jsx' value is still accepted as a deprecated alias.
// Styling on this tier is the blocks' own structured props
// (`<flex direction gap>`, `<grid columns>`) plus a JSON `style` object —
// NOT Tailwind utility classes. `source` is runtime metadata, so the build's
// Tailwind scan never sees it and an authored class produces no CSS and no
// error anywhere; `os validate` reports `page-source-className-tailwind`.
// (ADR-0065; ADR-0080's 2026-06-30 amendment.)
if (kind === 'html' || kind === 'jsx') {
const src = (schema as { source?: string }).source ?? '';
const { tree, diagnostics } = compile(src, getJsxManifest());
Expand Down
21 changes: 19 additions & 2 deletions packages/react-runtime/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,8 @@ lazy-load it behind a capability flag.
> and everything in scope. It is not isolated, not restricted, and not
> validated. Only run source you would run as first-party code.
>
> For untrusted authors use **`kind:'html'`** instead: constrained JSX/HTML +
> Tailwind, parsed into a schema tree and never executed. See
> For untrusted authors use **`kind:'html'`** instead: constrained JSX, parsed
> into a schema tree and never executed. See
> [React pages](../../content/docs/guide/react-pages.md).

## Installation
Expand Down Expand Up @@ -86,6 +86,23 @@ that reads `scope.import`:

Anything not provided there throws `Module not found`.

## Styling

**Do not author Tailwind utility classes in page `source`** — on either tier. A
page's `source` is *runtime metadata*: the console's Tailwind is compiled at
build time by scanning the console's own `src`, and there is no safelist, so it
never sees your page. An authored utility class produces CSS only by coincidence
(when objectui already ships that exact class) and otherwise produces nothing,
with no error anywhere. `os validate` reports it as
`page-source-className-tailwind`, a warning on both tiers. (ADR-0065; ADR-0080's
2026-06-30 amendment.)

Style a `kind:'react'` page with inline `style` objects, and a `kind:'html'` page
with the blocks' own structured props (`<flex direction gap>`, `<grid columns>`)
plus a JSON `style` object. Colors on both tiers come from the theme as
`hsl(var(--token))`, so a page follows light/dark and whatever theme the
deployment installs.

## Stable scope identity matters

Every evaluation produces a **new** component function — a new element *type* —
Expand Down
2 changes: 1 addition & 1 deletion packages/sdui-parser/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
* ObjectUI — SDUI JSX-source parser (ADR-0080)
*
* Types shared by the constrained JSX-source compiler. The parser turns a
* constrained JSX/HTML+Tailwind *text* into the existing SDUI `SchemaNode`
* constrained JSX *text* into the existing SDUI `SchemaNode`
* tree. It PARSES — it never executes. No `import`, no `eval`, no JS.
*/

Expand Down
Loading