Skip to content

pigmilcom/a11y

@pigmilcom/a11y

About Simple, lightweight accessibility widget that enhances usability and adds control features to any website instantly. Zero dependencies. WCAG 2.1 Ready. Drop in one <script> tag or install via npm — visitors get instant control over text size, contrast, colour filters, motion, and more. Preferences persist via localStorage.


CDN — Plug & Play

No React, no npm, no build step. Add one tag to your <head>.

jsDelivr (recommended)

Served from the npm registry via jsDelivr's global edge network — always the latest published version:

<script
  src="https://cdn.jsdelivr.net/npm/@pigmilcom/a11y/dist/a11y.cdn.js"
  data-position="bottom-right"
></script>

Pin to a specific version:

<script
  src="https://cdn.jsdelivr.net/npm/@pigmilcom/a11y@1.0.1/dist/a11y.cdn.js"
  data-position="bottom-right"
></script>

Self-hosted (pigmil CDN)

Use your own CDN endpoint for full control over deployment:

<script
  src="https://cdn.pigmil.com/a11y/dist/a11y.cdn.js"
  data-position="bottom-right"
></script>

The widget auto-mounts on DOMContentLoaded. React 18 and all CSS are bundled inside — nothing else needed.

data-position values

Value Placement
bottom-right Bottom-right corner (default)
bottom-left Bottom-left corner
top-right Top-right corner
top-left Top-left corner

data-theme values

Value Behaviour
omitted / auto Matches <html class="dark"> or color-scheme: dark; otherwise light
light Always use the light theme
dark Always use the dark theme

data-lang values

Value Language
en English (default)
es Spanish
fr French
de German
pt Portuguese
zh Chinese (Simplified)
ar Arabic
hi Hindi

Language resolution works like this:

  1. If data-lang or the React lang prop is provided, that language is used and saved to localStorage['a11y-lang'].
  2. If data-lang / lang is omitted, the widget reads localStorage['a11y-lang'].
  3. If localStorage['a11y-lang'] does not exist yet, the widget defaults to 'en' and saves it.

This means the widget language can be changed dynamically by updating localStorage['a11y-lang'].

<!-- Default — follows the page's html theme, reads a11y-lang from localStorage, falls back to en -->
<script
  src="https://cdn.jsdelivr.net/npm/@pigmilcom/a11y/dist/a11y.cdn.js"
  data-position="bottom-right"
></script>

<!-- Equivalent explicit auto mode -->
<script
  src="https://cdn.jsdelivr.net/npm/@pigmilcom/a11y/dist/a11y.cdn.js"
  data-position="bottom-right"
  data-theme="auto"
></script>

<!-- Force light theme -->
<script
  src="https://cdn.jsdelivr.net/npm/@pigmilcom/a11y/dist/a11y.cdn.js"
  data-position="bottom-right"
  data-theme="light"
></script>

<!-- Force dark theme -->
<script
  src="https://cdn.jsdelivr.net/npm/@pigmilcom/a11y/dist/a11y.cdn.js"
  data-position="bottom-right"
  data-theme="dark"
></script>

<!-- Spanish UI -->
<script
  src="https://cdn.jsdelivr.net/npm/@pigmilcom/a11y/dist/a11y.cdn.js"
  data-position="bottom-right"
  data-lang="es"
></script>
localStorage.setItem('a11y-lang', 'fr');
window.dispatchEvent(new CustomEvent('pgm-a11y-language-change', {
  detail: { lang: 'fr' }
}));

If you still pass data-lang="fr", that explicit value wins and also updates localStorage['a11y-lang'].

Programmatic control

The CDN build exposes window.PigmilA11y for manual control:

PigmilA11y.mount();    // mount the widget
PigmilA11y.unmount();  // remove the widget and clean up

npm Installation

npm install @pigmilcom/a11y
# yarn
yarn add @pigmilcom/a11y
# pnpm
pnpm add @pigmilcom/a11y

Features

  • Text size — Normal / Large / X-Large
  • High contrast
  • Invert colours
  • Grayscale
  • Reduce motion
  • Highlight links
  • Text spacing (WCAG 1.4.12)
  • Dyslexia-friendly font
  • Persists in localStorage
  • Full keyboard & screen-reader support (role="dialog", aria-checked, aria-expanded)
  • Zero runtime dependencies (React peer dep only)

Dist files

Every build outputs these files to dist/:

File Format Description
index.js CJS npm (unminified, for bundlers)
index.mjs ESM npm (unminified, for bundlers)
index.min.js CJS npm (minified)
index.min.mjs ESM npm (minified)
index.css CSS Stylesheet import (@pigmilcom/a11y/styles)
index.min.css CSS Minified stylesheet
a11y.cdn.js IIFE CDN bundle — React bundled, CSS inlined, minified

Quick start (React)

1 — Import the stylesheet (once, globally)

import '@pigmilcom/a11y/styles';

This single import contains both the panel UI styles and all the accessibility class rules applied to <html>.

2 — Drop in the widget

import a11y from '@pigmilcom/a11y';

// Assign to a capitalized variable to use in JSX
const A11y = a11y;

<A11y className="fixed bottom-4 right-4 rounded" />

The className prop is merged onto the trigger button — use Tailwind classes, custom classes, or anything your bundler supports to position the widget. The lang prop is reactive, and when provided it also syncs localStorage['a11y-lang'].


