Floating input placeholders with HTML/CSS
Tags: html, css, forms
Published: 2019-11-20 20:13:31

Here is a cool way to make the placeholders "become" labels as the input receives focus.




Here is a pen with the final result.

{% codepen https://codepen.io/felix-guerin/pen/GRReZVm %}


First, you have to put the `label` AFTER the `input` in the HTML.

```html
<form action="">
  <input type="text" id="fullName" name="fullName">
  <label for="fullName">Full Name</label>
</form>
```

This will ensure that we can target the `label` with our CSS.

Then, instead of giving the `input` a `placeholder` directly in the HTML, we will use our `label` as the placeholder. In order for the `label` to be inside the `input`, we can use `transform: translate()`. We can also use a lighter color to really make it look like a native placeholder.

Here is the relevant CSS for the initial state (the values used for the translation can vary depending on font size, positioning, etc.).

```css
    label {
      color: #999;
      transform: translate(0.25rem, -1.5rem);
      transition: all 0.2s ease-out;
    }
```

Now, to position it on top of the text when the `input` is in focus, use the following.

```css
    input:focus + label {
      color: #111;
      transform: translate(0, -2.75rem);
    }
```

It works! 

But we still have a problem. The styles are are only applied to the `label` when the `input` is in focus. This means that if you type something in the `input` then focus elsewhere, the `label` will come back to it's original position. 

To fix this, we will have to use the `:placeholder-shown` selector. This selector allows to style an element whenever an `input`'s `placeholder` is, well, shown. In our case, we actually want to style the `label` when the `placeholder` is NOT shown, so we will also have to use the `:not()` selector. Finally, for this to work, your `input` has to have a placeholder. Since we want to use our `label` as the `placeholder` text, we can simply write an empty space as the `placeholder` value (an empty string `""` will not work).

```html
<form action="">
  <input type="text" id="fullName" name="fullName" placeholder=" ">
  <label for="fullName">Full Name</label>
</form>
```


```css
    input:focus + label,
    input:not(:placeholder-shown) + label  {
      color: #111;
      transform: translate(0, -2.75rem);
    }
```

Now everything works like we want it to. 

The `:placeholder-shown` selector is relatively new, but browser support is pretty good. The only browser to not recognize it is Edge. To remedy this, we can use a `@supports` query hack to check if the code is run in Edge (you can find more hacks like this at http://browserhacks.com/). 

Usually, this hack is used write code that is only supported in Edge. Since we want the opposite, we can add `not` in front of the condition of the query. 

We should also apply the final position to the `label` before anything else. This way, the Edge browser will display the labels on top of the inputs like a normal form, and the other browsers will apply the 'placeholder' styling to it.

```css
label {
  display: block;
  transform: translate(0, -2.75rem);
}

@supports (not (-ms-ime-align:auto)) {
    label {
      color: #999;
      transform: translate(0.25rem, -1.5rem);
      transition: all 0.2s ease-out;
    }
    
    input:focus + label,
    input:not(:placeholder-shown) + label {
      color: #111;
      transform: translate(0, -2.75rem);
    }
}
```

Another possibility for this cool form style is to only move the `label` when a user types something. For this, you can simply remove the `input:focus + label` selector. Here is the result.

{% codepen https://codepen.io/felix-guerin/pen/bGGZgyZ %}

I hope you found this useful! Let me know in the comments.