npm.io
1.1.14 • Published 10m ago

ally-widget

Licence
MIT
Version
1.1.14
Deps
0
Size
1.1 MB
Vulns
0
Weekly
2.1K

Ally Widget

A lightweight, framework-free accessibility widget for any website. Drop it in with a single <script> tag or import it as an npm package — it adds a floating panel of accessibility controls that users can customize per-session.

  • No dependencies — vanilla JS, ~154 KB minified
  • Three delivery formats — CDN (IIFE), ESM (bundlers), CJS (Node/SSR)
  • Languages — English and Nepali built-in
  • WCAG 2.x aligned — focus trapping, ARIA roles, keyboard navigation throughout

Installation

CDN / Direct script tag

<script src="https://cdn.jsdelivr.net/npm/ally-widget/dist/ally-widget.min.js"></script>

Or download dist/ally-widget.min.js and host it yourself.

npm
npm install ally-widget
// ESM bundler (Vite, webpack, Rollup…)
import AllyWidget from 'ally-widget';

// CommonJS (Node / SSR)
const AllyWidget = require('ally-widget');

Quick Start

Script tag (zero config)

The widget auto-initialises on DOMContentLoaded. Just include the script:

<script src="dist/ally-widget.min.js"></script>

A button appears at the bottom-right corner. Press Alt + A to toggle the panel.

Configure before loading

Set window.AllyWidgetOptions before the script tag:

<script>
  window.AllyWidgetOptions = {
    position: 'bottom-right',
    lang: 'ne',                      // 'en' | 'ne'
    primaryColor: '#e11d48',
    poweredByText: 'My Company',
    poweredByUrl: 'https://mycompany.com'
  };
</script>
<script src="dist/ally-widget.min.js"></script>
ESM / npm
import AllyWidget from 'ally-widget';

const widget = new AllyWidget({
  position: 'bottom-left',
  primaryColor: '#0ea5e9',
  disableFeatures: ['hide-images', 'annotations'],
  onFeatureToggle(key, enabled) {
    analytics.track('a11y_toggle', { key, enabled });
  }
});

widget.startAccessibleWebWidget();

All Options

Option Type Default Description
position string 'bottom-right' 'bottom-right' | 'bottom-left' | 'top-right' | 'top-left'
offset [number, number] [20, 20] [horizontal, vertical] px from edge
size string '52px' Toggle button size — px, em, rem, or %
lang string browser lang 'en' or 'ne'
storageKey string 'ally-wgt' localStorage key — change to avoid collisions when multiple instances share a domain
keyboardShortcut boolean true Alt+A toggles the panel. Set false to disable
showViolationBubble boolean true Red badge on the toggle button showing the axe-core violation count. Set false to hide it
icon string 'default' Built-in variant name or a raw SVG string for the toggle button icon
icons object Override any icon in the full icon set (see Icon Keys)
poweredByText string 'Ally Widget' Footer link label
poweredByUrl string widget repo URL Footer link href
disableFeatures string[] [] Feature keys to hide from the panel (see Feature Keys)
featureOverrides object {} Override label, icon, iconColor, or iconInnerColor per feature key (use '*' as wildcard)
theme object All theme tokens nested under one key (alternative to passing tokens at the top level)
ttsNativeVoiceName string Exact browser voice name to prefer for TTS
ttsNativeVoiceLang string BCP-47 language tag for TTS voice selection (e.g. 'ne-NP')
ttsProxyUrl string URL of your server-side TTS proxy (see Nepali TTS — Server Proxy). When set, the widget POSTs ?text=…&lang=ne to this URL instead of calling Google directly.
ttsRate number 1 Speech rate — 0.5 to 2
ttsPitch number 1 Speech pitch — 0 to 2
onOpen function Called when the panel opens
onClose function Called when the panel closes
onFeatureToggle function(key, enabled) Called on every feature toggle
onReset function Called when the user resets all settings

Nepali TTS — Server Proxy

