Documentation
Webflow
Code
Setup: External Scripts
External Scripts in Webflow
Make sure to always put the External Scripts before the Javascript step of the resource.
In this video you learn where to put these in your Webflow project? Or how to include a paid GSAP Club plugin in your project?
HTML
Step 1: Copy structure to Webflow
Copy structure to Webflow
In the video below we described how you can copy + paste the structure of this resource to your Webflow project.
Copy to Webflow
Webflow structure is not required for this resource.
Step 1: Add HTML
HTML
<div data-form-validate="" class="form-group w-form">
<form id="wf-form-Default-Form" name="wf-form-Default-Form" data-name="Default Form" method="get" class="form" data-wf-page-id="6769821f0c3d54c63be676ce" data-wf-element-id="c6a904ac-9041-909d-442f-9a89534d58b5">
<div data-validate="" class="form-field-group">
<label for="name" class="form-label">Name <span class="form-required">*</span></label>
<div class="form-field">
<input class="form-input w-input" maxlength="256" name="name" data-name="Name" min="3" placeholder="Osmo" type="text" id="name" required="">
<div class="form-field-icon is--success"><svg xmlns="http://www.w3.org/2000/svg" width="100%" viewBox="0 0 24 24" fill="none"><path d="M11.25 14.25L9 12" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"></path><path d="M15 10.5L11.25 14.25" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"></path><path d="M3 12C3 16.9706 7.02943 21 12 21C16.9706 21 21 16.9706 21 12C21 7.02943 16.9706 3 12 3C7.02943 3 3 7.02943 3 12Z" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round"></path></svg></div>
<div class="form-field-icon is--error"><svg xmlns="http://www.w3.org/2000/svg" width="100%" viewBox="0 0 24 24" fill="none"><path opacity="0.1" d="M12 3C16.971 3 21 7.029 21 12C21 16.971 16.971 21 12 21C7.029 21 3 16.971 3 12C3 7.029 7.029 3 12 3Z" fill="currentColor" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"></path><path d="M12 3C16.971 3 21 7.029 21 12C21 16.971 16.971 21 12 21C7.029 21 3 16.971 3 12C3 7.029 7.029 3 12 3Z" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round"></path><path d="M12 12.5V7.5" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"></path><path d="M11.996 14.5C11.444 14.5 10.996 14.948 11 15.5C11 16.052 11.448 16.5 12 16.5C12.552 16.5 13 16.052 13 15.5C13 14.948 12.552 14.5 11.996 14.5Z" fill="currentColor"></path></svg></div>
</div>
</div>
<div data-validate="" class="form-field-group">
<label for="email" class="form-label">Email Address <span class="form-required">*</span></label>
<div class="form-field">
<input class="form-input w-input" maxlength="256" name="email" data-name="Email" placeholder="hello@osmo.supply" type="email" id="email" required="">
<div class="form-field-icon is--success"><svg xmlns="http://www.w3.org/2000/svg" width="100%" viewBox="0 0 24 24" fill="none"><path d="M11.25 14.25L9 12" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"></path><path d="M15 10.5L11.25 14.25" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"></path><path d="M3 12C3 16.9706 7.02943 21 12 21C16.9706 21 21 16.9706 21 12C21 7.02943 16.9706 3 12 3C7.02943 3 3 7.02943 3 12Z" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round"></path></svg></div>
<div class="form-field-icon is--error"><svg xmlns="http://www.w3.org/2000/svg" width="100%" viewBox="0 0 24 24" fill="none"><path opacity="0.1" d="M12 3C16.971 3 21 7.029 21 12C21 16.971 16.971 21 12 21C7.029 21 3 16.971 3 12C3 7.029 7.029 3 12 3Z" fill="currentColor" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"></path><path d="M12 3C16.971 3 21 7.029 21 12C21 16.971 16.971 21 12 21C7.029 21 3 16.971 3 12C3 7.029 7.029 3 12 3Z" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round"></path><path d="M12 12.5V7.5" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"></path><path d="M11.996 14.5C11.444 14.5 10.996 14.948 11 15.5C11 16.052 11.448 16.5 12 16.5C12.552 16.5 13 16.052 13 15.5C13 14.948 12.552 14.5 11.996 14.5Z" fill="currentColor"></path></svg></div>
</div>
</div>
<div for="message" data-validate="" class="form-field-group">
<label for="message" class="form-label">Message <span class="form-required">*</span></label>
<div class="form-field">
<textarea class="form-input is--textarea w-input" maxlength="5000" name="message" data-name="Message" min="3" placeholder="Hello Osmo, " id="message" required=""></textarea>
<div class="form-field-icon is--success"><svg xmlns="http://www.w3.org/2000/svg" width="100%" viewBox="0 0 24 24" fill="none"><path d="M11.25 14.25L9 12" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"></path><path d="M15 10.5L11.25 14.25" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"></path><path d="M3 12C3 16.9706 7.02943 21 12 21C16.9706 21 21 16.9706 21 12C21 7.02943 16.9706 3 12 3C7.02943 3 3 7.02943 3 12Z" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round"></path></svg></div>
<div class="form-field-icon is--error"><svg xmlns="http://www.w3.org/2000/svg" width="100%" viewBox="0 0 24 24" fill="none"><path opacity="0.1" d="M12 3C16.971 3 21 7.029 21 12C21 16.971 16.971 21 12 21C7.029 21 3 16.971 3 12C3 7.029 7.029 3 12 3Z" fill="currentColor" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"></path><path d="M12 3C16.971 3 21 7.029 21 12C21 16.971 16.971 21 12 21C7.029 21 3 16.971 3 12C3 7.029 7.029 3 12 3Z" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round"></path><path d="M12 12.5V7.5" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"></path><path d="M11.996 14.5C11.444 14.5 10.996 14.948 11 15.5C11 16.052 11.448 16.5 12 16.5C12.552 16.5 13 16.052 13 15.5C13 14.948 12.552 14.5 11.996 14.5Z" fill="currentColor"></path></svg></div>
</div>
</div>
<div class="form-field-group">
<div class="form-divider"></div>
</div>
<div class="form-field-group">
<div class="form-field">
<div data-submit="" tabindex="0" class="form-submit-btn">
<p class="form-submit-btn-p">Submit</p><input type="submit" data-wait="Please wait..." class="form-submit w-button" value="Submit">
</div>
</div>
</div>
</form>
<div class="form-notifcation w-form-done">
<div class="form-notification-bg"></div>
<div class="form-notification-p">Success! We’ll be in touch soon.</div>
<div class="form-notification-icon"><svg xmlns="http://www.w3.org/2000/svg" width="100%" viewBox="0 0 24 24" fill="none"><path d="M11.25 14.25L9 12" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"></path><path d="M15 10.5L11.25 14.25" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"></path><path d="M3 12C3 16.9706 7.02943 21 12 21C16.9706 21 21 16.9706 21 12C21 7.02943 16.9706 3 12 3C7.02943 3 3 7.02943 3 12Z" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round"></path></svg></div>
</div>
<div class="form-notifcation is--error w-form-fail">
<div class="form-notification-bg"></div>
<div class="form-notification-p">Something went wrong while submitting.</div>
<div class="form-notification-icon"><svg xmlns="http://www.w3.org/2000/svg" width="100%" viewBox="0 0 24 24" fill="none"><path opacity="0.1" d="M12 3C16.971 3 21 7.029 21 12C21 16.971 16.971 21 12 21C7.029 21 3 16.971 3 12C3 7.029 7.029 3 12 3Z" fill="currentColor" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"></path><path d="M12 3C16.971 3 21 7.029 21 12C21 16.971 16.971 21 12 21C7.029 21 3 16.971 3 12C3 7.029 7.029 3 12 3Z" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round"></path><path d="M12 12.5V7.5" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"></path><path d="M11.996 14.5C11.444 14.5 10.996 14.948 11 15.5C11 16.052 11.448 16.5 12 16.5C12.552 16.5 13 16.052 13 15.5C13 14.948 12.552 14.5 11.996 14.5Z" fill="currentColor"></path></svg></div>
</div>
</div>
HTML structure is not required for this resource.
Step 2: Add CSS
CSS
.form-group {
grid-column-gap: 1.5em;
grid-row-gap: 1.5em;
flex-flow: column;
width: 25em;
display: flex;
}
.form {
grid-column-gap: 1.5em;
grid-row-gap: 1.5em;
flex-flow: column;
width: 100%;
display: flex;
}
.form-field-group {
grid-column-gap: .75em;
grid-row-gap: .75em;
flex-flow: column;
align-items: flex-start;
display: flex;
}
.form-field {
width: 100%;
position: relative;
}
.form-input {
outline-offset: 0px;
-webkit-appearance: none;
appearance: none;
box-sizing: border-box;
vertical-align: middle;
background-color: #efeeec;
border: 1px solid #efeeec;
border-radius: .328125em;
outline: 0 #0000;
height: auto;
margin-bottom: 0;
padding: .9em 3.5em .9em 1em;
font-size: 1.125em;
font-weight: 500;
line-height: 1.2;
box-shadow: 0 0 #0000;
}
.form-input:focus {
border-color: #cbc8c5;
}
.form-input::placeholder {
color: #1313134d;
background-color: #efeeec;
}
.form-input.is--textarea {
resize: vertical;
min-height: 9em;
}
.form-submit-btn-p {
margin-bottom: 0;
font-size: 1.125em;
font-weight: 500;
line-height: 1.2;
}
.form-notification-p {
color: inherit;
font-size: 1.125em;
font-weight: 500;
}
.form-notification-icon {
pointer-events: none;
color: inherit;
justify-content: center;
align-items: center;
width: 3.5em;
padding-left: 1em;
padding-right: 1em;
display: flex;
position: absolute;
top: 50%;
right: 0;
transform: translateY(-50%);
}
/* Field: Error */
[data-validate].is--error input,
[data-validate].is--error textarea {
border-color: #FF4C24;
}
[data-validate].is--error .form-field-icon.is--error {
opacity: 1;
}
/* Field: Success */
[data-validate].is--success .form-field-icon.is--success {
opacity: 1;
}
@media screen and (max-width: 767px) {
.form-group {
width: 100%;
}
}
Step 2: Add custom Javascript
Custom Javascript in Webflow
In this video, Ilja gives you some guidance about using JavaScript in Webflow:
Step 2: Add Javascript
Step 3: Add Javascript
Javascript
function initBasicFormValidation() {
const forms = document.querySelectorAll('[data-form-validate]');
forms.forEach((form) => {
const fields = form.querySelectorAll('[data-validate] input, [data-validate] textarea');
const submitButtonDiv = form.querySelector('[data-submit]'); // The div wrapping the submit button
const submitInput = submitButtonDiv.querySelector('input[type="submit"]'); // The actual submit button
// Capture the form load time
const formLoadTime = new Date().getTime(); // Timestamp when the form was loaded
// Function to validate individual fields (input or textarea)
const validateField = (field) => {
const parent = field.closest('[data-validate]'); // Get the parent div
const minLength = field.getAttribute('min');
const maxLength = field.getAttribute('max');
const type = field.getAttribute('type');
let isValid = true;
// Check if the field has content
if (field.value.trim() !== '') {
parent.classList.add('is--filled');
} else {
parent.classList.remove('is--filled');
}
// Validation logic for min and max length
if (minLength && field.value.length < minLength) {
isValid = false;
}
if (maxLength && field.value.length > maxLength) {
isValid = false;
}
// Validation logic for email input type
if (type === 'email' && !/\S+@\S+\.\S+/.test(field.value)) {
isValid = false;
}
// Add or remove success/error classes on the parent div
if (isValid) {
parent.classList.remove('is--error');
parent.classList.add('is--success');
} else {
parent.classList.remove('is--success');
parent.classList.add('is--error');
}
return isValid;
};
// Function to start live validation for a field
const startLiveValidation = (field) => {
field.addEventListener('input', function () {
validateField(field);
});
};
// Function to validate and start live validation for all fields, focusing on the first field with an error
const validateAndStartLiveValidationForAll = () => {
let allValid = true;
let firstInvalidField = null;
fields.forEach((field) => {
const valid = validateField(field);
if (!valid && !firstInvalidField) {
firstInvalidField = field; // Track the first invalid field
}
if (!valid) {
allValid = false;
}
startLiveValidation(field); // Start live validation for all fields
});
// If there is an invalid field, focus on the first one
if (firstInvalidField) {
firstInvalidField.focus();
}
return allValid;
};
// Anti-spam: Check if form was filled too quickly
const isSpam = () => {
const currentTime = new Date().getTime();
const timeDifference = (currentTime - formLoadTime) / 1000; // Convert milliseconds to seconds
return timeDifference < 5; // Return true if form is filled within 5 seconds
};
// Handle clicking the custom submit button
submitButtonDiv.addEventListener('click', function () {
// Validate the form first
if (validateAndStartLiveValidationForAll()) {
// Only check for spam after all fields are valid
if (isSpam()) {
alert('Form submitted too quickly. Please try again.');
return; // Stop form submission
}
submitInput.click(); // Simulate a click on the <input type="submit">
}
});
// Handle pressing the "Enter" key
form.addEventListener('keydown', function (event) {
if (event.key === 'Enter' && event.target.tagName !== 'TEXTAREA') {
event.preventDefault(); // Prevent the default form submission
// Validate the form first
if (validateAndStartLiveValidationForAll()) {
// Only check for spam after all fields are valid
if (isSpam()) {
alert('Form submitted too quickly. Please try again.');
return; // Stop form submission
}
submitInput.click(); // Trigger our custom form submission
}
}
});
});
}
// Initialize Basic Form Validation
document.addEventListener('DOMContentLoaded', () => {
initBasicFormValidation();
});
Step 3: Add custom CSS
Step 2: Add custom CSS
Custom CSS in Webflow
Curious about where to put custom CSS in Webflow? Ilja explains it in the below video:
CSS
/* Field: Error */
[data-validate].is--error input,
[data-validate].is--error textarea {
border-color: #FF4C24;
}
[data-validate].is--error .form-field-icon.is--error {
opacity: 1;
}
/* Field: Success */
[data-validate].is--success .form-field-icon.is--success {
opacity: 1;
}
Implementation
Form Parent
Add the attribute [data-form-validate]
to the outer container of your form. This is usually the parent element of the <form>
element. This attribute signals the validation script where to look for form fields that need validation.
Form Element
Ensure the actual <form>
element resides inside the container with [data-form-validate]
. This is required for detecting form-level events such as “Enter” key presses.
Field Group
Add [data-validate]
to each field group. A field group typically includes a <label>
and its corresponding <input>
or <textarea>
element. Each group must have the [data-validate] attribute to enable validation.
Field Validation Rules
Input min and max
Use the attributes [min="3"]
and/or [max="12"]
with [data-validate] to define the minimum and maximum character limits for a field. These limits ensure that users enter values of appropriate lengths.
Email
No additional attributes are required for email validation. Email fields are automatically validated to ensure the entered address is in the correct format.
Additional Features
Validation Classes
The following classes are dynamically added or removed based on the validation state of a field:
- Class
.is--error
— Applied when the field fails validation. - Class
.is--success
— Applied when the field passes validation. - Class
.is--filled
— Applied when the user has entered a value into the field.
Anti-Spam Protection
To minimize spam submissions. The form includes a built-in delay mechanism. If the form is submitted in less than 5 seconds after loading, the submission will be rejected with a notification. This feature helps block bots that attempt to submit forms instantly.
Custom Submit Button
For enhanced customization:
- Wrap your submit button inside an element with the
[data-submit]
attribute. - The actual submit button should be an
<input type="submit">
inside the[data-submit]
container. - The script intercepts the click event on
[data-submit]
, validates the form, and only submits it programmatically if all fields pass validation.
Keyboard Behavior
Pressing “Enter” in any input field will trigger the form’s custom submission logic, ensuring all validations are performed before submission. This behavior is disabled for <textarea>
elements to allow users to add multi-line text.
Advanced Version
Need more functionality? We also have a Live Form Validation (Advanced)