This might seem a little silly in a language that doesn’t have any notion of timers or intervals, but the secret is that CSS does — it’s just packaged in a little feature called CSS Animations.
Take a look at this double-click handler. Note that CSS doesn’t know what a click is, much less a double click:
How does it work? Here’s a recording of the important elements, recolored and slowed down:
The idea is that when you click on the button for the first time, it moves the double-click element in place under the cursor, but it also causes a masking element to start moving up to cover it.
- If the second click comes quickly enough after the first (as on the left side of the recording), it happens on the double-click (blue) element.
- Otherwise (as on the right side of the recording), it happens on the masking (yellow) element, which has the effect of a single-click element.
(For an in-depth explanation, take a look at my write-up on the pure-CSS double-click handler here.)
There are two ideas in play here:
- Animations can be used to manage states according to a set pattern. (I’m using the term “state” loosely.)
- By changing an element’s position, we can change whether or not an user is allowed to take an action.
That’s all we need!
Now, instead of having our target scroll into view, we could instead use
animation-timing-function: step-end to have it pop in and out, sort of like a mole in a hole:
I tried a few different options for moving the mole out of the way, and changing its absolute position — here
left — seems to work the best. Using a translation would be convenient, but unfortunately leaves Firefox thinking the cursor is on the wrong element because changing
transform in the Gecko layout engine doesn’t trigger a re-layout. (Normally, this would be a good thing for performance reasons, but not so much for our little demo!)
With a bit of styling, we can make it look more like a game element:
The “mole” here is basically a restyled CSS label that triggers the checkbox hack. So is the hole. When the mole’s animation takes it over the hole, clicking in the region triggers the mole’s radio input; when the mole is out of the way, clicking triggers the hole’s radio input.
The next step is to put a couple of holes next to each other and have the moles bounce around among them, each on a different negative
animation-delay. With a state machine reading off which of the moles have been hit and which of the holes have been collapsed, it turns into a neat little game.
I used short Python scripts to generate both the state machine selectors and the mole keyframes. It gets a bit unwieldy for hand-coding otherwise. This is mostly because CSS also has no concept of random numbers, which leaves us no choice but to hardcode the “random” mole behavior.
There we have it: a complete reaction-based game, entirely in HTML and CSS.