From c277d8157fb5b66de5b46e083481921fa1e75c1c Mon Sep 17 00:00:00 2001 From: "google-labs-jules[bot]" <161369871+google-labs-jules[bot]@users.noreply.github.com> Date: Fri, 22 May 2026 19:51:43 +0000 Subject: [PATCH] feat(ux): add keyboard accessibility to interactive cards - Added `role="button"` and `tabIndex={0}` to interactive `ProjectCard` and `ExperienceCard` motion.divs - Implemented `onKeyDown` handlers to trigger click action when pressing "Enter" or "Space" - Added `focus-visible` ring styles to ensure visual feedback when navigating via keyboard - Added an entry to `.Jules/palette.md` noting the necessity of this fix for `motion.div` interactable components Co-authored-by: Dev22603 <92785712+Dev22603@users.noreply.github.com> --- .Jules/palette.md | 4 ++++ frontend/src/components/ExperienceCard.jsx | 13 ++++++++++++- frontend/src/components/ProjectCard.jsx | 11 +++++++++++ 3 files changed, 27 insertions(+), 1 deletion(-) diff --git a/.Jules/palette.md b/.Jules/palette.md index d878f67..d5fe54c 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. + +## 2025-02-12 - Missing Keyboard Events on Motion Cards +**Learning:** In Framer Motion or interactive `div` elements used as cards (like `ProjectCard` and `ExperienceCard`), attaching an `onClick` handler isn't enough for keyboard accessibility. These elements do not natively receive focus or handle enter/space keypresses. +**Action:** When making non-interactive elements (like `div` or `motion.div`) act as buttons or links, always add `role="button"`, `tabIndex={0}`, `onKeyDown` (for Enter/Space), and `focus-visible` styling to provide focus indicators for keyboard navigation. diff --git a/frontend/src/components/ExperienceCard.jsx b/frontend/src/components/ExperienceCard.jsx index ef809b3..22a93d9 100644 --- a/frontend/src/components/ExperienceCard.jsx +++ b/frontend/src/components/ExperienceCard.jsx @@ -5,8 +5,17 @@ import TechTag from "./TechTag"; import { cn } from "@/lib/utils"; export default function ExperienceCard({ experience, onClick }) { + const handleKeyDown = (e) => { + if (onClick && (e.key === "Enter" || e.key === " ")) { + e.preventDefault(); + onClick(e); + } + }; + return ( {/* Current status indicator bar */} {experience.status === "Current" && ( diff --git a/frontend/src/components/ProjectCard.jsx b/frontend/src/components/ProjectCard.jsx index c86c0fe..d70787f 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" }, @@ -29,10 +36,13 @@ export default function ProjectCard({ project, useModal = false, onModalClick, f return (