@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/CHECKLIST.md +228 -0
- package/INSTALLATION_GUIDE.md +270 -0
- package/PLUGIN_STATUS.md +206 -0
- package/README.md +251 -0
- package/package.json +49 -0
- package/setup.sh +40 -0
- package/src/components/AccessibilityButton.js +56 -0
- package/src/components/AccessibleText.js +50 -0
- package/src/context/AccessibilityContext.js +125 -0
- package/src/hooks/useDynamicColors.js +32 -0
- package/src/hooks/usePageRead.js +75 -0
- package/src/hooks/useThemeColors.js +169 -0
- package/src/index.js +49 -0
- package/src/services/AccessibilityStorage.js +40 -0
- package/src/services/NativeAccessibilityBridge.js +171 -0
- package/src/services/TTSService.js +117 -0
- package/src/utils/AccessibilityUtils.js +223 -0
- package/src/utils/Colors.js +203 -0
- package/src/utils/Fonts.js +24 -0
|
@@ -0,0 +1,223 @@
|
|
|
1
|
+
// Accessibility utility functions and constants
|
|
2
|
+
|
|
3
|
+
export const ACCESSIBILITY_PROFILES = {
|
|
4
|
+
NONE: 'none',
|
|
5
|
+
BLIND: 'blind',
|
|
6
|
+
DYSLEXIA: 'dyslexia',
|
|
7
|
+
LOW_VISION: 'low_vision',
|
|
8
|
+
COGNITIVE: 'cognitive',
|
|
9
|
+
EPILEPSY_SAFE: 'epilepsy_safe',
|
|
10
|
+
ADHD_FOCUS: 'adhd_focus',
|
|
11
|
+
};
|
|
12
|
+
|
|
13
|
+
export const COLOR_THEMES = {
|
|
14
|
+
LIGHT: 'light',
|
|
15
|
+
DARK: 'dark',
|
|
16
|
+
HIGH_CONTRAST: 'high_contrast',
|
|
17
|
+
SEPIA: 'sepia',
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
export const TEXT_ALIGNMENT = {
|
|
21
|
+
LEFT: 'left',
|
|
22
|
+
CENTER: 'center',
|
|
23
|
+
RIGHT: 'right',
|
|
24
|
+
JUSTIFY: 'justify',
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
// Default accessibility state
|
|
28
|
+
export const DEFAULT_ACCESSIBILITY_STATE = {
|
|
29
|
+
// Font & Text
|
|
30
|
+
fontScale: 1.0,
|
|
31
|
+
lineHeight: 1.5,
|
|
32
|
+
letterSpacing: 0,
|
|
33
|
+
textAlignment: TEXT_ALIGNMENT.LEFT,
|
|
34
|
+
|
|
35
|
+
// Colors & Display
|
|
36
|
+
highContrast: false,
|
|
37
|
+
whiteHighContrast: false,
|
|
38
|
+
darkHighContrast: false,
|
|
39
|
+
colorInversion: false,
|
|
40
|
+
greyscale: false,
|
|
41
|
+
lowSaturation: false,
|
|
42
|
+
highSaturation: false,
|
|
43
|
+
colorTheme: COLOR_THEMES.LIGHT,
|
|
44
|
+
accentColor: '#007AFF',
|
|
45
|
+
textColor: null,
|
|
46
|
+
backgroundColor: null,
|
|
47
|
+
|
|
48
|
+
// Visual Aids
|
|
49
|
+
hideImages: false,
|
|
50
|
+
enlargeButtons: false,
|
|
51
|
+
reducedMotion: false,
|
|
52
|
+
highlightLinks: false,
|
|
53
|
+
|
|
54
|
+
// Reading Assistance
|
|
55
|
+
readingMask: false,
|
|
56
|
+
readingLine: false,
|
|
57
|
+
textMagnifier: false,
|
|
58
|
+
textToSpeech: false,
|
|
59
|
+
dictionary: false,
|
|
60
|
+
|
|
61
|
+
// Accessibility Features
|
|
62
|
+
screenReader: false,
|
|
63
|
+
|
|
64
|
+
// Native Settings (from system)
|
|
65
|
+
nativeScreenReader: false,
|
|
66
|
+
nativeReducedMotion: false,
|
|
67
|
+
nativeBoldText: false,
|
|
68
|
+
nativeGrayscale: false,
|
|
69
|
+
nativeInvertColors: false,
|
|
70
|
+
nativeReduceTransparency: false,
|
|
71
|
+
|
|
72
|
+
// Active Profile
|
|
73
|
+
activeProfile: ACCESSIBILITY_PROFILES.NONE,
|
|
74
|
+
};
|
|
75
|
+
|
|
76
|
+
// Profile configurations with detailed feature sets
|
|
77
|
+
export const PROFILE_CONFIGS = {
|
|
78
|
+
[ACCESSIBILITY_PROFILES.BLIND]: {
|
|
79
|
+
textToSpeech: true,
|
|
80
|
+
screenReader: true,
|
|
81
|
+
highContrast: true,
|
|
82
|
+
colorTheme: COLOR_THEMES.HIGH_CONTRAST,
|
|
83
|
+
hideImages: true,
|
|
84
|
+
fontScale: 1.2,
|
|
85
|
+
reducedMotion: true,
|
|
86
|
+
enlargeButtons: true,
|
|
87
|
+
highlightLinks: true,
|
|
88
|
+
dictionary: true,
|
|
89
|
+
},
|
|
90
|
+
|
|
91
|
+
[ACCESSIBILITY_PROFILES.DYSLEXIA]: {
|
|
92
|
+
fontScale: 1.5,
|
|
93
|
+
lineHeight: 2.4,
|
|
94
|
+
letterSpacing: 0.5,
|
|
95
|
+
highlightLinks: true,
|
|
96
|
+
enlargeButtons: true,
|
|
97
|
+
colorTheme: COLOR_THEMES.LIGHT,
|
|
98
|
+
textAlignment: TEXT_ALIGNMENT.LEFT,
|
|
99
|
+
readingLine: true,
|
|
100
|
+
},
|
|
101
|
+
|
|
102
|
+
[ACCESSIBILITY_PROFILES.LOW_VISION]: {
|
|
103
|
+
fontScale: 2.0,
|
|
104
|
+
highContrast: true,
|
|
105
|
+
whiteHighContrast: true,
|
|
106
|
+
colorTheme: COLOR_THEMES.HIGH_CONTRAST,
|
|
107
|
+
enlargeButtons: true,
|
|
108
|
+
textMagnifier: true,
|
|
109
|
+
lineHeight: 2.2,
|
|
110
|
+
letterSpacing: 0.6,
|
|
111
|
+
highlightLinks: true,
|
|
112
|
+
dictionary: true,
|
|
113
|
+
},
|
|
114
|
+
|
|
115
|
+
[ACCESSIBILITY_PROFILES.COGNITIVE]: {
|
|
116
|
+
readingLine: true,
|
|
117
|
+
readingMask: false,
|
|
118
|
+
dictionary: true,
|
|
119
|
+
reducedMotion: true,
|
|
120
|
+
highlightLinks: true,
|
|
121
|
+
fontScale: 1.3,
|
|
122
|
+
lineHeight: 2.0,
|
|
123
|
+
letterSpacing: 0.3,
|
|
124
|
+
colorTheme: COLOR_THEMES.LIGHT,
|
|
125
|
+
enlargeButtons: true,
|
|
126
|
+
},
|
|
127
|
+
|
|
128
|
+
[ACCESSIBILITY_PROFILES.EPILEPSY_SAFE]: {
|
|
129
|
+
reducedMotion: true,
|
|
130
|
+
colorTheme: COLOR_THEMES.DARK,
|
|
131
|
+
lowSaturation: true,
|
|
132
|
+
greyscale: false,
|
|
133
|
+
hideImages: false,
|
|
134
|
+
fontScale: 1.1,
|
|
135
|
+
highlightLinks: true,
|
|
136
|
+
},
|
|
137
|
+
|
|
138
|
+
[ACCESSIBILITY_PROFILES.ADHD_FOCUS]: {
|
|
139
|
+
readingMask: true,
|
|
140
|
+
readingLine: true,
|
|
141
|
+
hideImages: true,
|
|
142
|
+
reducedMotion: true,
|
|
143
|
+
fontScale: 1.3,
|
|
144
|
+
lineHeight: 2.0,
|
|
145
|
+
highlightLinks: true,
|
|
146
|
+
enlargeButtons: true,
|
|
147
|
+
colorTheme: COLOR_THEMES.LIGHT,
|
|
148
|
+
},
|
|
149
|
+
};
|
|
150
|
+
|
|
151
|
+
// Apply profile settings
|
|
152
|
+
export const applyProfile = (profile) => {
|
|
153
|
+
if (profile === ACCESSIBILITY_PROFILES.NONE) {
|
|
154
|
+
return DEFAULT_ACCESSIBILITY_STATE;
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
const profileConfig = PROFILE_CONFIGS[profile];
|
|
158
|
+
return {
|
|
159
|
+
...DEFAULT_ACCESSIBILITY_STATE,
|
|
160
|
+
...profileConfig,
|
|
161
|
+
activeProfile: profile,
|
|
162
|
+
};
|
|
163
|
+
};
|
|
164
|
+
|
|
165
|
+
// Color theme values
|
|
166
|
+
export const COLOR_THEME_VALUES = {
|
|
167
|
+
[COLOR_THEMES.LIGHT]: {
|
|
168
|
+
background: '#FFFFFF',
|
|
169
|
+
text: '#000000',
|
|
170
|
+
primary: '#007AFF',
|
|
171
|
+
secondary: '#5856D6',
|
|
172
|
+
border: '#C7C7CC',
|
|
173
|
+
},
|
|
174
|
+
[COLOR_THEMES.DARK]: {
|
|
175
|
+
background: '#000000',
|
|
176
|
+
text: '#FFFFFF',
|
|
177
|
+
primary: '#0A84FF',
|
|
178
|
+
secondary: '#5E5CE6',
|
|
179
|
+
border: '#38383A',
|
|
180
|
+
},
|
|
181
|
+
[COLOR_THEMES.HIGH_CONTRAST]: {
|
|
182
|
+
background: '#000000',
|
|
183
|
+
text: '#FFFF00',
|
|
184
|
+
primary: '#FFFFFF',
|
|
185
|
+
secondary: '#00FF00',
|
|
186
|
+
border: '#FFFFFF',
|
|
187
|
+
},
|
|
188
|
+
[COLOR_THEMES.SEPIA]: {
|
|
189
|
+
background: '#F4ECD8',
|
|
190
|
+
text: '#5C4B37',
|
|
191
|
+
primary: '#8B4513',
|
|
192
|
+
secondary: '#A0522D',
|
|
193
|
+
border: '#D2B48C',
|
|
194
|
+
},
|
|
195
|
+
};
|
|
196
|
+
|
|
197
|
+
// Calculate adjusted font size
|
|
198
|
+
export const getAdjustedFontSize = (baseFontSize, fontScale) => {
|
|
199
|
+
return baseFontSize * fontScale;
|
|
200
|
+
};
|
|
201
|
+
|
|
202
|
+
// Calculate adjusted line height
|
|
203
|
+
export const getAdjustedLineHeight = (baseFontSize, lineHeightMultiplier, fontScale) => {
|
|
204
|
+
return baseFontSize * fontScale * lineHeightMultiplier;
|
|
205
|
+
};
|
|
206
|
+
|
|
207
|
+
// Get button padding adjustment
|
|
208
|
+
export const getButtonPadding = (baseSize, enlargeButtons) => {
|
|
209
|
+
return enlargeButtons ? baseSize * 1.5 : baseSize;
|
|
210
|
+
};
|
|
211
|
+
|
|
212
|
+
// Minimum touch target size (48dp)
|
|
213
|
+
export const MIN_TOUCH_TARGET = 48;
|
|
214
|
+
|
|
215
|
+
// Apply color inversion filter
|
|
216
|
+
export const applyColorInversion = (enabled) => {
|
|
217
|
+
return enabled ? 'invert(100%)' : 'none';
|
|
218
|
+
};
|
|
219
|
+
|
|
220
|
+
// Apply greyscale filter
|
|
221
|
+
export const applyGreyscale = (enabled) => {
|
|
222
|
+
return enabled ? 'grayscale(100%)' : 'none';
|
|
223
|
+
};
|
|
@@ -0,0 +1,203 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Colors.js
|
|
3
|
+
* Dynamic color provider for accessibility plugin
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
// Base color definitions
|
|
7
|
+
const BASE_COLORS = {
|
|
8
|
+
primaryButtonColor: '#8B63FF',
|
|
9
|
+
secondaryButtonColor: '#FF974F',
|
|
10
|
+
primaryTextColor: '#2D3142',
|
|
11
|
+
primaryInactive: '#9c9eb9',
|
|
12
|
+
textInputBorder: '#d6d9e0',
|
|
13
|
+
successColor: '#4fab53',
|
|
14
|
+
placeholderTextColor: "#A9B2C2",
|
|
15
|
+
modalBackground: 'rgba(0, 0, 0, 0.5)',
|
|
16
|
+
red: "#ff3b30",
|
|
17
|
+
defaultBackground: '#FFFFFF',
|
|
18
|
+
boxBackground: '#F4F6FA',
|
|
19
|
+
lightBlue: '#E4DFFF',
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
// Helper to convert hex to RGB
|
|
23
|
+
const hexToRgb = (hex) => {
|
|
24
|
+
const result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex);
|
|
25
|
+
return result
|
|
26
|
+
? {
|
|
27
|
+
r: parseInt(result[1], 16),
|
|
28
|
+
g: parseInt(result[2], 16),
|
|
29
|
+
b: parseInt(result[3], 16),
|
|
30
|
+
}
|
|
31
|
+
: null;
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
// Helper to convert RGB to hex
|
|
35
|
+
const rgbToHex = (r, g, b) => {
|
|
36
|
+
return '#' + [r, g, b].map((x) => {
|
|
37
|
+
const hex = Math.round(Math.max(0, Math.min(255, x))).toString(16);
|
|
38
|
+
return hex.length === 1 ? '0' + hex : hex;
|
|
39
|
+
}).join('');
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
// Helper to determine if a color is light or dark
|
|
43
|
+
const isLightColor = (hex) => {
|
|
44
|
+
if (hex.startsWith('rgba')) return true;
|
|
45
|
+
const rgb = hexToRgb(hex);
|
|
46
|
+
if (!rgb) return true;
|
|
47
|
+
const luminance = (0.299 * rgb.r + 0.587 * rgb.g + 0.114 * rgb.b) / 255;
|
|
48
|
+
return luminance > 0.5;
|
|
49
|
+
};
|
|
50
|
+
|
|
51
|
+
// Apply grayscale to a color
|
|
52
|
+
const applyGrayscale = (hex) => {
|
|
53
|
+
if (hex.startsWith('rgba')) return hex;
|
|
54
|
+
const rgb = hexToRgb(hex);
|
|
55
|
+
if (!rgb) return hex;
|
|
56
|
+
const gray = Math.round(0.299 * rgb.r + 0.587 * rgb.g + 0.114 * rgb.b);
|
|
57
|
+
return rgbToHex(gray, gray, gray);
|
|
58
|
+
};
|
|
59
|
+
|
|
60
|
+
// Apply saturation adjustment
|
|
61
|
+
const applySaturation = (hex, factor) => {
|
|
62
|
+
if (hex.startsWith('rgba')) return hex;
|
|
63
|
+
const rgb = hexToRgb(hex);
|
|
64
|
+
if (!rgb) return hex;
|
|
65
|
+
|
|
66
|
+
const gray = 0.299 * rgb.r + 0.587 * rgb.g + 0.114 * rgb.b;
|
|
67
|
+
const r = gray + factor * (rgb.r - gray);
|
|
68
|
+
const g = gray + factor * (rgb.g - gray);
|
|
69
|
+
const b = gray + factor * (rgb.b - gray);
|
|
70
|
+
|
|
71
|
+
return rgbToHex(r, g, b);
|
|
72
|
+
};
|
|
73
|
+
|
|
74
|
+
// Invert a color
|
|
75
|
+
const invertColor = (hex) => {
|
|
76
|
+
if (hex.startsWith('rgba')) return hex;
|
|
77
|
+
const rgb = hexToRgb(hex);
|
|
78
|
+
if (!rgb) return hex;
|
|
79
|
+
return rgbToHex(255 - rgb.r, 255 - rgb.g, 255 - rgb.b);
|
|
80
|
+
};
|
|
81
|
+
|
|
82
|
+
// Process colors based on accessibility settings
|
|
83
|
+
const processColors = (colors, settings) => {
|
|
84
|
+
if (!settings) return colors;
|
|
85
|
+
|
|
86
|
+
let processed = { ...colors };
|
|
87
|
+
|
|
88
|
+
// Apply grayscale
|
|
89
|
+
if (settings.greyscale) {
|
|
90
|
+
processed = Object.keys(processed).reduce((acc, key) => {
|
|
91
|
+
acc[key] = applyGrayscale(processed[key]);
|
|
92
|
+
return acc;
|
|
93
|
+
}, {});
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
// Apply saturation adjustments
|
|
97
|
+
if (settings.lowSaturation && !settings.greyscale) {
|
|
98
|
+
processed = Object.keys(processed).reduce((acc, key) => {
|
|
99
|
+
if (key !== 'modalBackground') {
|
|
100
|
+
acc[key] = applySaturation(processed[key], 0.3);
|
|
101
|
+
} else {
|
|
102
|
+
acc[key] = processed[key];
|
|
103
|
+
}
|
|
104
|
+
return acc;
|
|
105
|
+
}, {});
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
if (settings.highSaturation && !settings.greyscale && !settings.lowSaturation) {
|
|
109
|
+
processed = Object.keys(processed).reduce((acc, key) => {
|
|
110
|
+
if (key !== 'modalBackground') {
|
|
111
|
+
acc[key] = applySaturation(processed[key], 2.0);
|
|
112
|
+
} else {
|
|
113
|
+
acc[key] = processed[key];
|
|
114
|
+
}
|
|
115
|
+
return acc;
|
|
116
|
+
}, {});
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
// Apply color inversion
|
|
120
|
+
if (settings.colorInversion) {
|
|
121
|
+
processed = Object.keys(processed).reduce((acc, key) => {
|
|
122
|
+
if (key !== 'modalBackground') {
|
|
123
|
+
acc[key] = invertColor(processed[key]);
|
|
124
|
+
} else {
|
|
125
|
+
acc[key] = processed[key];
|
|
126
|
+
}
|
|
127
|
+
return acc;
|
|
128
|
+
}, {});
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
return processed;
|
|
132
|
+
};
|
|
133
|
+
|
|
134
|
+
// Get colors with accessibility transformations applied
|
|
135
|
+
export const getColors = (accessibilitySettings) => {
|
|
136
|
+
const settings = accessibilitySettings || {};
|
|
137
|
+
|
|
138
|
+
// Apply dark mode first
|
|
139
|
+
let colors = { ...BASE_COLORS };
|
|
140
|
+
if (settings.colorTheme === 'dark' || settings.darkMode) {
|
|
141
|
+
colors = {
|
|
142
|
+
...colors,
|
|
143
|
+
primaryTextColor: '#FFFFFF',
|
|
144
|
+
defaultBackground: '#1E1E1E',
|
|
145
|
+
boxBackground: '#2C2C2E',
|
|
146
|
+
textInputBorder: '#3A3A3C',
|
|
147
|
+
placeholderTextColor: '#8E8E93',
|
|
148
|
+
primaryInactive: '#6E6E73',
|
|
149
|
+
};
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
// Apply high contrast
|
|
153
|
+
if (settings.highContrast) {
|
|
154
|
+
if (settings.colorTheme === 'dark' || settings.darkMode) {
|
|
155
|
+
if (settings.darkHighContrast) {
|
|
156
|
+
colors = {
|
|
157
|
+
...colors,
|
|
158
|
+
defaultBackground: '#000000',
|
|
159
|
+
primaryTextColor: '#FFFFFF',
|
|
160
|
+
boxBackground: '#1A1A1A',
|
|
161
|
+
primaryButtonColor: '#FFFFFF',
|
|
162
|
+
secondaryButtonColor: '#FFFF00',
|
|
163
|
+
textInputBorder: '#FFFFFF',
|
|
164
|
+
};
|
|
165
|
+
}
|
|
166
|
+
} else {
|
|
167
|
+
if (settings.whiteHighContrast) {
|
|
168
|
+
colors = {
|
|
169
|
+
...colors,
|
|
170
|
+
defaultBackground: '#FFFFFF',
|
|
171
|
+
primaryTextColor: '#000000',
|
|
172
|
+
boxBackground: '#F5F5F5',
|
|
173
|
+
primaryButtonColor: '#0000FF',
|
|
174
|
+
secondaryButtonColor: '#000080',
|
|
175
|
+
textInputBorder: '#000000',
|
|
176
|
+
};
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
// Apply color filters
|
|
182
|
+
const processedColors = processColors(colors, settings);
|
|
183
|
+
|
|
184
|
+
// Apply custom text and background color overrides
|
|
185
|
+
if (settings.textColor) {
|
|
186
|
+
processedColors.primaryTextColor = settings.textColor;
|
|
187
|
+
processedColors.buttonTextColor = settings.textColor;
|
|
188
|
+
} else {
|
|
189
|
+
processedColors.buttonTextColor = processedColors.boxBackground;
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
if (settings.backgroundColor) {
|
|
193
|
+
processedColors.defaultBackground = settings.backgroundColor;
|
|
194
|
+
if (!settings.textColor) {
|
|
195
|
+
const isLight = isLightColor(settings.backgroundColor);
|
|
196
|
+
processedColors.buttonTextColor = isLight ? '#000000' : '#FFFFFF';
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
return processedColors;
|
|
201
|
+
};
|
|
202
|
+
|
|
203
|
+
export default BASE_COLORS;
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { StyleSheet, Platform } from 'react-native';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Default font styles for accessibility plugin
|
|
5
|
+
* Users can override these by providing custom fonts in their app
|
|
6
|
+
*/
|
|
7
|
+
export default StyleSheet.create({
|
|
8
|
+
Regular: {
|
|
9
|
+
fontFamily: Platform.OS === 'ios' ? 'System' : 'sans-serif',
|
|
10
|
+
fontWeight: '400',
|
|
11
|
+
},
|
|
12
|
+
Medium: {
|
|
13
|
+
fontFamily: Platform.OS === 'ios' ? 'System' : 'sans-serif-medium',
|
|
14
|
+
fontWeight: '500',
|
|
15
|
+
},
|
|
16
|
+
SemiBold: {
|
|
17
|
+
fontFamily: Platform.OS === 'ios' ? 'System' : 'sans-serif',
|
|
18
|
+
fontWeight: '600',
|
|
19
|
+
},
|
|
20
|
+
Bold: {
|
|
21
|
+
fontFamily: Platform.OS === 'ios' ? 'System' : 'sans-serif',
|
|
22
|
+
fontWeight: '700',
|
|
23
|
+
},
|
|
24
|
+
});
|