diff --git a/content/docs/protocol/objectui/concept.mdx b/content/docs/protocol/objectui/concept.mdx index 54f97e9e6c..23ab6f53d2 100644 --- a/content/docs/protocol/objectui/concept.mdx +++ b/content/docs/protocol/objectui/concept.mdx @@ -601,7 +601,9 @@ actions: ### 3. Conditional Field Visibility -Fields appear based on other field values: +Fields appear based on other field values. The key is **`visibleWhen`**, and its value +is a **CEL predicate string** evaluated per record — the field is shown only when the +predicate is TRUE: ```yaml fields: @@ -611,9 +613,26 @@ fields: - name: shipping_address type: textarea label: Shipping Address - visible: { shipping_required: true } # ← Show only if checkbox checked + # Shown only while the checkbox is checked. `has()` guards the unset case: + # an unbound reference faults, and visibility's fallback is "shown". + visibleWhen: "has(record.shipping_required) && record.shipping_required" ``` + + There is **no `visible` key on a field**. `FieldSchema` is a strict object, so + `visible` is refused *by name* — before any value shape is examined — and no + filter object, boolean map or breakpoint map makes it parse. Which key you want + depends on the form the answer takes, and the two have **opposite polarity**: + + - a **static** answer is `hidden`, which is **inverted** — `visible: false` + becomes `hidden: true`; + - a **per-record** answer is `visibleWhen`, the CEL predicate above, shown when + the predicate is **true**. + + Its siblings are `readonlyWhen` and `requiredWhen`. See the + [Field Reference](/docs/references/data/field). + + ### 4. Field Dependencies and Cascading ```yaml diff --git a/content/docs/protocol/objectui/layout-dsl.mdx b/content/docs/protocol/objectui/layout-dsl.mdx index 9b3901843b..20f618e1e5 100644 --- a/content/docs/protocol/objectui/layout-dsl.mdx +++ b/content/docs/protocol/objectui/layout-dsl.mdx @@ -521,35 +521,48 @@ tab points at a named list view. See ## Responsive Layout Modifiers -### Device-Specific Visibility +**Responsive behaviour is not a form-field or form-section key.** A form field's +only conditional-visibility key is `visibleWhen` — a CEL predicate over the +record, so it varies per *record*, never per *viewport* — and a section's +`columns` is a single scalar (`1`–`4`). Both shapes are strict, so a breakpoint +or orientation map written on either is a **loud parse error** naming the key, +not a layout that quietly does nothing. -```yaml -fields: - - name: detailed_description - span: 12 - visible: - desktop: true # Show on desktop - tablet: true # Show on tablet - mobile: false # Hide on mobile - - - name: short_summary - span: 12 - visible: - desktop: false # Hide on desktop - tablet: false # Hide on tablet - mobile: true # Show on mobile -``` +### Per-Breakpoint Styling -### Orientation-Specific Layout +Breakpoint-driven show/hide and per-breakpoint layout live one tier up, on a +**page component**, and are expressed as scoped CSS through `responsiveStyles` +([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 +render: ```yaml -section: - columns: - portrait: 1 # 1 column in portrait mode - landscape: 2 # 2 columns in landscape mode - fields: [field_a, field_b, field_c] +# On a page component (*.page.ts) — NOT on a form field or a form section +responsiveStyles: + large: { gridColumn: 'span 6' } # unconditional base (desktop-first) + medium: { gridColumn: 'span 12' } # applied at ≤ medium + xsmall: { display: 'none' } # hidden at the smallest breakpoint ``` +The four buckets are `large` / `medium` / `small` / `xsmall`; values are a +CSS-property map with camelCase keys. Prefer design tokens +(`var(--space-6)`) over literals. + + + Earlier revisions of this page documented a field-level `visible:` breakpoint + map (`desktop` / `tablet` / `mobile`) and a section-level `columns:` + orientation map (`portrait` / `landscape`). **Neither existed on any schema.** + `visible` is not a form-field key at all — `FormFieldSchema` declares `hidden` + (static, and **inverted**: `visible: false` is `hidden: true`) and + `visibleWhen` (CEL predicate) — and `FormSection.columns` has always been a + scalar. Both are **removed rather than implemented**, because field-level + breakpoint visibility exists nowhere in the spec: there is no key to rename + them onto. + + Nor is `responsive.hiddenOn` the answer. That layout block was retired in + 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; + `responsiveStyles` above is the only per-breakpoint channel that is. + + ## Related Lists: Embedding Child Records Display child records within a parent record's page.