Stateful CSS Mouseover

I've been toying with a similar idea from a CSS slideshow example I wrote. This version is similar to it, but not quite as powerful as I'd like it to be (I'd like all the buttons stacked).

I recommend that you read up on z-index and absolute and relative positioning in case you aren't familiar with those CSS2 properties. They are both used to create the stateful mouseover effect.

This uses a relatively positioned box to create a frame (I used body here) that different bits of content are stacked in. Which piece of the content depends on which button was last got a mouse-over. if you mouse-over the button, then mouse-off the button, the content stays. This works only inside the frame, but there's no reason the frame can't be the size of the entire page with a bit of extra work.

You'll need :hover on arbitrary elements for the effect to work, of course.

HTML

<body>
  <div class="wrap tile" id="z">
    <div class="tile" id="a">First</div>
    <span class="content">Box one content</span>
  </div>
  <div class="wrap tile" id="x">
    <div class="tile" id="b">Second</div>
    <span class="content">Box two content</span>
  </div>
  <div class="wrap tile" id="y">
    <div class="tile" id="c">Third</div>
    <span class="content">Box three content</span>
  </div>
  <div class="wrap tile" id="w">
    <div class="tile" id="d">Fourth</div>
    <span class="content">Box four content</span>
  </div>
  <div class="wrap tile"></div>
</body>

The body is the frame for this example, but it could be any element that establishes a containing block (position anything other than static).

A button and its content are wrapped in a "wrap tile". This wrap tile is the size of the frame and allows the mouseover to be stateful.

Inside the frame is a button tile and some content.

The last empty wrap tile prevents any buttons or content from being selected by default. (If you want something selected by default, remove the empty wrap tile and place the selected item last).

CSS

And now for the CSS, I'll break it up into chunks for easy digestion:

html { background: white; }

body {
  position: relative;
  margin: 1em;
  padding: 0;
  width: 16em;
  height: 16em;
  background: yellow;
}

Gives the html and body some sensible defaults, and have body establish a new containing block.

.tile {
  position: absolute;
  border: 1px solid black;
  width: 4em;
  height: 4em;
  background: silver;
}

Default style for a tile, to cut down on duplication.

.wrap {
  width: 16em;
  height: 16em;
  background: transparent;
}

wrap tiles are larger than plain tiles, and you can see through them too. This allows them all to have the same background and makes them the same size as the frame (body element).

.content {
  display: none;
  margin: 4em;
}

Content is initially hidden, otherwise it would overwrite itself.

.wrap:hover .tile { background: aqua }

A hovered button gets a highlight color so we know what is selected. Note that the :hover goes on .wrap, not on .tile. .wrap is the outer frame for the content, not the button.

#a { }
#b { left: 12em; }
#c { top: 12em; }
#d { top: 12em; left: 12em; }

Position the buttons at the corners of the frame. (As good a place as any for this example.)

#a, #b, #c, #d { z-index: 2; }

Buttons are higher up than the frame background.

#z:hover, #x:hover, #y:hover, #w:hover { z-index: 1; }

And this is the *really* tricky part, after hovering a button, which sticks above the empty bottom-most frame, the button's corresponding frame *also* gets hovered. This keeps its contents up on top and visible.

#z:hover .content, #x:hover .content,
#y:hover .content, #w:hover .content { display: block; }

And finally, display the content when the frame is hovered.

Try It Out

Create yourself a little example page, and load it up in your favorite :hover-supporting browser. When you first load it, you should see a large yellow box with four smaller grey boxes inside its corners just like this one

First
Box one content
Second
Box two content
Third
Box three content
Fourth
Box four content

Hover the corner-boxes and content shows up in the middle of the big yellow box.

Hover the yellow area, the content stays and the button stays highlighted.

Move to the white background area, the content disappears, and the button returns to grey.

Move from one corner box to another, and the content switches.

How Does It All Work?

The example is built up of layers and uses :hover to change which layer is where. Here's how it looks from top (closest to your mouse) to bottom when your mouse is outside the yellow area:

  2) Buttons (<div class="tile">)
  1) nothing
0.4) Empty wrap tile (<div class="wrap tile"></div>)
0.3) wrap tile #w
0.2) wrap tile #y
0.1) wrap tile #x
0.0) wrap tile #z

The empty wrap tile covers the others, preventing a button from being highlighted and content showing when mousing onto the yellow area.

When you mouse the First button, the #z:hover rules changes the z-index of the wrap tile, and the layers look like this:

2) Buttons
1) Wrap tile for button last moused-over
0) Rest of the wrap tiles.

Notice that the wrap tile for the button you hovered is brought forward above the rest. This allows the item to stay selected. Since buttons are always on top, you can always switch to another wrap tile.

Uses

Use a hierarchical menu to create two-level navigation bar. (Expand the frame enough above/below the first-level, and the navigation bar won't be hard to select.)

Remember that the buttons don't have to be visible! The buttons can be empty transparent boxes that overlay parts of your content or an image.

Highlight product features with layered images.

Use large, hidden buttons overlapping long article to change text in a sidebar or present selected quotes.