Most desktop browsers do not ship a ne-NP (Nepali) voice. When lang: 'ne' is set and no native voice is found, the widget automatically falls back to Google Translate TTS. However, Google's TTS endpoint blocks direct browser requests.

The solution is a server-side proxy: a small endpoint on your own server that forwards the request to Google with the correct headers. Set ttsProxyUrl to your proxy's path and the widget will call it instead.

AllyWidget.init({
  lang: 'ne',
  ttsProxyUrl: '/api/tts',   // your proxy endpoint
});

The widget calls: GET /api/tts?text=<encoded-text>&lang=ne

Your proxy must return the audio (audio/mpeg) directly in the response body.

See INTEGRATION.md → Nepali TTS Proxy for copy-paste implementations in Next.js, Express, PHP, Laravel, and WordPress.


Theme Customization

Pass any of these tokens directly in options, or group them under theme: { … }:

window.AllyWidgetOptions = {
  // Option A — flat
  primaryColor: '#e11d48',
  borderRadius: '8px',

  // Option B — grouped (same result)
  theme: {
    primaryColor: '#e11d48',
    borderRadius: '8px'
  }
};
Theme tokens
Token Default Notes
primaryColor #1976d2 Main brand color — fallback for any unset granular key
primaryColorLight #42a5f5 Lighter tint
primaryColorDark #0d47a1 Darker shade
backgroundColor #f5f7fa Panel background
textColor #222222 Panel text
textColorInverted #ffffff Text on primary background
cardBackground #ffffff Feature card background
borderColor #d1d5db Dividers and borders
focusRingColor #1976d2 Keyboard focus ring
hoverColor #42a5f5 Button hover border/shadow color
activeColor #0d47a1 Legacy active color
borderRadius 8px Panel corner radius
buttonBorderRadius 0.4rem Toggle button shape
headerHeight 54px Panel header height
focusBorderWidth 3px Focus ring width
focusOutlineOffset 2px Focus ring offset
zIndex 100000 Stacking context
buttonSize 52px Alias for size
Granular color tokens

Each element of the widget can be independently colored. Unset keys fall back to primaryColor.

Toggle button (floating launcher)

Token Default Controls
toggleButtonBg primaryColor Floating button background
toggleButtonRingColor primaryColor Inset ring color
toggleButtonIconColor #ffffff SVG fill on the toggle icon
toggleButtonBorderRadius 50% Button corner radius (e.g. '16px' for rounded-square)
toggleButtonShadow 0 4px 14px rgba(0,0,0,0.22) Resting drop shadow
toggleButtonShadowHover 0 8px 22px rgba(0,0,0,0.22) Shadow on hover / focus
toggleButtonLabel 'Accessibility' Text revealed on hover-expand
toggleButtonShortcut '' Shortcut hint revealed on hover-expand (e.g. 'Ctrl+F2')
toggleLabelFontSize 1rem Font size of the hover label
toggleLabelFontWeight 700 Font weight of the hover label
toggleShortcutFontSize 0.7rem Font size of the shortcut hint
toggleShortcutFontWeight 600 Font weight of the shortcut hint

Menu header bar

Token Default Controls
menuHeaderBg primaryColor Header background
menuHeaderColor #ffffff Header title text and icon fill

Feature buttons (inside the menu)

Token Default Controls
featureIconColor #222222 Default icon fill for all feature buttons
featureIconActiveColor #ffffff Icon fill when a feature is ON
featureButtonActiveBg primaryColor Button background when feature is ON
featureButtonActiveBorderColor primaryColor Button border when feature is ON
featureButtonHoverBg #f3f4f6 Button hover background

Section headings

Token Default Controls
sectionTitleColor #6b7280 Section heading text color

Panel sizing & scaling

