Introduction
Forms are at the heart of the user experience on the web. A well-designed form with smooth animations and clear visual validation can transform a tedious task into a pleasant interaction.
In this tutorial, we'll explore 5 essential techniques for creating modern inputs: floating labels, animated borders, visual validation, custom checkboxes/radios and toggle switches. Each example is interactive and the code is ready to be copied.
All these techniques primarily use CSS with the :focus selector and the :placeholder-shown pseudo-selector. JavaScript is only needed for dynamic validation.
1. Floating Label Animation
The floating label has become a standard of modern design. The label "floats" above the input when the user starts typing, saving space while keeping context visible.
<!-- HTML -->
<div class="floating-input-group">
<input type="text" class="floating-input" placeholder=" " id="email">
<label class="floating-label" for="email">Email Address</label>
</div>
.floating-input-group {
position: relative;
width: 300px;
}
.floating-input {
width: 100%;
padding: 20px 16px 8px;
font-size: 1rem;
background: rgba(255, 255, 255, 0.05);
border: 2px solid rgba(255, 255, 255, 0.1);
border-radius: 12px;
color: white;
outline: none;
transition: all 0.3s ease;
}
.floating-input:focus {
border-color: #6366f1;
background: rgba(99, 102, 241, 0.05);
box-shadow: 0 0 0 4px rgba(99, 102, 241, 0.1);
}
.floating-label {
position: absolute;
left: 16px;
top: 50%;
transform: translateY(-50%);
font-size: 1rem;
color: #a1a1aa;
pointer-events: none;
transition: all 0.3s ease;
}
/* Animation on focus or when filled */
.floating-input:focus ~ .floating-label,
.floating-input:not(:placeholder-shown) ~ .floating-label {
top: 8px;
transform: translateY(0);
font-size: 0.75rem;
color: #6366f1;
}
How it works
The magic relies on two CSS selectors:
:focus: detects when the input is active:not(:placeholder-shown): detects when the input contains text (the placeholder is no longer visible)
The placeholder=" " (space) is crucial: it allows the :placeholder-shown selector to work without displaying visible text.
2. Input with Animated Border
This technique uses an animated line that extends from the center to the edges when the input receives focus. It's an elegant effect inspired by Material Design.
.border-input-group {
position: relative;
width: 300px;
}
.border-input {
width: 100%;
padding: 16px;
font-size: 1rem;
background: transparent;
border: none;
border-bottom: 2px solid rgba(255, 255, 255, 0.2);
color: white;
outline: none;
}
/* Ligne animee */
.border-input-line {
position: absolute;
bottom: 0;
left: 50%;
width: 0;
height: 2px;
background: linear-gradient(135deg, #6366f1, #8b5cf6);
transition: all 0.4s ease;
}
.border-input:focus ~ .border-input-line {
left: 0;
width: 100%;
}
/* Animated label */
.border-input-label {
position: absolute;
left: 0;
top: 16px;
color: #a1a1aa;
pointer-events: none;
transition: all 0.3s ease;
}
.border-input:focus ~ .border-input-label,
.border-input:not(:placeholder-shown) ~ .border-input-label {
top: -12px;
font-size: 0.8rem;
color: #6366f1;
}
3. Visual Validation (Success/Error)
Visual validation gives immediate feedback to the user. We'll create an input that displays success and error states with subtle animations.
.validation-input {
width: 100%;
padding: 16px 48px 16px 16px;
background: rgba(255, 255, 255, 0.05);
border: 2px solid rgba(255, 255, 255, 0.1);
border-radius: 12px;
color: white;
transition: all 0.3s ease;
}
/* Success state */
.validation-input.success {
border-color: #10b981;
background: rgba(16, 185, 129, 0.05);
}
/* Error state with shake */
.validation-input.error {
border-color: #ef4444;
background: rgba(239, 68, 68, 0.05);
animation: shake 0.5s ease;
}
@keyframes shake {
0%, 100% { transform: translateX(0); }
20%, 60% { transform: translateX(-5px); }
40%, 80% { transform: translateX(5px); }
}
/* Validation icons */
.validation-icon {
position: absolute;
right: 16px;
top: 50%;
transform: translateY(-50%) scale(0);
width: 24px;
height: 24px;
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
transition: all 0.3s ease;
}
.validation-input.success ~ .success-icon {
transform: translateY(-50%) scale(1);
background: #10b981;
}
.validation-input.error ~ .error-icon {
transform: translateY(-50%) scale(1);
background: #ef4444;
}
4. Animated Custom Checkbox and Radio
Native checkboxes and radios are difficult to style. Here's how to create custom versions with satisfying animations.
/* Custom Checkbox */
.custom-checkbox {
display: flex;
align-items: center;
gap: 12px;
cursor: pointer;
color: #a1a1aa;
transition: color 0.2s;
}
.custom-checkbox:hover {
color: white;
}
/* Hide native input */
.custom-checkbox input {
display: none;
}
.checkbox-box {
width: 24px;
height: 24px;
border: 2px solid rgba(255, 255, 255, 0.2);
border-radius: 6px;
position: relative;
transition: all 0.3s ease;
display: flex;
align-items: center;
justify-content: center;
}
/* CSS checkmark */
.checkbox-box::after {
content: '';
width: 6px;
height: 12px;
border: 2px solid white;
border-top: none;
border-left: none;
transform: rotate(45deg) scale(0);
margin-bottom: 3px;
transition: transform 0.2s ease;
}
/* Checked state */
.custom-checkbox input:checked ~ .checkbox-box {
background: #6366f1;
border-color: #6366f1;
animation: checkPop 0.3s ease;
}
.custom-checkbox input:checked ~ .checkbox-box::after {
transform: rotate(45deg) scale(1);
}
@keyframes checkPop {
0% { transform: scale(1); }
50% { transform: scale(1.2); }
100% { transform: scale(1); }
}
/* Custom Radio */
.custom-radio {
display: flex;
align-items: center;
gap: 12px;
cursor: pointer;
}
.custom-radio input {
display: none;
}
.radio-circle {
width: 24px;
height: 24px;
border: 2px solid rgba(255, 255, 255, 0.2);
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
transition: all 0.3s ease;
}
.radio-circle::after {
content: '';
width: 12px;
height: 12px;
background: #6366f1;
border-radius: 50%;
transform: scale(0);
transition: transform 0.2s ease;
}
.custom-radio input:checked ~ .radio-circle {
border-color: #6366f1;
}
.custom-radio input:checked ~ .radio-circle::after {
transform: scale(1);
animation: radioPop 0.3s ease;
}
@keyframes radioPop {
0% { transform: scale(0); }
50% { transform: scale(1.3); }
100% { transform: scale(1); }
}
5. Modern Toggle Switch
The toggle switch is ideal for on/off settings. Here is a modern version with a smooth animation using a cubic-bezier for a bounce effect.
.toggle-switch {
display: flex;
align-items: center;
gap: 12px;
cursor: pointer;
}
.toggle-switch input {
display: none;
}
.toggle-track {
width: 56px;
height: 30px;
background: rgba(255, 255, 255, 0.1);
border-radius: 100px;
position: relative;
transition: background 0.3s ease;
}
.toggle-thumb {
position: absolute;
top: 3px;
left: 3px;
width: 24px;
height: 24px;
background: white;
border-radius: 50%;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.3);
/* Bounce effect */
transition: left 0.3s cubic-bezier(0.68, -0.55, 0.265, 1.55);
}
/* Active state */
.toggle-switch input:checked ~ .toggle-track {
background: #6366f1;
}
.toggle-switch input:checked ~ .toggle-track .toggle-thumb {
left: 29px;
}
Best Practices and Accessibility
Creating beautiful inputs is not enough: they must also be accessible and performant.
Accessibility
- Always associate label and input with
forandid - Never hide the input with
display: nonewithout an alternative (useopacity: 0withposition: absoluteto keep keyboard navigation) - Sufficient contrast: validation colors must be visible
- Focus visible: always keep a clear focus indicator
/* Respect user preferences */
@media (prefers-reduced-motion: reduce) {
.floating-input,
.floating-label,
.border-input-line,
.checkbox-box,
.toggle-thumb {
transition: none;
}
.validation-input.error {
animation: none;
}
}
/* Visible focus for keyboard navigation */
.custom-checkbox input:focus-visible ~ .checkbox-box,
.custom-radio input:focus-visible ~ .radio-circle,
.toggle-switch input:focus-visible ~ .toggle-track {
outline: 2px solid #6366f1;
outline-offset: 2px;
}
Performance
- Prefer
transformovertop/leftfor animations - Use
will-changesparingly on animated elements - Avoid complex box-shadows on many elements
For custom checkboxes and radios, the best practice is to use position: absolute and opacity: 0 rather than display: none to preserve keyboard accessibility.
Conclusion
Animated inputs transform the user experience of your forms. With these 5 techniques, you have everything you need to create modern and engaging interfaces.
Don't forget that animation must serve UX, not hinder it. Keep your effects subtle, performant and accessible. Your users will thank you!
Find dozens of ready-to-use form components in our effects library, with one-click copyable code.