Switch with can-toggle
can-toggle stores one shared on or off value. Clicking the element adds or
removes its toggled class in every connected browser.
- 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>Switch with can-toggle</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; }
.switch {
display: inline-flex;
align-items: center;
gap: 0.75rem;
padding: 0.75rem 1rem;
border: 2px solid #1c1c1c;
background: #ebe4d5;
box-shadow: 4px 4px 0 #1c1c1c;
cursor: pointer;
font: 700 1rem/1 ui-sans-serif, system-ui, sans-serif;
}
.track {
display: flex;
width: 3.5rem;
padding: 0.2rem;
border: 2px solid #1c1c1c;
background: #d7cfc0;
}
.thumb {
width: 1.25rem;
height: 1.25rem;
background: #1c1c1c;
transition: translate 150ms;
}
.on-label { display: none; }
.switch.toggled { background: #b9dfad; }
.switch.toggled .thumb { translate: 1.55rem 0; }
.switch.toggled .off-label { display: none; }
.switch.toggled .on-label { display: inline; }
</style>
</head>
<body>
<main>
<h1>Shared switch</h1>
<p class="intro">Click the switch. Everyone sees the same state.</p>
<button id="shared-switch" class="switch" type="button" can-toggle>
<span class="track"><span class="thumb"></span></span>
<span class="off-label">off</span>
<span class="on-label">on</span>
</button>
</main>
<script type="module">
import { playhtml } from "playhtml";
// can-toggle adds or removes the toggled class.
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 one persistent on or off switch across browsers.
// ABOUTME: Renders the switch directly from CanToggleElement data.
import { PlayProvider, CanToggleElement } from "@playhtml/react";
export default function App() {
return (
<PlayProvider initOptions={{ developmentMode: true }}>
<main>
<h1>Shared switch</h1>
<p>Click the switch. Everyone sees the same state.</p>
<CanToggleElement>
{({ data }) => (
<button
id="shared-switch"
type="button"
className={data.on ? "switch is-on" : "switch"}
aria-pressed={data.on}
>
<span className="track"><span className="thumb" /></span>
<span>{data.on ? "on" : "off"}</span>
</button>
)}
</CanToggleElement>
</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(28rem, 100%); text-align: center; }
h1 { margin: 0 0 0.35rem; font-size: clamp(2rem, 9vw, 3.5rem); }
p { margin: 0 0 1.25rem; }
.switch {
display: inline-flex;
align-items: center;
gap: 0.75rem;
padding: 0.75rem 1rem;
border: 2px solid #1c1c1c;
background: #ebe4d5;
box-shadow: 4px 4px 0 #1c1c1c;
cursor: pointer;
font: 700 1rem/1 system-ui, sans-serif;
}
.track {
display: flex;
width: 3.5rem;
padding: 0.2rem;
border: 2px solid #1c1c1c;
background: #d7cfc0;
}
.thumb { width: 1.25rem; height: 1.25rem; background: #1c1c1c; transition: translate 150ms; }
.is-on { background: #b9dfad; }
.is-on .thumb { translate: 1.55rem 0; }
`}</style>
</PlayProvider>
);
} Style the shared state
Section titled “Style the shared state”Target the toggled class in CSS:
#shared-switch.toggled {
background: #b9dfad;
}
The class represents shared state, so avoid adding a second click handler that
changes the same state. In React, CanToggleElement already handles the click.
Test the switch
Section titled “Test the switch”Open the private-window link, then click the switch in either window. Both switches should show the same value. Reloading keeps the current value.
See can-toggle for reset behavior and the complete React form.