Presence with can-hover
can-hover shares current hover presence without saving it. Style
[data-playhtml-hover] so remote hover produces the same effect as local hover.
- Keep this page open in your normal window.
- Copy the private-window link, then paste it into a private or incognito window.
- Interact in either window and watch the other one update.
Copy the code
Both versions use the same shared data and behavior. The live playground runs the Vanilla HTML version.
Save this as index.html, or open it in the playground to
test and change it.
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>Presence with can-hover</title>
<style>
:root {
color: #1c1c1c;
background: #f4efe5;
font-family: ui-sans-serif, system-ui, sans-serif;
}
* { box-sizing: border-box; }
body { display: grid; min-height: 100vh; margin: 0; padding: 1.5rem; place-items: center; }
main { width: min(36rem, 100%); }
h1 { margin: 0 0 0.35rem; font-size: clamp(2rem, 8vw, 3.5rem); line-height: 1; }
.intro { margin: 0 0 1.25rem; line-height: 1.5; }
main { text-align: center; }
.hover-card {
display: grid;
min-height: 12rem;
place-items: center;
border: 2px solid #1c1c1c;
background: #dce8cf;
box-shadow: 5px 5px 0 #1c1c1c;
font-size: 1.4rem;
font-weight: 800;
transition: background 150ms, transform 150ms;
}
.hover-card[data-playhtml-hover] {
background: #f3cf58;
transform: scale(1.03);
}
</style>
</head>
<body>
<main>
<h1>Shared hover</h1>
<p class="intro">Hover over the card. It lights up for everyone currently connected.</p>
<div id="shared-hover-card" class="hover-card" can-hover>
hover here
</div>
</main>
<script type="module">
import { playhtml } from "playhtml";
// can-hover manages the data-playhtml-hover attribute.
await playhtml.init({ developmentMode: true });
</script>
</body>
</html>
Start with a React + TypeScript project, install the packages below,
then replace src/App.tsx with the component.
npm install playhtml @playhtml/react // ABOUTME: Shares ephemeral hover state across connected browsers.
// ABOUTME: Styles the element from the data-playhtml-hover attribute.
import { PlayProvider, CanHoverElement } from "@playhtml/react";
export default function App() {
return (
<PlayProvider initOptions={{ developmentMode: true }}>
<main>
<h1>Shared hover</h1>
<p>Hover over the card. It lights up for everyone currently connected.</p>
<CanHoverElement>
<div id="shared-hover-card" className="hover-card">
hover here
</div>
</CanHoverElement>
</main>
<style>{`
:root { color: #1c1c1c; background: #f4efe5; font-family: system-ui, sans-serif; }
* { box-sizing: border-box; }
body { margin: 0; }
#root { display: grid; min-height: 100vh; padding: 1.5rem; place-items: center; }
main { width: min(32rem, 100%); text-align: center; }
h1 { margin: 0 0 0.35rem; font-size: clamp(2rem, 9vw, 3.5rem); }
p { margin: 0 0 1.25rem; line-height: 1.5; }
.hover-card {
display: grid;
min-height: 12rem;
place-items: center;
border: 2px solid #1c1c1c;
background: #dce8cf;
box-shadow: 5px 5px 0 #1c1c1c;
font-size: 1.4rem;
font-weight: 800;
transition: background 150ms, transform 150ms;
}
.hover-card[data-playhtml-hover] { background: #f3cf58; transform: scale(1.03); }
`}</style>
</PlayProvider>
);
} Style remote hover
Section titled “Style remote hover”Use the PlayHTML attribute instead of the local-only :hover selector:
#shared-hover-card[data-playhtml-hover] {
background: #f3cf58;
}
The attribute remains present while at least one connected reader is hovering. It disappears when everyone leaves the element or disconnects.
Test presence
Section titled “Test presence”Open the private-window link and hover the card in one window. Both cards should light up. Move the pointer away and both should return to normal.
See can-hover when you need the full roster of people hovering instead of a single on or off effect.