Blog / CSS

Animated Inputs: Floating Labels and Visual Validation

Create modern and interactive forms with floating labels, validation animations and custom controls. All in pure CSS with a touch of JavaScript.

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.

💡
Good to know

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.

floating-label.html
<!-- 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-label.css
.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-animated.css
.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.

Email valide Email invalide
Email valide Email invalide
validation-input.css
.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.css
/* 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.css
/* 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.css
.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 for and id
  • Never hide the input with display: none without an alternative (use opacity: 0 with position: absolute to keep keyboard navigation)
  • Sufficient contrast: validation colors must be visible
  • Focus visible: always keep a clear focus indicator
accessibility.css
/* 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 transform over top/left for animations
  • Use will-change sparingly on animated elements
  • Avoid complex box-shadows on many elements
⚠️
Important

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!

🎨
Go further

Find dozens of ready-to-use form components in our effects library, with one-click copyable code.