Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 20 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,7 @@ Composable React controls for display preferences and reading assistance. Choose
the controls in JSX, keep the widget outside your application layout, and let
the provider own state, persistence, effects, focus behavior, and cleanup.

The `2.0.2` patch release adds automatic widget stylesheet injection on package
import while retaining the standalone stylesheet export for explicit pipelines.
The `2.3.0` minor release introduces modular accessible control primitives (`FeatureCard`, `ToggleControl`, `ChoiceControl`, `StepperControl`), state UI (`SearchCount`, `EmptyState`, `ErrorState`), profile previews (`ProfilePreviewCard`), locale and oversized control modes (`largeControls`), and automated releases across npm, GitHub Releases, and GitHub Packages.
It supports React 18.3.1 and React 19.x, ships ESM, CommonJS, TypeScript declarations,
and scoped plain CSS, and has no runtime dependency other than React and React DOM.

Expand All @@ -18,10 +17,18 @@ and scoped plain CSS, and has no runtime dependency other than React and React D

## Installation

### From npm (recommended)

```sh
npm install accessibility-plugin-react
```

### From GitHub Packages

```sh
npm install @prabothcharith/accessibility-plugin-react --registry=https://npm.pkg.github.com
```

React and React DOM are peer dependencies and must use the same major/minor
line. The package requires Node.js 22.13 or newer for development and builds.
The widget itself does not require an API key, backend, environment variable,
Expand Down Expand Up @@ -104,6 +111,17 @@ and grayscale are applied in a deterministic order. Defaults leave host styles
unchanged, and the provider restores host inline values and priorities when an
effect is disabled or the provider is removed.

## Control primitives and profiles

Build custom accessible panels or custom features with modular primitives that include ARIA semantics, keyboard navigation, and Clean light styling:

- **`FeatureCard`**: Accessible container with header, icon, description, status badge, and alert states (`idle`, `active`, `error`, `unavailable`, `permission-required`).
- **`ToggleControl`**: Native switch button with `role="switch"`, `aria-checked`, and >=44px touch targets.
- **`ChoiceControl`**: Segmented or grouped choices with `role="radiogroup"`, Arrow key navigation (Left/Right/Up/Down/Home/End), and visual check indicators.
- **`StepperControl`**: Bounded decrement/increment buttons, `<output aria-live="polite">`, and optional range slider.
- **`SearchCount` & `EmptyState`**: Polite live announcements for feature filtering and search zero-results states.
- **`ProfilePreviewCard`**: Previews affected accessibility profile settings (e.g. low-vision, dyslexia, cognitive) and provides one-click reversal back to custom settings.

## Custom features

Use the two-argument hook for simple state, or provide validation and a
Expand Down
26 changes: 19 additions & 7 deletions docs/API.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,18 @@ All exports are named from `accessibility-plugin-react`. JavaScript consumers re

## Components

| Component | Props |
| --------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| AccessibilityPlugin | `children: ReactNode` required; `position?: 'left' \| 'right' \| 'top' \| 'bottom'` default right; `persistenceKey?: string` default `react-accessibility-plugin:state`; `persist?: boolean` default true |
| AccessibilityButton | `draggable?: boolean` default true (pointer/Alt+arrow movement); `icon?: ReactNode` default inline accessibility SVG; `title?: string` accessible name, default Accessibility; `description?: string` supplementary accessible description; `className?: string`; `style?: CSSProperties` |
| AccessibilityList | `footer?: ReactNode` extra fixed actions; `children: ReactNode` required; `title?: string` visible heading and region name, default Accessibility settings; `description?: string` default Adjust your reading experience (empty string omits it); `closeLabel?: string` default Close accessibility settings; `className?: string`; `style?: CSSProperties` |
| All fifteen controllers listed in FEATURES.md | No props. Pick/omit in JSX. For custom labels or behavior, compose a control with useAccessibility. |
| Component | Props |
| --------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| AccessibilityPlugin | `children: ReactNode` required; `position?: 'left' \| 'right' \| 'top' \| 'bottom'` default right; `persistenceKey?: string` default `react-accessibility-plugin:state`; `persist?: boolean` default true; `storage?: 'local' \| 'session' \| 'none'` default local; `locale?: string`; `statementUrl?: string`; `largeControls?: boolean` default false |
| AccessibilityButton | `draggable?: boolean` default true (pointer/Alt+arrow movement); `icon?: ReactNode` default inline accessibility SVG; `title?: string` accessible name, default Accessibility; `description?: string` supplementary accessible description; `className?: string`; `style?: CSSProperties`; `largeControls?: boolean` |
| AccessibilityList | `footer?: ReactNode` extra fixed actions; `children: ReactNode` required; `title?: string` visible heading and region name, default Accessibility settings; `description?: string` default Adjust your reading experience; `closeLabel?: string` default Close; `statementUrl?: string`; `largeControls?: boolean`; `locale?: string`; `className?: string`; `style?: CSSProperties` |
| FeatureCard | `title: ReactNode` required; `children: ReactNode` required; `featureKey?: string`; `icon?: ReactNode`; `description?: ReactNode`; `statusBadge?: string`; `status?: 'idle' \| 'active' \| 'error' \| 'unavailable' \| 'permission-required'`; `errorMessage?: string`; `unavailableMessage?: string`; `permissionMessage?: string`; `onRequestPermission?: () => void`; `largeControls?: boolean` |
| ToggleControl | `label: ReactNode` required; `checked: boolean` required; `onChange: (checked: boolean) => void` required; `disabled?: boolean`; `unavailable?: boolean`; `unavailableReason?: string`; `largeControls?: boolean` |
| ChoiceControl | `label: string` required; `value: T` required; `options?: ChoiceOption<T>[]`; `groups?: ChoiceGroup<T>[]`; `onChange: (value: T) => void` required; `disabled?: boolean`; `largeControls?: boolean` |
| StepperControl | `label: string` required; `value: number` required; `min: number`; `max: number`; `step: number`; `unit?: string` default '%'; `showSlider?: boolean`; `formatValue?: (val: number) => string`; `onChange: (value: number) => void` required; `disabled?: boolean`; `largeControls?: boolean` |
| ProfilePreviewCard | `profile: ProfileDefinition` required; `isActive: boolean` required; `onApply: (profile: ProfileDefinition) => void` required; `onRevert: () => void` required; `largeControls?: boolean` |
| SearchCount / EmptyState / ErrorState | Standalone accessible status and announcement primitives for search counts, zero-results fallbacks, and error recovery banners. |
| All fifteen controllers listed in FEATURES.md | No props. Pick/omit in JSX. For custom labels or behavior, compose a control with useAccessibility or the modular control primitives. |

