@pigmilcom/a11y
v1.2.9
Published
WCAG 2.1 accessibility widget for React — text size, contrast, dyslexia font, motion reduction, and more.
Maintainers
Readme
@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/[email protected]/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:
- If
data-langor the Reactlangprop is provided, that language is used and saved tolocalStorage['a11y-lang']. - If
data-lang/langis omitted, the widget readslocalStorage['a11y-lang']. - 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 upnpm Installation
npm install @pigmilcom/a11y
# yarn
yarn add @pigmilcom/a11y
# pnpm
pnpm add @pigmilcom/a11yFeatures
- 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!importantwhere 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-langorlangis provided, that value is normalized and saved toa11y-lang. - If no explicit language is provided, the widget reads
a11y-lang. - If
a11y-langis missing, the widget sets it to'en'automatically. - Updating
a11y-langand dispatchingpgm-a11y-language-changeupdates 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:
- Email: [email protected]
- Web: pigmil.com
