diff --git a/.jules/palette.md b/.jules/palette.md new file mode 100644 index 0000000..ea2569f --- /dev/null +++ b/.jules/palette.md @@ -0,0 +1,3 @@ +## 2024-05-24 - Accessible Icon Buttons +**Learning:** Screen readers cannot infer the purpose of icon-only buttons without text content, making actions like "Delete note" inaccessible to visually impaired users. In this app, many utility buttons (like the trash icon in Notepad) relied solely on visual metaphors. +**Action:** Always add `accessible={true}`, `accessibilityRole="button"`, and a descriptive `accessibilityLabel` to `` components that only contain an icon, using existing translation keys when possible. diff --git a/src/i18n/translations.ts b/src/i18n/translations.ts index 8ccd1e2..a79e7bd 100644 --- a/src/i18n/translations.ts +++ b/src/i18n/translations.ts @@ -121,6 +121,7 @@ const it = { contactNoResults: 'Nessun risultato', // Password passwordTitle: 'Password', passwordAdd: 'Aggiungi', + passwordShow: 'Mostra password', passwordHide: 'Nascondi password', passwordModalNew: 'Nuova voce', passwordModalEdit: 'Modifica voce', passwordNameLabel: 'Nome *', passwordNamePh: 'es. Portale HR EasyJet', passwordUsernameLabel: 'Username / Email', passwordUsernamePh: 'es. mario.rossi@easyjet.com', @@ -279,6 +280,7 @@ const en: typeof it = { contactNoResults: 'No results', // Password passwordTitle: 'Password', passwordAdd: 'Add', + passwordShow: 'Show password', passwordHide: 'Hide password', passwordModalNew: 'New entry', passwordModalEdit: 'Edit entry', passwordNameLabel: 'Name *', passwordNamePh: 'e.g. EasyJet HR Portal', passwordUsernameLabel: 'Username / Email', passwordUsernamePh: 'e.g. mario.rossi@easyjet.com', diff --git a/src/screens/PasswordScreen.tsx b/src/screens/PasswordScreen.tsx index c5338f1..83c785d 100644 --- a/src/screens/PasswordScreen.tsx +++ b/src/screens/PasswordScreen.tsx @@ -51,7 +51,6 @@ const EMPTY_MODAL: ModalState = { // ─── PIN Overlay ───────────────────────────────────────────────────────────── function PinOverlay({ onUnlock, onCancel, title }: { onUnlock: (pin: string) => void; onCancel?: () => void; title: string }) { const { colors } = useAppTheme(); - const { t } = useLanguage(); const s = useMemo(() => makePinStyles(colors), [colors]); const [digits, setDigits] = useState(''); @@ -103,6 +102,7 @@ function PinOverlay({ onUnlock, onCancel, title }: { onUnlock: (pin: string) => // ─── Password Row ───────────────────────────────────────────────────────────── function PasswordRowComponent({ item, onEdit, onDelete }: { item: PasswordEntry; onEdit: () => void; onDelete: () => void }) { const { colors } = useAppTheme(); + const { t } = useLanguage(); const s = useMemo(() => makeRowStyles(colors), [colors]); const [revealed, setRevealed] = useState(false); @@ -113,7 +113,7 @@ function PasswordRowComponent({ item, onEdit, onDelete }: { item: PasswordEntry; {item.username ? {item.username} : null} {revealed ? item.password : '••••••••'} - setRevealed(r => !r)} style={s.eyeBtn}> + setRevealed(r => !r)} style={s.eyeBtn} accessible accessibilityRole="button" accessibilityLabel={revealed ? t('passwordHide') : t('passwordShow')}> @@ -136,8 +136,8 @@ const PasswordRow = React.memo(PasswordRowComponent); // ─── Main Screen ────────────────────────────────────────────────────────────── export default function PasswordScreen() { - const { colors } = useAppTheme(); const { t } = useLanguage(); + const { colors } = useAppTheme(); const s = useMemo(() => makeStyles(colors), [colors]); const [entries, setEntries] = useState([]); @@ -330,7 +330,7 @@ export default function PasswordScreen() { secureTextEntry={!showPw} autoCapitalize="none" /> - setShowPw(p => !p)} style={s.eyeModal}> + setShowPw(p => !p)} style={s.eyeModal} accessible accessibilityRole="button" accessibilityLabel={showPw ? t('passwordHide') : t('passwordShow')}>