A Bit Of Access

Google style text fields using float labels

Published by

After reading an article about the change in design of Google's text fields, I wondered how I could replicate their Float Label style in a way that took very little syntax, was accessible, and preferably used no JavaScript. In this article I describe three methods of achieving this.

The idea is for these text fields is not original, but it made for a nice exercise and research project. I avoided looking up other implementations.

I worked out three methods, all of which change the style on focus and when the text field is not blank. Having the change in style for a non-blank field was the tricky part. Ideally we could use the :blank CSS selector, if only it was actually developed and supported. That leaves us with several options. Two methods use only CSS by using the placeholder or required attributes, but these are not ideal in practicality, browser support or accessibility. The third uses a minimal amount of JavaScript, which works best but obviously requires JavaScript to be enabled.

After the examples we'll go over the HTML, CSS, and Javascript used in the following segments:

  1. The basics, the HTML and CSS used for all 3 methods
  2. Method 1, using the placeholder attribute
  3. Method 2, using the required attribute
  4. Method 3, using JavaScript

Links to documentation: In the HTML examples the opening tags link to the relevant MDN web docs page for those elements, while in the CSS examples the property names, and pseudo classes and elements link to their MDN web docs pages.

The examples

Interactive example: using the placeholder attribute.
Interactive example: using the required attribute.
Interactive example: using JavaScript.

The basics


The aim was to keep the HTML as simple and accessible as possible. What I ended up with was simply an input element wrapped in a label, with a span element for the label text which we'll position using CSS.

	<input type="text" name="name" value="">
HTML: input and span wrapped in label.


The CSS is where the magic happens. We have our label, input, and span to provide basic style and (very important in this case) position.

First we define our label style. The height defines the height of our entire element, if we change this, we will need to change the height and / or padding of our input element, and the position of our span element. A cursor is defined to ensure the expected cursor for an input element is shown wherever we hover on the element.

label {
	display: inline-block;
	width: auto;
	height: 60px;
	font-size: 12pt;
	font-family: sans-serif;
	background-color: #EFEFEF;
	color: #555;
	border-radius: 4px;
	overflow: hidden;
	cursor: text;
CSS: style for label.

We are going to fill out our entire label element with our input using a combination of height and padding. The width we apply here determines the width of our entire element. Lastly we provide a bottom border and apply a transition value, to make style changes appear more smoothly.

label input {
	display: block;
	height: 40px;
	width: 200px;
	padding: 18px 10px 0 10px;
	background-color: #EFEFEF;
	color: #444;
	font-size: 1em;
	border: 0;
	border-bottom: 2px solid #555;
	transition: 0.3s;
CSS: style for an input in a label.

With our input element filling out our label, we need to position our helpful span element. We move its relative position up, and make the initial text size a bit larger for when the input value is still empty. To make our transition smooth and consistent, we give it the same value as we gave our input element.

label span {
	position: relative;
	top: -44px;
	font-size: 1.25em;
	padding: 0 10px;
	transition: 0.3s;
CSS: style for a span in a label.

To make it clear we are hovering over the input element we'll make the background a tad lighter. We'll keep this lighter background when it has focus too.

label:hover input, label input:focus {
	background-color: #F5F5F5;
CSS: style for an input with focus or in a label with hover.

We want to move our label text out of the way when we start typing, to do that we reposition our span element and resize the text when the input has focus. As an extra focus indicator we'll also change the text color.

label input:focus + span {
	top: -58px;
	font-size: 0.9em;
	color: #55F;
CSS: style for a span while its preceding input sibling has focus.

Lastly we define a text and border color on our input to indicate focus. I disabled the standard outline, you should only do that when you provide adequate alternative focus indicators.

label input:focus {
	outline: none;
	color: #55F;
	border-bottom-color: #55F;
CSS: style for an input with focus in a label.

With this we get this text field that's pretty nice already, but after we type something things get ugly when we lose focus (isn't that a universal truth?).

Interactive example: our text field is not quite there.

Losing focus with style

Method one: the placeholder attribute

For this method we use the placeholder attribute with a space, because we don't actually want a visible placeholder text. When an input isn't blank, any placeholder text it has is hidden from view.

	<input type="text" name="name" value="" placeholder=" ">
HTML: input (with placeholder attribute) and span wrapped in label.

To directly target our span when the input isn't showing the placeholder, we use a combination of the :not() and :placeholder-shown selectors in our CSS. Here we move the span up, and resize the text.

label input.placeholder:not(:placeholder-shown) + span {
	top: -58px;
	font-size: 0.9em;
CSS: style for a span while its preceding input sibling is not displaying the placeholder.

This method is not recommended because screen readers may announce the value of the placeholder attribute as "blank", which might lead to confusion. Aside from that, placeholders in general have accessibility considerations as the information they convey disappears when you start typing in the field. It also appears that the CSS selector is not supported in IE11 and Edge at the time of writing.

Method two: the required attribute

For this method we use the required attribute on the input element. This will make it so we can apply a style using the :valid selector in CSS. When no additional requirements are given, a required field will be deemed valid as long as it is not empty.

	<input type="text" name="name" value="" required>
HTML: input (with required attribute) and span wrapped in label.

To directly target our span when the input isn't blank, we use the following bit of CSS.

label input.required:valid + span {
	top: -58px;
	font-size: 0.9em;
CSS: style for a span while its preceding input sibling is valid.

This method is not recommended unless every field in your form is required. In that case you should make sure this is communicated to the user in a clear way, and you'll want to provide adequate error messages.

Method three: let's get some help from JavaScript

Now we know just CSS isn't going to work for us, let's see how we can get this working with a bit of JavaScript. What we are going to do is make a function which we call on the onkeyup event like this.

	<input type="text" name="name" value="" onkeyup="checkValue(this)">
HTML: input (with onkeyup event handler) and span wrapped in label.

Our function is going to check if the value is empty. If it's not empty we add a class to the text field, if it is empty we simply remove the class.

function checkValue(input) { 
	if (input.value != '') { 
	} else { 
JavaScript: function to toggle a class depending on value.

When using this bit of JavaScript to toggle the class, we can use the class selector in our CSS like this.

label input.notblank + span {
	top: -58px;
	font-size: 0.9em;
CSS: style for a span while its preceding input sibling has class "notblank".

With this method we finally achieve the desired result, without the drawbacks of the other methods. With a choice between these methods, I would recommend the JavaScript solution.

Notes on accessibility

While this element is accessible in a technical and semantical sense using methods 2 or 3, you should always consider the text sizes and color contrast, and provide adequate label text. If a field is required, be sure to signal this clearly to the user with explanation of any requirements.

Relevant documentation: