How to create Google's Material Design Text Input Field using CSS and JavaScript?
Tags: css, javascript, tutorial, webdev
Published: 2021-07-18 09:01:03

In this tutorial, we are trying to recreate Google's text input field animation and design from scratch with the help of CSS as well as JavaScript.


## HTML

We are not going to use [pseudo-elements](https://developer.mozilla.org/en-US/docs/Web/CSS/Pseudo-elements) to create this effect, but we will be taking help of `div` element instead.

We are wrapping the `input` element and its related `div`s inside a container. To create a placeholder, we have defined a separate `div` which will act as a placeholder rather than using the `:placeholder` pseudo-element.

```html
<div class="input-contain">
  <input type="text" id="fname" name="fname" autocomplete="off" value="" aria-labelledby="placeholder-fname">
  <label class="placeholder-text" for="fname" id="placeholder-fname">
     <div class="text">First Name</div>
  </label>
</div>
```

## CSS

First of all, let's define the properties for the `input` element and it's container.

```css

.input-contain{
    position: relative;
}
input{
    height: 5rem;
    width: 40rem;
    border: 2px solid black;
    border-radius: 1rem;
}
```

We will be placing the placeholder text on top of the `input` element by setting the position of the placeholder text to `absolute` so that it matches the width and height of the `input` container.

```css
.placeholder-text{
    position: absolute;
    top: 0;
    bottom: 0;
    left: 0;
    right: 0;
    border: 3px solid transparent;
    background-color: transparent;
    display: flex;
    align-items: center;
}
```
But there's an issue. You can't click on the input element because the placeholder element is on the top of the input element. In order to overcome this situation, just set the value of `pointer-events` to `none` for the placeholder element.

```css
.placeholder-text{
    pointer-events: none;
}
```

Now, let's style the placeholder text a little bit.

```css
.text{
    font-size: 1.4rem;
    padding: 0 0.5rem;
    background-color: transparent;
    color: black;
}

input, .placeholder-text{
    font-size: 1.4rem;
    padding: 0 1.2rem;
}
```

Next up, let's define what should happen when the input element is focused.
We are going to change the `border-color` rather than keeping the outline on focus event.

```css
input:focus{
    outline: none;
    border-color: blueviolet;
}
```

We want the placeholder text to translate along Y-axis(to go up) and reduce it's `font-size` a little bit when the input element is focused. Also, we can change the color of the placeholder text. Here's how we can do that. Change the `background-color` to resemble the surrounding color to make it more elegant.

```css
input:focus + .placeholder-text .text{
    background-color: white;
    font-size: 1.1rem;
    color: black;
    transform: translate(0, -170%);
    border-color: blueviolet;
    color: blueviolet;
}
```

For a smooth transition, add `transition` property to the placeholder text.

```css
.text{
    transform: translate(0);
    transition: transform 0.15s ease-out, font-size 0.15s ease-out, background-color 0.2s ease-out, color 0.15s ease-out;
}
```

Up until now everything is fine, but now a problem arises. When you enter text in the input element and then remove the focus from the input element, the placeholder text comes to it's original position and we don't want that. We want the placeholder text to remain above the input text when something is already entered in the input field. Hence, we will be taking the help of JavaScript and we will modify CSS.

If you remember, we have already defined `value` attribute for the `input` element. This will come handy.
```html
<input type="text" id="fname" name="fname" autocomplete="off" value="" aria-labelledby="placeholder-fname">
```

Let's modify some CSS. As already discussed, when the value is an empty string, the placeholder text should come back to its original position, but when the value is other than an empty string, the placeholder text should remain transformed(above the input text). We can achieve that by defining a `:not` pseudo-class on the input element value. Here's how we can do that.

```css
input:focus + .placeholder-text .text, :not(input[value=""]) + .placeholder-text .text{
    background-color: white;
    font-size: 1.1rem;
    color: black;
    transform: translate(0, -170%);
}

input:focus + .placeholder-text .text{
    border-color: blueviolet;
    color: blueviolet;
}
``` 

But wait. The value attribute will remain the same in HTML. How can we change and set it to the string entered by the user? That's where JavaScript comes into action.

We will set the value of the `value` attribute to the string entered by the user just like this,

```javascript
let input_element = document.querySelector("input");

input_element.addEventListener("keyup", () => {
    input_element.setAttribute("value", input_element.value);
})
```

That's it. You just made a modern material design text input field.

Here's the final output:

{% codepen https://codepen.io/seekertruth/pen/LYyLYGK %}