Taking a simple Contact Form to the next level with css animation and three.js
Today I’m going to make a boring Contact Form fun. Here is glimpse of what we will start building today :
In this post I will discuss the HTML and css part with a bit of JavaScript to make the form fully functional and in the next post I’ll discuss how I made the interactive star-field background with three.js.
Let’s Start with HTML
The HTML should be pretty strait forward, two label
and two input-box with type="text"
<div class="container">
<form id="send-it" class="being-sent">
<div class="inputbox hidden" style="--delay:100ms;">
<label class="placeholder">name :</label>
<input class="input-text" type="text" minlength="3" required />
</div>
<div class="inputbox hidden" style="--delay:200ms;">
<label class="placeholder">Email :</label>
<input class="input-email" type="email" required />
</div>
<button class="btn hidden" type="submit" style="--delay:400ms;">Send It</button>
</form>
<span id="roket"><span>
</div>
<span id="msg">Sent !</span>
Two things to notice here,
1 ) first notice I wrapped both the label and input element with divs because I wanted to make the labels to look like permanent placeholders.
2) The required
and minlength
in text input-boxes. require
enforces that those input fields needs to be filled before submitting the form and minlength=3
enforces the input to be at least 3 character long.
You might be wondering why bother with it tho? —I promise it will make sense when we will style them.
CSS to Make it awesome
It could be nice I were to explain my css line by line but that will make this post too long which I definitely want to avoid, but I will walk you through most of important bits and pieces.
Styling the form
I made the form a flex
container with flex-direction:column;
and centered them horizontally centered with align-items:center;
also I made the form centered in the middel of the page with display:grid;
and place-items:center;
and DON’T forget to use min-height:100vh
on the grid-container.
To make the label
to look like a placeholder made the position:relative;
and style the label
with following properties:
.placeholder {
text-transform: capitalize;
color: var(--clr-light-gray);
font-weight: 600; position: absolute;
top: 50%;
left: 0.5em;
transform: translateY(-50%); z-index: 2;
pointer-events: none;
}
First three properties will make it all capital with bold font and a grayish color, 2nd set of properties will place the label
vertically centered and horizontally left aligned inside the input-box. The last two will bring it on top of the input element and remove all pointer events, i.e. it will not interact with any pointing device.
Now for placeholder to work properly the input
element will need a padding-left:6ch;
I also added additional styling to the input
element with :focus
and :valid
. “focus” pseudo-class is simple with someone clicks it it gains focus and activates the set of styling, in my case I added a deep-orange colored left border to it.
The “valid” is triggered when the form becomes valid, for my case the form is valid when name is more than 3 character long and correctly formatted email address is entered. Now that should explain why I added those attributes( required
& minlength
) 😉. I also added border transition to make the transition between border-colors smoother.
.input-text:focus,
.input-email:focus {
outline: none;
border-left: 2px solid var(--clr-primary);
}.input-text:valid,
.input-email:valid {
outline: none;
border-left: 2px solid var(--clr-green);
}
Animating the Loading-in animation
To do that I will use that hidden
class to hide elements with clip-path. To make it work properly we need to add clip-path
property to the elements too which have the hidden
class and make it smooth we’ll add in a transition too.
.inputbox,
.btn{
clip-path: polygon(0 0, 100% 0, 100% 100%, 0 100%);
transition: clip-path 500ms ease-in-out var(--delay);
}.hidden {
clip-path: polygon(0 0, 100% 0, 100% 0, 0 0);
}
Notice I have put var(--delay)
as the delay of the transition, well where does it come from ?
It came form the css custom properties I defined within the style
attribute of the elements. it is replaced with thouse defined values (100ms, 200ms & 400ms). It will make the transition start at different times.
Now we will use JS to remove the hidden class to complete the process.
const hiddens = document.querySelectorAll(".hidden");document.addEventListener("DOMContentLoaded", () => {
hiddens.forEach((hid) => hid.classList.remove("hidden"));
});
Making the 🚀 launch
For this I have used the span with the id “rocket”, first we’ll add styling to it. We will add in animation later.
#roket {
z-index: 1;
position: absolute;
width: 2px;
height: 0px;
background-image: linear-gradient(
var(--clr-white) 60%,
var(--clr-primary),
transparent
);
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
}
The height
is zero so we wont be able to see it for now. Here the important bit is the linear-gradient
which will make it look like a space ship with burning fuel form the bottom. and it is centered in the middle right over the form with display:absolute
and
top:50%;
left:50%;
transform:translate(-50%,-50%):
Now we should focus on the form animation where I decided to squeeze it down to a line with again clip-path
then bringing the opacity
to 0. This keyframes
animation will do just that :
@keyframes clipit {
from {
clip-path: polygon(0 0, 100% 0, 100% 100%, 0 100%);
opacity: 1;
}
75% {
clip-path: polygon(49.5% 0, 50.5% 0, 50.5% 100%, 49.5% 100%);
opacity: 0.90;
}
to {
clip-path: polygon(49.5% 0, 50.5% 0, 50.5% 100%, 49.5% 100%);
opacity: 0;
}
}
To add the animation to the form we need to include the following form element.
animation-name: clipit;
animation-delay: 150ms;
animation-duration: 1s;
animation-fill-mode: forwards;
animation-play-state: paused;
and the rocket keyframe
animation will be :
@keyframes sending {
from {
height: 0;
transform: translate(-50%, -50%);
}
15% {
transform: translate(-50%, -50%);
}
25% {
height: 200px;
}
to {
height: 200px;
transform: translate(-50%, -110vh);
}
}
It will make the height
to 200px within 25% of the animation-duration
so it will become visible and it will maintain the height throughout the animation. The transform will make it shoot out of the screen but it will not start immediately after the animation starts, it will delayed by 15% of the animation-duration
.Now lets attach it to the #roket
with following properties to the style of it:
animation-name: sending;
animation-delay: 750ms;
animation-duration: 1.25s;
animation-fill-mode: forwards;
animation-play-state: paused;
the 750ms delay will make the room for the previous “form-closing” animation and start this at the right time. Both the animation are in paused
state, with the help of JS we will change the animation-play-state
to running
to play the animation and it will play the the animation once as the animation-fill-mode
is set to forwards
.
const form = document.getElementById("send-it");
const roket = document.getElementById("roket");
const msg = document.getElementById("msg");
form.addEventListener("submit", (e) => {
e.preventDefault();
e.target.style.animationPlayState = "running";
roket.style.animationPlayState = "running";msg.style.zIndex = 2;
msg.style.opacity = 1;
});
The last three lines will make the span
with the msg
class appear from behind.
That’s it we have done it. The HMTL css part is over, now the contact form should look like this, but without the interactive background, which I’ve discussed in my next post Adding a Custom Star-Field Background with threejs .
I have used clippy to make my clip-path
which makes it lot easier to use clip-path
. This made for Scrimba’s Weekly Web Dev Challenge 3rd week of April, 2021.