---
title: "CSS Form Animations: Inputs, Toggles & Validation"
description: "Six free CSS form animations with live previews: focus glow, floating labels, toggle switches, checkboxes, button clicks, and validation shakes. Pure CSS."
url: https://frontendhelpers.com/css-form-animations
updated: 2026-10-12
---

# CSS form animations

Focus glows, floating labels, toggle switches and validation shakes — all built on real form controls, so keyboard and screen reader support stays intact.

## The code for all 6 forms & inputs, explained

Every effect above, with the markup to use and the complete CSS. Each one is self-contained: paste the CSS once, add the class, done.

### Focus Glow

The soft ring makes focus unmistakable without an harsh outline. Never remove focus styling — replace it with something better, like this.

Markup

```html
<input type="text" class="fh-input-glow" placeholder="Your email" />
```

CSS

```css
.fh-input-glow {
  border: 1px solid #3a4358;
  border-radius: 10px;
  padding: 12px 16px;
  background: transparent;
  color: inherit;
  outline: none;
  transition: border-color 0.25s ease, box-shadow 0.25s ease;
}
.fh-input-glow:focus {
  border-color: #06b6d4;
  box-shadow: 0 0 0 4px rgba(6, 182, 212, 0.15);
}
```

### Floating Label

The placeholder=" " (one space) is the trick — :placeholder-shown can then detect whether the field is empty, no JavaScript needed.

Markup

```html
<div class="fh-float-field">
  <input type="text" placeholder=" " />
  <label>Email address</label>
</div>
```

CSS

```css
.fh-float-field {
  position: relative;
}
.fh-float-field input {
  width: 100%;
  border: 1px solid #3a4358;
  border-radius: 10px;
  padding: 18px 14px 6px;
  background: transparent;
  color: inherit;
  outline: none;
}
.fh-float-field input:focus { border-color: #06b6d4; }
.fh-float-field label {
  position: absolute;
  left: 14px;
  top: 50%;
  translate: 0 -50%;
  color: #94a3b8;
  pointer-events: none;
  text-transform: none;
  transition: all 0.2s ease;
}
.fh-float-field input:focus + label,
.fh-float-field input:not(:placeholder-shown) + label {
  top: 10px;
  translate: 0 0;
  font-size: 0.68em;
  color: #06b6d4;
}
```

### Toggle Switch

A real checkbox restyled — so keyboard, forms and screen readers all keep working. The springy easing sells the flip.

Markup

```html
<input type="checkbox" class="fh-toggle" />
```

CSS

```css
.fh-toggle {
  appearance: none;
  width: 52px;
  height: 28px;
  padding: 0;
  border: none;
  border-radius: 14px;
  background: #3a4358;
  position: relative;
  cursor: pointer;
  transition: background 0.25s ease;
}
.fh-toggle::before {
  content: '';
  position: absolute;
  top: 3px;
  left: 3px;
  width: 22px;
  height: 22px;
  border-radius: 50%;
  background: #fff;
  transition: left 0.25s cubic-bezier(0.34, 1.56, 0.64, 1);
}
.fh-toggle:checked { background: #06b6d4; }
.fh-toggle:checked::before { left: 27px; }
```

### Checkbox Pop

The tick scales in with an overshoot — the same pattern used by the checklists on this site's Dev Essentials page.

Markup

```html
<input type="checkbox" class="fh-check" />
```

CSS

```css
.fh-check {
  appearance: none;
  width: 26px;
  height: 26px;
  padding: 0;
  border: 2px solid #3a4358;
  border-radius: 8px;
  cursor: pointer;
  display: grid;
  place-content: center;
  transition: background 0.2s ease, border-color 0.2s ease;
}
.fh-check::before {
  content: '';
  width: 12px;
  height: 12px;
  clip-path: polygon(14% 44%, 0 65%, 50% 100%, 100% 16%, 80% 0%, 43% 62%);
  background: #04110b;
  transform: scale(0);
  transition: transform 0.25s cubic-bezier(0.34, 1.56, 0.64, 1);
}
.fh-check:checked { background: #10b981; border-color: #10b981; }
.fh-check:checked::before { transform: scale(1); }
```

### Button Press

The :active state compresses the button and flattens its shadow — a 120ms cue that the click registered. Tactile feedback, zero JavaScript.

Markup

```html
<button class="fh-btn-press">Save changes</button>
```

