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

@servicenow/eslint-plugin-aiux-a11y

v0.4.0

Published

ESLint rules for AIUX accessibility conventions in Lit templates. Static-analysis companion to the runtime accessibility audit panel.

Readme

@servicenow/eslint-plugin-aiux-a11y

ESLint rules for AIUX accessibility conventions in Lit templates. Catches WCAG 2.2 violations that are statically detectable. Static-analysis companion to the runtime accessibility audit panel (components/audit-panel).

Install

pnpm add -D @servicenow/eslint-plugin-aiux-a11y

Usage

import aiuxA11y from '@servicenow/eslint-plugin-aiux-a11y';

export default [
  ...aiuxA11y.recommended
  // or build your own using the rules export:
  // {
  //   plugins: {'@servicenow/aiux-a11y': {rules: aiuxA11y.rules}},
  //   rules: {
  //     '@servicenow/aiux-a11y/button-needs-accessible-name': 'error',
  //     '@servicenow/aiux-a11y/img-needs-alt': 'error',
  //     '@servicenow/aiux-a11y/interactive-element-needs-role': 'error',
  //     '@servicenow/aiux-a11y/role-needs-accessible-name': 'error',
  //     '@servicenow/aiux-a11y/aria-prohibited-attr': 'error',
  //     '@servicenow/aiux-a11y/select-needs-accessible-name': 'error',
  //   },
  // },
];

Named imports work too:

import {recommended, rules} from '@servicenow/eslint-plugin-aiux-a11y';
export default [...recommended];

Rules

button-needs-accessible-name

WCAG 2.2 Success Criterion 4.1.2 (Name, Role, Value). Flags <button> elements that have no programmatic name — no text content, no aria-label, no aria-labelledby, no title.

// ✗ Icon-only button (the most common a11y bug)
return html`<button><aiux-icon name="close" /></button>`;

// ✗ Empty button
return html`<button></button>`;

// ✓ Text content
return html`<button>Save</button>`;

// ✓ Icon button with aria-label
return html`<button aria-label="Close dialog">
  <aiux-icon name="close" />
</button>`;

// ✓ Interpolated children — trusted (static analysis can't see the value)
return html`<button>${label}</button>`;

// ✓ Opt-out
return html`<button data-a11y-skip><aiux-icon name="x" /></button>`;

Conservative by design — the rule only flags buttons whose inner content the linter can fully see and prove is empty (or icon-only). Buttons whose children include ${...} interpolations or aria-label=${expr} bindings are trusted.

img-needs-alt

WCAG 2.2 Success Criterion 1.1.1 (Non-text Content). Flags <img> elements without an alt attribute (or an equivalent decorative-image signal).

// ✗
return html`<img src="hero.png" />`;

// ✓ Meaningful description
return html`<img src="hero.png" alt="Welcome dashboard" />`;

// ✓ Decorative image (screen readers skip it)
return html`<img src="flourish.png" alt="" />`;

// ✓ Alternative ways to mark decorative
return html`<img src="x.png" role="presentation" />`;
return html`<img src="x.png" aria-hidden="true" />`;

// ✓ Interpolated alt — trusted
return html`<img src="x.png" alt=${altText} />`;

interactive-element-needs-role

WCAG 2.2 Success Criterion 4.1.2 (Name, Role, Value). Flags non-interactive elements (<div>, <span>, <li>, etc.) that have Lit event bindings (@click, @keydown, etc.) but no role attribute.

// ✗ Clickable div without role (invisible to screen readers)
return html`<div @click=${handler}>Click me</div>`;

// ✗ Clickable li without role
return html`<li @click=${handler} tabindex="0">Item</li>`;

// ✓ Clickable div with role
return html`<div role="button" @click=${handler}>Click me</div>`;

// ✓ Native interactive element — no role needed
return html`<button @click=${handler}>Save</button>`;

// ✓ Interpolated role — trusted
return html`<div role=${role} @click=${handler}>X</div>`;

role-needs-accessible-name

WCAG 2.2 SC 1.1.1 and 4.1.2. Flags elements with ARIA widget roles (progressbar, slider, dialog, meter, toolbar, img, region, etc.) that have no accessible name.

// ✗ Progressbar without label
return html`<div role="progressbar" aria-valuenow="50"></div>`;

// ✗ Dialog without label
return html`<div role="dialog" class="modal"></div>`;

// ✓ Progressbar with aria-label
return html`<div
  role="progressbar"
  aria-label="Upload progress"
  aria-valuenow="50"
></div>`;

// ✓ Dialog with aria-labelledby
return html`<div role="dialog" aria-labelledby="dialog-title"></div>`;

// ✓ Interpolated aria-label — trusted
return html`<div role="progressbar" aria-label=${label}></div>`;

aria-prohibited-attr

WCAG 2.2 SC 4.1.2. Flags aria-label and aria-labelledby on elements with a generic implicit ARIA role (<span>, <div>, <p>, etc. without an explicit role). These attributes have no effect on generic-role elements and are an authoring error.

// ✗ span with aria-label but no role (label is ignored)
return html`<span aria-label="Status">Active</span>`;

// ✗ div with aria-label but no role
return html`<div aria-label="Container">content</div>`;

// ✓ span with role — naming is allowed
return html`<span role="status" aria-label="Active">Active</span>`;

// ✓ button — natively nameable
return html`<button aria-label="Close">×</button>`;

select-needs-accessible-name

WCAG 2.2 SC 4.1.2. Flags <select>, <input>, and <textarea> elements without an accessible name (aria-label, aria-labelledby, title, or an id that may reference a <label>).

// ✗ Select without any label
return html`<select class="page-size"></select>`;

// ✗ Input with only placeholder (not an accessible name)
return html`<input type="text" placeholder="Search..." />`;

// ✓ Select with aria-label
return html`<select aria-label="Page size"></select>`;

// ✓ Input with id (may have associated label)
return html`<input type="text" id="username" />`;

// ✓ Hidden input — doesn't need a label
return html`<input type="hidden" name="csrf" />`;

Pairing with the runtime audit panel

The audit panel walks the live DOM checking the same things. Use the linter to catch issues in the editor / on CI; use the audit panel for spot-checks in the browser. Same gaps, two surfaces.

Pairing with Evinced

The Evinced MCP Server performs runtime WCAG scanning against the live rendered page. Use the lint rules to catch statically-detectable issues early; use Evinced for runtime issues like color contrast that require computed styles.