form validation with jitter effects
02 Jul 2025Ever notice how form validation can feel⦠boring? You click submit, some fields turn red, maybe you get an error message. Functional, sure. But what if we could make invalid fields literally shake their heads at you?
try it out
Jitter Effect Preview
Fill out this form and try the validation buttons to see different jitter animations on required empty fields.
the problem with standard validation
HTML5 gives us built-in form validation with the required attribute and input types like email. But the default browser behavior is pretty bland. And if you add novalidate to your form (which many of us do for custom validation), youāre on your own for feedback.
<form id="testForm" novalidate>
<input type="email" required>
</form>
That novalidate attribute tells the browser āthanks but no thanks, Iāll handle validation myself.ā Which opens the door for more creative feedbackā¦
enter jitter animations
Instead of just turning fields red, what if they physically reacted to being empty? Hereās a collection of CSS animations that give form fields personality:
@keyframes jitter-shake {
0%, 100% { transform: translateX(0); }
10%, 30%, 50%, 70%, 90% { transform: translateX(-4px); }
20%, 40%, 60%, 80% { transform: translateX(4px); }
}
@keyframes jitter-bounce {
0%, 100% { transform: translateY(0); }
25% { transform: translateY(-8px); }
50% { transform: translateY(-4px); }
75% { transform: translateY(-2px); }
}
@keyframes jitter-pulse {
0%, 100% { transform: scale(1); }
50% { transform: scale(1.05); }
}
@keyframes jitter-wobble {
0%, 100% { transform: rotate(0deg); }
25% { transform: rotate(-2deg); }
50% { transform: rotate(2deg); }
75% { transform: rotate(-1deg); }
}
Each animation has its own personality and color:
- shake: the classic ānopeā head shake (red #f7768e)
- bounce: a gentle hop like āhey, over here!ā (orange #ff9e64)
- pulse: subtle breathing effect for a softer touch (purple #bb9af7)
- wobble: playful rotation that feels less aggressive (pink #f7768e)
The colors are applied along with the animation class:
.jitter-shake {
animation: jitter-shake 0.6s ease-in-out;
border-color: #f7768e !important;
}
This means when an input gets the jitter effect, it not only animates but also changes its border color to match the animationās personality. The !important ensures the color override takes precedence during the animation.
the validation logic
Hereās where it gets interesting. Instead of validating fields one by one, we collect all empty required fields and animate them simultaneously. The function gets called from the test buttons, each passing a different animation type:
<button type="button" onclick="validateForm('shake')">šø Test Shake</button>
<button type="button" onclick="validateForm('bounce')">š¹ Test Bounce</button>
<button type="button" onclick="validateForm('pulse')">šø Test Pulse</button>
<button type="button" onclick="validateForm('wobble')">š¹ Test Wobble</button>
And hereās the validation function itself:
function validateForm(animationType = 'shake') {
const form = document.getElementById('testForm');
const requiredInputs = form.querySelectorAll('input[required], textarea[required], select[required]');
let emptyFields = [];
let emptyInputs = [];
// first pass: identify all empty fields
requiredInputs.forEach(input => {
if (isEmpty(input)) {
emptyFields.push(input.name || input.id);
emptyInputs.push(input);
}
});
// second pass: jitter all empty fields simultaneously
if (emptyInputs.length > 0) {
emptyInputs.forEach(input => {
jitterElement(input.id, animationType);
});
statusDiv.className = 'status error';
statusDiv.textContent = `ā Please fill in: ${emptyFields.join(', ')}`;
return false;
}
return true;
}
The two-pass approach means all invalid fields react at once, creating a unified āthese need attentionā moment rather than a sequential cascade.
applying the effect
The jitter function handles the animation lifecycle cleanly:
function jitterElement(elementId, animationType = 'shake') {
const element = document.getElementById(elementId);
const className = `jitter-${animationType}`;
// only remove classes if this element doesn't already have the target class
if (!element.classList.contains(className)) {
// remove any existing jitter classes
element.classList.remove('jitter-shake', 'jitter-bounce', 'jitter-pulse', 'jitter-wobble');
}
// add the new jitter class
element.classList.add(className);
// remove after animation completes
setTimeout(() => {
element.classList.remove(className);
}, 600);
}
subtle touches
You can also add jitter on blur for immediate feedback:
document.querySelectorAll('input[required]').forEach(input => {
input.addEventListener('blur', function() {
if (isEmpty(this)) {
setTimeout(() => jitterElement(this.id, 'shake'), 100);
}
});
});
That 100ms delay prevents the animation from feeling too aggressive when users are just tabbing through fields.
The form submission also triggers validation:
document.getElementById('testForm').addEventListener('submit', function(e) {
e.preventDefault();
if (validateForm('shake')) {
// form is valid, show success message
}
});
So validation happens in three scenarios:
- When clicking any of the test buttons (with their specific animation)
- When blurring out of a required field thatās empty (always uses shake)
- When submitting the form (uses shake by default)
when to use what
Different animations work better for different contexts:
- use shake for critical errors or final form submission
- use bounce for friendly reminders
- use pulse for subtle hints in longer forms
- use wobble for playful interfaces or less serious applications
The key is matching the animation personality to your formās context. A medical form probably wants subtle pulse effects. A game signup might embrace the full wobble.
performance notes
CSS transforms are GPU-accelerated, so these animations wonāt cause layout thrashing. The 600ms duration is long enough to be noticeable but short enough to not feel sluggish. And since weāre using classes rather than inline styles, the browser can optimize the animations.
Form validation doesnāt have to be boring. Sometimes a little shake is all you need to make the experience memorable without being annoying.