CSS

```css
.fh-btn-press {
  padding: 12px 26px;
  border: 1px solid rgba(255, 255, 255, 0.15);
  border-radius: 10px;
  background: linear-gradient(135deg, #8b5cf6, #06b6d4);
  color: #fff;
  font-weight: 600;
  cursor: pointer;
  box-shadow: 0 6px 16px -6px rgba(139, 92, 246, 0.5);
  transition: transform 0.12s ease, box-shadow 0.12s ease;
}
.fh-btn-press:hover { transform: translateY(-1px); }
.fh-btn-press:active {
  transform: translateY(1px) scale(0.97);
  box-shadow: 0 2px 6px -2px rgba(139, 92, 246, 0.4);
}
```

### Validation Shake

Add the class from JavaScript when validation fails, and remove it on the animationend event so it can shake again next time.

Markup

```html
<input class="fh-field-error" /> <!-- add the class on failed validation -->
```

CSS

```css
.fh-field-error {
  border: 1px solid #ef4444;
  box-shadow: 0 0 0 4px rgba(239, 68, 68, 0.12);
  animation: fh-fieldShake 0.4s ease-in-out;
}
@keyframes fh-fieldShake {
  0%, 100% { transform: translateX(0); }
  25% { transform: translateX(-6px); }
  50% { transform: translateX(6px); }
  75% { transform: translateX(-3px); }
}
```

## Style real controls, never replace them

Every effect here restyles a genuine `<input>` — the toggle switch is a checkbox, the animated tick is a checkbox, the shake is applied to a text field. That matters enormously: a real input arrives with keyboard operability, form submission, screen reader semantics and browser autofill already working.

The common alternative — building a "toggle" from divs and click handlers — throws all of that away and then requires re-implementing it badly with ARIA. `appearance: none` lets you strip a control's native styling while keeping its native behaviour, and that's the technique underpinning this whole category.

## Focus styling is not optional

The single most damaging thing done to forms is `outline: none` with nothing to replace it. It removes the only indication of where a keyboard user is, turning a form into an invisible maze. If you dislike the default outline, replace it — a focus ring built from `box-shadow`, as the focus-glow effect here does, is both attractive and compliant.

Use `:focus-visible` rather than `:focus` so the ring appears for keyboard navigation but not on mouse clicks, which is the behaviour most designers actually want.

## Floating labels and the placeholder trap

The floating-label effect uses a pure-CSS trick: a placeholder of a single space makes `:placeholder-shown` a reliable test for whether the field is empty, so the label can move without JavaScript.

What it must never do is replace the label entirely. Placeholder-only fields lose their description the moment the user types, fail contrast requirements at default styling, and confuse screen readers. Floating labels work precisely because the label persists — it moves rather than disappearing.

## Validation feedback that helps

The shake animation signals a problem instantly, but motion alone isn't enough: colour and movement are not accessible signals on their own. Pair the shake with a text message describing what's wrong and how to fix it, associate it with the field using `aria-describedby`, and set `aria-invalid`. Add the shake class on failed submission and remove it on `animationend` so it can fire again next time.

## Frequently asked questions

### How do I style a checkbox or toggle without breaking accessibility?

Use appearance: none on a real input element and style it with CSS, including its ::before pseudo-element. The control keeps keyboard operability, form submission and screen reader semantics — everything a div-based replacement loses.

### Is it okay to remove the focus outline?

Only if you replace it with something equally visible, such as a box-shadow focus ring. Removing focus styling with nothing in its place makes a form unusable by keyboard and fails WCAG.

### Can floating labels replace the label element?

No. Floating labels work because the label persists and moves rather than disappearing. Placeholder-only fields lose their description as soon as the user types and cause problems for screen readers.

## Related categories

- [CSS Shake Animation for Errors (Accessible Version)](https://frontendhelpers.com/css-shake-animation): A CSS shake animation for form errors: decaying translateX keyframes, a clean restart with element.animate(), plus ARIA and reduced-motion support.
 
- [UI Feedback](https://frontendhelpers.com/css-ui-feedback-animations): Toasts, tooltips, modals and progress bars
 
- [Hover Effects](https://frontendhelpers.com/css-hover-effects): Button lifts, glows and shine sweeps
 
- [Accordions](https://frontendhelpers.com/css-accordion-animations): Smooth expand, chevron rotate and plus-minus morphs
