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

have-wcag

v1.2.3

Published

WCAG accessibility toolkit - Widget for users

Readme

have-wcag

A comprehensive WCAG 2.1 accessibility toolkit that empowers both end-users and developers to create and maintain accessible web applications. Have-WCAG provides a user-facing accessibility widget and developer auditing tools that work seamlessly across all modern frameworks.

Overview

have-wcag is a dual-purpose accessibility toolkit:

  1. 🎯 Widget — Empowers end-users with accessibility controls they can use on any website
  2. 🔍 Auditor — Helps developers audit and fix accessibility issues during development

Perfect for ensuring your web applications meet WCAG 2.1 Level AA (and AAA) compliance while respecting user preferences and diverse accessibility needs.


Features

🎛️ Widget Features (End-User Accessibility Controls)

The widget provides an intuitive floating panel with accessibility features:

| Feature | Description | Use Case | |---------|-------------|----------| | 📏 Text Size | Increase/decrease font size dynamically | Users with low vision | | 🌓 High Contrast | Toggle high contrast mode for better visibility | Users with color blindness or low vision | | 🌙 Dark Mode | Invert colors to reduce eye strain | Users with light sensitivity or photophobia | | 🔤 Dyslexia Font | Apply OpenDyslexic font for easier reading | Users with dyslexia | | ↕️ Line Spacing | Adjust line height for better readability | Users with reading disorders | | 🔗 Link Highlighting | Visually emphasize all links on the page | Users who need clear link identification | | 🖱️ Cursor Size | Choose small, medium, or large cursor | Users with motor impairment or low vision | | 📖 Reading Guide | Horizontal ruler that follows the cursor | Users with focusing difficulties |

🔍 Auditor Features (Developer Accessibility Checks)

Automated auditing for common accessibility issues:

| Check | Standards | Details | |-------|-----------|---------| | Color Contrast | WCAG 1.4.3, 1.4.6 | Validates text/background contrast ratios (AA & AAA levels) | | Keyboard Navigation | WCAG 2.1.1, 2.4.3 | Ensures interactive elements are keyboard accessible | | Text Resize | WCAG 1.4.4 | Detects text that breaks when resized |

Features:

  • Visual highlighting of accessibility issues
  • Detailed fix suggestions with CSS output
  • Configurable WCAG compliance levels (A, AA, AAA)
  • Real-time audit results
  • Element-level auditing capability

Installation

npm install have-wcag

Or with yarn:

yarn add have-wcag

Or with pnpm:

pnpm add have-wcag

Quick Start

Simplest Setup (Widget Only)

import { widget } from "have-wcag";

// Initialize with defaults
widget.init();

This displays an accessibility widget in the bottom-right corner of your page.

Widget + Auditor Setup

import { widget, auditor } from "have-wcag";

// Initialize widget (for all users)
widget.init({ position: "bottom-right" });

Plain HTML Usage

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>My Accessible Website</title>
</head>
<body>
  <h1>Welcome to my website</h1>
  <p>Your content here...</p>

  <!-- Auto-initializing bundle (simplest method) -->
  <script src="https://unpkg.com/have-wcag/dist/bundle.js"></script>
</body>
</html>

Or with configuration:

<script>
  window.HAVE_WCAG_CONFIG = {
    widget: {
      enabled: true,
      position: "bottom-right",
      theme: {
        primaryColor: "#1a1a2e",
        accentColor: "#007bff",
      },
    },
  };
</script>
<script src="https://unpkg.com/have-wcag/dist/bundle.js"></script>

Framework Integration Guides

⚛️ React

Basic Setup

import { useEffect } from "react";
import { widget, auditor } from "have-wcag";

function App() {
  useEffect(() => {
    // Initialize widget
    widget.init({ position: "bottom-right" });

    // Cleanup on unmount
    return () => {
      widget.destroy();
    };
  }, []);

  return (
    <div>
      <h1>My React App</h1>
      {/* Your app content */}
    </div>
  );
}

export default App;

With Custom Configuration

import { useEffect } from "react";
import { widget, auditor } from "have-wcag";

function App() {
  useEffect(() => {
    widget.init({
      position: "bottom-right",
      features: ["textSize", "contrast", "darkMode", "dyslexiaFont"],
      theme: {
        primaryColor: "#667eea",
        accentColor: "#764ba2",
      },
    });

    return () => {
      widget.destroy();
    };
  }, []);

  return <div>{/* Your content */}</div>;
}

export default App;

Create a Reusable Provider

import { useEffect } from "react";
import { widget, auditor } from "have-wcag";

export function AccessibilityProvider({ children }) {
  useEffect(() => {
    widget.init({ position: "bottom-right" });

    return () => {
      widget.destroy();
    };
  }, []);

  return children;
}

