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.
No React, no npm, no build step. Add one tag to your <head>.
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>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.
| Value | Placement |
|---|---|
bottom-right |
Bottom-right corner (default) |
bottom-left |
Bottom-left corner |
top-right |
Top-right corner |
top-left |
Top-left corner |
| 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 |
| 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'].
The CDN build exposes window.PigmilA11y for manual control:
PigmilA11y.mount(); // mount the widget
PigmilA11y.unmount(); // remove the widget and clean upnpm install @pigmilcom/a11y
# yarn
yarn add @pigmilcom/a11y
# pnpm
pnpm add @pigmilcom/a11y- 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)
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 |
import '@pigmilcom/a11y/styles';This single import contains both the panel UI styles and all the accessibility
class rules applied to <html>.
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'].
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 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.
// 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.
// 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" />
</>
);
}| 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' |
| 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' }
}));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 |
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.
No dependencies. Works in any HTML page.
| Peer dependency | Version |
|---|---|
react |
≥ 17 |
react-dom |
≥ 17 |
MIT © pigmilcom
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
The full version removes all limits and PIGMIL branding and can be fully white-labeled for your product.
For inquiries contact PIGMIL:
- Email: webmaster@pigmil.com
- Web: pigmil.com