import type { ComponentPropsWithRef, ReactNode } from "react"; /** * Everything a caller writes on a `FieldGroup` reaches the * `
` — `id`, `hidden`, `data-*`, `aria-*`, `ref`, and the * three attributes that are the element's own: `disabled`, `form` * and `name`. * * This is the **opposite** destination from `Field`'s, and the * difference is not a preference. `Field` clones onto its child, so * its props are that control's props; `FieldGroup` renders its * children as-is, so the only element it owns is the `
` and * a prop has nowhere else to go. One rule reads both: * ***a component's props land on the element it owns*** * ([decision](../../../../docs/decisions/2026-08-21-a-slot-components-rest-props-are-the-controls-props.md)). * * `disabled` is worth knowing about before reaching for it. On a * `
` it is not decoration — the platform disables every * form control inside the element, not just the box itself. That is * usually what a caller wants and occasionally a surprise, and it is * a thing a `
`-based group could not have offered at all. */ export type FieldGroupProps = ComponentPropsWithRef<"fieldset"> & { /** * The controls. **Rendered as-is, not cloned** — that is the whole * difference from `Field`, and it is why this is a separate * component rather than a boolean on that one. */ children: ReactNode; /** * The `
`, which is also the outermost element this * component renders — so `className` is not the exception here that * it is on `Field`. */ className?: string; /** Standing help for the group as a whole. */ description?: ReactNode; /** Present means the group is invalid. */ error?: ReactNode; isRequired?: boolean; label: ReactNode; }; /** * A label over **several** controls. * * `Field` mints one id and clones it onto one child, which is the * right shape for a text input and the wrong shape the moment there * are two of them: an `id` can only name one element, and a * `