I'm not sure such question belongs on SO, before mods will chime in, welcome and have a couple of remarks:
This could save some duplicate positions. Instead of checkboxes and state tree, you can merge identical positions into single one and switch them like
<style>div:not(:target){display:none}</style>
<a href="#s00">start</a>
<div id="s00"><a href="#s01">1</a></div>
<div id="s01"><a href="#s02">2</a></div>
<div id="s02">...</div>
I'm not sure the benefit will be huge, but HTML should shrink at least a bit.
You can have minimal HTML and offload logic to server with "streaming" partial HTML content and "signals" from CSS. It might sound like "cheating" but since your badge tells "full stack" you might like it. From the client-side perspective it would really be "HTML+CSS only".
<button id=b1>1</button>
<style>#b1:active{background:url(ping?b1&<sessionID_or_state>);}</style>
When server receives "ping" it sends next chunk to "unfinished" document:
<style>#b1::before{content:'X'}</style>
<button type="button" id=b2>2</button>
<style>/*etc*/</style>
See: https://github.com/kkuchta/css-only-chat
Initial chunk would be truly minimal, but page would grow with each new "layer".
Finally, you could ditch CSS and do it the proper way with form submission and full page reload. That would be the ultimate no-JS, no-CSS, pure-HTML goodness like our ancestors intended.
<form action="?state=A1xA2o">
<table>
<tr><td>x<td>o<td><button type="submit" name="move" value="A3"></button>
</table>
</form>
Or even plain links with state and move in href
.
Am I joking? Who knows…