diff --git a/src/components/AchievementGrid.tsx b/src/components/AchievementGrid.tsx
index e339632..3fc1b5a 100644
--- a/src/components/AchievementGrid.tsx
+++ b/src/components/AchievementGrid.tsx
@@ -86,7 +86,7 @@ function AchievementTile({ achievement }: { achievement: Achievement }) {
opacity,
}}
>
- {achievement.icon}
+ {achievement.icon}
- {item.icon}
+ {item.icon}
- {icon}
+ {icon}
{
}
return (
{
padding: spacing.lg,
}}
>
- β οΈ
+ β οΈ
{
{this.state.error?.message || 'An unexpected error occurred'}
- {icon}
+ {icon}
- π€
+ π€
{count} proof{count === 1 ? '' : 's'} waiting to sync
@@ -48,6 +49,8 @@ export default function PendingProofsBanner({
-
+
{tier.emoji}
- {streak > 0 ? 'π₯' : 'π§'}
+ 0 ? 'Fire' : 'Ice'}>{streak > 0 ? 'π₯' : 'π§'}
diff --git a/src/components/TabBarIcon.tsx b/src/components/TabBarIcon.tsx
index a8f401a..7dde8d3 100644
--- a/src/components/TabBarIcon.tsx
+++ b/src/components/TabBarIcon.tsx
@@ -20,6 +20,8 @@ export default function TabBarIcon({
fontSize: size,
opacity: focused ? 1 : 0.5,
}}
+ accessible={true}
+ accessibilityLabel="Tab icon"
>
{emoji}
diff --git a/src/components/TaskCard.tsx b/src/components/TaskCard.tsx
index 4d35754..56b4900 100644
--- a/src/components/TaskCard.tsx
+++ b/src/components/TaskCard.tsx
@@ -41,6 +41,9 @@ export default function TaskCard({
return (
onPress(id)}
+ accessibilityRole="button"
+ accessibilityLabel={`Task: ${title}`}
+ accessibilityHint="Navigates to task details"
style={{
backgroundColor: colors.surface,
borderRadius: 12,
@@ -52,7 +55,7 @@ export default function TaskCard({
borderColor: colors.border,
}}
>
-
+
{TASK_TYPE_CONFIG[type]?.icon || 'π'}
diff --git a/src/components/TransactionHistory.tsx b/src/components/TransactionHistory.tsx
index 87bdfa9..05eae8a 100644
--- a/src/components/TransactionHistory.tsx
+++ b/src/components/TransactionHistory.tsx
@@ -60,7 +60,7 @@ export default function TransactionHistory({
if (loading) {
return (
-
+
+
{error}
- void loadPayments()}
- accessibilityRole="button"
- style={{
- marginTop: spacing.xs,
- paddingVertical: spacing.sm,
- paddingHorizontal: spacing.md,
- minHeight: 44,
- justifyContent: 'center',
- }}
- >
- Retry
+
+
+ Retry
+
);
@@ -131,7 +129,7 @@ export default function TransactionHistory({
borderColor: colors.border,
}}
>
-
+
{isSent ? 'βοΈ' : 'βοΈ'}
diff --git a/src/screens/EditProfileScreen.tsx b/src/screens/EditProfileScreen.tsx
index 1f23eb5..924ea7d 100644
--- a/src/screens/EditProfileScreen.tsx
+++ b/src/screens/EditProfileScreen.tsx
@@ -72,17 +72,9 @@ export default function EditProfileScreen() {
>
navigation.goBack()}
+ style={{ marginBottom: spacing.md }}
+ accessibilityLabel="Cancel editing profile"
accessibilityRole="button"
- accessibilityLabel="Cancel"
- style={{
- alignSelf: 'flex-start',
- paddingVertical: spacing.sm,
- paddingHorizontal: spacing.md,
- marginLeft: -spacing.md,
- marginBottom: spacing.md,
- minHeight: 44,
- justifyContent: 'center',
- }}
>
Cancel
@@ -112,6 +104,7 @@ export default function EditProfileScreen() {
onChangeText={setName}
placeholder="Your name"
placeholderTextColor={colors.textSecondary}
+ accessibilityLabel="Display Name input"
style={{
backgroundColor: colors.surface,
borderRadius: 12,
@@ -138,6 +131,7 @@ export default function EditProfileScreen() {
onChangeText={setBio}
placeholder="Tell others about yourself"
placeholderTextColor={colors.textSecondary}
+ accessibilityLabel="Bio input"
multiline
numberOfLines={4}
textAlignVertical="top"
@@ -157,6 +151,8 @@ export default function EditProfileScreen() {
void handleSave()}
disabled={isSaving}
+ accessibilityLabel="Save Changes"
+ accessibilityRole="button"
style={{
padding: spacing.md,
backgroundColor: colors.primary,
@@ -165,7 +161,10 @@ export default function EditProfileScreen() {
opacity: isSaving ? 0.5 : 1,
}}
>
-
+
{isSaving ? 'Saving...' : 'Save Changes'}
diff --git a/src/screens/HomeScreen.tsx b/src/screens/HomeScreen.tsx
index 62c7955..0b9b756 100644
--- a/src/screens/HomeScreen.tsx
+++ b/src/screens/HomeScreen.tsx
@@ -64,7 +64,11 @@ export default function HomeScreen() {
paddingBottom: spacing.lg,
}}
>
- navigation.navigate('Profile')}>
+ navigation.navigate('Profile')}
+ accessibilityLabel={`Go to Profile. ${getGreeting()}, ${profile?.name || 'Eco Warrior'}`}
+ accessibilityRole="button"
+ >
{getGreeting()},
@@ -178,6 +182,8 @@ export default function HomeScreen() {
navigation.navigate('Tasks')}
+ accessibilityLabel="Browse Tasks"
+ accessibilityRole="button"
style={{
padding: spacing.lg,
backgroundColor: colors.primary,
@@ -190,7 +196,14 @@ export default function HomeScreen() {
Browse Tasks
- πΏ
+
+ πΏ
+
@@ -262,7 +275,12 @@ export default function HomeScreen() {
a.status === 'pending' ? colors.warning : colors.border,
}}
>
-
+
{TASK_TYPE_CONFIG[a.taskType as TaskType]?.icon || 'π'}
diff --git a/src/screens/MapScreen.tsx b/src/screens/MapScreen.tsx
index 5f5c115..671d1be 100644
--- a/src/screens/MapScreen.tsx
+++ b/src/screens/MapScreen.tsx
@@ -261,8 +261,14 @@ export default function MapScreen() {
{/* ββ Header ββ */}
-
- πΊοΈ Discover Tasks
+
+
+ πΊοΈ Discover Tasks
+
{isLoading && (
)}
{error != null && (
- void refresh()}
+
Retry
@@ -289,6 +297,8 @@ export default function MapScreen() {
key={km}
testID={`radius-btn-${km}`}
onPress={() => setRadiusKm(km)}
+ accessibilityLabel={`Set radius to ${km} kilometers`}
+ accessibilityRole="button"
style={[
styles.radiusOpt,
radiusKm === km && styles.radiusOptActive,
@@ -312,6 +322,8 @@ export default function MapScreen() {
{
mapRef.current?.animateToRegion(
{
@@ -324,14 +336,26 @@ export default function MapScreen() {
);
}}
>
- β
+
+ β
+
)}
{/* ββ No-location fallback notice ββ */}
{!location && (
-
+
π Enable location to see tasks near you
diff --git a/src/screens/NotificationPreferencesScreen.tsx b/src/screens/NotificationPreferencesScreen.tsx
index 7952fa1..6a3a455 100644
--- a/src/screens/NotificationPreferencesScreen.tsx
+++ b/src/screens/NotificationPreferencesScreen.tsx
@@ -80,6 +80,8 @@ export default function NotificationPreferencesScreen() {
quietHours.to,
);
}}
+ accessibilityLabel={`Cycle Quiet Hours Start Time, currently ${quietHours.from}`}
+ accessibilityRole="button"
style={{
padding: spacing.md,
backgroundColor: colors.surface,
@@ -100,6 +102,8 @@ export default function NotificationPreferencesScreen() {
`${nh.toString().padStart(2, '0')}:${(m || 0).toString().padStart(2, '0')}`,
);
}}
+ accessibilityLabel={`Cycle Quiet Hours End Time, currently ${quietHours.to}`}
+ accessibilityRole="button"
style={{
padding: spacing.md,
backgroundColor: colors.surface,
@@ -113,6 +117,8 @@ export default function NotificationPreferencesScreen() {
navigation.goBack()}
+ accessibilityLabel="Done editing preferences"
+ accessibilityRole="button"
style={{
marginTop: spacing.lg,
padding: spacing.md,
diff --git a/src/screens/OnboardingScreen.tsx b/src/screens/OnboardingScreen.tsx
index 776d514..feb0f5c 100644
--- a/src/screens/OnboardingScreen.tsx
+++ b/src/screens/OnboardingScreen.tsx
@@ -72,7 +72,14 @@ export default function OnboardingScreen() {
}}
>
- π±
+
+ π±
+
{error}
@@ -109,6 +117,8 @@ export default function OnboardingScreen() {
void connectFreighter()}
disabled={busy}
+ accessibilityLabel="Connect Freighter wallet"
+ accessibilityRole="button"
style={{
padding: spacing.md,
backgroundColor: colors.primary,
@@ -130,6 +140,8 @@ export default function OnboardingScreen() {
void connectLobstr()}
disabled={busy}
+ accessibilityLabel="Connect Lobstr wallet"
+ accessibilityRole="button"
style={{
padding: spacing.md,
backgroundColor: colors.surface,
@@ -149,6 +161,8 @@ export default function OnboardingScreen() {
void handleCreateWallet()}
disabled={busy}
+ accessibilityLabel="Create Test Wallet"
+ accessibilityRole="button"
style={{
padding: spacing.md,
backgroundColor: colors.surface,
@@ -167,6 +181,8 @@ export default function OnboardingScreen() {
setShowImport(v => !v)}
disabled={busy}
+ accessibilityLabel={showImport ? 'Cancel Import' : 'Import Existing Wallet'}
+ accessibilityRole="button"
style={{
padding: spacing.md,
backgroundColor: colors.surface,
@@ -188,6 +204,7 @@ export default function OnboardingScreen() {
onChangeText={setSecretKey}
placeholder="Paste your secret key (S...)"
placeholderTextColor={colors.textSecondary}
+ accessibilityLabel="Secret Key Input"
secureTextEntry
autoCapitalize="none"
autoCorrect={false}
@@ -205,6 +222,8 @@ export default function OnboardingScreen() {
void handleImport()}
disabled={busy || !secretKey.trim()}
+ accessibilityLabel="Import Wallet"
+ accessibilityRole="button"
style={{
padding: spacing.md,
backgroundColor: colors.primary,
diff --git a/src/screens/ProfileScreen.tsx b/src/screens/ProfileScreen.tsx
index a8df86e..c6b724f 100644
--- a/src/screens/ProfileScreen.tsx
+++ b/src/screens/ProfileScreen.tsx
@@ -48,7 +48,12 @@ export default function ProfileScreen() {
borderColor: colors.primary,
}}
>
-
+
{profile?.avatarUrl ? 'π€' : 'π±'}
@@ -155,6 +160,8 @@ export default function ProfileScreen() {
disconnectWallet();
logout();
}}
+ accessibilityLabel="Disconnect and Sign Out"
+ accessibilityRole="button"
style={{
marginTop: spacing.lg,
padding: spacing.md,
@@ -184,6 +191,8 @@ function SettingsRow({
return (
- navigation.goBack()}
- accessibilityRole="button"
- accessibilityLabel="Cancel"
- style={{
- alignSelf: 'flex-start',
- paddingVertical: spacing.sm,
- paddingHorizontal: spacing.md,
- marginLeft: -spacing.md,
- minHeight: 44,
- justifyContent: 'center',
- }}
- >
+ navigation.goBack()} accessibilityRole="button" accessibilityLabel="Cancel">
Cancel
setAsset(a)}
+ accessibilityRole="button"
+ accessibilityLabel={`Select asset ${a}`}
+ accessibilityState={{ selected: asset === a }}
style={{
flex: 1,
padding: spacing.md,
@@ -288,6 +281,7 @@ export default function SendTokensScreen() {
{error && (
void handleSend()}
disabled={isSending}
+ accessibilityRole="button"
+ accessibilityLabel={isLobstr ? 'Send via Lobstr' : 'Send'}
+ accessibilityState={{ disabled: isSending }}
style={{
padding: spacing.md,
backgroundColor: colors.primary,
diff --git a/src/screens/SubmitProofScreen.tsx b/src/screens/SubmitProofScreen.tsx
index 09398eb..83f1b35 100644
--- a/src/screens/SubmitProofScreen.tsx
+++ b/src/screens/SubmitProofScreen.tsx
@@ -200,22 +200,8 @@ export default function SubmitProofScreen() {
return (
- navigation.goBack()}
- accessibilityRole="button"
- accessibilityLabel="Back"
- style={{
- alignSelf: 'flex-start',
- paddingVertical: spacing.sm,
- paddingHorizontal: spacing.md,
- marginLeft: -spacing.md,
- minHeight: 44,
- justifyContent: 'center',
- }}
- >
-
- {'\u2190'} Back
-
+ navigation.goBack()} accessibilityRole="button" accessibilityLabel="Go back">
+ Back
) : (
- π·
+ π·
@@ -266,15 +252,10 @@ export default function SubmitProofScreen() {
{hasPermission === false && (
void requestPermission()}
+ onPress={requestPermission}
accessibilityRole="button"
- style={{
- marginTop: spacing.md,
- paddingVertical: spacing.sm,
- paddingHorizontal: spacing.md,
- minHeight: 44,
- justifyContent: 'center',
- }}
+ accessibilityLabel="Grant Permission"
+ style={{ marginTop: spacing.md, padding: spacing.sm }}
>
Grant Permission
@@ -307,6 +288,7 @@ export default function SubmitProofScreen() {
{progress !== 'idle' && (
void handleCapture()}
disabled={isSubmitting}
+ accessibilityRole="button"
+ accessibilityLabel="Take Photo"
+ accessibilityState={{ disabled: isSubmitting }}
style={{
flex: 1,
padding: spacing.md,
@@ -368,6 +354,9 @@ export default function SubmitProofScreen() {
setActivityId(null);
}}
disabled={isSubmitting}
+ accessibilityRole="button"
+ accessibilityLabel="Retake Photo"
+ accessibilityState={{ disabled: isSubmitting }}
style={{
flex: 1,
padding: spacing.md,
@@ -383,6 +372,9 @@ export default function SubmitProofScreen() {
void handleSubmit()}
disabled={isSubmitting}
+ accessibilityRole="button"
+ accessibilityLabel="Submit Proof"
+ accessibilityState={{ disabled: isSubmitting }}
style={{
flex: 1,
padding: spacing.md,
diff --git a/src/screens/SubmitScreen.tsx b/src/screens/SubmitScreen.tsx
index 565fbe8..ed3bcad 100644
--- a/src/screens/SubmitScreen.tsx
+++ b/src/screens/SubmitScreen.tsx
@@ -45,7 +45,7 @@ export default function SubmitScreen() {
Submit Proof
- πΈ
+ πΈ
navigation.navigate('Tasks')}
+ accessibilityRole="button"
+ accessibilityLabel="Browse Tasks"
style={{
marginTop: spacing.lg,
paddingVertical: spacing.md,
diff --git a/src/screens/TaskDetailScreen.tsx b/src/screens/TaskDetailScreen.tsx
index 1a6a0f7..d80d469 100644
--- a/src/screens/TaskDetailScreen.tsx
+++ b/src/screens/TaskDetailScreen.tsx
@@ -62,18 +62,8 @@ export default function TaskDetailScreen() {
alignItems: 'center',
}}
>
- {error || 'Task not found'}
- void loadTask()}
- accessibilityRole="button"
- style={{
- marginTop: spacing.md,
- paddingVertical: spacing.sm,
- paddingHorizontal: spacing.md,
- minHeight: 44,
- justifyContent: 'center',
- }}
- >
+ {error || 'Task not found'}
+
Try Again
@@ -91,24 +81,15 @@ export default function TaskDetailScreen() {
navigation.goBack()}
accessibilityRole="button"
- accessibilityLabel="Back"
- style={{
- alignSelf: 'flex-start',
- paddingVertical: spacing.sm,
- paddingHorizontal: spacing.md,
- marginLeft: -spacing.md,
- marginTop: spacing.xl,
- marginBottom: spacing.md,
- minHeight: 44,
- justifyContent: 'center',
- }}
+ accessibilityLabel="Go back"
+ style={{ marginBottom: spacing.md, marginTop: spacing.xl }}
>
{'\u2190'} Back
-
+
{TASK_TYPE_CONFIG[task.type]?.icon || 'π'}
@@ -213,6 +194,9 @@ export default function TaskDetailScreen() {
{
selectTask({ ...task, id: task.id || taskId });
navigation.navigate('SubmitProof', {
diff --git a/src/screens/TaskListScreen.tsx b/src/screens/TaskListScreen.tsx
index a297f55..21bb833 100644
--- a/src/screens/TaskListScreen.tsx
+++ b/src/screens/TaskListScreen.tsx
@@ -121,18 +121,8 @@ export default function TaskListScreen() {
alignItems: 'center',
}}
>
- {error}
- void refresh()}
- accessibilityRole="button"
- style={{
- marginTop: spacing.md,
- paddingVertical: spacing.sm,
- paddingHorizontal: spacing.md,
- minHeight: 44,
- justifyContent: 'center',
- }}
- >
+ {error}
+
Retry
@@ -168,6 +158,9 @@ export default function TaskListScreen() {
setActiveType(t.key)}
+ accessibilityRole="button"
+ accessibilityLabel={`Filter by ${t.label}`}
+ accessibilityState={{ selected: activeType === t.key }}
style={{
flexDirection: 'row',
alignItems: 'center',
@@ -209,6 +202,9 @@ export default function TaskListScreen() {
setStatusFilter(s.key)}
+ accessibilityRole="button"
+ accessibilityLabel={`Filter by status ${s.label}`}
+ accessibilityState={{ selected: statusFilter === s.key }}
style={{
paddingHorizontal: spacing.md,
paddingVertical: spacing.xs,
@@ -243,6 +239,7 @@ export default function TaskListScreen() {
placeholderTextColor={colors.textSecondary}
autoCapitalize="none"
autoCorrect={false}
+ accessibilityLabel="Search tasks"
style={{
backgroundColor: colors.surface,
borderRadius: 12,
@@ -268,6 +265,9 @@ export default function TaskListScreen() {
setSortMode(opt.key)}
+ accessibilityRole="button"
+ accessibilityLabel={`Sort by ${opt.label}`}
+ accessibilityState={{ selected: sortMode === opt.key }}
style={{
paddingHorizontal: spacing.md,
paddingVertical: spacing.xs,
@@ -303,6 +303,9 @@ export default function TaskListScreen() {
setRadiusKm(km)}
+ accessibilityRole="button"
+ accessibilityLabel={`Radius ${km} kilometers`}
+ accessibilityState={{ selected: radiusKm === km }}
style={{
paddingHorizontal: spacing.sm,
paddingVertical: spacing.xs,
diff --git a/src/screens/WalletScreen.tsx b/src/screens/WalletScreen.tsx
index 722eb1e..f614f86 100644
--- a/src/screens/WalletScreen.tsx
+++ b/src/screens/WalletScreen.tsx
@@ -149,6 +149,8 @@ export default function WalletScreen() {
navigation.navigate('SendTokens')}
+ accessibilityRole="button"
+ accessibilityLabel="Send Tokens"
style={{
marginTop: spacing.xl,
padding: spacing.md,
@@ -162,6 +164,8 @@ export default function WalletScreen() {