Skip to content

docs(guide): react-pages.md contradicts the framework's sources on four points — Tailwind styling, record:* scope, adapter.find options, and the block inventory #5413

Description

@os-elon

Filed from the framework repo (objectstack-ai/objectstack) while porting
content/docs/guide/react-pages.md into the customer docs site as
content/docs/ui/react-pages.mdx — framework issue
#10210, PR
#10281.

The port was explicitly instructed to re-verify every claim against the framework's
sources rather than copy, and to report contradictions rather than silently pick a
side. Four came up. All were measured against objectstack@502dc6fe7; none was carried
into the new page.

This guide is not dead material — @object-ui/react-runtime's README links it — so the
divergence is live.


1. Tailwind styling — the guide teaches the primitive an ADR amendment retracted

The guide's headline react example styles with Tailwind:

<divclassName="grid grid-cols-2 gap-4">

and the "What is in scope" section instructs:

Layout containers are deliberately not injected. In react mode you compose layout
with real HTML and Tailwind [...] use <div className="flex gap-4">.

objectstack/docs/adr/0080-ai-authored-ui-jsx-source.md carries an explicit header
amendment:

Amendment (2026-06-30 — ADR-0065 styling correction). The "HTML + Tailwind"
framing for page source is superseded on styling. A page's source is runtime
metadata
, so the console's build-time Tailwind never scans it — authored utility
classNames silently produce no CSS (the exact failure ADR-0065 was written to
prevent; the Task Desk modal's bg-black/50 backdrop rendered transparent). [...]
Do not author Tailwind classes in page source.

ADR-0065 gives the mechanism: the console's CSS scans only apps/console/src, there is
no safelist, so a class in page metadata renders only if it coincidentally appears in
objectui's own source.

Corroborating, in the framework repo:

  • the generated react contract (skills/objectstack-ui/references/react-blocks.md):
    "Do NOT use Tailwind className in page source."
  • packages/lint/src/validate-responsive-styles.ts raises style-classname-tailwind:
    "these are not compiled from metadata and will silently do nothing."
  • all three real kind:'react' pages in examples/app-showcase contain zero
    className and style exclusively with inline style objects and
    hsl(var(--token)) colors.

This is the highest-cost item: a reader following the guide gets a page that renders
with correct structure, correct data, and no styling, and nothing reports anything.

The half of that paragraph that is still correct — layout containers are not injected
on the react tier — is confirmed by buildComponentScope in
packages/components/src/renderers/layout/react-page.tsx (if (!tag || cfg.isContainer) continue;). Only the "so use Tailwind" conclusion needs replacing.

2. record:* blocks are offered as an in-scope example, and the publish gate rejects them

The guide's scope table says:

The block tags come from the curated public contract (PUBLIC_BLOCKS), converted
from kebab-case to PascalCase: object-grid<ObjectGrid>, record:details
<RecordDetails>.

The tag mapping is accurate — buildComponentScope really does inject every public
non-container block, record:* included, so <RecordDetails> resolves at runtime. But
the framework withdrew the family from the tier (REACT_RECORD_BLOCK_ALTERNATIVES and
the ledger above it in packages/spec/src/ui/react-blocks.ts), and os validate now
fails the build on it. Measured against the real CLI:

 ✗ Author-time rules failed (1 issue)
• page "showcase_renewals_pipeline" › RecordHighlights: RecordHighlights renders
"record:highlights", which reads its record from the record context a record page
mounts — a kind:'react' page never mounts one, so the block renders empty no matter
how it is bound (its objectName/recordId are not read by the renderer).
rule: react-block-needs-record-context

So the guide's illustrative example is the one tag class that cannot ship. Suggest
keeping the mapping rule and changing the example, plus a line noting the exclusion.

3. The Live data sample passes an option key the adapter does not read

adapter.find('showcase_project',{filters: [['status','=','open']]})

filters is not a QueryParams key. packages/types/src/data.ts declares $select,
$filter, $orderby, $skip, $top, $expand, $search, $count, and
ObjectStackAdapter.find reads $filter (array form via translateFilterArray). An
unprefixed filters reaches no branch and is dropped, so the sample returns the
object's records unfiltered
, with no error.

The framework's own react page uses the working spelling:
{ $filter: ['account', '=', sel] }.

4. The block inventory does not match the published contract

The guide lists the injected data blocks as <ObjectGrid>, <ListView>,
<ObjectForm>, <ObjectKanban>, <ObjectChart>, <ObjectMetric>, <Markdown>, and
demonstrates <ObjectGrid objectName pageSize={25}> and <Block type="object-tree">.

Two different things are being conflated, and both are true separately:

  • the runtime scope is everything public and non-container, which is roughly the
    guide's list;
  • the authored contract — what has published props, is checked by os validate, and
    is what an author (human or AI) should write against — is
    REACT_BLOCKS: ObjectForm, ListView, ObjectChart, Block. The generated
    contract's own footer routes the rest through the escape hatch: "Kanban/calendar/
    gantt/timeline/map of an object = <ListView navigation={…} /> with the matching
    visualization, or <Block type="object-kanban" …/>."

pageSize is also not a published prop on any of them (pagination is).

Suggest stating the two-tier distinction explicitly rather than listing tags, and
linking the generated contract as the prop authority.


What the port did

Resolved every point toward the framework's sources, and said so in the PR body rather
than quietly choosing. No file in this repo was touched — the framework card's scope
forbids it. The three claims the guide gets right and that were carried over, for the
record: the new Function / no-sandbox security boundary, the react-pages capability
plus OS_PAGE_REACT=off kill switch, and the implicit-export default source-shape
rule (verified against normalizeCode in packages/react-runtime/src/index.tsx).

Metadata

Metadata

Assignees

Labels

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions