first commit
This commit is contained in:
@@ -0,0 +1,78 @@
|
||||
# Field Variants (cva config)
|
||||
|
||||
## Purpose
|
||||
|
||||
`fieldVariants`, `fieldLabelVariants`, `fieldDescriptionVariants`, and `fieldErrorVariants` are the four cva (class-variance-authority) configurations powering the `<Field>` primitives. They are split from `field.tsx` into their own module so an ejected source copy under `npx shadcn@latest add @suaveplan/field` keeps the cva configurations in a dedicated file. Theme overrides flow through `useThemedVariants("Field" | "FieldLabel" | "FieldDescription" | "FieldError")`.
|
||||
|
||||
The container cva uses Tailwind's named-group selector (`group/field`) so the descendant `<FieldLabel>` instances can react to `data-invalid="true"` on the container without depending on direct-parent semantics.
|
||||
|
||||
## Features
|
||||
|
||||
- Container `flex flex-col gap-2` layout (NOT `space-y-*` per shadcn skill rule).
|
||||
- `data-[invalid=true]:[&_label]:text-destructive` on the container — every descendant `<label>` (including nested `<FieldLabel>`) flips colour.
|
||||
- `data-[disabled=true]:opacity-70` decay.
|
||||
- `<FieldLabel>` cva inherits the destructive colour via `group-data-[invalid=true]/field:text-destructive`.
|
||||
- `<FieldDescription>` muted via `text-sm text-muted-foreground`.
|
||||
- `<FieldError>` destructive via `text-sm font-medium text-destructive`.
|
||||
|
||||
## Usage: Basic Example
|
||||
|
||||
```ts
|
||||
import {
|
||||
fieldVariants,
|
||||
fieldLabelVariants,
|
||||
fieldErrorVariants,
|
||||
} from "@suaveplan/react-ui";
|
||||
|
||||
const containerCls = fieldVariants();
|
||||
const labelCls = fieldLabelVariants();
|
||||
const errorCls = fieldErrorVariants();
|
||||
```
|
||||
|
||||
## Usage: Advanced Example
|
||||
|
||||
```ts
|
||||
import { fieldVariants, cn } from "@suaveplan/react-ui";
|
||||
|
||||
export function CustomField({
|
||||
invalid,
|
||||
children,
|
||||
}: {
|
||||
invalid: boolean;
|
||||
children: React.ReactNode;
|
||||
}) {
|
||||
return (
|
||||
<div
|
||||
data-invalid={invalid ? "true" : undefined}
|
||||
className={cn(fieldVariants(), "rounded border p-2")}
|
||||
>
|
||||
{children}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
## API Reference
|
||||
|
||||
### `fieldVariants(): string`
|
||||
|
||||
Returns the container classes — `group/field flex flex-col gap-2 …`.
|
||||
|
||||
### `fieldLabelVariants(): string`
|
||||
|
||||
Returns the label classes including the `group-data-[invalid=true]/field:text-destructive` selector.
|
||||
|
||||
### `fieldDescriptionVariants(): string`
|
||||
|
||||
Returns the muted helper-text classes.
|
||||
|
||||
### `fieldErrorVariants(): string`
|
||||
|
||||
Returns the destructive error-text classes.
|
||||
|
||||
## Implementation Notes
|
||||
|
||||
- The named group `group/field` is named (not the bare `group` class) so nested `<Field>` instances do not collide — Tailwind treats `group/field` as a distinct selector from a parent `group/section`.
|
||||
- The container cva applies the destructive colour to ALL descendant `<label>` elements (`[&_label]:text-destructive`) so even a hand-rolled `<label>` inside `<Field>` picks up the colour without needing `<FieldLabel>`. This is intentional: consumers using the form library may render their own labels via `react-hook-form` field arrays.
|
||||
- Per the shadcn skill rule, the cva NEVER uses `space-y-*` / `space-x-*` — `gap-*` flows the same in RTL.
|
||||
- `<FieldError>` inherits the destructive colour directly (via `text-destructive`) rather than via the group selector so it always reads as an error even when `<Field invalid>` is not set (e.g. server-side error surfaces).
|
||||
Reference in New Issue
Block a user