Skip to content

Commit a0f466d

Browse files
miguel-heygenclaude
andcommitted
fix(player): fix controls hitbox with proper z-index layering
The iframe's large layout box (1920x1080 before CSS scale) was intercepting clicks meant for the controls bar. Fixed by: - Replacing container click handler with a dedicated overlay div - Z-index stacking: iframe (none) → overlay (5) → controls (10) - Controls clicks stay on controls, overlay clicks toggle play/pause - Larger play/pause button (40x40) with bigger icon (24x24) Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent d84a6dd commit a0f466d

3 files changed

Lines changed: 31 additions & 21 deletions

File tree

packages/player/src/controls.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ export function formatTime(seconds: number): string {
1414
}
1515

1616
export function createControls(
17-
shadow: ShadowRoot,
17+
parent: ShadowRoot | HTMLElement,
1818
callbacks: ControlsCallbacks,
1919
): {
2020
updateTime: (current: number, duration: number) => void;
@@ -45,7 +45,7 @@ export function createControls(
4545
controls.appendChild(playBtn);
4646
controls.appendChild(scrubber);
4747
controls.appendChild(time);
48-
shadow.appendChild(controls);
48+
parent.appendChild(controls);
4949

5050
let isPlaying = false;
5151
let hideTimeout: ReturnType<typeof setTimeout> | null = null;
@@ -107,7 +107,7 @@ export function createControls(
107107
}, 3000);
108108
};
109109

110-
const host = shadow.host as HTMLElement;
110+
const host = parent instanceof ShadowRoot ? (parent.host as HTMLElement) : parent;
111111
host.addEventListener("mousemove", () => {
112112
controls.classList.remove("hfp-hidden");
113113
startHideTimer();

packages/player/src/hyperframes-player.ts

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -45,13 +45,16 @@ class HyperframesPlayer extends HTMLElement {
4545
this.iframe.title = "HyperFrames Composition";
4646

4747
this.container.appendChild(this.iframe);
48-
this.shadow.appendChild(this.container);
4948

50-
// Click on the container area toggles play/pause
51-
this.container.addEventListener("click", () => {
49+
// Overlay above iframe, below controls — click to toggle play/pause
50+
const overlay = document.createElement("div");
51+
overlay.className = "hfp-overlay";
52+
overlay.addEventListener("click", () => {
5253
if (this._paused) this.play();
5354
else this.pause();
5455
});
56+
this.container.appendChild(overlay);
57+
this.shadow.appendChild(this.container);
5558

5659
this.resizeObserver = new ResizeObserver(() => this._updateScale());
5760

@@ -345,8 +348,7 @@ class HyperframesPlayer extends HTMLElement {
345348
);
346349
this.iframe.style.width = `${this._compositionWidth}px`;
347350
this.iframe.style.height = `${this._compositionHeight}px`;
348-
this.iframe.style.transform = `scale(${scale})`;
349-
this.iframe.style.transformOrigin = "center center";
351+
this.iframe.style.transform = `translate(-50%, -50%) scale(${scale})`;
350352
}
351353

352354
private _setupControls() {
@@ -356,7 +358,7 @@ class HyperframesPlayer extends HTMLElement {
356358
onPause: () => this.pause(),
357359
onSeek: (fraction) => this.seek(fraction * this._duration),
358360
};
359-
this.controlsApi = createControls(this.shadow, callbacks);
361+
this.controlsApi = createControls(this.container, callbacks);
360362
}
361363

362364
private _setupPoster() {

packages/player/src/styles.ts

Lines changed: 20 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -8,18 +8,23 @@ export const PLAYER_STYLES = /* css */ `
88
}
99
1010
.hfp-container {
11-
width: 100%;
12-
height: 100%;
13-
display: flex;
14-
align-items: center;
15-
justify-content: center;
11+
position: absolute;
12+
inset: 0;
1613
overflow: hidden;
14+
}
15+
16+
.hfp-overlay {
17+
position: absolute;
18+
inset: 0;
19+
z-index: 5;
1720
cursor: pointer;
1821
}
1922
2023
.hfp-iframe {
24+
position: absolute;
25+
top: 50%;
26+
left: 50%;
2127
border: none;
22-
flex-shrink: 0;
2328
pointer-events: none;
2429
}
2530
@@ -44,7 +49,8 @@ export const PLAYER_STYLES = /* css */ `
4449
color: #fff;
4550
font-family: system-ui, -apple-system, sans-serif;
4651
font-size: 13px;
47-
z-index: 2;
52+
z-index: 10;
53+
pointer-events: auto;
4854
opacity: 1;
4955
transition: opacity 0.3s ease;
5056
user-select: none;
@@ -60,13 +66,15 @@ export const PLAYER_STYLES = /* css */ `
6066
border: none;
6167
color: #fff;
6268
cursor: pointer;
63-
padding: 4px;
69+
padding: 8px;
6470
display: flex;
6571
align-items: center;
6672
justify-content: center;
67-
width: 32px;
68-
height: 32px;
73+
width: 40px;
74+
height: 40px;
6975
flex-shrink: 0;
76+
position: relative;
77+
z-index: 11;
7078
}
7179
7280
.hfp-play-btn:hover {
@@ -103,5 +111,5 @@ export const PLAYER_STYLES = /* css */ `
103111
}
104112
`;
105113

106-
export const PLAY_ICON = `<svg width="18" height="18" viewBox="0 0 18 18" fill="currentColor"><polygon points="4,2 16,9 4,16"/></svg>`;
107-
export const PAUSE_ICON = `<svg width="18" height="18" viewBox="0 0 18 18" fill="currentColor"><rect x="3" y="2" width="4" height="14"/><rect x="11" y="2" width="4" height="14"/></svg>`;
114+
export const PLAY_ICON = `<svg width="24" height="24" viewBox="0 0 18 18" fill="currentColor"><polygon points="4,2 16,9 4,16"/></svg>`;
115+
export const PAUSE_ICON = `<svg width="24" height="24" viewBox="0 0 18 18" fill="currentColor"><rect x="3" y="2" width="4" height="14"/><rect x="11" y="2" width="4" height="14"/></svg>`;

0 commit comments

Comments
 (0)