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.