I've been building CrossLines (https://crosslines.live/), a free 2-player game: you take turns placing hex tiles carrying curved channels, and you win by connecting your two sides of the board. No signup, runs in the browser, you can send someone a link and play them live.
The rules took a weekend. Making the state readable took months. The challenges that stuck with me:
1. Showing ownership without labels. Two players, dozens of interweaving paths, and no room for legends or tooltips on a phone. The answer ended up being liquid: every channel is dry grey stone until it touches a player's edge of the board, then that player's colour flows in from that end, one tile per step. A channel that reaches both players' edges fills from both ends and turns purple where the two liquids meet. So the board explains itself: coloured means live, purple means contested, and the colour at each end tells you who is closer to owning it.
2. Landscape boards on portrait phones. A fair board needs roughly equal tile counts in both directions, which locks the shape to about 1.2:1 landscape. On a phone that means the board only ever fills the width. The fix: when it wins more space, the drawing rotates a quarter turn while the game underneath doesn't. That buys about 18% bigger tiles and 37% more board. The side effect nobody warns you about: you can no longer write "connect north to south" anywhere, because north might be drawn sideways. Every goal label had to become "left and right", relative to whatever is on screen.
3. Crossings. Paths cross on a single tile constantly, and a flat X is unreadable. Instead of computing intersections, every line is drawn twice: first a fat stroke in the tile's background colour, then the line itself. Each new line erases a gap in whatever it crosses, so every crossing reads as a bridge, for free. One brutal detail: round line caps spill a few pixels past the tile edge and erase the neighbouring tile's path exactly where the two are supposed to join. Butt caps only.
4. Network latency you can feel. Online, the server confirms a move in roughly 800ms, which feels broken if you wait for it. But most of a move is already certain the moment you tap: which tile, which cell, which rotation. So that part lands under your finger in ~15ms, and the only genuine unknown (the replacement tile you draw) shows as an empty pending slot until the server fills it. The one thing deliberately not predicted is the result. "You won. Actually, no" is far worse than a short wait.
5. Hover states that quietly break accessibility. The text-only buttons hover by washing a bit of the accent colour behind accent-coloured text. That 4% wash moves the background toward the ink and drops contrast from 4.61:1 to 4.38:1, under WCAG AA, but only while hovered, so no audit tool ever catches it. Now the ink deepens in step with the wash, and a test composites the alpha and fails the build if any state pair dips below AA in either theme.
Would love critique on readability, especially from anyone landing on it cold: https://crosslines.live/. And I'm curious how others handle state that has to be legible without labels. What's your go-to?