Customising the widget with CSS

The widget exposes stable a11y-widget-* class names on its key elements as a public API. Target them from a <style> tag (CDN / self-hosted) or your own stylesheet (React):

<style>
  /* Trigger button */
  .a11y-widget-btn {
    border-radius: 8px !important;
    background: rgba(15, 15, 40, 0.9) !important;
  }

  /* Dialog panel */
  .a11y-widget-dialog {
    border-color: rgba(99, 102, 241, 0.4) !important;
  }

  /* Toggle option rows */
  .a11y-widget-toggle-btn:hover {
    background: rgba(99, 102, 241, 0.06) !important;
  }
</style>
Public class Element
a11y-widget-btn Trigger button
a11y-widget-dialog Dialog panel
a11y-widget-toggle-btn Each toggle option

Place your <style> tag after the widget <script> tag so it takes precedence in the cascade. Use !important where the widget's base styles have higher specificity.


Tailwind CSS support

Tailwind utility classes work transparently via the className prop:

import a11y from '@pigmilcom/a11y';
const A11y = a11y;

// Tailwind classes are passed straight through to the trigger button
<A11y className="fixed bottom-6 right-6 rounded-full shadow-xl" lang="de" />

Because the classes come from your source files, Tailwind's JIT scanner picks them up automatically. No purge exceptions or safelist entries needed.


Framework examples

Next.js App Router

// app/layout.jsx
import '@pigmilcom/a11y/styles';
import a11y from '@pigmilcom/a11y';

const A11y = a11y;

export default function RootLayout({ children }) {
    return (
        <html lang="en">
            <body>
                {children}
                <A11y className="fixed bottom-4 right-4 rounded" lang="en" />
            </body>
        </html>
    );
}

The component is already marked 'use client' — no extra wrapper needed.

Vite + React

// src/App.jsx
import a11y from '@pigmilcom/a11y';

const A11y = a11y;

export default function App() {
    return (
        <>
            {/* …your content… */}
            <A11y className="fixed bottom-4 right-4 rounded" lang="pt" />
        </>
    );
}

Props

Prop Type Default Description
className string Extra classes added to the trigger <button>
theme string inferred Widget colour theme: 'auto' | 'light' | 'dark'
lang string localStorage['a11y-lang'] or 'en' Widget UI language: 'en', 'es', 'fr', 'de', 'pt', 'zh', 'ar', 'hi'

theme prop

Value Behaviour
omitted / 'auto' Matches <html class="dark"> or color-scheme: dark; otherwise light
'light' Always renders the light theme
'dark' Always renders the dark theme
import a11y from '@pigmilcom/a11y';
const A11y = a11y;

// Follow the page's html theme (default)
<A11y className="fixed bottom-4 right-4" />

// Equivalent explicit auto mode
<A11y className="fixed bottom-4 right-4" theme="auto" />

// Always light
<A11y className="fixed bottom-4 right-4" theme="light" />

// Always dark
<A11y className="fixed bottom-4 right-4" theme="dark" />

// Change the widget UI language
<A11y className="fixed bottom-4 right-4" lang="zh" />
localStorage.setItem('a11y-lang', 'ar');
window.dispatchEvent(new CustomEvent('pgm-a11y-language-change', {
  detail: { lang: 'ar' }
}));

CSS classes applied to <html>

When a preference is enabled the widget adds a class to document.documentElement. All rules live in @pigmilcom/a11y/styles and can be freely overridden in your own stylesheet.

Class Feature
a11y-text-lg Font size +18 %
a11y-text-xl Font size +45 %
a11y-high-contrast Contrast filter (1.6×)
a11y-invert Invert colours (re-inverts images/video)
a11y-grayscale Grayscale filter
a11y-reduce-motion Strips all animations & transitions
a11y-highlight-links Outlines every link and [role="link"]
a11y-text-spacing Letter / word / line spacing (WCAG 1.4.12)
a11y-dyslexia Switches to a high-readability system font

Storage

Preferences are saved under the localStorage key pgm-a11y as a JSON object. The widget reads and re-applies them on first client render, so preferences survive page reloads and navigation. On SSR the widget hydrates without errors — all DOM access is guarded by a mounted flag.

Widget language is stored separately under localStorage['a11y-lang'].

  • If data-lang or lang is provided, that value is normalized and saved to a11y-lang.
  • If no explicit language is provided, the widget reads a11y-lang.
  • If a11y-lang is missing, the widget sets it to 'en' automatically.
  • Updating a11y-lang and dispatching pgm-a11y-language-change updates the widget language dynamically on the same page.

Requirements

CDN

No dependencies. Works in any HTML page.

npm package

Peer dependency Version
react ≥ 17
react-dom ≥ 17

License

MIT © pigmilcom

Free Usage

The free version is available for any domain and includes:

  • Up to 1,000 requests / day per domain
  • Up to 30,000 requests / month per domain

Full version (white-label)

The full version removes all limits and PIGMIL branding and can be fully white-labeled for your product.

For inquiries contact PIGMIL:

About

Simple, lightweight accessibility widget that enhances usability and adds WCAG 2.1 features to any website instantly. Zero dependencies

Topics

Resources

License

Code of conduct

Contributing

Security policy

Stars

Watchers

Forks

Packages

 
 
 

Contributors