Skip to content

Error Shake

A copy-paste form states component in pure HTML & CSS. Zero dependencies, framework-agnostic, MIT-licensed.

Form StatesHTMLCSSany framework

Copy into your project

HTML
<!-- Error Shake (toggle .is-error on the parent) -->
<label class="nuda-shake is-error">
  <input class="nuda-shake__input" type="text" />
  <span class="nuda-shake__msg">
    <svg viewBox="0 0 12 12" width="10" height="10"
         fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round">
      <circle cx="6" cy="6" r="5" />
      <path d="M6 3 L6 7" />
      <path d="M6 9 L6 9" />
    </svg>
    Invalid value
  </span>
</label>
CSS
/* Error Shake
   Horizontal shake on the input, message slides + fades in below.
   Customize: --err-color */

.nuda-shake {
  --err-color: #ff6363;
  display: flex;
  flex-direction: column;
  gap: 6px;
  width: 100%;
  max-width: 240px;
}

.nuda-shake__input {
  width: 100%;
  padding: 10px 14px;
  background: rgba(255, 255, 255, 0.03);
  border: 1px solid rgba(255, 255, 255, 0.08);
  border-radius: 8px;
  color: #fafafa;
  font: 500 0.875rem ui-sans-serif, system-ui, sans-serif;
  outline: none;
  transition: border-color 0.25s, box-shadow 0.25s;
}

.nuda-shake.is-error .nuda-shake__input {
  border-color: var(--err-color);
  box-shadow: 0 0 0 3px rgba(255, 99, 99, 0.12);
  animation: nuda-shake 0.4s cubic-bezier(0.36, 0.07, 0.19, 0.97);
}

.nuda-shake__msg {
  display: flex;
  align-items: center;
  gap: 5px;
  font: 600 0.75rem ui-sans-serif, system-ui, sans-serif;
  color: var(--err-color);
  opacity: 0;
  transform: translateY(-4px);
}

.nuda-shake.is-error .nuda-shake__msg {
  opacity: 1;
  transform: translateY(0);
  transition:
    opacity 0.25s 0.15s,
    transform 0.25s 0.15s;
}

@keyframes nuda-shake {
  10%, 90%       { transform: translateX(-1px); }
  20%, 80%       { transform: translateX(2px); }
  30%, 50%, 70%  { transform: translateX(-4px); }
  40%, 60%       { transform: translateX(4px); }
}

@media (prefers-reduced-motion: reduce) {
  .nuda-shake.is-error .nuda-shake__input { animation: none; }
}

How to use Error Shake

Paste the HTML where you need it and the CSS into a global stylesheet (or a <style> tag). Every class is prefixed nuda- so it never collides with Tailwind or your own styles. Tweak the CSS custom properties to match your design system.

Works in React, Vue, Svelte, Astro, Next.js, Nuxt, Laravel Blade, Django, Rails — or a single .html file. No npm install, no build step.

More form states components

← Browse all NudaUI components