, 'type'> & {
/** Adjacent label text. If omitted, render the bare input only. */
label?: ReactNode;
/** Optional helper text rendered under the label. */
hint?: ReactNode;
};
/**
* Checkbox + label + optional hint. Wraps the input + label in a flex row so
* spacing stays consistent regardless of label length.
*/
export function FormCheckbox({ className = '', label, hint, id, ...rest }: FormCheckboxProps) {
const input = (
);
if (!label) {
return input;
}
return (
);
}
export function FormLabel({
htmlFor,
variant = 'regular',
children,
className = '',
required,
}: {
htmlFor?: string;
/** 'eyebrow' = text-xs uppercase; 'regular' = text-sm font-medium */
variant?: 'eyebrow' | 'regular';
children: ReactNode;
className?: string;
required?: boolean;
}) {
const base = variant === 'eyebrow' ? LABEL_EYEBROW : LABEL_REGULAR;
return (
);
}
export function FormHint({ children, className = '' }: { children: ReactNode; className?: string }) {
return {children}
;
}
export function FormError({ children, className = '' }: { children: ReactNode; className?: string }) {
return (
{children}
);
}
/**
* Field group: label + control + optional hint/error.
*
* Convenience component for the most common form shape. Pass the control as
* the child, and the group wraps it with consistent spacing.
*/
export function FormField({
label,
htmlFor,
required,
hint,
error,
variant = 'regular',
children,
className = '',
}: {
label: ReactNode;
htmlFor?: string;
required?: boolean;
hint?: ReactNode;
error?: ReactNode;
variant?: 'eyebrow' | 'regular';
children: ReactNode;
className?: string;
}) {
return (
{label}
{children}
{error ? {error} : hint ? {hint} : null}
);
}
// Re-export common types so consumers can extend.
export type { ChangeEvent };