diff --git a/src/components/ToggleSwitch.tsx b/src/components/ToggleSwitch.tsx
index ce0944ee..81165b7e 100644
--- a/src/components/ToggleSwitch.tsx
+++ b/src/components/ToggleSwitch.tsx
@@ -7,6 +7,8 @@ interface ToggleSwitchProps {
value: boolean;
onValueChange: (val: boolean) => void;
disabled?: boolean;
+ /** What the switch turns on or off, read out by screen readers. */
+ accessibilityLabel?: string;
}
/**
@@ -16,7 +18,7 @@ interface ToggleSwitchProps {
* - thumb: h-4 w-4 rounded-full bg-background
* - translate-x-6 (checked) / translate-x-1 (unchecked)
*/
-export default function ToggleSwitch({ value, onValueChange, disabled = false }: ToggleSwitchProps) {
+export default function ToggleSwitch({ value, onValueChange, disabled = false, accessibilityLabel }: ToggleSwitchProps) {
const c = useColors();
const styles = React.useMemo(() => makeStyles(c), [c]);
return (
@@ -28,6 +30,7 @@ export default function ToggleSwitch({ value, onValueChange, disabled = false }:
]}
onPress={() => !disabled && onValueChange(!value)}
accessibilityRole="switch"
+ accessibilityLabel={accessibilityLabel}
accessibilityState={{ checked: value, disabled }}
>
updateAction(index, { keepCopy: v || undefined })}
+ accessibilityLabel={t('settings.filters.forward_keep_copy', 'Keep a copy')}
/>
>
@@ -446,7 +447,11 @@ export function FilterRuleModal({ visible, rule, mailboxes, onSave, onClose }: F
{t('settings.filters.stop_processing', 'Stop processing subsequent rules')}
-
+
{/* Folder rules skip spam unless the rule opts in (webmail 6241ed61). */}
@@ -455,7 +460,11 @@ export function FilterRuleModal({ visible, rule, mailboxes, onSave, onClose }: F
{t('settings.filters.include_spam', 'Also move messages marked as spam')}
-
+
)}
diff --git a/src/components/settings/FilterSettings.tsx b/src/components/settings/FilterSettings.tsx
index 73893a2e..9f972b8b 100644
--- a/src/components/settings/FilterSettings.tsx
+++ b/src/components/settings/FilterSettings.tsx
@@ -385,7 +385,7 @@ export function FilterSettings({ onOpenVacation }: FilterSettingsProps = {}) {
return (
- handleToggle(rule.id)} />
+ handleToggle(rule.id)} accessibilityLabel={rule.name} />
0 && (
{t('settings.filters.expanded_view', 'Expanded view')}
- updateSetting('filtersExpandedView', v)} />
+ updateSetting('filtersExpandedView', v)}
+ accessibilityLabel={t('settings.filters.expanded_view', 'Expanded view')}
+ />
)}
diff --git a/src/components/settings/SidebarAppsSettings.tsx b/src/components/settings/SidebarAppsSettings.tsx
index 7a57eb18..438a5b9b 100644
--- a/src/components/settings/SidebarAppsSettings.tsx
+++ b/src/components/settings/SidebarAppsSettings.tsx
@@ -188,7 +188,11 @@ function AppForm({ initial, onSave, onCancel }: AppFormProps) {
{t('settings.sidebar_apps.form.show_on_mobile', "Show on mobile")}
-
+
diff --git a/src/components/settings/TemplateSettings.tsx b/src/components/settings/TemplateSettings.tsx
index b6628d21..b8ca34fc 100644
--- a/src/components/settings/TemplateSettings.tsx
+++ b/src/components/settings/TemplateSettings.tsx
@@ -246,11 +246,19 @@ export function TemplateSettings() {
{tr('settings.templates.favorite', 'Favorite')}
-
+
{tr('settings.templates.is_html', 'Body is HTML')}
-
+
diff --git a/src/components/settings/settings-section.tsx b/src/components/settings/settings-section.tsx
index c998cd79..22eb3140 100644
--- a/src/components/settings/settings-section.tsx
+++ b/src/components/settings/settings-section.tsx
@@ -1,4 +1,4 @@
-import React, { useState } from 'react';
+import React, { useContext, useState } from 'react';
import {
View,
Text,
@@ -65,6 +65,10 @@ export function SettingsSection({
);
}
+// The label of the SettingItem a control sits in. A switch has no text of
+// its own, so without it a screen reader only announces "switch, off".
+const SettingLabelContext = React.createContext(undefined);
+
interface SettingItemProps {
label: string;
description?: string;
@@ -96,7 +100,11 @@ export function SettingItem({
{description && {description}}
- {children && {children}}
+ {children && (
+
+ {children}
+
+ )}
);
}
@@ -105,14 +113,18 @@ interface ToggleSwitchProps {
checked: boolean;
onChange: (v: boolean) => void;
disabled?: boolean;
+ /** Defaults to the label of the enclosing SettingItem. */
+ accessibilityLabel?: string;
}
-export function ToggleSwitch({ checked, onChange, disabled }: ToggleSwitchProps) {
+export function ToggleSwitch({ checked, onChange, disabled, accessibilityLabel }: ToggleSwitchProps) {
+ const settingLabel = useContext(SettingLabelContext);
return (
);
}