Token Default Controls
menuScale 1 CSS transform: scale() — scales the entire panel uniformly (text, icons, padding). transform-origin auto-set from position. E.g. '0.85'
menuMaxWidth 430px Maximum width of the panel
menuFontSize 16px Base font size of the panel
menuBorderRadius 16px Panel corner radius
menuContentPadding 14px Horizontal padding inside the panel
menuSectionGap 16px Gap between top-level sections (Speech, Text, Color…)
menuCategoryGap 10px Gap between items within a section
sectionTitleFontSize 14px Font size of section headings (Speech, Text…)
featureButtonGap 12px Gap between feature buttons in the grid
featureButtonPadding 10px Inner padding of each feature button
featureButtonFontSize 15px Base font size inside feature buttons
featureButtonLabelSize 13px Font size of feature button labels
featureIconSize 24px SVG icon size inside feature buttons
headerIconSize 28px SVG icon size in the menu header (close / back)

Feature Keys

Use these keys with disableFeatures and featureOverrides.

Text
Key Label
text-scale Font Size (slider)
bold-text Font Weight
line-spacing Line Height
letter-spacing Letter Spacing
readable-text Dyslexia Font
Color & Contrast
Key Label
contrast-toggle Contrast (cycles light → dark)
saturation-toggle Saturation (cycles low → high)
invert-colors Invert Colors
Reading Aids
Key Label
highlight-links Highlight Links
highlight-title Highlight Titles
reading-aid Reading Guide
simple-layout Simplify Layout
Interaction
Key Label
large-pointer Big Cursor
pause-motion Stop Animations
hide-images Hide Images
high-contrast-mode High Contrast
Speech
Key Label
text-to-speech Text to Speech
Dev mode only (?ally-dev=true)
Key Label
annotations Annotations (axe-core overlays)
accessibility-report Accessibility Report

Disabling Features

window.AllyWidgetOptions = {
  disableFeatures: [
    'hide-images',
    'readable-text',   // remove Dyslexia Font button
    'annotations',
    'accessibility-report'
  ]
};

Feature Label, Icon & Color Overrides

Override any feature button's label, icon SVG, or icon colors — per feature key, without touching the source.

window.AllyWidgetOptions = {
  featureOverrides: {
    // Rename a button
    'bold-text': {
      label: 'Bold'
    },

    // Rename + swap icon SVG
    'text-to-speech': {
      label: 'Read Aloud',
      icon: '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">…</svg>'
    },

    // Color overrides — no SVG rewrite needed
    'reading-aid': {
      iconColor: '#0369a1'            // main icon fill
    },
    'contrast-toggle': {
      iconColor: '#6d28d9',           // outer path color
      iconInnerColor: '#c4b5fd'       // inner/secondary path color
    },

    // Wildcard — applies to all features not individually overridden
    '*': {
      iconColor: '#374151'
    }
  }
};

iconColor sets the main SVG fill. iconInnerColor targets secondary paths on compound icons (contrast, high-contrast, saturation, invert-colors). Both override featureIconColor from the theme. Per-feature values always beat the wildcard.


Icon Keys

Pass a partial icons object to swap out any icon in the set:

window.AllyWidgetOptions = {
  icons: {
    largePointer:      '<svg>…</svg>',
    pauseMotion:       '<svg>…</svg>',
    readingAid:        '<svg>…</svg>',
    textToSpeech:      '<svg>…</svg>',
    highContrast:      '<svg>…</svg>',
    simplifyLayout:    '<svg>…</svg>',
    boldText:          '<svg>…</svg>',
    lineSpacing:       '<svg>…</svg>',
    letterSpacing:     '<svg>…</svg>',
    hideImages:        '<svg>…</svg>',
    dyslexiaFont:      '<svg>…</svg>',
    highlightLinks:    '<svg>…</svg>',
    highlightTitle:    '<svg>…</svg>',
    adjustFontSize:    '<svg>…</svg>',
    contrast:          '<svg>…</svg>',
    invertColors:      '<svg>…</svg>',
    saturation:        '<svg>…</svg>',
    annotations:       '<svg>…</svg>',
    accessibilityReport: '<svg>…</svg>',
    reset:             '<svg>…</svg>',
    accessibility:     '<svg>…</svg>'  // toggle button default icon
  }
};

