A form needs correct HTML before it needs utility classes. This demonstration checks the fields and reports a local result; it sends no data.
Version and setup
These examples are compiled with Tailwind CSS 3.3.2. Copy document includes the required CSS, so that document runs without a Tailwind installation. To use the utility classes in a project, follow the versioned setup guide.
This site customizes its spacing scale: p-10 is 10px here, while standard Tailwind 3 uses 2.5rem. The examples below use unaffected spacing keys or explicit arbitrary values. Check your project configuration before assuming a utility’s size. Tailwind 4 has a different setup; use its official installation guide for a new version-4 project.
Try validation and feedback
Tailwind CSS 3.3.2; the CSS tab includes the compiled styles.
HTML
<form class="max-w-md p-4 border border-gray-300 rounded">
<label for="name" class="block font-semibold">Name</label
><input
id="name"
name="name"
autocomplete="name"
required
class="block w-full p-3 mt-2 mb-4 border border-gray-500 rounded"
/>
<label for="email" class="block font-semibold">Email</label
><input
id="email"
name="email"
type="email"
autocomplete="email"
required
class="block w-full p-3 mt-2 mb-4 border border-gray-500 rounded"
/>
<label for="message" class="block font-semibold">Message</label
><textarea
id="message"
name="message"
required
rows="3"
class="block w-full p-3 mt-2 mb-4 border border-gray-500 rounded"
></textarea>
<button type="button" class="px-4 py-3 text-white bg-blue-700 rounded">
Check draft
</button>
<p role="status" class="mt-4"></p>
</form>
<script>
const form = document.querySelector("form"); function showResult() { document.querySelector("[role=status]").textContent = "Draft checked locally. Nothing was sent."; } form.addEventListener("submit", event => { event.preventDefault(); showResult(); }); document.querySelector("button").addEventListener("click", () => { if (form.reportValidity()) showResult(); });
</script>
CSS
*,
::before,
::after {
box-sizing: border-box;
border: 0 solid #e5e7eb;
}
h1,
h2,
h3,
h4,
h5,
h6,
p,
figure,
blockquote,
dl,
dd {
margin: 0;
}
h1,
h2,
h3,
h4,
h5,
h6 {
font-size: inherit;
font-weight: inherit;
}
button,
input,
select,
textarea {
font: inherit;
color: inherit;
}
button {
background: none;
}
img,
svg,
video {
display: block;
max-width: 100%;
}
img,
video {
height: auto;
}
a {
color: inherit;
text-decoration: inherit;
}
ol,
ul,
menu {
list-style: none;
margin: 0;
padding: 0;
}
.mb-4 {
margin-bottom: 1rem;
}
.mt-2 {
margin-top: 0.5rem;
}
.mt-4 {
margin-top: 1rem;
}
.block {
display: block;
}
.w-full {
width: 100%;
}
.max-w-md {
max-width: 28rem;
}
.rounded {
border-radius: 0.25rem;
}
.border {
border-width: 1px;
}
.border-gray-300 {
--tw-border-opacity: 1;
border-color: rgb(209 213 219 / var(--tw-border-opacity));
}
.border-gray-500 {
--tw-border-opacity: 1;
border-color: rgb(107 114 128 / var(--tw-border-opacity));
}
.bg-blue-700 {
--tw-bg-opacity: 1;
background-color: rgb(29 78 216 / var(--tw-bg-opacity));
}
.p-3 {
padding: 0.75rem;
}
.p-4 {
padding: 1rem;
}
.px-4 {
padding-left: 1rem;
padding-right: 1rem;
}
.py-3 {
padding-top: 0.75rem;
padding-bottom: 0.75rem;
}
.font-semibold {
font-weight: 600;
}
.text-white {
--tw-text-opacity: 1;
color: rgb(255 255 255 / var(--tw-text-opacity));
}
Edit HTML or CSS to update the preview as you type. Copy code snippet copies the selected tab. Preview spacing and font defaults are not included.
Check an empty form, an invalid email, then a completed form. The button calls reportValidity() to show native validation and produces the local message only for valid fields. It uses type="button" so the preview’s submission restrictions do not block this inspection. The submit handler also prevents navigation in a standalone copy. Every label’s for matches its input ID, and the controls retain their browser focus indicators.
What a production form still needs
The name attributes identify values for a real submission. A server must validate them again and provide clear success and error handling. Client validation is helpful feedback, not a trust boundary. This example intentionally has no server connection.
w-full makes fields use their container width. Padding and borders fit within that width because the copied styles include border-box sizing. Do not use focus:outline-none without a tested replacement. Continue with the native form and label lessons.
Sources
Reference 1 Reference 2 Reference 3 provide the technical details and current compatibility information.