This article is currently only live for peer review purposes and is a work in progress. All contents are subject to change as the other parts are completed.

Forms Part 1, the PROPER Markup

This tutorial was originally going to be created much later, but thanks to an acquaintance complaining about this very topic I'm going to go ahead and fast-track this to the top of the pile. So as our first tutorial, we have how to properly write a form in HTML, and for the second tutorial I'm going to cover handling the results of the form server-side in PHP.

Forms are incredibly simple, and all the HTML tags have very specific meaning and purposes that aren't that hard to grasp... So why in BLAZES is it that 90% of the forms I come across on websites are poorly coded inaccessible crap?!?.

Like a great many things, the basic answer is people just copy from bad examples that were copied from bad examples, because the W3C specification is written in a gibberish legalese that few "mere mortals" can be expected to digest. Worse, it's written more to tell developers how to make browsers than it is to tell us how websites should be built -- a joke when the specifications in question are entirely about building websites!

Telltales of Improperly Written Forms

In this section I'm going to cover the things that if you look at the code and you see -- or dont see -- is an indication that something is wrong with how it was built.

Scripting Only Functionality

Going with the theme of my Progressive Enhancement article, we have to keep in mind that JavaScript is NOT accessible. If you are building a form on a website you want as many users as possible to actually use, functionality CANNOT be provided using JavaScript. You should ALWAYS write the form to work without scripting first!. Hence the unwritten rule of JavaScript that was highly touted a decade ago that's fallen into disuse: If you can't write a working website without JavaScript FIRST, you likely have no business adding scripting to it!

That said, I am not saying you cannot add scripting to a form!. Scripting can enhance functionality and make things easier on the user. All I'm saying is that it should at least be usable WITHOUT JavaScript should it be unavailable or outright blocked.

A LOT of people facing bandwidth caps in places like Canada and Australia are blocking scripts and even images (more on that in a future article) so as to try and prevent butting heads with having their service turned off or facing overage charges. This is a situation that's only going to get worse as few if any people are building new backbone infrastructure so there's an impending "bandwidth crunch" on the horizon.

Likewise a lot of users just distrust JavaScript thanks to years of abuse by dirtbag scam artists, or are actively blocking it because excessive use of the scripting chews on the battery of their mobile device. The millions of downloads of browser extensions like NoScript is all the proof you need of that; much less browsers like Classic Opera or Vivaldi having the functionality to turn it on or off on a per-site basis built in.

This also means that when or if you add scripting, do not hook it with the onevent type methods like onclick or onchange! You want to add that, do it from your scripting! No sense in wasting the bandwidth on sending scripting commands to users who won’t use them...

Improper code

Quite often you'll see forms that are oversimplified or overcomplicated because the person who wrote it didn't do their research or never learned to build forms properly.

One big sign is if they have no <fieldset> or <label> tags. If you don't see them in the form, it's an improperly built inaccessible mess on anything other than a visual user-agent! (aka screen media browser)

Likewise it's often amusing how many people resort to <table> around their form elements either out of ignorance or ineptitude. It is FAR too common a practice that results in bloated forms that are near impossible to use on non-visual user-agents. CUT IT OUT!. With CSS features like inline-block, flex-box, or even simply leveraging floats and negative margins, there's no excuse for it! If you see a table, and all there is in the form is one or two columns without "related rows" like a spreadsheet, the code is ineptly developed rubbish!

Coding a form

This section should be considered intermediate level, as it assumes you have at least a passing familiarity with HTML syntax and tags like <form> and <input>.

The <form> tag

This is one of the few things people don't screw up TOO badly since no matter what, you do this wrong, it won't work anywhere. Even so, there are some outdated buggy practices people do use that can in fact break it!

There are only three unique attributes you really need to worry about on a form, and one of them you only need if file transfers are involved.

The URL to be called when the form is submitted
Either "post" or "get", in LOWER CASE!. "post" means the values will be passed in the HTTP request header so you don't see it in the URI. "get" means the value is passed at the end of the URI using the ?name=value format.
A MIME type used to say what type of encoding is used on the data. For most normal forms you can omit this, but if you have any <input type="file"> inside your form, you will need to say enctype="multipart/form-data" for the file to be passed reliably in all browsers.

The one people screw up the most is saying "GET" or "POST" in uppercase. This does not pass data reliably in all browsers, so stick to lower case on those.

A more common mistake you'll find is people slapping DIV around forms for no good reason.


Contrary to popular belief every form should have at least one <fieldset> wrapping the elements the user can choose from or enter data into. There is also nothing wrong with putting fieldsets into fieldsets. The only thing I don't put in fieldsets is the submit/reset buttons, as those are not "data" elements -- likewise "hidden" inputs the user can't interact with -- so typically I'll give them a <div class="submitsAndHiddens" around them.

<fieldset> does NOT mean "draw a border around this". Remember, if you are choosing your tags based on their default appearance, you are choosing all the wrong tags for all the wrong reasons!

<legend> - now here's a problem!

The <legend> tag is supposed to be a heading to describe the content of a fieldset. The problem is that it is a royal pain in the arse to style as no two browsers treat it the same. If/when you can live with the default appearance, go for it; however if you want to style it, my advice is to use the appropriate level numbered heading (h1..h6) instead.

In modern (HTML 5) documents the legend is optional, by if you are using HTML 4 Strict it is not... this is one of the few changes I have to agree with HTML 5 on.


Once of the most important tags for accessibility that people blow right past as if it doesn't exist -- or worse use outside of forms for Christmas only knows what reason. The label should contain the text describing what the <input>, <select>, or <textarea> is for. That's it; that's what it does. If you have one of those tags without a corresponding <label> your form is accessibility rubbish!

