Skip to content
Merged
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
3 changes: 3 additions & 0 deletions .Jules/palette.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,3 +9,6 @@
## 2026-06-13 - Added screen reader text for tooltip divs
**Learning:** When using `title` attributes on non-interactive elements like icon-only `div`s for tooltips, screen readers might not announce them properly because they aren't focusable. The visual tooltip is not enough for accessibility.
**Action:** Always add a visually hidden `<span className="sr-only">[Tooltip Text]</span>` inside non-interactive elements that rely on a `title` attribute so that screen readers have text content to announce.
## 2026-06-18 - Added keyboard accessibility to scrollable regions
**Learning:** Horizontally scrollable regions (like the `SectionRoadmap` component) are not accessible to keyboard-only users unless they can receive focus. Keyboard users must be able to focus the container to scroll its content using arrow keys.
**Action:** For proper keyboard accessibility in custom scrollable regions, always include `tabIndex={0}`, an appropriate `aria-label`, `role="region"`, and explicit focus visible styling (e.g., `focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-cyan-300`).
12 changes: 9 additions & 3 deletions apps/desktop/src/features/workspace/SectionRoadmap.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import type { RehearsalSong, RehearsalRole } from "@bandscope/shared-types";
import { useMemo } from "react";
import { useId, useMemo } from "react";
import { createTranslator, detectPreferredLocale } from "../../i18n";
import { ConfidenceBadge } from "./ConfidenceBadge";
import { Card, CardContent, CardHeader } from "@/components/ui/card";
Expand All @@ -15,6 +15,7 @@ interface SectionRoadmapProps {

/** Documented. */
export function SectionRoadmap({ song, activeRole, onSongUpdate }: SectionRoadmapProps) {
const sectionRoadmapTitleId = useId();
const locale = useMemo(() => detectPreferredLocale(), []);
const t = useMemo(() => createTranslator(locale), [locale]);

Expand Down Expand Up @@ -70,14 +71,19 @@ export function SectionRoadmap({ song, activeRole, onSongUpdate }: SectionRoadma
return (
<div className="mt-6 space-y-4">
<div className="flex items-center justify-between">
<h2 className="flex items-center text-xl font-black tracking-tight text-white">
<h2 id={sectionRoadmapTitleId} className="flex items-center text-xl font-black tracking-tight text-white">
<Music2 className="mr-2 size-5 text-cyan-300" aria-hidden="true" />
{t("sectionRoadmapTitle")}
</h2>
<span className="text-xs font-semibold uppercase tracking-[0.2em] text-slate-400">{t("sectionRoadmapScrollHint")}</span>
</div>

<div className="hide-scrollbar flex snap-x snap-mandatory gap-6 overflow-x-auto pb-6 pt-2">
<div
className="hide-scrollbar flex snap-x snap-mandatory gap-6 overflow-x-auto rounded-xl pb-6 pt-2 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-cyan-300"
role="region"
tabIndex={0}
aria-labelledby={sectionRoadmapTitleId}
>
{song.sections.map((section) => (
<Card
key={section.id}
Expand Down