Frontend6 min read
Hover on a touch screen
Half the design was hover states, and half the visitors had no pointer. One client agent maps hover onto scroll position, and the sections it lights up stay on the server.
Hover is a desktop idea
A catalogue card that reveals its detail on hover is a good design decision and an incomplete one. On a phone there is no hover, so either the detail is always visible, which ruins the grid, or it is never visible, which loses the feature for most of the traffic.
The usual fixes are worse than the problem: show everything on touch, or fake hover with a tap and eat a wrong navigation every time someone means to scroll.
One agent, many sections
The rule I settled on is that where there is no pointer, the middle of the screen is the pointer. A single client agent watches for elements that opt in, and gives the one crossing a band through the middle of the viewport the same state a mouse would.
Sections opt in with a data attribute and nothing else. They do not import the agent, they do not become interactive, they do not know it exists. That is what keeps them server components, which was the whole point: the interactive part of the site is one file, not one file per section.
Hover on a touch screen is not a gesture to invent. It is a question about where the reader is looking, and scroll position already answers it.
The bug worth keeping
The first version lit every card in the catalogue and left them lit. The cause is a detail of the intersection observer that I now repeat to anyone who will listen: when you give it a root element, it reports intersections with that root, and it does not care whether the root itself is anywhere near the screen.
new IntersectionObserver(onBand, { root: list, rootMargin: BAND });
// true while the card is inside the list,
// even when the list scrolled out of the viewport an hour agoThe fix is to ask both questions: is the card inside the band, and is the band on screen at all. Obvious once written down, invisible on a desktop where the list is always in view.
What I would do again
Solve hover once for the whole site instead of per component. Let sections opt in with an attribute so they stay server-rendered. And when an observer behaves strangely, check what you handed it as a root before blaming the browser.