Skip to content

Commit adf70f7

Browse files
Elon Muskclaude
andauthored
docs(objectui): replace the phantom field-level visible key with the keys that exist (#13094)
`FieldSchema` and `FormFieldSchema` are strict objects that refuse `visible` by name, before either documented value shape is reached. Rewrite the two protocol pages onto the keys that are really declared: - concept.mdx section 3: `visibleWhen` (CEL predicate over `record`), plus a callout naming the polarity trap (`hidden` is INVERTED). - layout-dsl.mdx "Responsive Layout Modifiers": no field-level breakpoint visibility exists in the spec at all, so the `desktop`/`tablet`/`mobile` map and the adjacent `portrait`/`landscape` `columns` map are removed rather than renamed; the section now documents `responsiveStyles` on a page component, which is the only per-breakpoint channel that is actually applied. No spec change: objectui#6514's maintainer ruling declined adding a field-level `visible` key to the contract. Co-authored-by: Claude <noreply@anthropic.com>
1 parent b15d260 commit adf70f7

2 files changed

Lines changed: 57 additions & 25 deletions

File tree

content/docs/protocol/objectui/concept.mdx

Lines changed: 21 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -601,7 +601,9 @@ actions:
601601
602602
### 3. Conditional Field Visibility
603603
604-
Fields appear based on other field values:
604+
Fields appear based on other field values. The key is **`visibleWhen`**, and its value
605+
is a **CEL predicate string** evaluated per record — the field is shown only when the
606+
predicate is TRUE:
605607

606608
```yaml
607609
fields:
@@ -611,9 +613,26 @@ fields:
611613
- name: shipping_address
612614
type: textarea
613615
label: Shipping Address
614-
visible: { shipping_required: true } # ← Show only if checkbox checked
616+
# Shown only while the checkbox is checked. `has()` guards the unset case:
617+
# an unbound reference faults, and visibility's fallback is "shown".
618+
visibleWhen: "has(record.shipping_required) && record.shipping_required"
615619
```
616620
621+
<Callout type="warn">
622+
There is **no `visible` key on a field**. `FieldSchema` is a strict object, so
623+
`visible` is refused *by name* — before any value shape is examined — and no
624+
filter object, boolean map or breakpoint map makes it parse. Which key you want
625+
depends on the form the answer takes, and the two have **opposite polarity**:
626+
627+
- a **static** answer is `hidden`, which is **inverted** — `visible: false`
628+
becomes `hidden: true`;
629+
- a **per-record** answer is `visibleWhen`, the CEL predicate above, shown when
630+
the predicate is **true**.
631+
632+
Its siblings are `readonlyWhen` and `requiredWhen`. See the
633+
[Field Reference](/docs/references/data/field).
634+
</Callout>
635+
617636
### 4. Field Dependencies and Cascading
618637

619638
```yaml

content/docs/protocol/objectui/layout-dsl.mdx

Lines changed: 36 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -521,35 +521,48 @@ tab points at a named list view. See
521521

522522
## Responsive Layout Modifiers
523523

524-
### Device-Specific Visibility
524+
**Responsive behaviour is not a form-field or form-section key.** A form field's
525+
only conditional-visibility key is `visibleWhen` — a CEL predicate over the
526+
record, so it varies per *record*, never per *viewport* — and a section's
527+
`columns` is a single scalar (`1``4`). Both shapes are strict, so a breakpoint
528+
or orientation map written on either is a **loud parse error** naming the key,
529+
not a layout that quietly does nothing.
525530

526-
```yaml
527-
fields:
528-
- name: detailed_description
529-
span: 12
530-
visible:
531-
desktop: true # Show on desktop
532-
tablet: true # Show on tablet
533-
mobile: false # Hide on mobile
534-
535-
- name: short_summary
536-
span: 12
537-
visible:
538-
desktop: false # Hide on desktop
539-
tablet: false # Hide on tablet
540-
mobile: true # Show on mobile
541-
```
531+
### Per-Breakpoint Styling
542532

543-
### Orientation-Specific Layout
533+
Breakpoint-driven show/hide and per-breakpoint layout live one tier up, on a
534+
**page component**, and are expressed as scoped CSS through `responsiveStyles`
535+
([ADR-0065](https://github.com/objectstack-ai/objectstack/blob/main/docs/adr/0065-sdui-styling-model.md)) — desktop-first buckets compiled to id-scoped CSS at
536+
render:
544537

545538
```yaml
546-
section:
547-
columns:
548-
portrait: 1 # 1 column in portrait mode
549-
landscape: 2 # 2 columns in landscape mode
550-
fields: [field_a, field_b, field_c]
539+
# On a page component (*.page.ts) — NOT on a form field or a form section
540+
responsiveStyles:
541+
large: { gridColumn: 'span 6' } # unconditional base (desktop-first)
542+
medium: { gridColumn: 'span 12' } # applied at ≤ medium
543+
xsmall: { display: 'none' } # hidden at the smallest breakpoint
551544
```
552545
546+
The four buckets are `large` / `medium` / `small` / `xsmall`; values are a
547+
CSS-property map with camelCase keys. Prefer design tokens
548+
(`var(--space-6)`) over literals.
549+
550+
<Callout type="warn">
551+
Earlier revisions of this page documented a field-level `visible:` breakpoint
552+
map (`desktop` / `tablet` / `mobile`) and a section-level `columns:`
553+
orientation map (`portrait` / `landscape`). **Neither existed on any schema.**
554+
`visible` is not a form-field key at all — `FormFieldSchema` declares `hidden`
555+
(static, and **inverted**: `visible: false` is `hidden: true`) and
556+
`visibleWhen` (CEL predicate) — and `FormSection.columns` has always been a
557+
scalar. Both are **removed rather than implemented**, because field-level
558+
breakpoint visibility exists nowhere in the spec: there is no key to rename
559+
them onto.
560+
561+
Nor is `responsive.hiddenOn` the answer. That layout block was retired in
562+
v17.x under [ADR-0049](https://github.com/objectstack-ai/objectstack/blob/main/docs/adr/0049-no-unenforced-security-properties.md) D2 precisely because no renderer ever applied it;
563+
`responsiveStyles` above is the only per-breakpoint channel that is.
564+
</Callout>
565+
553566
## Related Lists: Embedding Child Records
554567

555568
Display child records within a parent record's page.

0 commit comments

Comments
 (0)