The most common mistake people make is skipping having a <label> altogether and using the placeholder attribute instead. Even the HTML 5 specification comes right out and says "Placeholder is not a substitute for a Label". You can simply Google "Placeholder is not a Label" for hundreds of supporting documents of that statement. It's part of what's known as "False Simplicity" and a very easy usability trap to fall into - particularly when you have the artsy-fartsy types spanking it on their tablet and calling it "design".

Labels serve many purposes, the most important being as a visual queue even when the form element is selected to tell you what it is, being a second place the user can click to focus that element, and a directly associated description for non-visual access. This association is best handled by using the for attribute to point at the id of the associated <input>, <select>, or <textarea>. You can also form this association by wrapping the associated form element inside the label, though the "click on the label to select the element" doesn't work in all browsers that way.

Lately I've been seeing people using label just because, well... they don't know what it is or what it is for. They're slapping it into inline text like they would <strong> or <em> -- CUT IT OUT!!!. Just as if you have a form element without a label it makes no sense, it makes equally less sense to have a label without a form element to point it at!

<input>, <select>, and <textarea>

These are the most commonly used elements inside a form, coming in various types. We are going to assume you know these different types but if you are unfamiliar with them I suggest looking at the Mozilla Developer Network pages on the subject.

What I want to focus on here is the name and id attributes as this is where people screw up a lot... NO, A LOT!

Simply put the name attribute is what is sent server-side, and even in your scripting you should treat it as such. Using the older method of accessing it in the JavaScript by name really isn't considered good practice anymore.

You want to access it via scripting you should do so by the id, and since your label tags need a id to point at anyways, it's not like that's extra code for no reason.

I'd also like to point out that a LOT of the newer input types like number and e-mail degrade to simply being "text" format in older browsers. Generally speaking like ALL information sent from the client side you should be double-checking that on the server, resending the form back should those values fail the server-side check.


This tag goes inside of <select>, and it too is often misused, malformed or poorly crafted. The most common mistake you'll see with this one is the value attribute. value exists for when what you want to return to the server is different from the text inside the <option> tag!. If your value is the same as the text inside the <option>, do not waste bandwidth and time declaring a value attribute!

To that end, back-end code is often best set up to handle the CDATA content of the option than it is to use a separate value, unless that value is something like a numeric key.

Also if you took even the slightest bit of time to look at the specification for <option>, you'd notice that the only valid children of the tag is CDATA - aka plaintext. You don't put tags inside them. Likewise do NOT expect to be able to style these tags as that is not supported by the specification, even if some browsers like Firefox allows you to do so.

Array passing with []

Most server side languages when parsing data from a form can accept names with square brackets in them to create an array index. This can be handy to either associate values by a common key, or to automatically create keys for new entries.

A common usage scenario would be multiple checkboxes. Checkboxes when submit to the server exist in the result if they are selected, but are NOT sent to the server when they are not. As such you could use the key of the array as your value, or pass a value as normal on such elements.

So let's put this all together

Let's say we are making a form for a web hosting company where we'll want their name, e-mail address, what hosting plan they have, which department to send the message to, and the message itself. A well written accessible form should look something like this:

<!-- assumes site title is the H1 -->
<form action="contact.php" method="post" id="contactUs">
	<h2>Contact Us</h2>
		<label for="contact_name">Your Name:</label>
		<input type="text" name="name" id="contact_name">
		<label for="contact_mail">E-mail Address:</label>
		<input type="email" name="mail" id="contact_mail">
		<label for="contact_mailVerify">Re-Enter E-mail:</label>
		<input type="email" name="mailVerify" id="contact_mailVerify">
			<legend>Products you own:</legend>
			<input type="checkbox" name="product[]" value="1" id="contact_product1">
			<label for="contact_product1">Bargain Shared Hosting</label>
			<input type="checkbox" name="product[]" value="2" id="contact_product2">
			<label for="contact_product2">Premium Shared Hosting</label>
			<input type="checkbox" name="product[]" value="3" id="contact_product3">
			<label for="contact_product3">Bargain VPS</label>
			<input type="checkbox" name="product[]" value="4" id="contact_product4">
			<label for="contact_product4">Premium VPS</label>
		<label for="contact_department">Department to Contact:</label>
		<select name="department" id="contact_department">
		<label for="contact_message">Message:</label>
		<textarea name="message" id="contact_message" rows="6"></textarea>
	<div class="submitsAndHiddens">
		<input type="submit" value="Send Message">
		<input type="hidden" name="hash" value="put random hash here">

Here's a live demo of what we have for code so far. As you can see it's plain, not exactly pretty, but it's a working foundation from which we can build.

That's the basics of a properly written HTML form -- a label for every form element, id and name in there for what they are supposed to do, and no excess information we don't actually need! If your form varies significantly from this format it is highly likely that it is in fact inaccessible trash. Sorry if that sounds harsh, but that's the truth of the matter!

Anything else you want to do with that form for formatting should be handled from your CSS, which will covered in Part 2 (coming soon).


  • elementals.js
    A lightweight JavaScript library focusing on cross browser support, ECMAScript polyfills, and DOM manipulation.
  • eFlipper.js
    An image carousel script using elementals.js
  • eProgress.js
    A JavaScript controllable progress bar using elementals.js. Based on the nProgress project that relies on the much heavier jQuery library.


Browse code samples of people I've helped on various forums. These code snippets, images, and full rewrites of websites date back a decade or more, and are organized by the forum username of who I was helping. You'll find all sorts of oddball bits and pieces in here. You find any of it useful, go ahead, pick up the ball, and run with it.