Use one button/list pair per provider and one provider per document. UI IDs use React `useId`; do not target the old fixed `arap-panel` ID. Plugin children can include ordinary app content, but mounting the widget as a sibling makes intent clearer. Persistence configuration is fixed at mount; use `<AccessibilityPlugin key={newKey} persistenceKey={newKey}>` when switching storage identity.

Expand Down Expand Up @@ -37,7 +43,13 @@ Definitions/defaults are immutable for the provider lifetime; the first register

`FeatureRegistration` is `FeatureOptions<unknown>` plus `defaultValue: unknown`. Use the generic hook instead of manually casting callbacks. Legacy `registerReset(key, fn)` / `unregisterReset(key)` remain available; callbacks run before global state is cleared. They are not the primary effect lifecycle API. Callback owners must unregister their callback on teardown.

Exported types: `AccessibilityPluginProps`, `AccessibilityButtonProps`, `AccessibilityListProps`, `AccessibilityState`, `AccessibilityContextValue`, `PluginPosition`, `WidgetPlacement`, `FeatureOptions<T>`, `FeatureRegistration`.
Exported types: `AccessibilityPluginProps`, `AccessibilityButtonProps`, `AccessibilityListProps`, `AccessibilityState`, `AccessibilityContextValue`, `PluginPosition`, `WidgetPlacement`, `FeatureOptions<T>`, `FeatureRegistration`, `ToggleControlProps`, `ChoiceControlProps`, `ChoiceOption<T>`, `ChoiceGroup<T>`, `StepperControlProps`, `FeatureCardProps`, `FeatureStatus`, `SearchCountProps`, `EmptyStateProps`, `ErrorStateProps`, `ProfilePreviewCardProps`, `AccessibilityPreferences`, `ContrastMode`, `CursorSizePreference`, `FontFamilyPreference`, `ProfileDefinition`, `ProfilePresetId`, `ReaderMode`, `SaturationPreference`, `StorageMode`, `TextAlignPreference`, `LocaleStrings`.

Exported control components: `ToggleControl`, `ChoiceControl`, `StepperControl`, `FeatureCard`, `SearchCount`, `EmptyState`, `ErrorState`, `ProfilePreviewCard`.

Exported preference and profile utilities: `DEFAULT_PREFERENCES`, `PROFILE_DEFINITIONS`, `normalizePreferences`, `normalizePreferenceValue`, `resolveEffectivePreferences`, `preferencesReducer`, `createInitialPreferencesState`.

Exported localization utilities: `enLocale`, `registerLocale`, `resolveLocaleStrings`, `isRtlLocale`.

## Styling and labels

Expand Down
14 changes: 14 additions & 0 deletions docs/FEATURES.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,20 @@ All features are opt-in via controllers, use native buttons, support Enter/Space
| ReadingGuideController / readingGuide | false; boolean | 4px blue line with white border follows pointer or host focus. Can coexist with mask. |
| ResetController | No feature state | Clears display settings including custom/unmounted controls, preserves widget placement; runs legacy reset callbacks; keeps panel open. |

## Accessible control primitives and UI states

The library exports modular UI primitives conforming to WCAG 2.2 criteria:

| Primitive | Accessible Role & State | Key Capabilities |
| --------------------------- | --------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------- |
| `ToggleControl` | `role="switch"`, `aria-checked` | Keyboard Space/Enter toggle, disabled/unavailable states, >=44px touch targets (>=48px in largeControls mode). |
| `ChoiceControl` | `role="radiogroup"`, `role="radio"`, `aria-checked` | Arrow key (Left/Right/Up/Down/Home/End) cycling, check indicators, multi-level/category grouping. |
| `StepperControl` | `role="group"` with `<output aria-live="polite">` | Accessible increment/decrement buttons, boundary clamping, optional `role="slider"` range input. |
| `FeatureCard` | `role="group"`, `aria-label` | Card container with title, icon, description, status badge, and alert states (`idle`, `active`, `error`, `unavailable`, `permission-required`). |
| `SearchCount` | `role="status"`, `aria-live="polite"` | Live status announcement of matched and total features for search filtering. |
| `EmptyState` / `ErrorState` | `role="status"` / `role="alert"` | Clear messaging with action button for empty results and error recovery. |
| `ProfilePreviewCard` | `role="group"` | Previews affected settings for accessibility profiles (low-vision, dyslexia, etc.) and provides one-click reversal back to custom settings. |

## Original JavaScript and branch comparison

Reference: vanilla `47f44c5` (`accessibility-menu.js`, package 1.2.2); older React `7aef17c`; newer baseline `6dfbd44`. “Present” is source evidence, not proof of correctness. See baseline for defects.
Expand Down