Overview

onWindow(event, fn) attaches a bubble-phase listener on window, tied to the component lifetime.

onWindowCapture(event, fn) uses the capture phase — required for non-bubbling events like scroll, or when you must run before target handlers.

Both accept any WindowEventMap name plus synthetic 'wheelUp' / 'wheelDown'. Handlers may return cleanup. Listeners are removed on unmount. SSR no-op.

Bubble vs capture

API
Phase
Use when
onWindow bubble Most events (keydown, resize, online, …).
onWindowCapture capture scroll / focus / blur; intercept before target.

onWindow + onWindowCapture

Bubble Escape and capture-phase clicks. Listeners tear down with the component.

import { html, atom, onWindow, onWindowCapture, renderApp } from 'mates';

const App = () => {
const log = atom([]);

const push = (msg) => log.set(prev => [...prev.slice(-4), msg]);

// Bubble phase
onWindow('keydown', (e) => {
if (e.key === 'Escape') push('Escape (bubble)');
});

// Capture phase — runs before target handlers; needed for non-bubbling events
onWindowCapture('click', (e) => {
push('click capture: ' + (e.target?.tagName ?? '?'));
});

return () => html`
<div class="m-col m-gap">
<p class="muted">Press Escape or click below.</p>
<button class="btn-primary">Click me</button>
<div class="card card-body">
<span class="overline">Events</span>
${log().length === 0
? html`<div class="mono subtle">None yet…</div>`
: log().map(e => html`<div class="mono">${e}</div>`)}
</div>
</div>
`;
};

renderApp(App, document.getElementById('app'));