Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions .Jules/changelog.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,14 @@
- Uses `expo-haptics` with `Light` impact style for subtle feedback.
- **Technical:** Centralized haptic logic in `mobile/components/ui/` to ensure consistency and maintainability.

- **Consistent Focus/Hover States:** Added comprehensive `focus-visible` styles across key interactive web components to improve keyboard navigation accessibility.
- **Features:**
- Updated `Button` component with theme-specific (Neo/Glass) focus rings.
- Updated `Input` password visibility toggle to use `focus-visible` to prevent ugly rings for mouse users while supporting keyboard users.
- Added focus rings to `Modal` and `Toast` close buttons.
- Added focus rings to `Sidebar` navigation links.
- **Technical:** Modified `web/components/ui/Button.tsx`, `web/components/ui/Input.tsx`, `web/components/ui/Modal.tsx`, `web/components/ui/Toast.tsx`, and `web/components/layout/Sidebar.tsx`.

- **Mobile Accessibility:** Completed accessibility audit for all mobile screens.
- **Features:**
- Added `accessibilityLabel` to all interactive elements (buttons, inputs, list items).
Expand Down
5 changes: 3 additions & 2 deletions .Jules/todo.md
Original file line number Diff line number Diff line change
Expand Up @@ -78,8 +78,9 @@

### Web

- [ ] **[style]** Consistent hover/focus states across all buttons
- Files: `web/components/ui/Button.tsx`, usage across pages
- [x] **[style]** Consistent hover/focus states across all buttons
- Completed: 2026-03-13
- Files: `web/components/ui/Button.tsx`, `web/components/ui/Input.tsx`, `web/components/ui/Modal.tsx`, `web/components/ui/Toast.tsx`, `web/components/layout/Sidebar.tsx`
- Context: Ensure all buttons have proper hover + focus-visible styles
- Impact: Professional feel, keyboard users know where they are
- Size: ~35 lines
Expand Down
2 changes: 1 addition & 1 deletion web/components/layout/Sidebar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ export const Sidebar = () => {

<nav className="flex-1 flex flex-col gap-4">
{navItems.map((item) => (
<Link to={item.path} key={item.path}>
<Link to={item.path} key={item.path} className={`block focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-offset-2 ${style === THEMES.NEOBRUTALISM ? 'focus-visible:ring-black dark:focus-visible:ring-white rounded-none' : 'focus-visible:ring-blue-500 rounded-xl'}`}>
<div className={`flex items-center gap-3 px-4 py-3 transition-all ${
isActive(item.path)
? (style === THEMES.NEOBRUTALISM
Expand Down
6 changes: 3 additions & 3 deletions web/components/ui/Button.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ export const Button: React.FC<ButtonProps> = ({
}) => {
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-visible:outline-none focus-visible:ring-2 focus-visible:ring-offset-2";

const sizeStyles = {
sm: "px-3 py-1.5 text-sm",
Expand All @@ -31,7 +31,7 @@ export const Button: React.FC<ButtonProps> = ({
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 dark:focus-visible:ring-white";

if (variant === 'primary') themeStyles += " bg-neo-main text-white";
if (variant === 'secondary') themeStyles += " bg-neo-second text-black";
Expand All @@ -40,7 +40,7 @@ export const Button: React.FC<ButtonProps> = ({

} else {
// Glassmorphism
themeStyles = "rounded-xl backdrop-blur-md border border-white/20 shadow-lg hover:shadow-xl active:scale-95";
themeStyles = "rounded-xl backdrop-blur-md border border-white/20 shadow-lg hover:shadow-xl active:scale-95 focus-visible:ring-blue-500 focus-visible:ring-offset-transparent";

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";
Expand Down
6 changes: 3 additions & 3 deletions web/components/ui/Input.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -46,10 +46,10 @@ export const Input: React.FC<InputProps> = ({ label, error, className = '', type
<button
type="button"
onClick={() => setShowPassword(!showPassword)}
className={`absolute right-3 top-1/2 -translate-y-1/2 p-1 rounded-md transition-opacity hover:opacity-100 focus:outline-none focus:ring-2 focus:ring-offset-1 ${
className={`absolute right-3 top-1/2 -translate-y-1/2 p-1 rounded-md transition-opacity hover:opacity-100 focus:outline-none focus-visible:ring-2 focus-visible:ring-offset-1 ${
style === THEMES.NEOBRUTALISM
? (mode === 'dark' ? 'text-white opacity-80 focus:ring-white' : 'text-black opacity-60 focus:ring-black')
: 'text-white/60 hover:text-white focus:ring-white/50'
? (mode === 'dark' ? 'text-white opacity-80 focus-visible:ring-white' : 'text-black opacity-60 focus-visible:ring-black')
: 'text-white/60 hover:text-white focus-visible:ring-white/50'
}`}
aria-label={showPassword ? "Hide password" : "Show password"}
>
Expand Down
2 changes: 1 addition & 1 deletion web/components/ui/Modal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ export const Modal: React.FC<ModalProps> = ({ isOpen, onClose, title, children,
{/* Header */}
<div className={`p-6 flex justify-between items-center ${style === THEMES.NEOBRUTALISM ? 'border-b-2 border-black bg-neo-main text-white' : 'border-b border-white/10 bg-white/5'}`}>
<h3 id={titleId} className={`text-2xl font-bold ${style === THEMES.NEOBRUTALISM ? 'uppercase font-mono tracking-tighter' : ''}`}>{title}</h3>
<button type="button" onClick={onClose} className="hover:rotate-90 transition-transform duration-200" aria-label="Close modal">
<button type="button" onClick={onClose} className={`hover:rotate-90 transition-all duration-200 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-offset-2 rounded-md ${style === THEMES.NEOBRUTALISM ? 'focus-visible:ring-white focus-visible:ring-offset-neo-main' : 'focus-visible:ring-white'}`} aria-label="Close modal">
<X size={24} />
</button>
</div>
Expand Down
2 changes: 1 addition & 1 deletion web/components/ui/Toast.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ const ToastItem: React.FC<{ toast: Toast }> = ({ toast }) => {
<button
type="button"
onClick={() => removeToast(toast.id)}
className="shrink-0 hover:opacity-70 transition-opacity"
className={`shrink-0 hover:opacity-70 transition-all focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-offset-2 rounded-sm ${isNeo ? 'focus-visible:ring-black dark:focus-visible:ring-white focus-visible:ring-offset-transparent' : 'focus-visible:ring-white'}`}
aria-label="Close notification"
>
<X className="w-4 h-4" />
Expand Down
Loading
Loading