Language

The widget ships with English (en) and Nepali (ne).

window.AllyWidgetOptions = { lang: 'ne' };

The language picker in the panel footer lets users switch at runtime. The selection is persisted to localStorage.


Text to Speech

The widget uses the browser's native Web Speech API — no external service or API key required.

When TTS is enabled, click any text element on the page to hear it read aloud.

Nepali TTS — modern browsers (Chrome, Edge) include a ne-NP voice. To force a specific voice:

window.AllyWidgetOptions = {
  ttsNativeVoiceLang: 'ne-NP',
  ttsNativeVoiceName: 'Google नेपाली',  // optional — exact voice name
  ttsRate: 0.9,
  ttsPitch: 1.0
};

If the requested voice is unavailable the widget falls back to the browser's default voice.


Event Callbacks

window.AllyWidgetOptions = {
  onOpen() {
    console.log('Panel opened');
  },

  onClose() {
    console.log('Panel closed');
  },

  onFeatureToggle(key, enabled) {
    // key   — feature key string (e.g. 'bold-text')
    // enabled — boolean (true = on, false = off)
    analytics.track('a11y_feature', { key, enabled });
  },

  onReset() {
    console.log('All settings cleared');
  }
};

Data Attributes

You can configure the widget via attributes on the script tag instead of window.AllyWidgetOptions:

<script
  src="dist/ally-widget.min.js"
  data-ally-lang="ne"
  data-ally-position="bottom-left"
  data-ally-size="60px"
  data-ally-offset="24,24"
></script>

Supported attributes: data-ally-lang, data-ally-position, data-ally-offset, data-ally-size, data-ally-icon.


Framework Integration

The widget is browser-only. AllyWidget.init() returns null when called server-side, so it is always safe to import — but you must ensure it mounts after the DOM is ready.

Stack Approach
Plain HTML Script tag — just works
React / Vite useEffect dynamic import
Next.js App Router 'use client' component + useEffect
Next.js Pages Router useEffect in _app.jsx
Vue 3 onMounted in App.vue
Nuxt 3 .client.js plugin
SvelteKit onMount in +layout.svelte
Astro <script> with is:inline in Layout
WordPress wp_enqueue_script in functions.php
Laravel / Blade Script tag in layout
Angular ngAfterViewInit in AppComponent
PHP (plain) Script tag before </body>

Plain HTML
<!DOCTYPE html>
<html lang="en">
<head>...</head>
<body>
  <!-- your content -->

  <script>
    window.AllyWidgetOptions = {
      position: 'bottom-right',
      primaryColor: '#6366f1',
      lang: 'en'
    };
  </script>
  <script src="https://cdn.jsdelivr.net/npm/ally-widget/dist/ally-widget.min.js"></script>
</body>
</html>

React (Vite / CRA)
// src/components/AllyWidget.jsx
import { useEffect } from 'react'

export default function AllyWidget() {
  useEffect(() => {
    import('ally-widget').then(({ default: AllyWidget }) => {
      AllyWidget.init({ primaryColor: '#6366f1' })
    })
  }, [])

  return null
}
// src/App.jsx
import AllyWidget from './components/AllyWidget'

export default function App() {
  return (
    <>
      <YourApp />
      <AllyWidget />
    </>
  )
}

Next.js (App Router)
// components/AllyWidget.jsx
'use client'
import { useEffect } from 'react'

export default function AllyWidget() {
  useEffect(() => {
    import('ally-widget').then(({ default: AllyWidget }) => {
      AllyWidget.init({ primaryColor: '#6366f1', lang: 'en' })
    })
  }, [])

  return null
}
// app/layout.jsx
import AllyWidget from '@/components/AllyWidget'

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

Or — CDN via next/script (zero npm install):

// app/layout.jsx
import Script from 'next/script'

