Rotating Multi-State Button

One of the designers at Ariba, recently came up with a design that needed a tri-state button to govern toggling some content. We came up with an interesting solution where the button has three colors on the outside, with the bottom color governing the content being shown. I am not sure if the widget will survive a usability study, but I put together a proof of concept using HTML 5 technologies and wanted to share it, because I think it is neat.

Getting ready

Here is an example of the widget in action. Click on a color to cause the outside to rotate, so the clicked color is on the bottom.

Check out this Pen!

How do it…

Start with two simple, nested elements:

Add some CSS to handle the display of the inner and outer circles:

.circle {
  display: block;
  background: black;
  border-radius: 50%;
  height: 300px;
  width: 300px;
  margin: 0px;

.circle-wrapper {

  border-radius: 50%;
  background: transparent url( -230px -230px;
  height: 300px;
  padding: 20px;
  transition-property: all;
  transition-duration: 350ms;
  transition-timing-function: ease-in-out;
  width: 300px;

Add some more CSS to handle rotating the outer circle to the other positions:

.rotate-120 {
  -webkit-transform: rotate(120deg);
  -moz-transform: rotate(120deg);
  -o-transform: rotate(120deg);
  -ms-transform: rotate(120deg);

.rotate-240 {
  -webkit-transform: rotate(240deg);
  -moz-transform: rotate(240deg);
  -o-transform: rotate(240deg);
  -ms-transform: rotate(240deg);

And a chunk of JavaScript to handle interaction with the outer circle:

var el = $('#test-circle'),
    oOffset = el.offset(),
    aColors = ['red', 'blue', 'yellow']; {
  var elTarget = $(;

  if (!elTarget.hasClass('circle')) {
    var iLeft = e.pageX - oOffset.left,
        iTop = e.pageY -,
        iOldRotation = parseInt('rotation'), 10),

    if (259 > iTop && 173 > iLeft) {
      // left
      iRotation = 2;
    else if (259 > iTop && 173 < iLeft) {
      // right
      iRotation = 1;
    else {
      // bottom
      iRotation = 0;

    iRotation += iOldRotation;
    iRotation %= 3;


    if (iRotation === 2) {
    else if (iRotation === 1) {
    }'rotation', iRotation);

How it works…

The goal of the widget is to move whatever color is clicked on to the bottom position. It currently doesn’t have event handlers, but they should be easy to add.

The .circle class defines the dimensions and color of the inner circle, and uses border-radius to round the element into a circle. The .circle-wrapper class defines the dimensions of the outer circle, also uses border-radius to round the element, but uses a background image of a color wheel to divide the area into three colors. This means we will need to compute the clickable area for each color in the JavaScript, since there is no other indicator as to what part of the circle the user clicked on.

Since the color wheel divides the circle into three even sections of 120 degrees, we need two rotation rules that transform the outer element by rotating it either 120 or 240 degrees. The .circle-wrapper class also defines a transition rule (easing-in-out animation over 350ms), so the rotation transform is animated when it is applied. The transition-property is set to target all rules, because the transformation rules use vendor prefixes.

The JavaScript snippet then needs to assign a click handler to the outer div and determine what color was clicked on. We ignore any click that happens inside the inner button (or respond according to the needs of your application), and use mouse position, relative to precomputed values for the button position in the document, for determining which color zone was clicked. A data property on the outer element is used to keep track of the current state of the rotation, which is combined with the a numeric value associated with each click zone, then modulus three to determine which (if any) rotation should be applied.

I don’t like that I had to grab the offset of the outer element when the script loads, and not on the click event. If the element position changes as the user interacts with the page, then this widget will break. Unfortunately, this was a necessary evil, because rotated elements return odd offsets relative to the rotation, not the absolute position on the page. A third wrapping, unrotated element will fix that, but this works for now.

Anyway, this is not ready for prime time yet, and doesn’t work in legacy browsers, but introduces a new button concept. I have also experimented with four and five colors, and it works well. However, at five or more colors, the button looks too busy, and it becomes more difficult to tell what is selected. Please let me know what you think.