In the following lesson you will learn how to use Tailwind CSS utility-first approach to create a floating form field known from Material Design. This is inspired by the awesome video from fireship.io.
You can follow along in a new project or get the source code from GitHub.
Setup
Let's start in an empty directory and setup up a default package.json
file using npm init -y
.
Setup Tailwind CSS
Setup Tailwind by installing
npm i -D tailwindcss postcss-cli autoprefixer
Generate a Tailwind config file as you will customize Tailwind for the floating form field later.
npx tailwindcss init
Create postcss.config.js
file requiring tailwindcss
and autoprefixer
as a plugin:
module.exports = {
plugins: [
require("tailwindcss"),
require("autoprefixer")
]
};
Create your tailwind styles under css/tailwind.css
@tailwind base;
@tailwind components;
@tailwind utilities;
Add those scripts to your package.json
one to build and the other to watch changes made to the tailwind.css
file.
"scripts": {
"build": "postcss css/tailwind.css -o public/build/tailwind.css",
"dev": "postcss css/tailwind.css -o public/build/tailwind.css -w"
},
Setup HTML
Start with the following simple HTML layout - create an index.html
in the public directory.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>FFFwT</title>
<link rel="stylesheet" href="build/tailwind.css" />
</head>
<body class="antialiased p-10">
<form>
<div>
<input type="text" name="username" placeholder=" " />
<label for="username">Username</label>
</div>
</form>
</body>
</html>
You can use live-server to start a dev-server with live reload.
live-server public
Here you have your starting point - an input and a label.
Let's add styles to create a floating form field.
Floating Form Field
Focus Border
Start by adding a bottom border to the div
using border-b-{width}
<div class="border-b-2">
<input type="text" name="username" placeholder=" " />
<label for="username">Username</label>
</div>
To change the border color when the input is focused use the pseudo-class focus-within
. Enable the focus-within variant in Tailwind for borderColor
by adding it in the tailwind.config.js
under the variants section:
variants: {
borderColor: ['responsive', 'hover', 'focus', 'focus-within'],
},
Now add focus-within:border-blue-500
to change the border color on focus
<div class="my-4 border-b-2 focus-within:border-blue-500">
<input type="text" name="username" placeholder=" " />
<label for="username">Username</label>
</div>
Floating Label
Begin with changing the position of the div
to relative
so that you can use top
to control the position of the label
. Add class="absolute top-0"
to the label
.
input
is an inline element, add the Tailwind block
class to change it to a block element. Also set the input width to 100% with w-full
to tap the input on the whole form field. Additionally, add appearance-none
and focus:outline-none
to the input to remove browser specific styles.
<div class="relative my-4 border-b-2 focus-within:border-blue-500">
<input type="text" name="username" placeholder=" " class="block w-full appearance-none focus:outline-none" />
<label for="username" class="absolute top-0">Username</label>
</div>
Currently the label is covering the input field and preventing you from focusing the input π.
Let's change the z-index
of the label to be behind the input field by setting it to z-index: -1
. Extend the Tailwind theme to generate a negative z-index for you:
theme: {
extend: {
zIndex: {
"-1": "-1",
},
},
}
Add -z-1
class to the label, now the label is not visible anymore. Add bg-transparent
to the input
.
<div class="relative my-4 border-b-2 focus-within:border-blue-500">
<input type="text" name="username" placeholder=" " class="block w-full appearance-none focus:outline-none bg-transparent" />
<label for="username" class="absolute top-0 -z-1">Username</label>
</div>
The label is visible again and the input field can be focused by taping on the label too π΅.
Next make the label float above the input, again, using the pseudo-class focus-within
. Open your tailwind.css
and add the following CSS selector:
input:focus-within ~ label {
}
Now use Tailwind's @apply to transform, scale and change the label text color on input focus.
input:focus-within ~ label {
@apply transform scale-75 -translate-y-6 text-blue-500;
}
Also add duration-300
to your label class to control the labels transition duration.
Awesome the label is floating π, however, it stops floating when you remove the focus from the input π. The label should float if the input field has some content. Target the placeholder
, if its not shown input:not(:placeholder-shown) ~ label
thus the input has content and the label should float.
input:focus-within ~ label,
input:not(:placeholder-shown) ~ label {
@apply transform scale-75 -translate-y-6;
}
input:focus-within ~ label {
@apply text-blue-500;
}
Yeah π€©, the label floats on focus and if the input has content. It seems the label is not aligned with the input field. Set transform-origin
on the label to 0%. Let Tailwind generate it for you, open tailwind.config.js
and add it to the theme section:
theme: {
extend: {
transformOrigin: {
"0": "0%",
},
zIndex: {
"-1": "-1",
},
},
}
Now add origin-0
to the label and finally you have your own floating form field made with Tailwind CSS ππ
Building a form
Let's build a form field by duplicating the floating form field or by extracting the styles with @apply.
Add space-y-{size}
to the form to add margin between your input fields. You could also wrap it with a card.
<form class="max-w-sm mx-auto rounded-lg shadow-xl overflow-hidden p-6 space-y-10">
<h2 class="text-2xl font-bold text-center">Login</h2>
<div class="relative border-b-2 focus-within:border-blue-500">
<input type="text" name="username" placeholder=" " class="block w-full appearance-none focus:outline-none bg-transparent" />
<label for="username" class="absolute top-0 -z-1 duration-300 origin-0">Username</label>
</div>
<div class="relative border-b-2 focus-within:border-blue-500">
<input type="text" name="email" placeholder=" " class="block w-full appearance-none focus:outline-none bg-transparent" />
<label for="email" class="absolute top-0 -z-1 duration-300 origin-0">Email</label>
</div>
<div class="relative border-b-2 focus-within:border-blue-500">
<input type="password" name="password" placeholder=" " class="block w-full appearance-none focus:outline-none bg-transparent" />
<label for="password" class="absolute top-0 -z-1 duration-300 origin-0">Password</label>
</div>
</form>
Here you have your first form using the floating form field
Next, try out another style - Outline Form Field.
Outline Form Field
In this style the form field has an outline all around and the label floats into the outline. Reuse the floating form field and change border-b-2
to border-2
this gives you the outline.
Add padding p-4
to create space inside the outline and increase the font size text-lg
of the input and the label.
<div class="relative border-2 focus-within:border-blue-500">
<input type="text" name="username" placeholder=" " class="block p-4 w-full text-lg appearance-none focus:outline-none bg-transparent" />
<label for="username" class="absolute top-0 p-4 text-lg -z-1 duration-300 origin-0">Username</label>
</div>
Look at our outline form field
Focus the input and you notice the label is not covering up the outline.
To make the label cover up the outline customize the floating CSS applied to the outline form field. Duplicate the custom CSS in your tailwind.css
and add .outline
class to both selectors. Add outline
class to the div
around your input and label.
.outline input:focus-within ~ label,
.outline input:not(:placeholder-shown) ~ label {
@apply transform scale-75 -translate-y-6;
}
Update a few styles on the label to remove the top and bottom padding - py-0
. Also reduce the left and right padding to px-1
, add margin left ml-3
to align it with the input. Reset the z-index to z-0
that the label is above the outline. Finally, reduce the translate y value until the label is perfectly matching the outline.
.outline input:focus-within ~ label,
.outline input:not(:placeholder-shown) ~ label {
@apply transform scale-75 -translate-y-4 z-0 ml-3 px-1 py-0;
}
Perfect the label is exactly on the outline, but the line is still visible.
To hide the outline set the background of the label to the same background of the container where you place the form field. Add bg-white
to the label, this should do the trick.
<div class="outline relative border-2 focus-within:border-blue-500">
<input type="text" name="username" placeholder=" " class="block p-4 w-full text-lg appearance-none focus:outline-none bg-transparent" />
<label for="username" class="absolute top-0 text-lg bg-white p-4 -z-1 duration-300 origin-0">Username</label>
</div>
Now you have the outline form field working too. π
Building a form
Now create a form using the outline style.
<form class="max-w-sm mx-auto rounded-lg shadow-xl overflow-hidden p-6 space-y-10">
<h2 class="text-2xl font-bold text-center">Login</h2>
<div class="outline relative border-2 focus-within:border-blue-500">
<input type="text" name="username" placeholder=" " class="block p-4 w-full text-lg appearance-none focus:outline-none bg-transparent" />
<label for="username" class="absolute top-0 text-lg bg-white p-4 -z-1 duration-300 origin-0">Username</label>
</div>
<div class="outline relative border-2 focus-within:border-blue-500">
<input type="email" name="email" placeholder=" " class="block p-4 w-full text-lg appearance-none focus:outline-none bg-transparent" />
<label for="email" class="absolute top-0 text-lg bg-white p-4 -z-1 duration-300 origin-0">Email</label>
</div>
<div class="outline relative border-2 focus-within:border-blue-500">
<input type="password" name="password" placeholder=" " class="block p-4 w-full text-lg appearance-none focus:outline-none bg-transparent" />
<label for="password" class="absolute top-0 text-lg bg-white p-4 -z-1 duration-300 origin-0">Password</label>
</div>
</form>
If you followed along, create and customize your own floating form field w/ Tailwind CSS. Send me your own design on Twitter @mrcjln.
Top comments (8)
Cool, wanna see more tailwind components π
Tailwind is just awesome. I wrote something - dev.to/fayazara/making-a-navigatio...
Thanks for sharing!
Awesome, thanks for sharing.
Hi Marc,
you have a slight error in your markup. The input elements are missing its
id
. Currently thefor
attribute on the labels has no correspondingid
in the markup.My preferred markup(not tested) would look like this. You don't need an
id
orfor
if you wrap the<input>
in the<label>
. The label might needdisplay: block;
.Hi Jonas, thanks for catching that the
id
from the input is missing. Looks like a nice workaround, I will try it out!Cool! A very clear tutorial! Nice results as well.
Thanks! and its easily customisable with tailwind