export default function RootLayout({ children }) {
  return (
    <html lang="en">
      <body>
        {children}
        <Script id="ally-cfg" strategy="beforeInteractive">{`
          window.AllyWidgetOptions = { primaryColor: '#6366f1' };
        `}</Script>
        <Script
          src="https://cdn.jsdelivr.net/npm/ally-widget/dist/ally-widget.min.js"
          strategy="afterInteractive"
        />
      </body>
    </html>
  )
}

Next.js (Pages Router)
// pages/_app.jsx
import { useEffect } from 'react'

export default function App({ Component, pageProps }) {
  useEffect(() => {
    import('ally-widget').then(({ default: AllyWidget }) => {
      AllyWidget.init({ primaryColor: '#6366f1' })
    })
  }, [])

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

Vue 3 (Vite)
// src/App.vue
<script setup>
import { onMounted } from 'vue'

onMounted(async () => {
  const { default: AllyWidget } = await import('ally-widget')
  AllyWidget.init({ primaryColor: '#6366f1' })
})
</script>

Nuxt 3
// plugins/ally-widget.client.js
// The .client suffix tells Nuxt this is browser-only — no SSR guard needed
import AllyWidget from 'ally-widget'

export default defineNuxtPlugin(() => {
  AllyWidget.init({ primaryColor: '#6366f1' })
})

SvelteKit
<!-- src/routes/+layout.svelte -->
<script>
  import { onMount } from 'svelte'

  onMount(async () => {
    const { default: AllyWidget } = await import('ally-widget')
    AllyWidget.init({ primaryColor: '#6366f1' })
  })
</script>

<slot />

Astro
<!-- src/layouts/Layout.astro -->
---
// server-side — nothing here
---
<html lang="en">
  <head>...</head>
  <body>
    <slot />

    <script>
      // This block runs in the browser only
      import AllyWidget from 'ally-widget'
      AllyWidget.init({ primaryColor: '#6366f1' })
    </script>
  </body>
</html>

WordPress
// functions.php (child theme or plugin)
function enqueue_ally_widget() {
    wp_enqueue_script(
        'ally-widget',
        'https://cdn.jsdelivr.net/npm/ally-widget/dist/ally-widget.min.js',
        [],
        '1.0.0',
        true   // load in footer
    );

    wp_add_inline_script(
        'ally-widget',
        "window.AllyWidgetOptions = {
            position: 'bottom-right',
            primaryColor: '#6366f1',
            poweredByText: '" . get_bloginfo('name') . "'
        };",
        'before'
    );
}
add_action('wp_enqueue_scripts', 'enqueue_ally_widget');

Laravel / Blade
{{-- resources/views/layouts/app.blade.php --}}
<script>
  window.AllyWidgetOptions = {
    position: 'bottom-right',
    primaryColor: '#6366f1',
    poweredByText: '{{ config("app.name") }}'
  };
</script>
<script src="{{ asset('vendor/ally-widget/ally-widget.min.js') }}"></script>
{{-- or CDN: src="https://cdn.jsdelivr.net/npm/ally-widget/dist/ally-widget.min.js" --}}

Angular
// src/app/app.component.ts
import { Component, AfterViewInit } from '@angular/core'

@Component({ selector: 'app-root', templateUrl: './app.component.html' })
export class AppComponent implements AfterViewInit {
  ngAfterViewInit() {
    import('ally-widget').then(({ default: AllyWidget }) => {
      AllyWidget.init({ primaryColor: '#6366f1' })
    })
  }
}

PHP (plain)
<!-- footer.php or before </body> -->
<script>
  window.AllyWidgetOptions = {
    position: 'bottom-right',
    primaryColor: '#6366f1'
  };
</script>
<script src="https://cdn.jsdelivr.net/npm/ally-widget/dist/ally-widget.min.js"></script>

