`hidden`'s JSDoc promised "rendered but not visible (visibility: hidden)"; the
renderer returns `null` for it, exactly as for `visible: false`. Both keys are
legs of one `shouldHide` chain feeding one `_hidden` flag whose only consumer is
`if (evaluatedSchema._hidden) return null`, and nothing emits a `visibility`
style. The `visible` comment's `display: none` claim rests on the same false
premise and is corrected with it.
Records the synonymity as the decision it is (objectui#7088, maintainer ruling
2026-09-01) so the next reader does not read two keys as two behaviours, and
notes that synonymous in outcome is not synonymous in precedence. The declined
alternative — keep the node, hide it visually — is a behaviour change on a
published prop with zero named consumers and belongs to a feature card that
brings one.
Adds `SchemaRenderer.hiddenVisibleSynonymy.test.tsx`: the two keys render the
same output, and `hidden: true` leaves no node to carry a `visibility` style.
Comment-level change; no behaviour moves.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EMrWaQw3XS5DxTHxp4yRyC
Fixes #7088
BaseSchema.hidden's JSDoc promised a behaviour the renderer has never had. This corrects the comment, and adds the pin that keeps it true. Comment-only — no behaviour moves. Clause ②no, per the ruling.What the renderer measurably does — measured, not paraphrased
Read at
86be0a90fagainstorigin/mainfda1247b3:SchemaRendererdoesvisible: falseshouldHideleg 2 returns!evaluateVisibilityPredicate(newSchema.visible, 'visible')_hidden = truehidden: trueshouldHideleg 5 returnsevaluateVisibilityPredicate(newSchema.hidden, 'hidden'), un-negated_hidden = true_hiddenhas exactly one consumer in the package —if (evaluatedSchema._hidden) return null;inSchemaRenderer.tsx— so both keys converge on the same early return and the component is not rendered. Three measurements behind that:grep -rn "_hidden" packages/react/srcoutside tests: the assignment in theshouldHideblock, thereturn null, and the destructure that strips the key from the forwarded DOM props. No fourth site.visibilitystyle set tohiddenacrosspackages/*/src: zero hits. Nothing emits that style for either key, so "rendered but not visible" was never reachable.grep -rn "display: none"inpackages/react/src: zero hits — thevisiblecomment'sdisplay: noneclaim rests on the same false premise, so it is corrected in the same stroke.By the time
_hiddenis read the key that set it is gone, so no branch could tell the two apart even if one wanted to.Where the two blocks actually were
Line numbers had drifted from the card (
:296-301/:262-264); both located by symbol:fda1247b3hidden?: boolean;visible?: boolean | string;The
visibleblock had grown the objectui#4581 predicate-string paragraph since the card was filed; only its first two lines are touched, that paragraph is untouched.What the JSDoc now says
hidden: trueis not rendered — the renderer sets_hiddenand returnsnull, exactly as forvisible: false. No node kept, novisibility: hidden.visible*leg is consulted first, so a declaredvisible— an empty one included — short-circuitshidden. That distinction is what makes the note safe to write; it is pinned already inSchemaRenderer.expressions.test.tsxandSchemaRenderer.hiddenDeclaredGate.test.tsx, and is cross-referenced rather than re-pinned.schema-reference.md's "Inverse ofvisible" row is the half describing shipped behaviour. Unchanged, and it stays — per the ruling, and because it was the near-miss that made this card worth filing.The pin, and its ablation
New:
packages/react/src/__tests__/SchemaRenderer.hiddenVisibleSynonymy.test.tsx(2 cases).The two halves were already pinned separately (
SchemaRenderer.expressions.test.tsx: "does not render when visible is false", "hides when hidden is true"). What was not pinned is the claim the new JSDoc actually makes — that they hide the same way. Case 1 asserts the two rendered outputs against each other; case 2 asserts thathidden: trueleaves no element at all, which is the retracted promise stated as unreachable. Precedence and the DOM-prop strip are deliberately not duplicated here.Red-then-green ablation, run on the committed implementation:
hiddenleg'sreturn evaluateVisibilityPredicate(newSchema.hidden, 'hidden');becomesreturn false;— i.e. the declined "keep the node" differentiation.7f9427devs post-mutation blob5f49e22c; anchored counts original1 -> 0, injected marker0 -> 1.divcarryingdata-testid="probe-7088") wherenullwas expected. The node survives, which is exactly what avisibility: hiddenimplementation would produce.git diff HEADempty,git statusclean, disk blob back to7f9427de, original count back to1, marker count0.trap ... EXIT INT TERMwith absolute paths resolved fromgit rev-parse --show-toplevel.../SchemaRendererby relative path, so vitest transforms the mutated source directly — which the red leg itself demonstrates.Verification — all at
86be0a90f, tree cleanNot-measured trap checked rather than assumed:
tsc -p packages/react/tsconfig.test.json --listFileslists the new test file, so the react type-check green genuinely covers it.Heavy runs went through the shared verify lock; the wall-clock figures it printed are shared-box seconds, not idle-box readings.
Scope
Untouched, as fenced: any renderer behaviour;
content/docs/api/schema-reference.md;packages/types/src/blocks.*,index.ts,zod/index.zod.ts(#7432);zod/data-display.zod.tsanddata-display.ts(#7447). The zod mirror was checked and needs nothing —base.zod.tsdescribeshiddenas "Hidden control", which makes no claim this change retracts. No compile-time ledger forcedzod-mirror-parity.test.ts, so it is untouched too.Filed out of scope while here: #7455 —
hiddenis declaredbooleanwhile the renderer evaluates it as a predicate (expression-valuedhiddenis already pinned as working), making it the third key onvisible/disabled's evaluated path and the only one never widened. That is a declared-type change and needs its own ruling, exactly as #4580 did.Ruling
Comment 5494803259 — 项目总监席, 总监批 #28, 2026-09-01, maintainer verbatim 「同意」:
That supersedes comment 5489588894, which had relabelled the card
needs-user-decision; a reader going top-to-bottom meets the open fork first.🤖 Generated with Claude Code
https://claude.ai/code/session_01EMrWaQw3XS5DxTHxp4yRyC
Generated by Claude Code