diff --git a/.Jules/palette.md b/.Jules/palette.md
new file mode 100644
index 00000000..e9a02344
--- /dev/null
+++ b/.Jules/palette.md
@@ -0,0 +1,3 @@
+## 2025-05-22 - [Accessibility: Password Toggles]
+**Learning:** Icon-only buttons for password visibility toggles are a common accessibility gap. Adding `aria-label` and `aria-pressed` ensures screen reader users understand the button's purpose and state.
+**Action:** Always check password fields and add these attributes along with corresponding translations in `translations.ts`.
diff --git a/web/src/components/LoginPage.tsx b/web/src/components/LoginPage.tsx
index 115c1def..51e5ba8b 100644
--- a/web/src/components/LoginPage.tsx
+++ b/web/src/components/LoginPage.tsx
@@ -334,6 +334,8 @@ export function LoginPage() {
type="button"
onClick={() => setShowPassword(!showPassword)}
className="absolute right-3 top-1/2 -translate-y-1/2 text-zinc-600 hover:text-zinc-400 transition-colors"
+ aria-label={showPassword ? t('hidePassword', language) : t('showPassword', language)}
+ aria-pressed={showPassword}
>
{showPassword ? : }
diff --git a/web/src/i18n/translations.ts b/web/src/i18n/translations.ts
index 7d44356e..eb96949c 100644
--- a/web/src/i18n/translations.ts
+++ b/web/src/i18n/translations.ts
@@ -667,6 +667,8 @@ export const translations = {
passwordRequired: 'Password is required',
invalidEmail: 'Invalid email format',
passwordTooShort: 'Password must be at least 6 characters',
+ showPassword: 'Show password',
+ hidePassword: 'Hide password',
// Landing Page
features: 'Features',
@@ -1834,6 +1836,8 @@ export const translations = {
passwordRequired: '请输入密码',
invalidEmail: '邮箱格式不正确',
passwordTooShort: '密码至少需要6个字符',
+ showPassword: '显示密码',
+ hidePassword: '隐藏密码',
// Landing Page
features: '功能',