AllyWidget.init() reference
const instance = AllyWidget.init(options)
// Returns the widget instance, or null when called server-side.
// Handles DOMContentLoaded automatically — safe to call at any time.
Disabling auto-init

Suppress the automatic CDN init when you are calling AllyWidget.init() yourself:

<script>
  window.AllyWidgetOptions = { autoInit: false };
</script>

Customization Guide

Minimal setup — just the essentials

Hide features your site doesn't need to keep the panel focused:

AllyWidget.init({
  disableFeatures: [
    'readable-text',       // remove Dyslexia Font
    'hide-images',         // remove Hide Images
    'annotations',         // hide dev tools in prod
    'accessibility-report'
  ]
})

Custom colour theme

Pass any CSS value. Use primaryColor as a single fallback, or override each element independently with the granular tokens:

AllyWidget.init({
  // Single fallback — all granular keys derive from this if not set
  primaryColor: '#e11d48',

  // ── Toggle button ────────────────────────────────────────
  toggleButtonBg:       '#0f172a',     // dark button
  toggleButtonRingColor:'#e11d48',     // red ring
  toggleButtonIconColor:'#f8fafc',     // near-white icon

  // ── Menu header ──────────────────────────────────────────
  menuHeaderBg:         '#1e293b',     // dark header
  menuHeaderColor:      '#ffffff',

  // ── Feature buttons ──────────────────────────────────────
  featureIconColor:     '#374151',     // default icon fill
  featureIconActiveColor: '#ffffff',   // icon color when ON
  featureButtonActiveBg:  '#e11d48',  // active button bg
  featureButtonHoverBg:   '#f1f5f9',  // hover bg

  // ── Section titles ───────────────────────────────────────
  sectionTitleColor:    '#9ca3af',

  // ── Panel ────────────────────────────────────────────────
  backgroundColor:      '#0f172a',
  cardBackground:       '#1e293b',
  borderColor:          '#334155',
  borderRadius:         '8px',
  buttonBorderRadius:   '12px',
  zIndex:               99999
})

Expanding FAB toggle button

The toggle button expands on hover to reveal a label and optional keyboard shortcut. All aspects are configurable:

AllyWidget.init({
  size:                    '58px',
  toggleButtonBg:          '#C5161D',     // button background
  toggleButtonIconColor:   '#ffffff',     // icon color
  toggleButtonBorderRadius:'16px',        // rounded-square (use '50%' for circle)
  toggleButtonShadow:      '0 4px 14px rgba(0,0,0,0.25)',
  toggleButtonShadowHover: '0 8px 22px rgba(0,0,0,0.28)',

  // Text revealed on hover
  toggleButtonLabel:       'Accessibility',
  toggleButtonShortcut:    'Ctrl+F2',     // omit or set '' to hide
  toggleLabelFontSize:     '1.1rem',
  toggleLabelFontWeight:   '700',
  toggleShortcutFontSize:  '0.7rem',
  toggleShortcutFontWeight:'600',
})

The button animates from its square size to roughly 3× its width on hover, then snaps back when the menu is open.


Custom branding

Replace the footer link with your own, or pass an empty string to hide it entirely:

AllyWidget.init({
  poweredByText: 'My Company',      // shown in the menu footer
  poweredByUrl:  'https://mycompany.com'
})

// Hide the branding link completely:
AllyWidget.init({ poweredByText: '' })

Rename, re-icon, or recolor any button
AllyWidget.init({
  featureOverrides: {
    // Rename
    'text-to-speech': { label: 'Read Page Aloud' },
    'reading-aid':    { label: 'Focus Line' },

    // Rename + custom SVG
    'bold-text': {
      label: 'Bold',
      icon:  '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">…</svg>'
    },

    // Color only — no SVG required
    'contrast-toggle':  { iconColor: '#6d28d9', iconInnerColor: '#c4b5fd' },
    'highlight-links':  { iconColor: '#0369a1' },

    // Wildcard — all others
    '*': { iconColor: '#374151' }
  }
})

