Beautiful HTML Contact Forms
I’ve previously tried a few different form services — TypeForm, Jotform, Contact Forms 7 and a few others — but I’ve quickly hit their limits. They’re all beautiful and easy to use. So for a quick website, or one built by people who don’t want to write code, they’re a good option.
But I don’t like those form services. They’re slow, rely on external connections, hard to customise, bad for privacy and security, and ultimately, not much better than writing my own forms.
So I decided to write my own code for HTML contact forms to use in Ghost and WordPress. And here’s how you can!
If you’re thinking of using Ghost to host your website, the main reason I prefer Ghost is the writing experience in Ghost.
Give Ghost a try — you get a 2 week trial.
Why I prefer HTML forms to external form hosts like Typeform, Jotform, etc.
Apps like Typeform and Jotform solve the problem of easily making beautiful forms. They can host your forms, or you can embed your forms in a website.
They’re really useful at the early, hacky stage.
But the problems with external form hosts like Typeform and Jotform is that
- They’re slow to load, relying on JavaScript just to render sometimes. They add a second or more to your load time. In a world where we try to get load times under two seconds, that’s far too much.
- They load external JavaScript and CSS files, increasing the number of external requests, lowering speed further, and reducing my knowledge of how my website is built.
- Customising them to look nice often means writing CSS and HTML anyway, so I start to inch closer to writing my own forms.
- The data often gets captured in their database, which isn’t necessarily secure. I was using TypeForm when it was hacked and it caused a brief crisis (tens of thousands of emails exposed).
- I need to rely on their APIs and glue connecting forms to things like Google Sheets
- I have to rely on their interface to design forms, and which is sometimes impractical (e.g. the ConvertKit one wasn’t available behind my VPN, which killed it for me… I work often from places like the Middle East or China, where the government spies on people)
- They cost money, usually $10 a month, which is silly since I pay less to host my whole website.
That’s a lot of problems, especially considering the last one — that I’m paying for them all.
So I’ve started to write my own forms in HTML. I mean, how hard can it be?
How to Write Forms in HTML and Connect them with Zapier
Forms are one of the built in things that HTML was always supposed to be able to do, since several versions ago of HTML.
When I write my own forms, they’re quick to load — basically as quick as any text in a static site. Plus, I get full control over design, I get to send the data anywhere I want, and it’s free — aside from Zapier, which I use for other things.
It does mean learning a little CSS, and plugging in a bit of JavaScript to send the form information anywhere.
It also means having somewhere to receive the form information. You can email it to yourself, or receive it with anything with an API (including a Google Sheet with an API front end).
But a smarter thing is to ingest it with something like Zapier using their Webhooks functionality.
Setting up the HTML form — The Basics
A form consists of three parts
- HTML to make the form
- CSS to make it pretty
- JS to process and send it
- Zapier to capture it
You can make a form do anything you like. But my priority is to make a contact form, so all the examples below will be about a contact form.
Let’s start with the HTML. At its core, a contact form can be as simple as:
<h4>Contact me</h4>
<form method="post">
<input type="text" name="name" placeholder="Your name">
<input type="text" name="name"
placeholder="[email protected]">
<textarea rows="5" placeholder="Some message"></textarea>
<input type="submit"
value="Submit">
</form>
But that’s an ugly contact form. Here’s what looks like, when it’s rendered.
Also, the “Submit” button doesn’t do anything. It should, in its most basic form, email you.
So those are the two things we have to do:
- Make the form look better, and
- Make it do something — email you.
Styling the form with CSS
First up, let’s make the form look better. Here’s our goal.
If you want to style your form differently, my advice is to google around for CSS for forms, and find something you like. Then, borrow! That sounds obvious, but just explaining my layman’s process.
To make my form’s style, I’m going to create a style
block (to avoid creating a separate file). The elements I’m going to modify are: form
, input
, select
, textarea
, and button
. These make up the whole form.
The main modifications I do in this block are
- Change the font. I don’t know what font you’ll use, but I wanted to use a sans-serif font in the form.
- Adjust the placement. I want it to be full-width, vertically stacked, and with a few margins. This way, it’ll look good (and consistent) on different display sizes.
- Give each field a “selected” behaviour. It’s nice to draw attention to whatever field is being modified.
- Colour the button, and make it fade in/out. I’ve given the button a behaviour so it changes colour slightly when you hover on it.
- Catch robots: I’ve also got a hidden field in there. If a robot fills out the form, it’ll never get sent!
All this is to make it look neat (and cool)!
Put the below between to <style> tags:
<span class="token selector"><style> form, input, select, textarea, button</span> <span class="token punctuation">{</span> <span class="token property">font-family</span><span class="token punctuation">:</span> avenir next, avenir, helvetica neue, helvetica, ubuntu, roboto, noto, segoe ui, arial, sans-serif<span class="token punctuation">;</span> <span class="token property">display</span><span class="token punctuation">:</span> block<span class="token punctuation">;</span> <span class="token property">margin</span><span class="token punctuation">:</span> .5rem<span class="token punctuation">;</span> <span class="token property">padding</span><span class="token punctuation">:</span> .375rem .75rem<span class="token punctuation">;</span> <span class="token property">width</span><span class="token punctuation">:</span> 100% <span class="token important">!important</span><span class="token punctuation">;</span> <span class="token property">align-items</span><span class="token punctuation">:</span> flex-start<span class="token punctuation">;</span> <span class="token property">box-sizing</span><span class="token punctuation">:</span> border-box<span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token selector">input, select, textarea</span> <span class="token punctuation">{</span> <span class="token property">color</span><span class="token punctuation">:</span> darkslategray<span class="token punctuation">;</span> <span class="token property">background-color</span><span class="token punctuation">:</span> white<span class="token punctuation">;</span> <span class="token property">background-clip</span><span class="token punctuation">:</span> padding-box<span class="token punctuation">;</span> <span class="token property">line-height</span><span class="token punctuation">:</span> 1.5<span class="token punctuation">;</span> <span class="token property">border-radius</span><span class="token punctuation">:</span> .5rem<span class="token punctuation">;</span> <span class="token property">border</span><span class="token punctuation">:</span> 1px solid #ced4da<span class="token punctuation">;</span> <span class="token property">transition</span><span class="token punctuation">:</span> border-color .15s ease-in-out, box-shadow .15s ease-in-out<span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token selector">input:focus, select:focus, textarea:focus</span> <span class="token punctuation">{</span> <span class="token property">border-color</span><span class="token punctuation">:</span> royalblue<span class="token punctuation">;</span> <span class="token property">box-shadow</span><span class="token punctuation">:</span> none<span class="token punctuation">;</span> <span class="token property">-webkit-box-shadow</span><span class="token punctuation">:</span> none<span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token selector">button</span> <span class="token punctuation">{</span> <span class="token property">color</span><span class="token punctuation">:</span> white<span class="token punctuation">;</span> <span class="token property">background-color</span><span class="token punctuation">:</span> royalblue<span class="token punctuation">;</span> <span class="token property">border-color</span><span class="token punctuation">:</span> royalblue<span class="token punctuation">;</span> <span class="token property">user-select</span><span class="token punctuation">:</span> none<span class="token punctuation">;</span> <span class="token property">line-height</span><span class="token punctuation">:</span> 1.5<span class="token punctuation">;</span> <span class="token property">border-radius</span><span class="token punctuation">:</span> .5rem<span class="token punctuation">;</span> <span class="token property">border</span><span class="token punctuation">:</span> 1px solid transparent<span class="token punctuation">;</span> <span class="token property">transition</span><span class="token punctuation">:</span> color .15s ease-in-out, background-color .15s ease-in-out, border-color .15s ease-in-out, box-shadow .15s ease-in-out<span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token selector">button:hover</span> <span class="token punctuation">{</span> <span class="token property">filter</span><span class="token punctuation">:</span> <span class="token function">brightness</span><span class="token punctuation">(</span>80%<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token selector">select:required:invalid</span> <span class="token punctuation">{</span> <span class="token property">color</span><span class="token punctuation">:</span> silver<span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token selector">input:placeholder</span> <span class="token punctuation">{</span> <span class="token property">color</span><span class="token punctuation">:</span> silver<span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token selector">option[value=""][disabled]</span> <span class="token punctuation">{</span> <span class="token property">display</span><span class="token punctuation">:</span> none<span class="token punctuation">;</span> <span class="token property">color</span><span class="token punctuation">:</span> silver<span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token selector">option</span> <span class="token punctuation">{</span> <span class="token property">color</span><span class="token punctuation">:</span> black<span class="token punctuation">;</span> <span class="token punctuation">}</span></style>
CSS is its own bottomless pit of knowledge. I just taught myself, which is why some pros might not think the above is very clean.
The way I taught myself was by going to web pages I like, hovering over bits and typing “Inspect”. Then, I modify the code in the page and see what happens. Try it!
Make the form do something with JavaScript and Zapier
The second thing to do is to make your HTML form send the information somewhere.
Rather than write long, complicated programs with RESTful APIs, I like to use Zapier. Zapier is a bit of software that lets you connect things around the internet.
There are three things the form does
- Validate the form. This makes sure the user has filled in all the fields — except for the sneaky one to catch robots.
- Send the data. The form sends the data to a URL I set up in Zapier. But you can send it anywhere, if you have another place that can capture form data.
- Alert the user on what’s happening. I use the
formAlert
function to modify the HTML in theresponsemsg
span.
Here’s the JavaScript (which is between two <script>
tags):
<script>
function processForm() {
if (validateForm() === true) {
sendData();
} else {
formAlert("Please fill out all the fields.");
}
}
function sendData() {
formAlert("One second...");
var postURL = "https://hooks.zapier.com/hooks/catch/1963766/vgpqn6/";
var http = new XMLHttpRequest();
http.open("POST", postURL, true);
http.setRequestHeader("Content-type", "application/x-www-form-urlencoded");
var params = "name=" + document.forms["contact-form"]["name"].value +
"&email=" + document.forms["contact-form"]["email"].value +
"&subject=" + document.forms["contact-form"]["subject"].value +
"&message=" + document.forms["contact-form"]["message"].value +
"&source_url=" + window.location.href;
http.send(params);
http.onload = function () {
formAlert("Thank you, your message has been sent!");
document.getElementById("contact-form").reset();
}
}
function validateForm() {
var returner = false;
var x = document.forms["contact-form"]["name"].value.length +
document.forms["contact-form"]["email"].value.length +
document.forms["contact-form"]["message"].value.length;
var robotTextLength = document.forms["contact-form"]["_norobots"].value.length;
if (x !== 0 && robotTextLength === 0) {
returner = true;
}
return returner;
}
function formAlert(text) {
document.getElementById("responsemsg").innerHTML = "<br><p><em>" + text + "</em></p>";
}
</script>
The HTML for the form
This is the easiest part. But it only makes sense if you know the stuff above.
OK, now we can actually make the form!
The code is fairly self-explanatory. It’s only slightly more complicated than the most basic forms you’ll find on w3schools.
The only bits worth mentioning:
- I put “placeholders” rather than labels. It’s just more compact/easier to format.
- There’s a hidden input to capture robot form fillers.
- The button has an
onclick()
function that does two things: a) callsprocessForm()
, and b) prevents a new page from opening with the results. - There’s a
span
element which is where I send response messages.
<h4>Contact me</h4>
<form id="contact-form" method="post" data-format="inline"> <input type="text" id="name" placeholder="Your name"
required> <input type="text" id="email" placeholder="[email protected]" required> <select id="subject" required>
<option value="" disabled selected>Select an option</option>
<option value="compliment">I want to compliment you, you interesting person</option>
<option value="insult">I want to insult you, jerk!</option>
<option value="Media">I would like to paint you</option>
<option value="Writing">Where am I</option>
<option value="Other">Operator</option>
</select> <textarea rows="5" id="message" placeholder="Message" required></textarea> <input type="text"
name="_norobots" style="display:none !important;"> <button class="btn" type="submit" id="submit" value="Send"
onclick="processForm();return false;">Send</button> <span id="responsemsg"></span>
</form>
Catch the form information with Zapier (and do whatever you want!)
Zapier is a tool that lets you connect online services.
Online services are anything that sends and receives information. In this case, the two things I’m going to connect are 1. the form above and 2. email.
I’m going to make a tool that receives a form, then sends an email to me, and also to the person who filled out the form (for their records).
Most of Zapier is very self-explanatory (or just takes poking around). The only bit that’s slightly non-obvious is the first step, “Catch Form Hook”.
To create this trigger, you first create a new Zap. Your trigger (that starts the Zap’s process) is part of “Webhooks by Zapier”.
Then, within Webhooks, you choose “Catch Hook”.
On the next page, Zapier will give you a custom URL for your webhook. This is where your form sends data (look at the HTML form above to see what mine is). It will look something like https://hooks.zapier.com/hooks/catch/1234567/a1b2c3d4/
.
Take that URL and put it in your HTML and use that to test your Zap’s trigger.
The Form in Action
You can see my own form in action on my own contact page. If you found this useful, use it to say thanks!
By the way, if you have a suggestion on how to clean up ANY of the code, please tell me. I’m totally self-taught.