Previously, my approach to building interactive components for the web involved starting with structured HTML markup, achieving as much functionality as possible with CSS, and using JavaScript to fill in any gaps.
This approach worked well for me. It used progressive enhancement to build layers of functionality and polish. It worked well, that is, until touch.
The Problem
Take the example of a component that revealed additional content on hover.[foot]Let's leave aside the question of if revealing content on hover is a good design pattern. Let's assume there's sufficient affordance to communicate the available interaction to the user.[/foot] All of the relevant functionality can be achieved with CSS, but the touch experience isn't ideal. In iOS, a tap will trigger the hover state, but the user can't un-hover and return to the original state.
So we test for touch using
Modernizr, remove the hover styles for browsers that are touch-capable, and handle the touch interactions with JavaScript. iOS users are happy with a better experience. Now we turn to Windows 8; many Windows 8 devices have both a touchscreen and keyboard/mouse. IE10 users are fine because its
alternative implementation works really well. But non-IE users are left without their mouse triggering any effect. These users are left with a significant loss in functionality and experience.
We can no longer assume that devices have a single input.
A Solution
So do we abandon our attempts at better touch interaction experiences? No, we need to go against what has seemed right so far: we need to move more of our functionality to JavaScript.
Our original CSS hover base functionality needs to become our
no-js fallback functionality. Let JavaScript handle the various interaction events: hover, touch, click, etc. and add/remove classes as desired to reflect the changing state of the component. CSS hooks into these dynamic classes to handle the required visual changes for each state change.
Moving to this kind of interaction approach keeps all of the event handling in one place, the JavaScript; all of the appearance in one place, the CSS; and keeps our progressive enhancement with no-js hover states.