diff --git a/.Jules/palette.md b/.Jules/palette.md index bdd475d..06e7a39 100644 --- a/.Jules/palette.md +++ b/.Jules/palette.md @@ -1 +1,5 @@ # Palette Journal + +## 2024-05-22 - Password Toggles and Label Selectors +**Learning:** When implementing "Show/Hide Password" toggles within an input field, simply adding an icon button can create ambiguity for automated testing tools if not careful. While `htmlFor`/`id` binding connects the visual label to the input correctly, the toggle button's `aria-label` (e.g., "Show password") contains the same keyword ("password") as the main label. +**Action:** Always prefer selecting by strict label text or using specific input types (`input[type='password']`) in tests when a field has auxiliary interactive controls containing similar accessible names. Ensure the toggle button is clearly distinct from the input itself in the accessibility tree. diff --git a/web/components/ui/Button.tsx b/web/components/ui/Button.tsx index cfccd8e..66ea2e8 100644 --- a/web/components/ui/Button.tsx +++ b/web/components/ui/Button.tsx @@ -1,17 +1,21 @@ import React from 'react'; import { THEMES } from '../../constants'; import { useTheme } from '../../contexts/ThemeContext'; +import { Spinner } from './Spinner'; interface ButtonProps extends React.ButtonHTMLAttributes { variant?: 'primary' | 'secondary' | 'danger' | 'ghost'; size?: 'sm' | 'md' | 'lg'; + isLoading?: boolean; } export const Button: React.FC = ({ children, variant = 'primary', size = 'md', + isLoading = false, className = '', + disabled, ...props }) => { const { style } = useTheme(); @@ -47,8 +51,11 @@ export const Button: React.FC = ({ return ( ); diff --git a/web/components/ui/Spinner.tsx b/web/components/ui/Spinner.tsx new file mode 100644 index 0000000..dfb4e1a --- /dev/null +++ b/web/components/ui/Spinner.tsx @@ -0,0 +1,23 @@ +import React from 'react'; +import { Loader2 } from 'lucide-react'; + +interface SpinnerProps { + size?: number; + className?: string; + ariaLabel?: string; +} + +export const Spinner: React.FC = ({ + size = 20, + className = '', + ariaLabel = 'Loading' +}) => { + return ( + + ); +}; diff --git a/web/pages/Auth.tsx b/web/pages/Auth.tsx index 180001c..99f74a5 100644 --- a/web/pages/Auth.tsx +++ b/web/pages/Auth.tsx @@ -214,7 +214,8 @@ export const Auth = () => { exit={{ height: 0, opacity: 0 }} > setName(e.target.value)} required @@ -225,16 +226,18 @@ export const Auth = () => { setEmail(e.target.value)} required className={isNeo ? 'rounded-none' : ''} /> setPassword(e.target.value)} required @@ -251,8 +254,8 @@ export const Auth = () => { )} -