diff --git a/packages/studio/src/components/pointerTargetSize.test.tsx b/packages/studio/src/components/pointerTargetSize.test.tsx
index 54f7519f8..fd1923533 100644
--- a/packages/studio/src/components/pointerTargetSize.test.tsx
+++ b/packages/studio/src/components/pointerTargetSize.test.tsx
@@ -33,7 +33,7 @@ function mount(element: React.ReactNode) {
return host;
}
-describe("pointer target size (WCAG 2.5.8)", () => {
+describe("pointer target sizing classes (proxy for WCAG 2.5.8, not a geometry check)", () => {
it("gives the timeline zoom slider a 24px box without changing its 2px track or 10px thumb", () => {
const host = mount();
const slider = host.querySelector('input[aria-label="Timeline zoom"]');
diff --git a/packages/studio/src/player/components/TimelineClipDiamonds.test.tsx b/packages/studio/src/player/components/TimelineClipDiamonds.test.tsx
index aff08c082..fc00b55c0 100644
--- a/packages/studio/src/player/components/TimelineClipDiamonds.test.tsx
+++ b/packages/studio/src/player/components/TimelineClipDiamonds.test.tsx
@@ -641,7 +641,7 @@ describe("TimelineClipDiamonds", () => {
act(() => root.unmount());
});
- const renderSegmentLane = (lastAmbiguous: boolean) => {
+ const renderSegmentLane = (lastAmbiguous: boolean, clipWidthPx = 200) => {
const host = document.createElement("div");
document.body.append(host);
const root = createRoot(host);
@@ -660,7 +660,7 @@ describe("TimelineClipDiamonds", () => {
format: "percentage",
keyframes: [kf(0), kf(50), kf(100, { easeAmbiguous: lastAmbiguous })],
}}
- clipWidthPx={200}
+ clipWidthPx={clipWidthPx}
clipHeightPx={48}
accentColor="#4ba3d2"
isSelected
@@ -698,6 +698,21 @@ describe("TimelineClipDiamonds", () => {
expect(segment?.style.pointerEvents).toBe("none");
expect(ease?.style.pointerEvents).toBe("auto");
expect(Number(segment?.style.zIndex)).toBeGreaterThan(Number(diamond?.style.zIndex));
+ // Room to spare here, so the button carries the 24x24 WCAG 2.5.8 overlay.
+ expect(ease?.className).toContain("before:h-6");
+ act(() => root.unmount());
+ });
+
+ it("drops the 24x24 ease overlay when the segment is too narrow to hold it", () => {
+ // The segment wrapper outranks the diamonds, so an overlay wider than the
+ // clear span between them would steal their clicks at fit zoom. 40px of clip
+ // across three keyframes leaves well under 24px of clear span per segment.
+ const { host, root } = renderSegmentLane(false, 40);
+ const ease = host.querySelector("[data-keyframe-ease-button]");
+
+ expect(ease).not.toBeNull();
+ expect(ease?.className).not.toContain("before:h-6");
+ expect(ease?.style.width).toBe("16px");
act(() => root.unmount());
});
diff --git a/packages/studio/src/player/components/TimelineDiamondConnectors.tsx b/packages/studio/src/player/components/TimelineDiamondConnectors.tsx
index 6298dc62c..4b155ef71 100644
--- a/packages/studio/src/player/components/TimelineDiamondConnectors.tsx
+++ b/packages/studio/src/player/components/TimelineDiamondConnectors.tsx
@@ -62,6 +62,12 @@ export function TimelineDiamondConnectors({
// no source animation id (runtime-scanned) so there is no tween to target.
const target = keyframeTarget(kf);
const ease = kf.ease ?? globalEase;
+ // connectorWidth is the clear span between the two diamonds' edges, so a
+ // 24x24 target centred in it overhangs a diamond as soon as the span is
+ // narrower than 24. The segment wrapper sits at z-index 3, above the
+ // diamonds, so that overhang would win the hit test and steal their
+ // clicks at fit zoom. Grow the target only where the room exists.
+ const roomForFullTarget = connectorWidth >= 24;
return (