3.2 KiB
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-2layout (NOTspace-y-*per shadcn skill rule). data-[invalid=true]:[&_label]:text-destructiveon the container — every descendant<label>(including nested<FieldLabel>) flips colour.data-[disabled=true]:opacity-70decay.<FieldLabel>cva inherits the destructive colour viagroup-data-[invalid=true]/field:text-destructive.<FieldDescription>muted viatext-sm text-muted-foreground.<FieldError>destructive viatext-sm font-medium text-destructive.
Usage: Basic Example
import {
fieldVariants,
fieldLabelVariants,
fieldErrorVariants,
} from "@suaveplan/react-ui";
const containerCls = fieldVariants();
const labelCls = fieldLabelVariants();
const errorCls = fieldErrorVariants();
Usage: Advanced Example
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/fieldis named (not the baregroupclass) so nested<Field>instances do not collide — Tailwind treatsgroup/fieldas a distinct selector from a parentgroup/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 viareact-hook-formfield 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 (viatext-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).