diff --git a/.Jules/changelog.md b/.Jules/changelog.md index 11fc864..84d5534 100644 --- a/.Jules/changelog.md +++ b/.Jules/changelog.md @@ -7,6 +7,13 @@ ## [Unreleased] ### Added +- **Consistent Focus States:** Added `focus-visible` styles to all buttons in the web application. + - **Features:** + - Modified `Button` component to support explicit keyboard focus rings for both Glassmorphism and Neobrutalism themes. + - Updated generic components (Inputs, Toasts, Modals) with visible focus indicators. + - Updated interactive elements across pages (Auth, Dashboard, Friends, GroupDetails, Profile, SplitwiseImport, SplitwiseGroupSelection). + - **Technical:** Used Tailwind's `focus-visible:` pseudo-class to ensure focus rings are only shown for keyboard navigation, maintaining a clean visual UI for mouse users. + - **Password Strength Meter:** Added a visual password strength indicator to the signup form. - **Features:** - Real-time strength calculation (Length, Uppercase, Lowercase, Number, Symbol). diff --git a/.Jules/todo.md b/.Jules/todo.md index ebb0c7a..bddcfcc 100644 --- a/.Jules/todo.md +++ b/.Jules/todo.md @@ -78,7 +78,8 @@ ### Web -- [ ] **[style]** Consistent hover/focus states across all buttons +- [x] **[style]** Consistent hover/focus states across all buttons + - Completed: 2026-02-08 - Files: `web/components/ui/Button.tsx`, usage across pages - Context: Ensure all buttons have proper hover + focus-visible styles - Impact: Professional feel, keyboard users know where they are diff --git a/web/components/ui/Button.tsx b/web/components/ui/Button.tsx index f80c514..ff92a43 100644 --- a/web/components/ui/Button.tsx +++ b/web/components/ui/Button.tsx @@ -20,7 +20,7 @@ export const Button: React.FC = ({ }) => { const { style } = useTheme(); - const baseStyles = "transition-all duration-200 font-bold flex items-center justify-center gap-2 disabled:opacity-50 disabled:cursor-not-allowed"; + const baseStyles = "transition-all duration-200 font-bold flex items-center justify-center gap-2 disabled:opacity-50 disabled:cursor-not-allowed focus:outline-none focus-visible:ring-2 focus-visible:ring-offset-2"; const sizeStyles = { sm: "px-3 py-1.5 text-sm", @@ -31,7 +31,7 @@ export const Button: React.FC = ({ let themeStyles = ""; if (style === THEMES.NEOBRUTALISM) { - themeStyles = "border-2 border-black shadow-[4px_4px_0px_0px_rgba(0,0,0,1)] hover:translate-x-[2px] hover:translate-y-[2px] hover:shadow-[2px_2px_0px_0px_rgba(0,0,0,1)] active:translate-x-[4px] active:translate-y-[4px] active:shadow-none rounded-none uppercase tracking-wider font-mono"; + themeStyles = "border-2 border-black shadow-[4px_4px_0px_0px_rgba(0,0,0,1)] hover:translate-x-[2px] hover:translate-y-[2px] hover:shadow-[2px_2px_0px_0px_rgba(0,0,0,1)] active:translate-x-[4px] active:translate-y-[4px] active:shadow-none rounded-none uppercase tracking-wider font-mono focus-visible:ring-black focus-visible:ring-offset-white"; if (variant === 'primary') themeStyles += " bg-neo-main text-white"; if (variant === 'secondary') themeStyles += " bg-neo-second text-black"; @@ -42,10 +42,10 @@ export const Button: React.FC = ({ // Glassmorphism themeStyles = "rounded-xl backdrop-blur-md border border-white/20 shadow-lg hover:shadow-xl active:scale-95"; - if (variant === 'primary') themeStyles += " bg-gradient-to-r from-blue-500 to-purple-600 text-white shadow-blue-500/30"; - if (variant === 'secondary') themeStyles += " bg-white/10 text-white hover:bg-white/20"; - if (variant === 'danger') themeStyles += " bg-gradient-to-r from-red-500 to-pink-600 text-white shadow-red-500/30"; - if (variant === 'ghost') themeStyles += " bg-transparent border-none shadow-none hover:bg-white/10 text-current"; + if (variant === 'primary') themeStyles += " bg-gradient-to-r from-blue-500 to-purple-600 text-white shadow-blue-500/30 focus-visible:ring-blue-400 focus-visible:ring-offset-transparent"; + if (variant === 'secondary') themeStyles += " bg-white/10 text-white hover:bg-white/20 focus-visible:ring-white focus-visible:ring-offset-transparent"; + if (variant === 'danger') themeStyles += " bg-gradient-to-r from-red-500 to-pink-600 text-white shadow-red-500/30 focus-visible:ring-red-400 focus-visible:ring-offset-transparent"; + if (variant === 'ghost') themeStyles += " bg-transparent border-none shadow-none hover:bg-white/10 text-current focus-visible:ring-white/50 focus-visible:ring-offset-transparent"; } return ( diff --git a/web/components/ui/Input.tsx b/web/components/ui/Input.tsx index 80e7fff..a7c92d9 100644 --- a/web/components/ui/Input.tsx +++ b/web/components/ui/Input.tsx @@ -46,10 +46,10 @@ export const Input: React.FC = ({ label, error, className = '', type diff --git a/web/components/ui/Toast.tsx b/web/components/ui/Toast.tsx index 056ebb8..3eb84e3 100644 --- a/web/components/ui/Toast.tsx +++ b/web/components/ui/Toast.tsx @@ -57,7 +57,7 @@ const ToastItem: React.FC<{ toast: Toast }> = ({ toast }) => { @@ -605,7 +605,7 @@ export const GroupDetails = () => { @@ -656,21 +656,21 @@ export const GroupDetails = () => { @@ -704,7 +704,7 @@ export const GroupDetails = () => { @@ -920,7 +920,7 @@ export const GroupDetails = () => { key={m.userId} type="button" onClick={() => setPayerId(m.userId)} - className={`px-4 py-2 text-sm font-bold transition-all border ${payerId === m.userId + className={`px-4 py-2 text-sm font-bold transition-all border focus:outline-none focus-visible:ring-2 focus-visible:ring-blue-500 ${payerId === m.userId ? (style === THEMES.NEOBRUTALISM ? 'bg-black text-white border-black rounded-none' : 'bg-blue-600 border-blue-500 text-white rounded-full') : (style === THEMES.NEOBRUTALISM ? 'bg-white text-black border-black hover:bg-gray-100 rounded-none' : 'bg-transparent border-gray-600 text-gray-400 hover:border-gray-400 rounded-full') }`} @@ -936,7 +936,7 @@ export const GroupDetails = () => { @@ -1008,7 +1008,7 @@ export const GroupDetails = () => { key={mode.id} type="button" onClick={() => setUnequalMode(mode.id as UnequalMode)} - className={`flex-1 py-1.5 px-2 text-sm font-bold flex items-center justify-center gap-1 transition-all ${unequalMode === mode.id + className={`flex-1 py-1.5 px-2 text-sm font-bold flex items-center justify-center gap-1 transition-all focus:outline-none focus-visible:ring-2 focus-visible:ring-blue-500 ${unequalMode === mode.id ? (style === THEMES.NEOBRUTALISM ? 'bg-black text-white rounded-none' : 'bg-blue-600 text-white shadow-md rounded') : (style === THEMES.NEOBRUTALISM ? 'bg-gray-200 text-black rounded-none' : 'bg-gray-200 dark:bg-gray-800 opacity-70 hover:opacity-100 dark:text-gray-300 rounded') }`} @@ -1109,21 +1109,21 @@ export const GroupDetails = () => { @@ -1196,7 +1196,7 @@ export const GroupDetails = () => {