<div class="form-card">
<h2>Get in Touch</h2>
<form id="myForm" novalidate>
<div class="input-field">
<input type="text" id="name" name="name" required placeholder=" ">
<label for="name">Name</label>
<span class="error-hint" id="nameError"></span>
</div>
<div class="input-field">
<input type="email" id="email" name="email" required placeholder=" ">
<label for="email">Email</label>
<span class="error-hint" id="emailError"></span>
</div>
<button type="submit" id="submitBtn">
<span>Send Message</span>
<div class="loader"></div>
</button>
</form>
</div>
:root {
--error: #ef4444;
--success: #22c55e;
--primary: #6366f1;
}
* {
font-family: system-ui, "Segoe UI", Roboto, Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol";
box-sizing: border-box;
}
.form-card {
max-width: 400px;
margin: 40px auto;
padding: 30px;
background: white;
border-radius: 20px;
box-shadow: 0 10px 25px rgba(0,0,0,0.05);
}
.input-field {
position: relative;
margin-bottom: 25px;
}
/* Floating Label Logic */
.input-field input {
width: 100%;
padding: 12px 0;
border: none;
border-bottom: 2px solid #e2e8f0;
font-size: 1rem;
outline: none;
transition: all 0.3s ease;
}
.input-field label {
position: absolute;
top: 12px;
left: 0;
color: #94a3b8;
pointer-events: none;
transition: all 0.3s ease;
}
.input-field input:focus ~ label,
.input-field input:not(:placeholder-shown) ~ label {
top: -12px;
font-size: 0.8rem;
font-weight: 700;
color: var(--primary);
}
.input-field input:focus {
border-color: var(--primary);
}
/* Validation States */
.input-field.invalid input { border-color: var(--error); }
.input-field.invalid label { color: var(--error); }
.error-hint {
font-size: 0.75rem;
color: var(--error);
position: absolute;
bottom: -18px;
left: 0;
opacity: 0;
transform: translateY(-5px);
transition: all 0.2s ease;
}
.input-field.invalid .error-hint {
opacity: 1;
transform: translateY(0);
}
button {
width: 100%;
padding: 14px;
border-radius: 12px;
border: none;
background: var(--primary);
color: white;
font-weight: 600;
cursor: pointer;
transition: transform 0.2s;
}
button:active { transform: scale(0.98); }
const form = document.getElementById('myForm');
const inputs = form.querySelectorAll('input');
const validateInput = (input) => {
const container = input.parentElement;
const errorHint = container.querySelector('.error-hint');
// Use HTML5 validity check
if (!input.validity.valid) {
container.classList.add('invalid');
if (input.validity.valueMissing) {
errorHint.textContent = `${input.name.charAt(0).toUpperCase() + input.name.slice(1)} is required`;
} else if (input.validity.typeMismatch) {
errorHint.textContent = `Please enter a valid email address`;
}
} else {
container.classList.remove('invalid');
errorHint.textContent = '';
}
};
// Real-time validation
inputs.forEach(input => {
input.addEventListener('blur', () => validateInput(input));
input.addEventListener('input', () => {
if (input.parentElement.classList.contains('invalid')) {
validateInput(input);
}
});
});
form.addEventListener('submit', (e) => {
e.preventDefault();
// Validate all inputs before submit
let isFormValid = true;
inputs.forEach(input => {
validateInput(input);
if (!input.validity.valid) isFormValid = false;
});
if (isFormValid) {
const btn = form.querySelector('button');
btn.innerText = 'Success! ✓';
btn.style.backgroundColor = 'var(--success)';
console.log('FormData:', Object.fromEntries(new FormData(form)));
}
});