first commit

This commit is contained in:
2026-05-29 08:20:56 +01:00
commit 949299f58d
6368 changed files with 527176 additions and 0 deletions

View File

@@ -0,0 +1,131 @@
# FieldSet (suaveplan-only)
<!-- begin:auto-docs -->
_(no submodules)_
<!-- end:auto-docs --> - field-set.variants — `fieldSetVariants` and `fieldL…
- [field-set.variants](./field-set.variants.md) — `fieldSetVariants` and `fieldLegendVariants` are the cva (class-variance-authority) configurations powering `<FieldSet>…
- field-set.variants — `fieldSetVariants` and `fieldL…
- [field-set.variants](./field-set.variants.md) — `fieldSetVariants` and `fieldLegendVariants` are the cva (class-variance-authority) configurations powering `<FieldSet>…
- field-set.variants — `fieldSetVariants` and `fieldL…
- [field-set.variants](./field-set.variants.md) — `fieldSetVariants` and `fieldLegendVariants` are the cva (class-variance-authority) configurations powering `<FieldSet>…
- field-set.variants — `fieldSetVariants` and `fieldL…
- [field-set.variants](./field-set.variants.md) — `fieldSetVariants` and `fieldLegendVariants` are the cva (class-variance-authority) configurations powering `<FieldSet>…
- [field-set.variants](./field-set.variants.md) — `fieldSetVariants` and `fieldLegendVariants` are the cva (class-variance-authority) configurations powering `<FieldSet>…
## Purpose
`<FieldSet>` and `<FieldLegend>` are chassis primitives that wrap related controls (typically Checkbox / RadioGroup / Switch groups) inside the native `<fieldset>` + `<legend>` HTML5 elements. The native `<fieldset disabled>` attribute propagates to every contained form control automatically, which `<Field>` (a `<div>`) cannot replicate. Pair `<FieldLegend>` with `<FieldSet>` to provide an accessible grouping heading announced by screen readers when navigating into any contained control.
**Suaveplan addition** — shadcn does not ship a `FieldSet` registry item upstream. The chassis adds these primitives because the cross-cutting reconciliation note (#1) flagged the gap: `<Field>` is a `<div>` and cannot carry the native `<fieldset>` semantics. Documented as suaveplan-only so consumers know the source of truth lives in this package, not upstream shadcn.
## Features
- `<FieldSet>` renders the native `<fieldset>` so `disabled` propagates to every contained control via HTML5 semantics.
- `<FieldLegend>` renders the native `<legend>` so screen readers announce the grouping when navigating into any contained control.
- `data-invalid="true"` on `<FieldSet>` flips descendant `<FieldLegend>` colour to destructive via the cva `data-[invalid=true]:` selector.
- `data-disabled="true"` mirrors the native `disabled` so sibling cva selectors can react.
- Refs forwarded on both primitives.
- Theme-driven variant overrides via `useThemedVariants("FieldSet" | "FieldLegend")`.
- Layout uses `flex flex-col gap-3` (NOT `space-y-*` per shadcn skill rule).
## Usage: Basic Example
```tsx
import { FieldSet, FieldLegend, Checkbox } from "@suaveplan/react-ui";
export function PermissionsFieldSet() {
return (
<FieldSet>
<FieldLegend>Permissions</FieldLegend>
<div className="flex items-center gap-2">
<Checkbox id="read" defaultChecked />
<label htmlFor="read">Read</label>
</div>
<div className="flex items-center gap-2">
<Checkbox id="write" />
<label htmlFor="write">Write</label>
</div>
</FieldSet>
);
}
```
## Usage: Advanced Example
```tsx
import {
FieldSet,
FieldLegend,
RadioGroup,
RadioGroupItem,
} from "@suaveplan/react-ui";
import { useState } from "react";
export function NotificationFrequency() {
const [enabled, setEnabled] = useState(true);
const [freq, setFreq] = useState("daily");
// Disabling the entire fieldset propagates `disabled` to every
// contained <RadioGroupItem> via HTML5 fieldset semantics.
return (
<FieldSet disabled={!enabled}>
<FieldLegend>Notification frequency</FieldLegend>
<RadioGroup value={freq} onValueChange={setFreq}>
<div className="flex items-center gap-2">
<RadioGroupItem value="daily" id="freq-daily" />
<label htmlFor="freq-daily">Daily digest</label>
</div>
<div className="flex items-center gap-2">
<RadioGroupItem value="weekly" id="freq-weekly" />
<label htmlFor="freq-weekly">Weekly summary</label>
</div>
</RadioGroup>
</FieldSet>
);
}
```
## API Reference
### `<FieldSet>`
`forwardRef<HTMLFieldSetElement, FieldSetProps>` — renders the native `<fieldset>`.
#### `FieldSetProps`
Inherits every prop from `FieldsetHTMLAttributes<HTMLFieldSetElement>`. Adds:
| Prop | Type | Default | Description |
|---|---|---|---|
| `invalid` | `boolean` | `false` | Drives `data-invalid="true"` on the container so descendant `<FieldLegend>` flips to destructive colour. |
The native `disabled` attribute is forwarded as-is and additionally drives `data-disabled="true"` for cva selectors.
### `<FieldLegend>`
`forwardRef<HTMLLegendElement, FieldLegendProps>` — renders the native `<legend>`.
#### `FieldLegendProps`
Inherits every prop from `HTMLAttributes<HTMLLegendElement>`. Adds:
| Prop | Type | Default | Description |
|---|---|---|---|
| `invalid` | `boolean` | `false` | Drives `data-invalid="true"` on the legend itself so the destructive colour activates without depending on a parent `<FieldSet invalid>`. |
#### Cva resolvers
`fieldSetVariants` and `fieldLegendVariants` are exported as bare cva resolvers for external composition.
## Implementation Notes
- This is a suaveplan-only addition — shadcn does not ship a `field-set` registry item upstream. The chassis adds it because `<Field>` (a `<div>`) cannot carry the native `<fieldset>` semantics, and the cross-cutting reconciliation note (#1) flagged the gap.
- The native `<fieldset disabled>` attribute propagates `disabled` to every contained form control automatically. The chassis preserves this semantic by rendering the actual HTML5 element rather than wrapping in a `<div role="group">`.
- The `<FieldLegend>` `invalid` prop is exposed independently of `<FieldSet>` so consumers can render a legend in error state outside a fieldset (e.g. a custom layout that uses the chassis legend styling without the fieldset wrapper).
- Per the shadcn skill rule, the cva NEVER uses `space-y-*` — `gap-3` flows the same in RTL.
- Refs target the native `<fieldset>` / `<legend>` so consumers can measure or scroll-into-view programmatically.