Skip to content

Shared page color

The page’s background color is shared. Pick a swatch in either corner and the whole page changes color for everyone. The color is saved, so it’s still there for the next visitor.

Live exampleRoom connecting…
Remix
Test it with another person
  1. Keep this page open in your normal window.
  2. Copy the private-window link, then paste it into a private or incognito window.
  3. 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.

Remix

<!doctype html>
<html lang="en">
<head>
  <meta charset="utf-8" />
  <meta name="viewport" content="width=device-width, initial-scale=1" />
  <title>Shared page color</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; }
    body {
      display: grid;
      grid-template-rows: auto 1fr auto;
      place-items: stretch;
      padding: 0.75rem;
      transition: background 300ms;
    }
    .corner { display: flex; gap: 0.4rem; }
    .corner.top { justify-content: flex-start; }
    .corner.bottom { justify-content: flex-end; }
    .corner button {
      width: 26px;
      height: 26px;
      border: 1.5px solid #1c1c1c;
      border-radius: 50%;
      background: var(--swatch);
      cursor: pointer;
    }
    .corner button:hover { box-shadow: 2px 2px 0 #1c1c1c; }
    .middle { display: grid; gap: 0.6rem; place-content: center; text-align: center; }
    .middle p { margin: 0; max-width: 20rem; line-height: 1.5; }
    .readout {
      justify-self: center;
      padding: 0.2rem 0.5rem;
      border: 1px solid #1c1c1c;
      border-radius: 4px;
      background: white;
      font: 0.8rem ui-monospace, monospace;
    }
  </style>
</head>
<body>
  <nav class="corner top">
    <button data-color="#f4efe5" style="--swatch: #f4efe5" aria-label="Paint the page #f4efe5"></button>
    <button data-color="#f3cf58" style="--swatch: #f3cf58" aria-label="Paint the page #f3cf58"></button>
    <button data-color="#c9d8bf" style="--swatch: #c9d8bf" aria-label="Paint the page #c9d8bf"></button>
    <button data-color="#f2c4ce" style="--swatch: #f2c4ce" aria-label="Paint the page #f2c4ce"></button>
    <button data-color="#bcd4e6" style="--swatch: #bcd4e6" aria-label="Paint the page #bcd4e6"></button>
  </nav>
  <main id="page-color" class="middle">
    <p>Pick a color in either corner. It paints the whole page for everyone.</p>
    <code id="readout" class="readout"></code>
  </main>
  <nav class="corner bottom">
    <button data-color="#f4efe5" style="--swatch: #f4efe5" aria-label="Paint the page #f4efe5"></button>
    <button data-color="#f3cf58" style="--swatch: #f3cf58" aria-label="Paint the page #f3cf58"></button>
    <button data-color="#c9d8bf" style="--swatch: #c9d8bf" aria-label="Paint the page #c9d8bf"></button>
    <button data-color="#f2c4ce" style="--swatch: #f2c4ce" aria-label="Paint the page #f2c4ce"></button>
    <button data-color="#bcd4e6" style="--swatch: #bcd4e6" aria-label="Paint the page #bcd4e6"></button>
  </nav>

  <script type="module">
    import { playhtml } from "https://unpkg.com/playhtml@latest";



    await playhtml.init({ developmentMode: true });

    // The page's background color is one value in a page data channel.
    // No element owns it: any code on the page can read or write it by name.
    const pageColor = playhtml.createPageData("page-color", "#f4efe5");
    const readout = document.getElementById("readout");

    function paint(color) {
      document.body.style.background = color;
      readout.textContent = `"page-color": "${color}"`;
    }

    // Both corners' swatches write the same channel.
    for (const swatch of document.querySelectorAll("[data-color]")) {
      swatch.addEventListener("click", () => {
        pageColor.setData(swatch.dataset.color);
      });
    }

    pageColor.onUpdate(paint);
    paint(pageColor.getData());
  </script>
</body>
</html>

The color doesn’t belong to any element, so it lives in a page data channel named "page-color". Create it after playhtml.init resolves. A channel can hold a plain value like a string:

await playhtml.init();

const pageColor = playhtml.createPageData("page-color", "#f4efe5");

The top and bottom swatches are separate elements. Both write the same channel by name, and one onUpdate paints the page:

swatch.addEventListener("click", () => {
  pageColor.setData(swatch.dataset.color);
});

pageColor.onUpdate((color) => {
  document.body.style.background = color;
});

If two people pick at the same moment, the last pick wins. That’s what you want for one color.

See Page-level data for the full channel API.