JSFiddle - React, Tailwind, and code Playground

by nim_a101

HTML

<div class="toolbar">
  <button id="refresh">Refresh</button>
  <span id="version"></span>
</div>
<div id="grid" class="grid"></div>

CSS

:root {
  --cell: 34px;           /* cell size */
  --grid-border: #c9ced6; /* outer border */
  --cell-border: #e5e9ef; /* inner lines */
  --on: #2b7fff1a;        /* checked fill */
  --on-border: #2b7fff66; /* checked outline */
}

.grid {
  display: grid;
  grid-template-columns: repeat(10, var(--cell));
  grid-auto-rows: var(--cell);
  border: 1px solid var(--grid-border);
  width: max-content;
  background: white;
}

.cell {
  border-right: 1px solid var(--cell-border);
  border-bottom: 1px solid var(--cell-border);
}

/* remove right/bottom border on edges */
.cell:nth-child(10n) { border-right: 0; }
.cell:nth-last-child(-n + 10) { border-bottom: 0; }

/* make the checkbox fill the entire cell and remove native look */
.cell > input[type="checkbox"] {
  appearance: none;
  -webkit-appearance: none;
  width: 100%;
  height: 100%;
  margin: 0;
  outline: none;
  cursor: pointer;
  background: transparent;
  transition: background 80ms ease, box-shadow 80ms ease;
}

/* checked state = filled cell with subtle outline */
.cell > input[type="checkbox"]:checked {
  background: var(--on);
  box-shadow: inset 0 0 0 2px var(--on-border);
}

/* keyboard focus ring across full cell */
.cell > input[type="checkbox"]:focus-visible {
  box-shadow: inset 0 0 0 2px #00000066;
}

/* optional hover affordance */
.cell > input[type="checkbox"]:hover {
  background: #00000007;
}

JavaScript

const WORKER_BASE = 'https://checkbox-relay.nima-aghdaii.workers.dev';

const gridEl = document.getElementById('grid');
const versionEl = document.getElementById('version');
document.getElementById('refresh').addEventListener('click', loadState);

// build 10×10 once
(function build() {
  for (let r = 0; r < 10; r++) {
    for (let c = 0; c < 10; c++) {
      const cell = document.createElement('div');
      cell.className = 'cell';
      const cb = document.createElement('input');
      cb.type = 'checkbox';
      cb.dataset.r = r;
      cb.dataset.c = c;
      cb.addEventListener('change', onToggle);
      cell.appendChild(cb);
      gridEl.appendChild(cell);
    }
  }
  loadState();
})();

async function loadState() {
  try {
    const rsp = await fetch(`${WORKER_BASE}/state`, { headers: { Accept: 'application/json' } });
    const data = await rsp.json();
    versionEl.textContent = `v${data.version}`;
    const boxes = gridEl.querySelectorAll('input[type=checkbox]');
    for (const box of boxes) {
      const r = +box.dataset.r, c = +box.dataset.c;
      box.checked = !!(data.checked?.[r]?.[c]);
    }
  } catch (e) {
    versionEl.textContent = 'load failed';
    console.error(e);
  }
}

async function onToggle(e) {
  const box = e.currentTarget;
  const row = +box.dataset.r, col = +box.dataset.c, checked = box.checked;
  try {
    await fetch(`${WORKER_BASE}/toggle`, {
      method: 'POST',
      headers: { 'Content-Type': 'application/json', Accept: 'application/json' },
      body: JSON.stringify({ row, col, checked })
    });
  } catch (err) {
    // revert if server failed
    box.checked = !checked;
    console.error(err);
  }
}