facebook / facebook/astryx

Standardize layout component props: derive a shared prop set for Stack/Grid/Center/Section/Layout/FormLayout

Open
#3,223 2 comments 0 reactions 1 assignee Claimed by @cixzhang View on GitHub
component discussion enhancement
Dominant language
TypeScript
Stars
13k
Forks
1.1k
Avg merge
1d 15h
Merged PRs (30d)
690

Description

## Summary

Astryx's layout components (`group: 'Layout'` — `Stack`, `Grid`, `Center`, `Section`, `Layout`, `FormLayout`) have **no shared prop standard**. Each evolved its own vocabulary for the same concerns (sizing, spacing, alignment, escape hatches), and the gaps get filed one component at a time. This issue derives a **standard layout prop set** and tracks aligning the components to it.

The wiki [API Conventions](https://github.com/facebook/astryx/wiki/API-Conventions) documents **Input Component Props** and `BaseProps` in detail, but has **no Layout Component Props section** — so the common vocabulary was never recorded anywhere. This adds it.

## Findings: current inconsistencies

| Concern | Inconsistency across the 6 layout components |
|---|---|
| **Escape hatches** | `Stack`, `Grid`, `Center`, `FormLayout`, `StackItem` **re-declare** `xstyle`/`className`/`style` even though `BaseProps` already provides them (the Component Auditor flags this as redundant). `Section`/`Layout*` correctly inherit. |
| **`ref` type** | Mixed: some use `React.Ref`, others `React.Ref`, re-declared inconsistently. |
| **Sizing** | `Stack`/`Grid`/`Center` use `SizeValue` for `width`/`height`. `Section` adds `maxWidth`/`minHeight` only. `Layout`/`LayoutHeader`/`LayoutPanel` use raw `number \| string`, and `Layout` uses `contentWidth: number`. No component exposes the full `min/max` set. |
| **Spacing** | `gap` (Stack/Grid) vs `padding` (Section/Layout) — both `SpacingStep`, but no documented split of which applies where. |
| **Alignment** | `Stack`: `hAlign`/`vAlign` + `justify`/`align` aliases. `Grid`: its own `GridAlignment`. `Center`: `axis`. Three different alignment vocabularies. |
| **Dividers** | `Section`: `dividers: Array<'top'\|'bottom'\|'start'\|'end'>`. `Layout*`: `hasDivider: boolean`. No documented rule for which shape to use. |

## Related open issues (same root cause)

All of these are requests for layout props that a standard would systematize:

- #2623 — `overflow` + flex `grow`/`shrink`/`basis` on Section/Stack/HStack/VStack
- #2613 — `isSticky` / `stickyOffset` on layout primitives
- #2625 — `maxWidth`/`shellWidth` to cap the `Layout` shell
- #2594 — page-surface props (`background`, `padding`) on `Center`
- #2798 — responsive `ratio` on `AspectRatio`
- #2753 — child sizing/positioning props on `AspectRatio`
- #2987 — runtime pixel sizing primitive for resizable panels

## Proposed standard

### A. Universal — every layout component
| Prop | Type | Notes |
|---|---|---|
| `children` | `ReactNode` | |
| `ref` | `React.Ref` | one consistent type |
| `xstyle` | `StyleXStyles` | **inherited from `BaseProps` — never re-declare** |
| `className` | `string` | inherited from `BaseProps` |
| `style` | `React.CSSProperties` | inherited from `BaseProps` |

### B. Sizing — box primitives (Stack, Grid, Center, Section)
`width`, `height`, `minWidth`, `maxWidth`, `minHeight`, `maxHeight` — all `SizeValue` (number = px, string as-is). `Layout*` migrates from `number | string` to `SizeValue`.

### C. Spacing
- `gap` (+ `rowGap` / `columnGap` for Grid) — `SpacingStep`, on flex/grid containers
- `padding` (+ `paddingBlock` / `paddingInline`) — `SpacingStep`, on surface containers

### D. Alignment — flex/grid containers
- `align` — cross axis (`align-items`): `'start' | 'center' | 'end' | 'stretch'`
- `justify` — main axis (`justify-content`): `'start' | 'center' | 'end' | 'between' | 'around' | 'evenly'`

`hAlign`/`vAlign` remain Stack's direction-aware sugar; `align`/`justify` is the cross-component standard.

### E. Flow & flex-item control — reserved names (incremental implementation)
These are the capability gaps the related issues ask for; the standard reserves consistent names so they land uniformly:
`overflow` (`'visible'|'hidden'|'clip'|'auto'|'scroll'`), `isScrollable` (already on `LayoutContent`/`LayoutPanel` — promote), `grow` (`boolean|number`), `shrink` (`boolean|number`), `basis` (`SizeValue`), `isSticky` (`boolean`), `stickyOffset` (`SpacingStep`).

### F. Dividers — documented rule
- `hasDivider: boolean` when there's a single implied edge (Layout areas)
- `dividers: Array<'top'|'bottom'|'start'|'end'>` when edges are selectable (Section)

## Plan

1. Add a **"Layout Component Props"** section to the API Conventions wiki (mirrors the existing "Input Component Props" section).
2. **Draft PR** normalizing the existing components to the standard (safe changes first: drop redundant escape-hatch re-declarations, unify `ref` types, migrate sizing to `SizeValue`, add the missing `min/max` sizing props, align the alignment vocabulary).
3. Capability props (Tier E) implemented incrementally, each closing its linked issue.

Contributor guide

Open the contributing guide

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.