diff --git a/.Jules/palette.md b/.Jules/palette.md index d878f67..226a586 100644 --- a/.Jules/palette.md +++ b/.Jules/palette.md @@ -1,3 +1,7 @@ ## 2024-05-19 - Missing ARIA Labels on Modal Close Buttons **Learning:** Icon-only close buttons (like the 'X' in ProjectModal and ExperienceModal) were missing ARIA labels, making them inaccessible to screen readers. **Action:** Always verify that icon-only buttons include descriptive `aria-label` attributes to ensure keyboard and screen reader accessibility. + +## 2024-05-24 - Missing Keyboard Accessibility on Clickable Cards +**Learning:** Interactive `motion.div` elements (like `ProjectCard` and `ExperienceCard`) were missing keyboard accessibility, meaning users navigating with a keyboard couldn't trigger their click actions. +**Action:** When making non-interactive elements like `div` or `motion.div` clickable, always add `role="button"`, `tabIndex={0}`, an `onKeyDown` handler to simulate clicks on Enter/Space, and visible focus styles (`focus-visible`). diff --git a/frontend/src/components/ExperienceCard.jsx b/frontend/src/components/ExperienceCard.jsx index ef809b3..a5bcd33 100644 --- a/frontend/src/components/ExperienceCard.jsx +++ b/frontend/src/components/ExperienceCard.jsx @@ -5,6 +5,13 @@ import TechTag from "./TechTag"; import { cn } from "@/lib/utils"; export default function ExperienceCard({ experience, onClick }) { + const handleKeyDown = (e) => { + if (e.key === "Enter" || e.key === " ") { + e.preventDefault(); + onClick?.(); + } + }; + return ( {/* Current status indicator bar */} {experience.status === "Current" && ( diff --git a/frontend/src/components/ProjectCard.jsx b/frontend/src/components/ProjectCard.jsx index c86c0fe..74156fc 100644 --- a/frontend/src/components/ProjectCard.jsx +++ b/frontend/src/components/ProjectCard.jsx @@ -19,6 +19,13 @@ export default function ProjectCard({ project, useModal = false, onModalClick, f } }; + const handleKeyDown = (e) => { + if (e.key === "Enter" || e.key === " ") { + e.preventDefault(); + handleCardClick(); + } + }; + const statusColors = { Paused: { bg: "bg-yellow-500/10", text: "text-yellow-400", border: "border-yellow-500/30" }, Completed: { bg: "bg-[var(--color-primary-dim)]", text: "text-[var(--color-primary)]", border: "border-[var(--color-primary)]/30" }, @@ -33,6 +40,9 @@ export default function ProjectCard({ project, useModal = false, onModalClick, f whileTap={{ scale: 0.99 }} transition={{ duration: 0.3 }} onClick={handleCardClick} + onKeyDown={handleKeyDown} + role="button" + tabIndex={0} className={cn( "group relative cursor-pointer h-full", "bg-[var(--color-background-card)]", @@ -41,6 +51,7 @@ export default function ProjectCard({ project, useModal = false, onModalClick, f "transition-all duration-400", "hover:border-[var(--color-border-secondary)]", "hover:shadow-[0_20px_50px_-20px_var(--color-primary-dim)]", + "focus:outline-none focus-visible:ring-2 focus-visible:ring-[var(--color-primary)] focus-visible:ring-offset-2 focus-visible:ring-offset-[var(--color-background-primary)]", featured && "md:flex md:items-stretch" )} >