import { type RefObject } from 'react'; export interface UseScrollToFirstErrorOptions { /** * Limits the DOM query to this element. Useful when the page renders * more than one form, or when an unrelated component happens to share * a field name. Defaults to `document`. */ scope?: RefObject; /** * Forwarded to `scrollIntoView`. Defaults to * `{ behavior: 'smooth', block: 'center' }`. Pass * `{ behavior: 'auto' }` (or omit `behavior`) on consumers that * respect `prefers-reduced-motion` upstream — the kit doesn't shadow * the user's motion preference inside the hook, since some consumers * deliberately keep a near-instant snap for power-user form flows. */ scrollOptions?: ScrollIntoViewOptions; /** * Move keyboard focus to the errored field after scrolling. Default * `true`. The focus call uses `preventScroll: true` so the smooth * scroll the hook just did isn't replaced by a snap. * * Setting this `false` defeats the keyboard-user side of WCAG 3.3.1 * — the viewport will scroll but the active element stays on the * submit button, forcing a Shift+Tab back into the form. Only flip * this off when an upstream concern (e.g. a custom error summary * panel that owns its own focus) is taking responsibility. */ focus?: boolean; /** * On the very first render, do nothing even if `errors` already has * entries. Default `true`. Without this the hook would yank the * viewport whenever a form is hydrated with server-rendered errors * from a no-JS fallback path — bad UX, since the user landed on the * page and hasn't asked for any movement yet. Set `false` only when * the consumer explicitly wants the initial-mount scroll (rare). */ skipInitialMount?: boolean; } /** * Scrolls the first errored field into view and (by default) focuses * it, after `errors` changes. Backs the standard AlfaDocs rebrand * validation-error UX: after a 422 with `{ errors: { field: message } }` * is rendered inline, the user shouldn't be left staring at red labels * they can't see. Implements WCAG 3.3.1 / 3.3.3. * * The hook is stateless and never mutates the DOM beyond the * `scrollIntoView` + `focus` calls. * * @example * const fieldOrder = ['firstName', 'email', 'fiscalCode'] as const; * const [errors, setErrors] = useState>({}); * useScrollToFirstError(errors, fieldOrder); * * @example Radix-based field * * // hook scrolls to the visible