// Usage in App.jsx
export default function App() {
  return (
    <AccessibilityProvider>
      <YourApp />
    </AccessibilityProvider>
  );
}

🔴 Next.js

App Router (Recommended)

// app/accessibility-provider.jsx
"use client";

import { useEffect } from "react";
import { widget, auditor } from "have-wcag";

export function AccessibilityProvider({ children }) {
  useEffect(() => {
    // Widget for all users
    widget.init({ position: "bottom-right" });

    return () => {
      widget.destroy();
    };
  }, []);

  return children;
}
// app/layout.jsx
import { AccessibilityProvider } from "./accessibility-provider";

export default function RootLayout({ children }) {
  return (
    <html lang="en">
      <body>
        <AccessibilityProvider>{children}</AccessibilityProvider>
      </body>
    </html>
  );
}

Pages Router

// pages/_app.jsx
import { useEffect } from "react";
import { widget, auditor } from "have-wcag";
import type { AppProps } from "next/app";

export default function App({ Component, pageProps }: AppProps) {
  useEffect(() => {
    widget.init({ position: "bottom-right" });

    return () => {
      widget.destroy();
    };
  }, []);

  return <Component {...pageProps} />;
}

💚 Vue 3

Composition API

<!-- App.vue -->
<script setup>
import { onMounted, onUnmounted } from "vue";
import { widget, auditor } from "have-wcag";

onMounted(() => {
  // Initialize widget
  widget.init({
    position: "bottom-right",
    theme: {
      primaryColor: "#42b983",
      accentColor: "#35495e",
    },
  });
});

onUnmounted(() => {
  widget.destroy();
});
</script>

<template>
  <div>
    <h1>My Vue App</h1>
    <!-- Your content -->
  </div>
</template>

Options API

<script>
import { widget, auditor } from "have-wcag";

export default {
  name: "App",
  mounted() {
    widget.init({ position: "bottom-right" });
  },
  unmounted() {
    widget.destroy();
  },
};
</script>

<template>
  <div>
    <h1>My Vue App</h1>
    <!-- Your content -->
  </div>
</template>

Create a Plugin

// plugins/have-wcag.js
import { widget, auditor } from "have-wcag";

export default {
  install(app, options = {}) {
    widget.init({
      position: "bottom-right",
      ...options.widget,
    });
  },
};
// main.js
import { createApp } from "vue";
import App from "./App.vue";
import accessibilityPlugin from "./plugins/have-wcag";

const app = createApp(App);

app.use(accessibilityPlugin, {
  widget: { position: "bottom-right" },
  auditor: { level: "AA" },
});

app.mount("#app");

🅰️ Angular

Service-Based Approach (Recommended)

// services/accessibility.service.ts
import { Injectable, NgZone } from "@angular/core";
import { widget, auditor } from "have-wcag";
import type { WidgetConfig } from "have-wcag";

@Injectable({
  providedIn: "root",
})
export class AccessibilityService {
  private isInitialized = false;

  constructor(private ngZone: NgZone) {}

  /**
   * Initialize the accessibility widget
   * Must be called outside Angular zone for performance
   */
  initWidget(config?: WidgetConfig): void {
    if (this.isInitialized) return;

    this.ngZone.runOutsideAngular(() => {
      widget.init({
        position: "bottom-right",
        ...config,
      });
    });

    this.isInitialized = true;
  }

  /**
   * Clean up widget
   */
  destroy(): void {
    widget.destroy();
    this.isInitialized = false;
  }

  /**
   * Open widget panel
   */
  open(): void {
    widget.open?.();
  }

  /**
   * Close widget panel
   */
  close(): void {
    widget.close?.();
  }

  /**
   * Reset all widget settings
   */
  reset(): void {
    widget.reset?.();
  }
}

Usage in a Component

// app.component.ts
import { Component, OnInit, OnDestroy } from "@angular/core";
import { AccessibilityService } from "./services/accessibility.service";

@Component({
  selector: "app-root",
  templateUrl: "./app.component.html",
  styleUrls: ["./app.component.css"],
})
export class AppComponent implements OnInit, OnDestroy {
  title = "My Angular App";

  constructor(private a11y: AccessibilityService) {}

  ngOnInit(): void {
    // Initialize widget on app load
    this.a11y.initWidget({
      position: "bottom-right",
      features: [
        "textSize",
        "contrast",
        "darkMode",
        "dyslexiaFont",
        "lineHeight",
      ],
      theme: {
        primaryColor: "#3f51b5",
        accentColor: "#ff4081",
      },
    });
  }

