@accessibility-rn-js/react-native-accessibility-toolkit 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md ADDED
@@ -0,0 +1,251 @@
1
+ # React Native Accessibility Toolkit
2
+
3
+ A comprehensive, production-ready accessibility plugin for React Native applications. This toolkit provides features like text-to-speech, screen reader support, dynamic color adjustments, text scaling, and more to make your React Native apps accessible to all users.
4
+
5
+ ## ✨ Features
6
+
7
+ - **🔊 Text-to-Speech (TTS)** - Read content aloud with customizable voice controls
8
+ - **📱 Native Accessibility** - TalkBack (Android) & VoiceOver (iOS) integration
9
+ - **🎨 Dynamic Colors** - High contrast, grayscale, color inversion, saturation controls
10
+ - **📏 Text Controls** - Font scaling, line height, letter spacing adjustments
11
+ - **👁️ Visual Aids** - Hide images, enlarge buttons, reduced motion
12
+ - **📖 Reading Features** - Text alignment, reading guides, text magnification
13
+ - **🎯 Accessibility Profiles** - Pre-configured settings for specific needs
14
+ - **💾 Persistent Storage** - Automatically saves user preferences
15
+ - **🌐 Cross-Platform** - Works on both iOS and Android
16
+
17
+ ## 📦 Installation
18
+
19
+ ```bash
20
+ npm install @yourorg/react-native-accessibility-toolkit
21
+ # or
22
+ yarn add @yourorg/react-native-accessibility-toolkit
23
+ ```
24
+
25
+ ### Install Peer Dependencies
26
+
27
+ ```bash
28
+ npm install react-native-tts @react-native-async-storage/async-storage react-native-vector-icons
29
+ ```
30
+
31
+ ### iOS Setup
32
+
33
+ ```bash
34
+ cd ios && pod install
35
+ ```
36
+
37
+ Add to your `Info.plist`:
38
+ ```xml
39
+ <key>NSSpeechRecognitionUsageDescription</key>
40
+ <string>This app uses speech recognition for accessibility features</string>
41
+ ```
42
+
43
+ ### Android Setup
44
+
45
+ For react-native-vector-icons, add to `android/app/build.gradle`:
46
+ ```gradle
47
+ apply from: "../../node_modules/react-native-vector-icons/fonts.gradle"
48
+ ```
49
+
50
+ ## 🚀 Quick Start
51
+
52
+ ### 1. Wrap Your App
53
+
54
+ ```javascript
55
+ import { AccessibilityProvider } from '@yourorg/react-native-accessibility-toolkit';
56
+
57
+ export default function App() {
58
+ return (
59
+ <AccessibilityProvider>
60
+ {/* Your app content */}
61
+ </AccessibilityProvider>
62
+ );
63
+ }
64
+ ```
65
+
66
+ ### 2. Add Accessibility Button
67
+
68
+ ```javascript
69
+ import { AccessibilityButton } from '@yourorg/react-native-accessibility-toolkit';
70
+
71
+ function MyScreen() {
72
+ return (
73
+ <View>
74
+ {/* Your content */}
75
+ <AccessibilityButton />
76
+ </View>
77
+ );
78
+ }
79
+ ```
80
+
81
+ ### 3. Use Accessible Components
82
+
83
+ ```javascript
84
+ import { AccessibleText, useAccessibility } from '@yourorg/react-native-accessibility-toolkit';
85
+
86
+ function MyComponent() {
87
+ const { fontScale, textColor } = useAccessibility();
88
+
89
+ return (
90
+ <AccessibleText baseFontSize={16}>
91
+ This text respects accessibility settings!
92
+ </AccessibleText>
93
+ );
94
+ }
95
+ ```
96
+
97
+ ## 📖 API Documentation
98
+
99
+ ### Core Hooks
100
+
101
+ #### `useAccessibility()`
102
+ ```javascript
103
+ const {
104
+ fontScale, // Current font scale (1.0 - 2.0)
105
+ textColor, // Custom text color
106
+ backgroundColor, // Custom background color
107
+ updateSetting, // Update single setting
108
+ setProfile, // Apply accessibility profile
109
+ openModal, // Open accessibility settings
110
+ } = useAccessibility();
111
+ ```
112
+
113
+ #### `useDynamicColors()`
114
+ ```javascript
115
+ const colors = useDynamicColors();
116
+ // Returns: { primaryTextColor, defaultBackground, primaryButtonColor, etc. }
117
+ ```
118
+
119
+ #### `usePageRead(textContent)`
120
+ ```javascript
121
+ const { start, pause, resume, stop, isSpeaking } = usePageRead("Text to read");
122
+ ```
123
+
124
+ ### Components
125
+
126
+ - **`<AccessibleText>`** - Auto-scales and styles text
127
+ - **`<AccessibilityButton>`** - Floating accessibility menu button
128
+
129
+ ### Services
130
+
131
+ - **`TTSService`** - Text-to-speech functionality
132
+ - **`NativeAccessibilityBridge`** - Native platform integration
133
+
134
+ ### Profiles
135
+
136
+ ```javascript
137
+ import { ACCESSIBILITY_PROFILES } from '@yourorg/react-native-accessibility-toolkit';
138
+
139
+ // Available profiles:
140
+ ACCESSIBILITY_PROFILES.BLIND // Screen reader optimized
141
+ ACCESSIBILITY_PROFILES.DYSLEXIA // Increased spacing, reading aids
142
+ ACCESSIBILITY_PROFILES.LOW_VISION // Maximum text, high contrast
143
+ ACCESSIBILITY_PROFILES.COGNITIVE // Simplified, clear layout
144
+ ACCESSIBILITY_PROFILES.EPILEPSY_SAFE // No animations, reduced saturation
145
+ ACCESSIBILITY_PROFILES.ADHD_FOCUS // Reading mask, minimal distractions
146
+ ```
147
+
148
+ ## 🎨 Customization
149
+
150
+ ### Update Individual Settings
151
+
152
+ ```javascript
153
+ const { updateSetting } = useAccessibility();
154
+
155
+ updateSetting('fontScale', 1.5);
156
+ updateSetting('colorTheme', 'dark');
157
+ updateSetting('textColor', '#FF0000');
158
+ ```
159
+
160
+ ### Apply Complete Profile
161
+
162
+ ```javascript
163
+ const { setProfile } = useAccessibility();
164
+
165
+ setProfile(ACCESSIBILITY_PROFILES.DYSLEXIA);
166
+ ```
167
+
168
+ ## 🛠️ Advanced Usage
169
+
170
+ ### Custom Styling with Accessibility
171
+
172
+ ```javascript
173
+ import { useAccessibility, getAdjustedFontSize } from '@yourorg/react-native-accessibility-toolkit';
174
+
175
+ function CustomComponent() {
176
+ const { fontScale, backgroundColor, textColor } = useAccessibility();
177
+
178
+ const styles = StyleSheet.create({
179
+ container: {
180
+ backgroundColor: backgroundColor || '#FFFFFF',
181
+ },
182
+ text: {
183
+ fontSize: getAdjustedFontSize(16, fontScale),
184
+ color: textColor || '#000000',
185
+ },
186
+ });
187
+
188
+ return <View style={styles.container}>...</View>;
189
+ }
190
+ ```
191
+
192
+ ### Screen Reader Announcements
193
+
194
+ ```javascript
195
+ import { NativeAccessibilityBridge } from '@yourorg/react-native-accessibility-toolkit';
196
+
197
+ // Announce important messages
198
+ NativeAccessibilityBridge.announce("Form submitted successfully");
199
+ ```
200
+
201
+ ## 📱 Platform Support
202
+
203
+ - **iOS**: 12.4+
204
+ - **Android**: API 21+
205
+ - **React Native**: 0.60.0+
206
+
207
+ ## 🤝 Before Publishing
208
+
209
+ ### Update Package Name
210
+
211
+ Replace `@yourorg/react-native-accessibility-toolkit` with your actual npm organization/package name in:
212
+ - `package.json`
213
+ - This README
214
+ - Example code
215
+
216
+ ### Update Author & Repository
217
+
218
+ Edit `package.json`:
219
+ ```json
220
+ {
221
+ "author": "Your Name <your.email@example.com>",
222
+ "repository": {
223
+ "url": "https://github.com/yourusername/your-repo.git"
224
+ }
225
+ }
226
+ ```
227
+
228
+ ### Publishing to NPM
229
+
230
+ ```bash
231
+ cd PLUGIN_SETUP
232
+ npm login
233
+ npm publish --access public
234
+ ```
235
+
236
+ ## 📄 License
237
+
238
+ MIT
239
+
240
+ ## 🙏 Acknowledgments
241
+
242
+ Built following WCAG 2.1 accessibility guidelines to ensure apps work for everyone.
243
+
244
+ ## 📞 Support
245
+
246
+ - Issues: https://github.com/yourorg/react-native-accessibility-toolkit/issues
247
+ - Documentation: Full API docs available in the repository
248
+
249
+ ---
250
+
251
+ Made with ❤️ for accessibility
package/package.json ADDED
@@ -0,0 +1,49 @@
1
+ {
2
+ "name": "@accessibility-rn-js/react-native-accessibility-toolkit",
3
+ "version": "1.0.0",
4
+ "description": "A comprehensive accessibility toolkit for React Native applications with TTS, screen reader, color adjustments, and more",
5
+ "main": "src/index.js",
6
+ "scripts": {
7
+ "test": "jest",
8
+ "lint": "eslint ."
9
+ },
10
+ "keywords": [
11
+ "react-native",
12
+ "accessibility",
13
+ "a11y",
14
+ "screen-reader",
15
+ "tts",
16
+ "text-to-speech",
17
+ "wcag",
18
+ "inclusive-design"
19
+ ],
20
+ "author": "Jayesh Gupta <jayeshgupta3502@gmail.com>",
21
+ "license": "MIT",
22
+ "repository": {
23
+ "type": "git",
24
+ "url": "https://github.com/accessibility-rn-js/react-native-accessibility-toolkit.git"
25
+ },
26
+ "bugs": {
27
+ "url": "https://github.com/accessibility-rn-js/react-native-accessibility-toolkit/issues"
28
+ },
29
+ "homepage": "https://github.com/accessibility-rn-js/react-native-accessibility-toolkit#readme",
30
+ "peerDependencies": {
31
+ "react": ">=16.8.0",
32
+ "react-native": ">=0.60.0",
33
+ "react-native-vector-icons": ">=9.0.0"
34
+ },
35
+ "dependencies": {
36
+ "react-native-tts": "^4.1.0",
37
+ "@react-native-async-storage/async-storage": "^1.19.0",
38
+ "react-native-draggable": "^3.3.0",
39
+ "@miblanchard/react-native-slider": "^2.3.1",
40
+ "react-native-element-dropdown": "^2.10.0"
41
+ },
42
+ "devDependencies": {
43
+ "@babel/core": "^7.20.0",
44
+ "@babel/preset-env": "^7.20.0",
45
+ "@babel/runtime": "^7.20.0",
46
+ "eslint": "^8.19.0",
47
+ "jest": "^29.2.1"
48
+ }
49
+ }
package/setup.sh ADDED
@@ -0,0 +1,40 @@
1
+ #!/bin/bash
2
+
3
+ # Quick setup script for converting to plugin
4
+
5
+ echo "🚀 Setting up React Native Accessibility Toolkit Plugin..."
6
+
7
+ # Create directory structure
8
+ echo "📁 Creating directory structure..."
9
+ mkdir -p src/components src/context src/hooks src/services src/utils
10
+
11
+ # Copy files from existing project
12
+ echo "📋 Copying files..."
13
+ echo "Please manually copy these files:"
14
+ echo " - src/accessibility/*.js → src/components/"
15
+ echo " - src/accessibility/AccessibilityContext.js → src/context/"
16
+ echo " - src/accessibility/use*.js → src/hooks/"
17
+ echo " - src/accessibility/TTSService.js → src/services/"
18
+ echo " - src/accessibility/NativeAccessibilityBridge.js → src/services/"
19
+ echo " - src/accessibility/AccessibilityStorage.js → src/services/"
20
+ echo " - src/accessibility/AccessibilityUtils.js → src/utils/"
21
+
22
+ # Initialize npm if not already done
23
+ if [ ! -f "package.json" ]; then
24
+ echo "📦 Initializing npm..."
25
+ npm init -y
26
+ fi
27
+
28
+ # Install dev dependencies
29
+ echo "📥 Installing dev dependencies..."
30
+ npm install --save-dev @babel/core @babel/preset-env eslint jest
31
+
32
+ echo "✅ Setup complete!"
33
+ echo ""
34
+ echo "Next steps:"
35
+ echo "1. Copy your accessibility files to the new structure"
36
+ echo "2. Remove project-specific dependencies (Global, Colors, etc.)"
37
+ echo "3. Test the package locally with 'npm link'"
38
+ echo "4. Publish to npm with 'npm publish'"
39
+ echo ""
40
+ echo "📖 Read INSTALLATION_GUIDE.md for detailed instructions"
@@ -0,0 +1,56 @@
1
+ import React from 'react';
2
+ import {
3
+ StyleSheet,
4
+ TouchableOpacity,
5
+ Text,
6
+ Platform,
7
+ } from 'react-native';
8
+ import { useAccessibility } from '../context/AccessibilityContext';
9
+ import { MIN_TOUCH_TARGET } from '../utils/AccessibilityUtils';
10
+ import Icon from 'react-native-vector-icons/Ionicons';
11
+
12
+ const AccessibilityButton = () => {
13
+ const { openModal, enlargeButtons } = useAccessibility();
14
+
15
+ const buttonSize = enlargeButtons ? MIN_TOUCH_TARGET * 1.2 : MIN_TOUCH_TARGET;
16
+
17
+ return (
18
+ <TouchableOpacity
19
+ style={[
20
+ styles.floatingButton,
21
+ {
22
+ width: buttonSize,
23
+ height: buttonSize,
24
+ borderRadius: buttonSize / 2,
25
+ },
26
+ ]}
27
+ onPress={openModal}
28
+ accessible={true}
29
+ accessibilityLabel="Open Accessibility Menu"
30
+ accessibilityHint="Opens a menu with accessibility options like text size, contrast, and display settings"
31
+ accessibilityRole="button"
32
+ activeOpacity={0.8}
33
+ >
34
+ <Icon name="accessibility" size={enlargeButtons ? 28 : 24} color="#ffffff" />
35
+ </TouchableOpacity>
36
+ );
37
+ };
38
+
39
+ const styles = StyleSheet.create({
40
+ floatingButton: {
41
+ position: 'absolute',
42
+ bottom: 20,
43
+ right: 20,
44
+ backgroundColor: '#8B63FF',
45
+ justifyContent: 'center',
46
+ alignItems: 'center',
47
+ elevation: 5,
48
+ shadowColor: '#000',
49
+ shadowOffset: { width: 0, height: 2 },
50
+ shadowOpacity: 0.3,
51
+ shadowRadius: 4,
52
+ zIndex: 1000,
53
+ },
54
+ });
55
+
56
+ export default AccessibilityButton;
@@ -0,0 +1,50 @@
1
+ import React from 'react';
2
+ import { Text as RNText } from 'react-native';
3
+ import { useAccessibility } from '../context/AccessibilityContext';
4
+ import { getAdjustedFontSize, getAdjustedLineHeight } from '../utils/AccessibilityUtils';
5
+
6
+ /**
7
+ * Accessibility-aware Text component
8
+ * Automatically applies font scaling, line height, letter spacing, and text alignment
9
+ */
10
+ const AccessibleText = ({
11
+ style,
12
+ children,
13
+ baseFontSize = 14,
14
+ ...props
15
+ }) => {
16
+ const {
17
+ fontScale,
18
+ lineHeight: lineHeightMultiplier,
19
+ letterSpacing: letterSpacingValue,
20
+ textAlignment,
21
+ textColor,
22
+ nativeBoldText,
23
+ } = useAccessibility();
24
+
25
+ const adjustedFontSize = getAdjustedFontSize(baseFontSize, fontScale);
26
+ const adjustedLineHeight = getAdjustedLineHeight(baseFontSize, lineHeightMultiplier, fontScale);
27
+
28
+ const accessibilityStyles = {
29
+ fontSize: adjustedFontSize,
30
+ lineHeight: adjustedLineHeight,
31
+ letterSpacing: letterSpacingValue,
32
+ textAlign: textAlignment,
33
+ ...(textColor && { color: textColor }),
34
+ ...(nativeBoldText && { fontWeight: 'bold' }),
35
+ };
36
+
37
+ return (
38
+ <RNText
39
+ style={[style, accessibilityStyles]}
40
+ {...props}
41
+ accessible={true}
42
+ accessibilityRole="text"
43
+ allowFontScaling={true}
44
+ >
45
+ {children}
46
+ </RNText>
47
+ );
48
+ };
49
+
50
+ export default AccessibleText;
@@ -0,0 +1,125 @@
1
+ import React, { createContext, useContext, useState, useEffect, useCallback } from 'react';
2
+ import { DEFAULT_ACCESSIBILITY_STATE, applyProfile } from '../utils/AccessibilityUtils';
3
+ import { loadAccessibilityPreferences, saveAccessibilityPreferences } from '../services/AccessibilityStorage';
4
+ import NativeAccessibilityBridge from '../services/NativeAccessibilityBridge';
5
+
6
+ const AccessibilityContext = createContext();
7
+
8
+ export const useAccessibility = () => {
9
+ const context = useContext(AccessibilityContext);
10
+ if (!context) {
11
+ throw new Error('useAccessibility must be used within AccessibilityProvider');
12
+ }
13
+ return context;
14
+ };
15
+
16
+ export const AccessibilityProvider = ({ children }) => {
17
+ const [state, setState] = useState(DEFAULT_ACCESSIBILITY_STATE);
18
+ const [isModalVisible, setIsModalVisible] = useState(false);
19
+ const [isLoading, setIsLoading] = useState(true);
20
+
21
+ // Initialize native accessibility detection
22
+ useEffect(() => {
23
+ const initNative = async () => {
24
+ await NativeAccessibilityBridge.initialize((nativeSettings) => {
25
+ setState(prevState => ({
26
+ ...prevState,
27
+ ...nativeSettings,
28
+ }));
29
+ });
30
+ };
31
+
32
+ initNative();
33
+
34
+ return () => {
35
+ NativeAccessibilityBridge.cleanup();
36
+ };
37
+ }, []);
38
+
39
+ // Load preferences on mount
40
+ useEffect(() => {
41
+ const loadPreferences = async () => {
42
+ const preferences = await loadAccessibilityPreferences();
43
+ setState(preferences);
44
+ setIsLoading(false);
45
+ };
46
+
47
+ loadPreferences();
48
+ }, []);
49
+
50
+ // Save preferences whenever state changes
51
+ useEffect(() => {
52
+ if (!isLoading) {
53
+ saveAccessibilityPreferences(state);
54
+ }
55
+ }, [state, isLoading]);
56
+
57
+ // Update individual setting
58
+ const updateSetting = useCallback((key, value) => {
59
+ setState(prevState => ({
60
+ ...prevState,
61
+ [key]: value,
62
+ activeProfile: 'none',
63
+ }));
64
+ }, []);
65
+
66
+ // Update multiple settings
67
+ const updateSettings = useCallback((updates) => {
68
+ setState(prevState => ({
69
+ ...prevState,
70
+ ...updates,
71
+ }));
72
+ }, []);
73
+
74
+ // Apply profile
75
+ const setProfile = useCallback((profile) => {
76
+ const profileSettings = applyProfile(profile);
77
+ setState(profileSettings);
78
+ }, []);
79
+
80
+ // Reset defaults
81
+ const resetToDefault = useCallback(() => {
82
+ setState(DEFAULT_ACCESSIBILITY_STATE);
83
+ }, []);
84
+
85
+ // Modal controls
86
+ const toggleModal = useCallback(() => {
87
+ setIsModalVisible(prev => !prev);
88
+ }, []);
89
+
90
+ const openModal = useCallback(() => {
91
+ setIsModalVisible(true);
92
+ }, []);
93
+
94
+ const closeModal = useCallback(() => {
95
+ setIsModalVisible(false);
96
+ }, []);
97
+
98
+ // Screen reader announce
99
+ const announce = useCallback((message, options) => {
100
+ NativeAccessibilityBridge.announce(message, options);
101
+ }, []);
102
+
103
+ const value = {
104
+ ...state,
105
+ isModalVisible,
106
+ isLoading,
107
+
108
+ updateSetting,
109
+ updateSettings,
110
+ setProfile,
111
+ resetToDefault,
112
+ toggleModal,
113
+ openModal,
114
+ closeModal,
115
+ announce,
116
+ };
117
+
118
+ return (
119
+ <AccessibilityContext.Provider value={value}>
120
+ {children}
121
+ </AccessibilityContext.Provider>
122
+ );
123
+ };
124
+
125
+ export default AccessibilityContext;
@@ -0,0 +1,32 @@
1
+ /**
2
+ * useDynamicColors.js
3
+ * Hook to get colors that respect accessibility settings
4
+ */
5
+
6
+ import { useMemo } from 'react';
7
+ import { useAccessibility } from '../context/AccessibilityContext';
8
+ import { getColors } from '../utils/Colors';
9
+
10
+ export const useDynamicColors = () => {
11
+ const accessibilitySettings = useAccessibility();
12
+
13
+ const colors = useMemo(() => {
14
+ return getColors(accessibilitySettings);
15
+ }, [
16
+ accessibilitySettings.colorTheme,
17
+ accessibilitySettings.darkMode,
18
+ accessibilitySettings.highContrast,
19
+ accessibilitySettings.darkHighContrast,
20
+ accessibilitySettings.whiteHighContrast,
21
+ accessibilitySettings.colorInversion,
22
+ accessibilitySettings.greyscale,
23
+ accessibilitySettings.lowSaturation,
24
+ accessibilitySettings.highSaturation,
25
+ accessibilitySettings.textColor,
26
+ accessibilitySettings.backgroundColor,
27
+ ]);
28
+
29
+ return colors;
30
+ };
31
+
32
+ export default useDynamicColors;
@@ -0,0 +1,75 @@
1
+ import { useEffect, useMemo, useRef, useState } from 'react';
2
+ import Tts from 'react-native-tts';
3
+
4
+ export function usePageRead(textToRead) {
5
+ const [isSpeaking, setIsSpeaking] = useState(false);
6
+ const [currentWordIndex, setCurrentWordIndex] = useState(null);
7
+
8
+ // Memoize words
9
+ const words = useMemo(
10
+ () => textToRead.trim().split(/\s+/),
11
+ [textToRead]
12
+ );
13
+
14
+ const indexRef = useRef(0);
15
+
16
+ useEffect(() => {
17
+ const finishSub = Tts.addEventListener('tts-finish', () => {
18
+ indexRef.current += 1;
19
+
20
+ if (indexRef.current < words.length) {
21
+ setCurrentWordIndex(indexRef.current);
22
+ Tts.speak(words[indexRef.current]);
23
+ } else {
24
+ setIsSpeaking(false);
25
+ setCurrentWordIndex(null);
26
+ }
27
+ });
28
+
29
+ const cancelSub = Tts.addEventListener('tts-cancel', () => {
30
+ setIsSpeaking(false);
31
+ });
32
+
33
+ return () => {
34
+ finishSub?.remove?.();
35
+ cancelSub?.remove?.();
36
+ };
37
+ }, [words]);
38
+
39
+ return useMemo(() => ({
40
+ isSpeaking,
41
+ currentWordIndex,
42
+
43
+ start: async () => {
44
+ if (!words.length) return;
45
+
46
+ indexRef.current = 0;
47
+ setCurrentWordIndex(0);
48
+ setIsSpeaking(true);
49
+
50
+ await Tts.stop();
51
+ await Tts.speak(words[0]);
52
+ },
53
+
54
+ pause: async () => {
55
+ await Tts.stop();
56
+ setIsSpeaking(false);
57
+ },
58
+
59
+ resume: async () => {
60
+ if (indexRef.current < words.length) {
61
+ setIsSpeaking(true);
62
+ await Tts.speak(words[indexRef.current]);
63
+ }
64
+ },
65
+
66
+ stop: async () => {
67
+ await Tts.stop();
68
+ indexRef.current = 0;
69
+ setIsSpeaking(false);
70
+ setCurrentWordIndex(null);
71
+ },
72
+ }), [isSpeaking, currentWordIndex, words]);
73
+ }
74
+
75
+ export default usePageRead;