Goal

Creating this designed form field with an animated label:

Animated field label - input

The label automatically resizes and repositions when the field is in focus or has value.

Text field

When dealing with fields that support the placeholder attribute (such as text input and textarea), this can be achieved quite easily with HTML and CSS only.

HTML

<div class="form-group">
	<input
		id="website"
		name="website"
		type="text"
		class="form-control"
		placeholder=" "
	/>

	<label for="website">Website</label>
</div>

SCSS

.form-group {
	position: relative;
	margin-bottom: 1.5rem;

	.form-control {
		width: 100%;
		height: 48px;
		padding: 15px 10px 0;
		font-size: 1rem;
		font-family: inherit;
		color: #1b1b1b;
		outline: none;
		border: 1px solid #1b1b1b;
		border-radius: 8px;
		transition: all 0.2s ease-in-out;

		&::placeholder {
			color: transparent;
		}

		&:focus {
			border-width: 2px;
		}

		& + label {
			position: absolute;
			top: 50%;
			transform: translateY(-50%);
			left: 10px;
			cursor: text;
			color: #1b1b1b;
			transition: 0.2s;
			pointer-events: none;
		}

		&:focus + label,
		&:not(:placeholder-shown) + label {
			transform: initial;
			top: 5px;
			font-size: 0.75rem;
		}
	}
}

We can apply the same design and effect for a dropdown field:

Animated field label - select

To achieve that in fields that do not support the placeholder attribute (like the select tag), we can use a JS function to detect filled fields.

HTML

<div class="form-group">
	<select id="category" name="category" class="form-control">
		<option value hidden disabled selected></option>
		<option value="category_a">Category A</option>
		<option value="category_b">Category B</option>
	</select>

	<label for="category">Category</label>
</div>

JavaScript

A JS function like the updateFieldStatus function. This will provide an indication of the field status by adding a filled data attribute.

SCSS

We can use the same SCSS code as in the text field, with slight adjustments to the filled field selector:

.form-control {
	&:not(select):focus + label,
	&:not(select):not(:placeholder-shown) + label,
	&[data-filled='true'] + label {
		transform: initial;
		top: 5px;
		font-size: 0.75rem;
	}
}