by

Layering the feedback with CSS

Feedback Matters

Long gone are the days where all we did was stare at a website and absorb content. We fill out contact forms, buy stuff, hold chat sessions, Tweet this and unlike that. These website interactions become more complex as they slowly get better at mirroring real-world interactions. One of the steps in mirroring real-world interactions is providing natural, progressive and intuitive feedback.

Ask any pianist or drummer why they don’t like the cheap electronic equivalents and they’ll tell you, “It doesn’t feel real.” That bounce of the key, the way the sound gets louder as you hit it harder; the feedback is either missing, or just wrong. Contrarily, video gamers can’t imagine a day that their controllers didn’t vibrate because it gave them a sense of the game they weren’t getting with graphics.

For desktop websites, it’s the visual, but non-verbal, feedback that matters (since we don’t have vibrating mice). So let’s take the real-world example of filling out a comment card sitting in a wire stand on a table at a restaurant. As the comment card transitions from the stand into our hands, our interactions change and the visual feedback changes with it. As we design a comment form for a blog post, we’ll map layers of interaction with layers of feedback using pure CSS.

The foundation for feedback

So there are three CSS pseudo-classes which can be the foundation for all types of feedback. They’re specifically referred to as “dynamic pseudo-classes”

  • :hover
  • :focus
  • :active

Now, chances are, you’re familiar with using these to style the anchor tag ( a):

a:hover, a:visited, a:focus, a:active.

But guess what, you can apply the dynamic pseudo-classes to any element!  For example:

#respond input[type=text]:hover, #respond textarea:hover{
	border: 1px solid #d1d1d1;
	color: #556159;
	outline: none;
}

As you can see, I’m applying a hover style to two form fields.When someone’s mouse is over the field, we’ll get a grayish border and a new text color.

Creating Layers/Levels of Feedback

A comment card sitting on a table looks more clear and readable when it’s in your hand, and when you’re writing on, you’re really focusing on what it says. So we’re simply modeling the real world. So let’s translate this into a comment form for a blog post.

Let’s begin by assuming the following HTML

<div id="respond">
  <form id="commentform">
    <label for="author">Name</label> <span class="required">*</span>
    <input id="author" type="text" name="author" value="" />
    <label for="email">Email</label> <span class="required">*</span>
    <input id="email" type="text" name="email" value="" />
  </form>
</div>

Layer One

So, maybe instead of drawing a box around the form area, I want to style the input fields themselves. A box doesn’t give extra meaning to the task, but changing the state of the inputs does. By focusing on the state of the inputs, I’m being more task-driven in my feedback than design-driven.

Let’s start with the normal state of an input field. I’m putting a dashed line around the borders because it’s “fuzzy” or distant.

#respond input[type=text] {
	display: block;
	width: 60%;
	padding: .5em;
	border-radius: 3px;
	background: #f9f9f9;
	border: 1px dashed #dedede;
	color: #a9a9a9;
	outline: none;
}

Layer Two

Next, I need to style the input — but only when the user enters the form area. How do we do that? I begin with the hover pseudo-class of #comments, and then I use a child selector. We’re going from dashed line to a solid line, or fuzzy to clear.

#comments:hover &gt; #respond input[type=text]{
	border-style: solid;
}

Now, I want to style the input when the user’s mouse is hovering over the input. I’m being mindful of the previous snippet. It’s more specific than this one, so I’m mindful not to style the same properties again.

Now we’re giving a darker border and a darker color. So it’s not only clear, it’s close.

#respond input[type=text]:hover{
	border: 1px solid #d1d1d1;
	color: #556159;
	outline: none;
}

Layer 3

Now, the last state is the active or focus state. Active is when the element is being activated by the user, and focus is when the keyboard or another input device has given the element focus.

Let’s set the border a bit darker, change the color and darken the background. Now we know we’re filling in the ink in the right box!

#respond input[type=text]:focus, #respond input[type=text]:active{
	border: 1px solid #d1d1d1;
	outline: none;
	color: #696969;
	background: #eee;
}

Something’s off, though…

Now, something’s not quite right. When we hover the input, the label will be bold.But when we’re actually typing in the input, the label isn’t bold (thanks to Harry at Tahzoo for catching that one). We certainly want a bold label — but only when we’re typing.

So this is a two step fix. Firstly, the ~ sibling selector is the answer:

#respond input[type=text]:focus ~ label, #respond input[type=text]:active ~ label{
    color: #556159;
}

Now, when the input is focused, we can apply a change to the font color to label. The sibling selector is unidirectional, though; it must follow the order of the elements in the document tree. In the HTML, my label appears first; step two is to flip the order of the elements (and fix presentation with CSS)

<div id="respond">
  <form id="commentform">
    <input id="author" type="text" name="author" value="" />
    <label for="author">Name</label> <span class="required">*</span>
    <input id="email" type="text" name="email" value="" />
    <label for="email">Email</label> <span class="required">*</span>
  </form>
</div>
#comments label, #comments span{
position:relative;
    bottom: 2.75em;
}

But it all seems choppy and distracting…

Now that we’ve established layers of interactivity and feedback, we need to smooth things over so that the user doesn’t get distracted. As we pull a comment card off the stand and put it in front of us, it’s a transition. It’s gradual, not a bunch of hard changes; I treat my feedback the same way. I use two CSS3 transition snippets, the first I give to the normal state of my elements.

#respond input[type=text] {
-webkit-transition: all .3s ease-out;
-moz-transition: all .3s ease-out;
-o-transition: all .3s ease-out;
transition: all .3s ease-out;
}

The second thing I do is apply ease-in to the hover and focus states.

#respond input[type=text]:hover{
-webkit-transition: all .3s ease-in;
-moz-transition: all .3s ease-in;
-o-transition: all .3s ease-in;
transition: all .3s ease-in;
}

So there we go, we’ve modelled an interaction based on something in the real world, we’ve provided feedback based on those interactions, and we’ve smoothed over the shift from one layer of feedback to the next. Oh, and in case you’re thinking, “yeah, but CSS3 isn’t supported in all browsers, yadda yadda yadda,” that’s not true!. The only CSS3 technique is the transitions; everything else is old crusty CSS 2.1.

And if you’re wondering how it looks in action, post a comment!

[disclaimer: until I rewrite the comment_form() function in WordPress so my labels go under the input, You won’t see fancy bold labels when you’re typing]