{
+ if (!event.currentTarget.contains(event.relatedTarget)) onActiveIndexChange(null);
+ }}
+ >
+ {itemLabels.map((itemLabel, index) => (
+
{
+ itemRefs.current[index] = element;
+ }}
+ role="option"
+ aria-label={itemLabel}
+ aria-selected={activeIndex === index}
+ tabIndex={index === tabIndex ? 0 : -1}
+ className={cn(
+ "pointer-events-none focus-visible:outline-2 focus-visible:outline-offset-[-2px] focus-visible:outline-[var(--brand)]",
+ layout === "columns"
+ ? "h-full min-w-0 flex-1 rounded-sm"
+ : "absolute inset-0 rounded-full",
+ )}
+ onFocus={() => {
+ setRovingIndex(index);
+ onActiveIndexChange(index);
+ }}
+ onKeyDown={(event) => handleKeyDown(event, index)}
+ />
+ ))}
+
+
+ {activeIndex === null ? "" : itemLabels[activeIndex]}
+
+ >
+ );
+}
diff --git a/apps/web/src/components/ui/tile-bar-plot.tsx b/apps/web/src/components/ui/tile-bar-plot.tsx
index 79513ca..e11275a 100644
--- a/apps/web/src/components/ui/tile-bar-plot.tsx
+++ b/apps/web/src/components/ui/tile-bar-plot.tsx
@@ -15,6 +15,7 @@ import {
} from "../../hooks/useBarField";
import { usePrefersReducedMotion } from "../../hooks/usePrefersReducedMotion";
import { cn } from "../../lib/utils";
+import { ChartKeyboardList } from "./chart-keyboard-list";
const TICK_FRACTIONS = [1, 0.75, 0.5, 0.25, 0] as const;
@@ -31,6 +32,8 @@ export function TileBarPlot({
layout,
height,
formatTick,
+ ariaLabel,
+ itemLabels,
className,
}: {
/** `[column][band]`; a plain bar chart is one band per column. */
@@ -44,6 +47,8 @@ export function TileBarPlot({
height: number;
/** Provide to render a value axis on the left; omit for a bare plot. */
formatTick?: (value: number) => string;
+ ariaLabel: string;
+ itemLabels: readonly string[];
className?: string;
}) {
const canvasRef = useRef
(null);
@@ -94,6 +99,13 @@ export function TileBarPlot({
/>
))}
+ onHover(column === null ? null : { column, band: null })}
+ layout="columns"
+ />