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

a11y-prefs

v0.4.3

Published

Accessibility preferences panel as a web component. No dependencies, translatable, configurable position and shape.

Readme

a11y-prefs

An accessibility preferences panel, shipped as a web component. No dependencies, ~11 kB gzipped, translatable, with configurable position and shape. It works the same in plain HTML, Vite, React, Astro, PHP and WordPress, because the runtime is the browser rather than a framework.

MIT licensed. Use it, fork it, ship it to clients — no strings, and no obligation to open your own code (unlike the GPL alternatives in this space).

Patches welcome: contributing · translating · testing · security · code of conduct

What this is, and what it isn't

It is a preferences panel for your visitors: whoever lands on your site can ask for bigger text, more contrast or no animations, and the choice is remembered in their browser.

It is not a product that makes your site accessible on its own. Overlays that promise this have a poor record — in January 2025 the FTC fined accessiBe one million dollars over misleading claims about exactly that. Real accessibility is fixed in your HTML. This panel sits on top; it does not replace the work.

Install

A plain script tag — HTML, PHP, anything

<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/a11y-prefs.js"
        data-locale="en" data-position="bottom-right" data-shape="circle"
        defer></script>

Every option is a data-* attribute on the script itself, so there is no inline JavaScript to whitelist in your CSP.

A bundler — Vite, Astro, Next, anything

npm i a11y-prefs
import { defineA11yPrefs } from "a11y-prefs";

defineA11yPrefs({ locale: "es", position: "middle-left", shape: "pill" });

Nothing runs on import, because customElements does not exist during SSR. Call it on the client — an Astro client:load, a useEffect, a <script>. If you want the no-ceremony version, import "a11y-prefs/auto".

Writing the element yourself

Every option is also an attribute, which is what you need when a backend renders the markup:

<a11y-prefs locale="it" position="top-right" shape="rounded"
            accent="#0b57d0" statement-url="/accessibility"></a11y-prefs>

Options

| Option | Attribute | Values | Default | |---|---|---|---| | locale | locale | en, es, it, anything you add, or auto | auto (reads <html lang>) | | fallbackLocale | fallback-locale | language tag | en | | messages | messages (JSON) | your own dictionaries | — | | position | position | bottom-right, bottom-left, top-right, top-left, middle-right, middle-left | bottom-right | | offset | offset | any CSS length, shorthand for all four edges | 20px | | offsetTop | offset-top | any CSS length | falls back to offset | | offsetRight | offset-right | any CSS length | falls back to offset | | offsetBottom | offset-bottom | any CSS length | falls back to offset | | offsetLeft | offset-left | any CSS length | falls back to offset | | shape | shape | circle, rounded, square, pill | circle | | radiusTopLeft | radius-top-left | any CSS length | whatever shape uses | | radiusTopRight | radius-top-right | any CSS length | whatever shape uses | | radiusBottomRight | radius-bottom-right | any CSS length | whatever shape uses | | radiusBottomLeft | radius-bottom-left | any CSS length | whatever shape uses | | size | size | sm (44px), md (52px), lg (62px) | md | | accent | accent | colour | #0b57d0 | | accentContrast | accent-contrast | colour | derived from luminance | | icon | icon | universal, person, eye, wheelchair, or raw SVG | universal | | label | label | launcher text in pill shape | translated ui.open | | features | features | comma separated list | all of them | | statementUrl | statement-url | URL | — | | storageKey | storage-key | string | a11y-prefs | | zIndex | z-index | number | 2147483000 |

Languages

English, Spanish and Italian ship with the package. English is the base, so any other dictionary is allowed to be partial — missing keys fall through to fallbackLocale and then to English.

Fix one string or add a whole language without touching the package:

defineA11yPrefs({
  locale: "pt",
  messages: {
    pt: { "ui.title": "Acessibilidade", "feature.fontSize": "Tamanho do texto" },
    en: { "ui.title": "Display settings" },   // overrides just this key
  },
});

locale: "auto" picks up <html lang>, and es-MX resolves to es.

Preferences

Text size (4 levels) · Text spacing (3 levels; level 2 is exactly what WCAG 1.4.12 asks for) · Contrast (high, inverted, grayscale) · Dyslexia-friendly font · Highlight links · Highlight headings · Visible focus · Stop animations · Reading help (guide or mask) · Big cursor · Hide images · Align to start · Mark new-tab links · Outline form fields · Unstick fixed bars · High-contrast selection.

Show only the ones you want, in the order you want:

<a11y-prefs features="fontSize,contrast,stopAnimations,links"></a11y-prefs>

How it works

The element never touches your DOM. All it does is set data-a11y-* attributes on <html> and inject one stylesheet; the sixteen preferences are plain CSS, inspectable from devtools. If the component fails to load, your page is exactly as it was.

That also means you can retune it from your own CSS:

:root {
  --a11y-focus-color: #b91c1c;
  --a11y-dyslexia-font: "Atkinson Hyperlegible", sans-serif;
}

The panel itself lives in a shadow root, so your styles cannot break it and it cannot break yours.

The panel's own accessibility

Real buttons, role="dialog" with aria-modal, a focus trap, Esc to close, focus returned to the launcher, aria-pressed on every toggle, an aria-live region announcing each change, a 44px minimum touch target and prefers-reduced-motion respected. An inaccessible accessibility panel would be a bad joke.

Events

document.querySelector("a11y-prefs")
  .addEventListener("a11y-prefs-change", (event) => {
    console.log(event.detail.state);
  });

Known limitation

Text size scales the root font size, so it carries everything defined in rem. A site with hardcoded px type will not scale. There is no way to fix that from the outside without breaking layouts, so it is documented rather than faked.

Development

npm install
npm run build
npm run demo     # http://localhost:4333/demo/

Licence

MIT — EFEELE.