Floated Label Pattern with CSS Only

I came across Brad Frost's post about the Floated Label Pattern and instantly thought, "I think you can do this in CSS" and set out to prove it.

It turned out to be a little trickier than expected but I created a quick proof of concept:

The Example

How it works

I use the :valid pseudo class along with the pattern attribute on the input to style the associated label accordingly.

.field { position:relative; font-family: Arial; text-transform:uppercase; font-weight:bold; display:inline-block; }
label { position:absolute; left:0; top:0; transition: all .2s linear; color:#999; font-size:10px; }
input { margin-top:15px; border:1px solid #999; padding:3px 2px; }
input:invalid + label { top:3px; opacity:0; }
input:valid + label { opacity:1; top:0; }
input:focus { outline:none; }
input:focus + label { color:#33A; }

Problems with this

  • The label has to appear after the input. I'm using CSS trickery to get it into place. I'd love to be able to put the label first in the content but there's no way to style an element based on the content that comes after it.
  • It relies on the pattern attribute and the :valid pseudo class, which means that they can't be used for other things like validating email addresses. It'd be nice if there were a different pseudo class that I could use that detected presence of content.
Published February 11, 2014
Categorized as HTML and CSS
Short URL: http://snook.ca/s/1029

Conversation

10 Comments · RSS feed
Scott Gilbertson said on February 11, 2014

Very cool. Problem one will be solved by the subject select in CSS level 4. At least I think that's what this is talking about:

CSS Selectors4 Subject

As I understand that, it would let you put the label first and then write:

!label + input:invalid { }

Which, would be much nicer, though of course there's no browser support and it appear the ! syntax is still up for debate.

Gregor said on February 11, 2014

why not using input:empty / input:not(:empty) instead?

Patrick H. Lauke said on February 11, 2014

I like the general hacky approach. Of course, a few caveats on top of your own:

- from an accessibility point of view, you should explicitly associate your label with the relevant input. Although screenreaders may be able to use heuristics (by looking for adjacent text and announcing the placeholder attribute, although the spec clearly states that it's not a substitute for a more explicit label) to error-correct and try and announce the fields anyway.
- screenreaders will also generally announce that a field is "required" when the attribute is present. this may be confusing when the field is not, in fact, required. Additionally, some screenreaders will also announce that an input with validation constraints (such as "required") is currently "invalid".

So, with the above two points in mind, JAWS/Firefox for instance announces your first input as "First name edit invalid required entry...type in text"

- in actual use, this will also require the novalidate attribute on the form to suppress the browser's built-in validation if those required fields are actually optional.

Adam said on February 11, 2014

@GREGOR: CSS only recognizes the initial value of the input and doesn't update when the value changes.

Lu Nelson said on February 11, 2014

Couldn't you use the sibling selector '~' instead of '+', and thus be able to put the label before the input? You'd have to wrap each label and input in a span or div. Browser support is less good for sibling selector I think though

Gregor said on February 11, 2014

sorry, my fault, :empty is to select empty HTML nodes, not to select empty input tags. They way Jonathan did it seems the only way to do it in CSS-only

Daniel said on February 11, 2014

Hey Lu, the ~ selector is siblings after the element, not before it - would have been great!

Lu Nelson said on February 12, 2014

Ach. Bummer

xulai said on February 13, 2014

good!you NB!

Web Axe said on February 14, 2014

If designers weren't so silly we wouldn't have this problem to start with...

Sorry, comments are closed for this post. If you have any further questions or comments, feel free to send them to me directly.