  ngOnDestroy(): void {
    // Clean up on app destroy
    this.a11y.destroy();
  }
}
<!-- app.component.html -->
<div class="container">
  <h1>{{ title }}</h1>
  <p>Accessibility widget is active!</p>
  <!-- Your app content -->
</div>

Module Setup (Traditional)

// app.module.ts
import { NgModule } from "@angular/core";
import { BrowserModule } from "@angular/platform-browser";
import { AppComponent } from "./app.component";
import { AccessibilityService } from "./services/accessibility.service";

@NgModule({
  declarations: [AppComponent],
  imports: [BrowserModule],
  providers: [AccessibilityService],
  bootstrap: [AppComponent],
})
export class AppModule {}

Standalone Component Setup (Angular 14+)

// app.component.ts
import { Component, OnInit, OnDestroy } from "@angular/core";
import { AccessibilityService } from "./services/accessibility.service";

@Component({
  selector: "app-root",
  standalone: true,
  templateUrl: "./app.component.html",
  styleUrls: ["./app.component.css"],
  providers: [AccessibilityService],
})
export class AppComponent implements OnInit, OnDestroy {
  title = "My Angular App";

  constructor(private a11y: AccessibilityService) {}

  ngOnInit(): void {
    this.a11y.initWidget({
      position: "bottom-right",
    });
  }

  ngOnDestroy(): void {
    this.a11y.destroy();
  }
}

App Initializer (Advanced)

For application-wide initialization before any components load:

// app.config.ts (Angular 14+ standalone approach)
import { ApplicationConfig, NgZone } from "@angular/core";
import { provideRouter } from "@angular/router";
import { widget } from "have-wcag";
import { routes } from "./app.routes";

export const appConfig: ApplicationConfig = {
  providers: [
    provideRouter(routes),
    {
      provide: "APP_INITIALIZER",
      useFactory: (ngZone: NgZone) => {
        return () => {
          ngZone.runOutsideAngular(() => {
            widget.init({
              position: "bottom-right",
            });
          });
        };
      },
      deps: [NgZone],
      multi: true,
    },
  ],
};

Or in main.ts:

// main.ts
import { bootstrapApplication } from "@angular/platform-browser";
import { appConfig } from "./app/app.config";
import { AppComponent } from "./app/app.component";

bootstrapApplication(AppComponent, appConfig);

🌟 Nuxt 3

Auto-Import Plugin

// plugins/have-wcag.ts
import { widget, auditor } from "have-wcag";

export default defineNuxtPlugin(() => {
  // Only initialize on client side
  if (process.server) return;

  // Initialize widget for all users
  widget.init({
    position: "bottom-right",
  });
});

With Custom Configuration

// plugins/have-wcag.ts
import { widget, auditor } from "have-wcag";

export default defineNuxtPlugin((nuxtApp) => {
  if (process.server) return;

  const isDev = process.env.NODE_ENV === "development";

  widget.init({
    position: "bottom-right",
    features: ["textSize", "contrast", "darkMode"],
    theme: {
      primaryColor: "#00dc82",
      accentColor: "#ffffff",
    },
  });
});

Usage in Pages/Components

<!-- pages/index.vue -->
<script setup>
// Accessibility is already initialized via plugin
// No additional setup needed!
</script>

<template>
  <div>
    <h1>Welcome to Nuxt</h1>
    <p>Accessibility features are automatically available!</p>
  </div>
</template>

Configuration

Widget Configuration

interface WidgetConfig {
  // Position on screen
  position?: "bottom-right" | "bottom-left" | "top-right" | "top-left";
  
  // Which features to enable
  features?: Array<
    | "textSize"
    | "contrast"
    | "darkMode"
    | "dyslexiaFont"
    | "lineHeight"
    | "linkHighlight"
    | "cursorSize"
    | "readingGuide"
  >;
  
  // Theme customization
  theme?: {
    primaryColor?: string;        // Main color
    backgroundColor?: string;    // Panel background
    textColor?: string;           // Text color
    accentColor?: string;         // Accent/highlight color
    borderRadius?: string;        // Border radius
  };
  
  // Label for toggle button
  buttonLabel?: string;
  
  // Title shown in panel
  panelTitle?: string;
}

Example:

widget.init({
  position: "bottom-left",
  features: ["textSize", "contrast", "darkMode"],
  theme: {
    primaryColor: "#1a73e8",
    backgroundColor: "#ffffff",
    textColor: "#202124",
    accentColor: "#1a73e8",
    borderRadius: "8px",
  },
  buttonLabel: "Accessibility",
  panelTitle: "Accessibility Options",
});

Auditor Configuration

interface AuditorConfig {
  // Show the auditor toolbar
  showToolbar?: boolean;
  
  // Automatically run audit on init
  autoRun?: boolean;
  
