npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@fillament/a11y

v0.2.0

Published

Form-flow accessibility for Fillament: focus the first error on a failed submit, announce validation state via an ARIA live region, and render a focusable error summary with jump-to-field links. The form-level a11y the dumb primitives make you hand-wire.

Readme

@fillament/a11y

Form-flow accessibility for Fillament. React Aria and friends own input primitives — labels, aria-invalid, per-field error wiring (Fillament's Field does this already). What nobody hands you is the form-level flow: where does focus go when a submit fails, how do screen-reader users hear that it failed, and how do they jump to the offending fields. This package is that layer.

pnpm add @fillament/a11y

Tree-shakeable, side-effect-free. Drives entirely off the form's submitCount + validation state — no changes to @fillament/core or @fillament/react, and it resolves fields through the id / data-fillament-field attributes the default Field renderer already emits, so it works with zero wiring.


Three drop-ins

import { Form, Field, useForm } from "@fillament/react";
import { FocusFirstError, FormErrorAnnouncer, FormErrorSummary } from "@fillament/a11y";

function CheckoutForm() {
  const form = useForm({ schema, defaultValues });
  return (
    <Form form={form} onSubmit={save}>
      <FormErrorSummary form={form} />   {/* focusable summary with jump links   */}
      <FormErrorAnnouncer form={form} /> {/* visually-hidden ARIA live region     */}
      <FocusFirstError form={form} />    {/* focus first bad field on failed submit */}

      <Field name="email" label="Email" type="email" required />
      <Field name="card.number" label="Card number" required />
      <button type="submit">Pay</button>
    </Form>
  );
}

| Component | What it does | ARIA | | --- | --- | --- | | <FocusFirstError form /> | On every failed submit, moves focus to the first invalid field in DOM order. | — (focus only) | | <FormErrorAnnouncer form /> | Announces validation state on submit so screen-reader users hear what sighted users see. | visually-hidden aria-live region (role="alert" / "status") | | <FormErrorSummary form /> | A focusable summary box (the GOV.UK pattern): a heading + a list of errors as links that jump focus to each field; takes focus itself on a failed submit. | role="alert", tabIndex={-1}, aria-labelledby |

Pick a focus strategy. <FocusFirstError> sends focus straight to the field; <FormErrorSummary> (with focusOnSubmit, the default) sends focus to the summary so the user can review all errors first. They're alternatives — using both means they compete for focus. Most teams pick the summary for long forms, focus-first for short ones.


How it knows when a submit fails

submitCount is bumped at submit start, so these components wait for the submit to settle (validation done, isSubmitting back to false) before acting — once per completed submit, never on a bare form.validate(). That logic is exported as onSubmitSettled(form, handler) if you want to build your own.

How it finds the field

The default Field renderer emits id={name} on the control and data-fillament-field={name} on the wrapper. Resolution order:

  1. an element whose id is the field path (text / select / textarea / checkbox);
  2. the first focusable inside [data-fillament-field="<path>"] (radio groups, custom components).

Custom markup that keeps either convention works. If your layout has no <form> element, pass getContainer to scope lookups.


Options

<FocusFirstError> / useFocusFirstError(form, options)

| Option | Default | Description | | --- | --- | --- | | disabled | false | Turn the behavior off. | | scroll | true | Scroll the field into view (true / false / ScrollIntoViewOptions). | | getContainer | nearest <form> | Override the DOM root to search. |

The hook returns a ref to attach to an element inside the form (used only to locate the surrounding <form>); the component attaches a hidden marker for you.

<FormErrorAnnouncer>

| Option | Default | Description | | --- | --- | --- | | politeness | "assertive" | "assertive" (role="alert") interrupts; "polite" (role="status") waits. | | getMessage | "N errors found. <first>." | Build the failed-submit message from { errors }. | | successMessage | — | Announced on a successful submit (silent by default). |

<FormErrorSummary>

| Option | Default | Description | | --- | --- | --- | | heading | "There is a problem" | Summary heading content. | | headingLevel | 2 | 2 | 3 | 4. | | getLabel | <label> text → humanized path | Resolve the link label for a field path. | | focusOnSubmit | true | Move focus to the summary on failed submit. | | scroll | true | Scroll behavior when a link is followed. | | className, getContainer | — | Style hook; DOM-root override. |

The summary renders nothing while the form is valid. It carries no built-in styling beyond layout-neutral semantics — bring your own className.


Also exported

| Export | Purpose | | --- | --- | | useFocusFirstError(form, options) | Hook behind <FocusFirstError>. | | useFormErrorList(form) | Live { path, message }[] of current errors — build your own summary UI. | | useFormStateSnapshot(form) | Re-render on form-state change; returns the current FormState. | | onSubmitSettled(form, handler) | Subscribe to completed submits (valid or invalid). | | controlForField(root, path), firstInvalidControl(root, errorPaths), wrapperForField(root, path) | The DOM resolvers. | | visuallyHidden | The screen-reader-only style object. |

Pairs naturally with @fillament/agent: humans get focus + announcements, agents get the contract and structured errors — one validation engine underneath.


License

MIT © headlessButSmart