Custom toggle button icon
AllyWidget.init({
  // Built-in variants: 'default' | 'person' | 'eye' | 'universal'
  icon: 'universal',

  // Or pass any SVG string:
  icon: '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">…</svg>'
})

Position and size
AllyWidget.init({
  position: 'bottom-left',   // bottom-right | bottom-left | top-right | top-left
  offset:   [24, 32],        // [horizontal, vertical] px from edge
  size:     '60px'           // toggle button size
})

Event callbacks for analytics
AllyWidget.init({
  onOpen()  { analytics.track('ally_open') },
  onClose() { analytics.track('ally_close') },

  onFeatureToggle(key, enabled) {
    analytics.track('ally_feature', { key, enabled })
    // key examples: 'bold-text', 'contrast-toggle', 'text-to-speech'
  },

  onReset() { analytics.track('ally_reset') }
})

Full example — everything configured
window.AllyWidgetOptions = {
  // Placement
  position:         'bottom-right',
  offset:           [20, 20],
  size:             '56px',
  keyboardShortcut: true,            // Alt+A

  // Language
  lang:             'ne',            // 'en' | 'ne'

  // Storage
  storageKey:       'myapp-ally',

  // Branding
  poweredByText:    'My Company',
  poweredByUrl:     'https://mycompany.com',

  // Theme — base
  primaryColor:     '#6366f1',
  borderRadius:     '12px',
  buttonBorderRadius: '50%',
  zIndex:           9999,

  // Theme — toggle button FAB
  toggleButtonBg:           '#6366f1',
  toggleButtonIconColor:    '#ffffff',
  toggleButtonBorderRadius: '50%',
  toggleButtonShadow:       '0 4px 14px rgba(0,0,0,0.22)',
  toggleButtonShadowHover:  '0 8px 22px rgba(0,0,0,0.22)',
  toggleButtonLabel:        'Accessibility',
  toggleButtonShortcut:     'Ctrl+F2',
  toggleLabelFontSize:      '1rem',
  toggleLabelFontWeight:    '700',
  toggleShortcutFontSize:   '0.7rem',
  toggleShortcutFontWeight: '600',

  // Theme — menu
  menuHeaderBg:           '#4f46e5',
  featureIconColor:       '#374151',
  featureIconActiveColor: '#ffffff',
  featureButtonActiveBg:  '#6366f1',
  sectionTitleColor:      '#6b7280',

  // Features
  disableFeatures:  ['annotations', 'accessibility-report'],

  featureOverrides: {
    'text-to-speech': { label: 'Read Aloud' },
    '*':              { iconColor: '#374151' },
    'contrast-toggle':{ iconColor: '#6366f1', iconInnerColor: '#a5b4fc' }
  },

  // TTS
  ttsNativeVoiceLang: 'ne-NP',
  ttsRate:            0.9,
  ttsPitch:           1.0,

  // Callbacks
  onOpen()  { console.log('opened') },
  onClose() { console.log('closed') },
  onFeatureToggle(key, enabled) { console.log(key, enabled) },
  onReset() { console.log('reset') }
}

Dev Mode

Append ?ally-dev=true to any URL to enable the Annotations and Accessibility Report tools. These run an axe-core scan and overlay issue markers on the page — useful during development, hidden in production.

https://yoursite.com/page?ally-dev=true

The axe-core library is loaded on demand in dev mode only — zero weight in production.

Hiding the violation badge

When violations are found, a red number badge appears on the toggle button. To hide it:

AllyWidget.init({
  showViolationBubble: false
});

Keyboard Shortcut

Alt + A toggles the panel on any page. Disable it with keyboardShortcut: false.


Publishing to npm

# Set your token
export NPM_TOKEN=your_token_here   # or put it in .env

# Build then publish
npm run build
npm publish

The .npmrc at the project root reads NPM_TOKEN automatically:

//registry.npmjs.org/:_authToken=${NPM_TOKEN}

License

MIT — see LICENSE

Keywords