When you work on a large scale, or highly dynamic, site with a sufficient number of JavaScript driven widgets — especially if it needs to be responsive to any degree — you may occasionally find CSS pseudo-classes … inconvenient.

You have to write several selectors for :focus, :hover, and :active (which, often, I want to look the same).  And your :hover styles do nothing for touch devices.  On top of that (what I think of as) your real active styles, or styles for other custom states, don’t get a pseudo-class, instead they get a class like .current, .open, or .selected.

Wouldn’t it be better for all the states of certain interface elements be determined by JavaScript?  And styled based on that?  Sure, it’s a little icky in some ways, but I’ve found it useful.


<a href="#" data-states>Link</a>


<button class="tabs--tab" data-states="active:tabs--tab__active">Tab Button</button>

Add the attribute data-states to anything you want controlled by JavaScript (you can customize that selector).  If you want to customize the classes to be added (by default the simple normal, hover, active will be used), list them as key-value pairs.

This has the benefit of allowing a team to use a single convention to define how an element will look during interaction in advance–before plugins are even implemented–at the most convenient time in their workflow…when they’re initially writing and styling it.



By default the plugin will watch for mouseenter, mouseleave, focus, and blur and will create custom events for normal and active.  You can pass in additional events.  If possible, it will use a delegated event listener and will toggle out the classes as appropriate.

Developers have used this to abstract state indicators in other code.  For example, rather than taking its class configuration options separately, a simple toggle open/close plugin, may $toggle.trigger("active") when it opens and and $toggle.trigger("normal") when it closes.


  • Chrome 26, 27
  • Firefox 21, 22
  • IE 9, 10
  • Safari 5.1, 6

Probably more, really, but that’s all I tested.