  // WCAG compliance level
  level?: "A" | "AA" | "AAA";
  
  // Which checks to run
  checks?: Array<"contrast" | "textResize" | "keyboard">;
  
  // Callback when audit completes
  onComplete?: (results: AuditResults) => void;
}

Example:

auditor.init({
  showToolbar: true,
  level: "AA",
  checks: ["contrast", "keyboard", "textResize"],
  onComplete: (results) => {
    console.log("Audit complete:", results);
    console.log(`Found ${results.summary.failed} issues`);
  },
});

API Reference

Widget API

import { widget } from "have-wcag";

// Initialize with options
widget.init(config);

// Destroy and cleanup
widget.destroy();

// Open panel programmatically
widget.open();

// Close panel programmatically
widget.close();

// Reset all settings to defaults
widget.reset();

// Update configuration
widget.configure(newConfig);

Auditor API

import { auditor } from "have-wcag";

// Initialize with options
auditor.init(config);

// Destroy and cleanup
auditor.destroy();

// Run full page audit
const results = await auditor.audit();

// Audit specific element
const issue = auditor.auditElement(element);

// Update configuration
auditor.configure(newConfig);

Build Outputs & Distribution

The package provides multiple build formats for different use cases:

| File | Format | Use Case | |------|--------|----------| | dist/index.js | CommonJS | Node.js/CommonJS projects | | dist/index.esm.js | ES Module | Modern bundlers (Webpack, Vite, etc.) | | dist/widget.js | IIFE | Standalone widget in script tags | | dist/widget.esm.js | ESM | Widget as ES module | | dist/auditor.js | IIFE | Standalone auditor in script tags | | dist/auditor.esm.js | ESM | Auditor as ES module | | dist/bundle.js | IIFE | Auto-initializing bundle (script tag only) | | dist/types/*.d.ts | TypeScript Declarations | Type support |

Usage by Build Format

ES Modules (Recommended):

import { widget, auditor } from "have-wcag";

CommonJS:

const { widget, auditor } = require("have-wcag");

Script Tag (Widget Only):

<script src="https://unpkg.com/have-wcag/dist/widget.js"></script>
<script>
  HaveWcagWidget.init();
</script>

Script Tag (Auto-Init Bundle):

<script src="https://unpkg.com/have-wcag/dist/bundle.js"></script>

Complete Examples

Production Setup (All Frameworks)

// Initialize only widget for end users
widget.init({
  position: "bottom-right",
  features: [
    "textSize",
    "contrast",
    "darkMode",
    "dyslexiaFont",
    "lineHeight",
    "linkHighlight",
  ],
});

Development Setup (All Frameworks)

import { widget, auditor } from "have-wcag";

// Widget for all users
widget.init({ position: "bottom-right" });

Advanced Configuration

widget.init({
  position: "bottom-left",
  features: ["textSize", "contrast", "darkMode", "readingGuide"],
  theme: {
    primaryColor: "#667eea",
    backgroundColor: "#f7fafc",
    textColor: "#2d3748",
    accentColor: "#764ba2",
    borderRadius: "12px",
  },
  buttonLabel: "🎯 Accessibility",
  panelTitle: "Accessibility Settings",
});

Development

Setup

# Install dependencies
npm install

# Install Node version (if needed)
nvm use

Scripts

# Build for distribution
npm run build

# Build in watch mode (for development)
npm run dev

# Type check with TypeScript
npm run typecheck

# Run tests
npm test

# Run tests in watch mode
npm test -- --watch

Project Structure

src/
├── widget/           # End-user widget
├── auditor/          # Developer auditor
├── core/             # Core logic
├── checks/           # Audit checks
├── types/            # TypeScript types
├── ui/               # UI components
├── utils/            # Utilities
└── index.ts          # Main entry point

Browser Support

  • Chrome/Edge 90+
  • Firefox 88+
  • Safari 14+
  • Mobile browsers (iOS Safari 14+, Chrome Mobile)

Accessibility Standards

have-wcag is built to help implement:

  • WCAG 2.1 Level A, AA, and AAA
  • Web Accessibility Initiative (WAI) standards
  • Americans with Disabilities Act (ADA) compliance
  • European Accessibility Act (EAA) requirements

Contributing

We welcome contributions! Please feel free to submit pull requests or open issues for bugs and feature requests.


License

MIT


Authors

Ruby Shrestha, Maheshwor Nagarkoti - Logispark Technologies Pvt. Ltd.


Support

For questions, issues, or suggestions:

  • 📧 Open an issue on GitHub
  • 🐛 Report bugs with detailed reproduction steps
  • 💡 Suggest features with use cases

Make the web accessible for everyone with have-wcag! ✨