Initial Commit

This commit is contained in:
2026-05-27 23:47:53 +01:00
parent fbc70f96e6
commit 9b28db724f
18103 changed files with 1600554 additions and 0 deletions

View File

@@ -0,0 +1,141 @@
# Field
<!-- begin:auto-docs -->
_(no submodules)_
<!-- end:auto-docs --> - field.variants — `fieldVariants`, `fieldLabelVarian…
- [field.variants](./field.variants.md) — `fieldVariants`, `fieldLabelVariants`, `fieldDescriptionVariants`, and `fieldErrorVariants` are the four cva (class-var…
- field.variants — `fieldVariants`, `fieldLabelVarian…
- [field.variants](./field.variants.md) — `fieldVariants`, `fieldLabelVariants`, `fieldDescriptionVariants`, and `fieldErrorVariants` are the four cva (class-var…
- field.variants — `fieldVariants`, `fieldLabelVarian…
- [field.variants](./field.variants.md) — `fieldVariants`, `fieldLabelVariants`, `fieldDescriptionVariants`, and `fieldErrorVariants` are the four cva (class-var…
- field.variants — `fieldVariants`, `fieldLabelVarian…
- [field.variants](./field.variants.md) — `fieldVariants`, `fieldLabelVariants`, `fieldDescriptionVariants`, and `fieldErrorVariants` are the four cva (class-var…
- [field.variants](./field.variants.md) — `fieldVariants`, `fieldLabelVariants`, `fieldDescriptionVariants`, and `fieldErrorVariants` are the four cva (class-var…
## Purpose
`<Field>` is the chassis form-field container — a semantic wrapper around a single form control plus its label, description, and error message. It carries the `data-invalid` and `data-disabled` selectors so descendant `<FieldLabel>` instances pick up the destructive text colour and opacity decay automatically through the cva `group-data-[invalid=true]/field:` selector.
The four primitives in this module — `<Field>`, `<FieldLabel>`, `<FieldDescription>`, `<FieldError>` — compose into a complete form-field surface. Per the cross-cutting reconciliation note (#1) shadcn ships `field` as a separate registry item from `field-group`; this package mirrors that split so each primitive evolves independently.
`<Field>` does NOT auto-detect child invalid/disabled state — consumers pass the booleans explicitly. This keeps the chassis a pure styling primitive: the form library (sibling change `enterprise-react-forms-v2`) owns the validation orchestration and forwards `invalid` / `disabled` based on its own state.
## Features
- `<Field>` container with `flex flex-col gap-2` layout (NOT `space-y-*` per shadcn skill rule).
- `<FieldLabel>` built on `@radix-ui/react-label` so clicking the label focuses the matched input.
- `<FieldDescription>` renders a muted `<p>` for helper text — typically wired to the control via `aria-describedby`.
- `<FieldError>` renders a destructive `<p role="alert">` so screen readers announce the message immediately when it appears.
- `data-invalid` on `<Field>` automatically flips descendant `<FieldLabel>` colour to destructive.
- `data-disabled` on `<Field>` decays opacity.
- Refs forwarded on every primitive.
- Theme-driven variant overrides via `useThemedVariants("Field" | "FieldLabel" | "FieldDescription" | "FieldError")`.
## Usage: Basic Example
```tsx
import { Field, FieldLabel, Input } from "@suaveplan/react-ui";
export function NameField() {
return (
<Field>
<FieldLabel htmlFor="name">Name</FieldLabel>
<Input id="name" />
</Field>
);
}
```
## Usage: Advanced Example
```tsx
import {
Field,
FieldDescription,
FieldError,
FieldLabel,
Input,
} from "@suaveplan/react-ui";
import { useState } from "react";
export function PasswordField() {
const [pwd, setPwd] = useState("");
const tooShort = pwd.length > 0 && pwd.length < 8;
return (
<Field invalid={tooShort}>
<FieldLabel htmlFor="pwd">Password</FieldLabel>
<Input
id="pwd"
type="password"
value={pwd}
invalid={tooShort}
aria-invalid={tooShort}
aria-describedby="pwd-help pwd-err"
onChange={(e) => setPwd(e.currentTarget.value)}
/>
<FieldDescription id="pwd-help">
At least 8 characters.
</FieldDescription>
{tooShort ? (
<FieldError id="pwd-err">Too short.</FieldError>
) : null}
</Field>
);
}
```
## API Reference
### `<Field>`
`forwardRef<HTMLDivElement, FieldProps>` — renders a `<div>` container.
#### `FieldProps`
| Prop | Type | Default | Description |
|---|---|---|---|
| `invalid` | `boolean` | `false` | Drives `data-invalid="true"` on the container. |
| `disabled` | `boolean` | `false` | Drives `data-disabled="true"` on the container. |
| _all native_ `HTMLAttributes<HTMLDivElement>` | _native_ | _native_ | Forwarded as-is. |
### `<FieldLabel>`
`forwardRef<ComponentRef<typeof LabelPrimitive.Root>, FieldLabelProps>` — wraps `@radix-ui/react-label` with the chassis cva applied.
#### `FieldLabelProps`
Inherits every prop from `LabelPrimitive.Root` — `htmlFor`, `children`, native HTML attributes.
### `<FieldDescription>`
`forwardRef<HTMLParagraphElement, FieldDescriptionProps>` — renders a `<p>` with muted text.
#### `FieldDescriptionProps`
Inherits every prop from `HTMLAttributes<HTMLParagraphElement>`.
### `<FieldError>`
`forwardRef<HTMLParagraphElement, FieldErrorProps>` — renders a `<p>` with destructive colour and `role="alert"` by default.
#### `FieldErrorProps`
Inherits every prop from `HTMLAttributes<HTMLParagraphElement>`. The default `role="alert"` can be overridden by passing `role="status"` (for non-blocking errors).
#### Cva resolvers
`fieldVariants`, `fieldLabelVariants`, `fieldDescriptionVariants`, `fieldErrorVariants` are all exported as bare cva resolvers for external composition.
## Implementation Notes
- The four primitives are co-located in a single component module because they always compose together; splitting them into four directories would create import-tax for no benefit.
- `<Field>` does NOT walk children to detect invalid/disabled state — consumers pass the booleans explicitly. This keeps the chassis a pure styling primitive.
- The cva `group/field` named-group selector lets `<FieldLabel>` flip its colour without needing a sibling `<FieldError>` to be present.
- `<FieldError>` defaults to `role="alert"` (assertive announcement) so screen readers announce validation errors immediately when they appear. Consumers that want a polite announcement override with `role="status"`.
- Per the cross-cutting reconciliation note (#1), shadcn ships `field` and `field-group` as separate registry items; this chassis mirrors that split so the primitives evolve independently and can be ejected piecemeal.