Field
Una etiqueta visible y clicable sobre el control, y una pista debajo que dice cómo corregir un error.
Add the domain after the @, for example ana@studio.com.
Instalación
$ pnpm dlx shadcn@latest add @ssntl/fieldUso
import { Field, FieldHint, FieldLabel } from "@/components/ui/field"
<Field>
<FieldLabel htmlFor="email">Correo de trabajo</FieldLabel>
<Input id="email" type="email" aria-invalid aria-describedby="email-hint" />
<FieldHint id="email-hint" error>Agrega el dominio después de la @.</FieldHint>
</Field>- Etiquetas clicables: FieldLabel es un <label> real; htmlFor lo une al control.
- La etiqueta queda visible sobre el control; un placeholder nunca la reemplaza.
- Un error pone aria-invalid en el control, apunta a la pista con aria-describedby y dice cómo corregirlo.
- La pista va en tamaño caption y texto muted; destructive solo para errores.
Código
import * as React from "react"
import { cn } from "@/lib/utils"
// Essential Design `es-field`: a visible label above, the control, an optional
// hint below. The label is a real <label> (clicking it focuses the control);
// placeholders never replace it. An error hint says how to fix the problem.
function Field({ className, ...props }: React.ComponentProps<"div">) {
return (
<div
data-slot="field"
className={cn("flex min-w-0 flex-col gap-2", className)}
{...props}
/>
)
}
function FieldLabel({ className, ...props }: React.ComponentProps<"label">) {
return (
<label
data-slot="field-label"
className={cn(
"text-caption font-medium text-muted-foreground",
className
)}
{...props}
/>
)
}
function FieldHint({
className,
error = false,
...props
}: React.ComponentProps<"p"> & {
/** Destructive color plus the input's aria-invalid; the text names the fix. */
error?: boolean
}) {
return (
<p
data-slot="field-hint"
data-error={error || undefined}
role={error ? "alert" : undefined}
className={cn(
"text-caption text-pretty",
error ? "text-destructive" : "text-muted-foreground",
className
)}
{...props}
/>
)
}
export { Field, FieldHint, FieldLabel }