Component
Newsletter Signup
Newsletter Signup.
- Component
- Vanilla
- HTML
- MIT
Preview
Live component
No interactive preview for this component.
Install it with the CLI below, or read the implementation on this page.
Install
Add to your project
npx devsnips add Vanilla/Components/Forms/newsletter-signup Vanilla/Components/Forms/newsletter-signup <!--
Newsletter Signup Box with Success/Error States
- Clean, minimal design
- Real-time validation
- Success/error animations
- Accessible feedback
- Mobile-optimized layout
-->
<div class="newsletter-container">
<form class="newsletter-form" novalidate>
<div class="form-header">
<h2>Subscribe to Our Newsletter</h2>
<p>Get weekly updates and exclusive content directly in your inbox.</p>
</div>
<div class="form-content">
<div class="input-group">
<label for="email">Email Address</label>
<input
type="email"
id="email"
name="email"
required
placeholder="you@example.com"
aria-describedby="email-error"
>
<div id="email-error" class="error-message" aria-live="polite"></div>
</div>
<div class="consent-group">
<label class="checkbox-label">
<input
type="checkbox"
id="consent"
name="consent"
required
aria-describedby="consent-error"
>
<span>I agree to receive marketing emails</span>
</label>
<div id="consent-error" class="error-message" aria-live="polite"></div>
</div>
<button type="submit" class="submit-button" disabled>
<span class="button-text">Subscribe</span>
<span class="button-loader" hidden></span>
</button>
</div>
</form>
<div class="state-message success" hidden role="status">
<svg viewBox="0 0 24 24" width="48" height="48">
<path d="M9 16.2L4.8 12l-1.4 1.4L9 19 21 7l-1.4-1.4L9 16.2z" fill="currentColor"/>
</svg>
<h3>Thanks for subscribing!</h3>
<p>Please check your email to confirm your subscription.</p>
</div>
<div class="state-message error" hidden role="alert">
<svg viewBox="0 0 24 24" width="48" height="48">
<path d="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm1 15h-2v-2h2v2zm0-4h-2V7h2v6z" fill="currentColor"/>
</svg>
<h3>Oops! Something went wrong</h3>
<p>Please try again later or contact support if the problem persists.</p>
<button type="button" class="retry-button">Try Again</button>
</div>
</div>
<style>
.newsletter-container {
max-width: 500px;
margin: 2rem auto;
padding: 0 1rem;
}
.newsletter-form {
background: var(--ds-surface, #fff);
border-radius: var(--ds-radius-md, 8px);
box-shadow: var(--ds-shadow-sm, 0 2px 4px rgba(0, 0, 0, 0.1));
overflow: hidden;
}
.form-header {
padding: 1.5rem;
background: var(--ds-surface-2, #f8f9fa);
text-align: center;
}
.form-header h2 {
margin: 0 0 0.5rem;
color: var(--ds-foreground, #333);
}
.form-header p {
margin: 0;
color: var(--ds-muted, #666);
}
.form-content {
padding: 1.5rem;
}
.input-group,
.consent-group {
margin-bottom: 1rem;
}
label {
display: block;
margin-bottom: 0.25rem;
font-weight: 500;
color: var(--ds-foreground, #333);
}
.checkbox-label {
display: flex;
align-items: flex-start;
gap: 0.5rem;
font-weight: normal;
color: var(--ds-muted, #666);
}
input[type="email"] {
width: 100%;
padding: 0.75rem;
border: 2px solid var(--ds-border, #ddd);
border-radius: var(--ds-radius-sm, 4px);
font-size: 1rem;
transition: all 0.3s;
}
input[type="checkbox"] {
margin-top: 0.25rem;
}
.error-message {
margin-top: 0.25rem;
font-size: 0.875rem;
color: var(--ds-danger, #f44336);
min-height: 1.25rem;
}
.submit-button {
position: relative;
width: 100%;
padding: 0.875rem;
border: none;
border-radius: var(--ds-radius-sm, 4px);
background: var(--ds-accent, #4a90e2);
color: white;
font-size: 1rem;
font-weight: 500;
cursor: pointer;
transition: background-color 0.3s;
}
.submit-button:hover:not(:disabled) {
background: var(--ds-accent, #357abd);
}
.submit-button:disabled {
background: var(--ds-border, #ddd);
cursor: not-allowed;
}
.button-loader {
position: absolute;
top: 50%;
left: 50%;
width: 20px;
height: 20px;
margin-top: -10px;
margin-left: -10px;
border: 2px solid var(--ds-surface, #fff);
border-radius: 50%;
border-top-color: transparent;
animation: spin 0.6s linear infinite;
}
.state-message {
display: flex;
flex-direction: column;
align-items: center;
text-align: center;
padding: 2rem 1.5rem;
border-radius: var(--ds-radius-md, 8px);
animation: slideIn 0.3s ease-out;
}
.state-message.success {
background: var(--ds-success-soft, #e8f5e9);
color: var(--ds-success, #2e7d32);
}
.state-message.error {
background: var(--ds-danger-soft, #ffebee);
color: var(--ds-danger, #c62828);
}
.state-message svg {
margin-bottom: 1rem;
}
.state-message h3 {
margin: 0 0 0.5rem;
}
.state-message p {
margin: 0;
}
.retry-button {
margin-top: 1rem;
padding: 0.75rem 1.5rem;
border: none;
border-radius: var(--ds-radius-sm, 4px);
background: var(--ds-danger, #f44336);
color: white;
font-size: 1rem;
cursor: pointer;
transition: background-color 0.3s;
}
.retry-button:hover {
background: var(--ds-danger, #d32f2f);
}
@keyframes spin {
to { transform: rotate(360deg); }
}
@keyframes slideIn {
from {
transform: translateY(-20px);
opacity: 0;
}
to {
transform: translateY(0);
opacity: 1;
}
}
/* Focus styles */
input:focus {
outline: none;
border-color: var(--ds-accent, #4a90e2);
box-shadow: 0 0 0 2px rgba(74, 144, 226, 0.1);
}
.submit-button:focus-visible,
.retry-button:focus-visible {
outline: 2px solid var(--ds-accent, #4a90e2);
outline-offset: 2px;
}
/* Mobile optimization */
@media (max-width: 600px) {
.form-header,
.form-content {
padding: 1rem;
}
input[type="email"] {
font-size: 16px; /* Prevent iOS zoom */
}
}
/* devsnips-qa:quality-bar */
@media (prefers-reduced-motion: reduce){*{animation-duration:0.01ms !important;animation-iteration-count:1 !important;transition-duration:0.01ms !important;scroll-behavior:auto !important;}}
/* DevSnips design tokens (Swiss) — edit tokens.css to re-theme */
:root{
--ds-bg:#fff;--ds-surface:#fff;--ds-surface-2:#f7f7f7;--ds-surface-3:#ededed;--ds-foreground:#0a0a0a;--ds-muted:#525252;--ds-subtle:#a3a3a3;--ds-border:#e5e5e5;--ds-border-strong:#d4d4d4;--ds-accent:#2563eb;--ds-accent-hover:#1d4ed8;--ds-accent-fg:#fff;--ds-accent-soft:#eff6ff;--ds-success:#16a34a;--ds-success-soft:#f0fdf4;--ds-warning:#d97706;--ds-warning-soft:#fffbeb;--ds-danger:#dc2626;--ds-danger-soft:#fef2f2;--ds-info:#2563eb;--ds-info-soft:#eff6ff;--ds-font-sans:system-ui,-apple-system,'Segoe UI',Roboto,Inter,'Helvetica Neue',Arial,sans-serif;--ds-font-mono:ui-monospace,'SF Mono','JetBrains Mono',Menlo,Consolas,monospace;--ds-text-xs:.75rem;--ds-text-sm:.875rem;--ds-text-base:1rem;--ds-text-lg:1.125rem;--ds-text-xl:1.25rem;--ds-text-2xl:1.5rem;--ds-text-3xl:1.875rem;--ds-text-4xl:2.25rem;--ds-leading-tight:1.25;--ds-leading-normal:1.5;--ds-leading-relaxed:1.625;--ds-weight-normal:400;--ds-weight-medium:500;--ds-weight-semibold:600;--ds-weight-bold:700;--ds-space-0:0;--ds-space-px:1px;--ds-space-1:.25rem;--ds-space-2:.5rem;--ds-space-3:.75rem;--ds-space-4:1rem;--ds-space-5:1.25rem;--ds-space-6:1.5rem;--ds-space-8:2rem;--ds-space-10:2.5rem;--ds-space-12:3rem;--ds-space-16:4rem;--ds-radius-sm:.25rem;--ds-radius-md:.5rem;--ds-radius-lg:.75rem;--ds-radius-xl:1rem;--ds-radius-2xl:1.5rem;--ds-radius-full:9999px;--ds-shadow-sm:0 1px 2px 0 rgba(0,0,0,.05);--ds-shadow-md:0 4px 6px -1px rgba(0,0,0,.08),0 2px 4px -2px rgba(0,0,0,.05);--ds-shadow-lg:0 10px 15px -3px rgba(0,0,0,.1),0 4px 6px -4px rgba(0,0,0,.05);--ds-duration-fast:120ms;--ds-duration-normal:200ms;--ds-duration-slow:300ms;--ds-ease-out:cubic-bezier(.16,1,.3,1);--ds-ease-in-out:cubic-bezier(.4,0,.2,1);--ds-ring:0 0 0 2px #2563eb;--ds-container:1200px;--ds-gutter:1rem;
}
@media (prefers-color-scheme: dark){:root{--ds-bg:#0a0a0a;--ds-surface:#111;--ds-surface-2:#171717;--ds-surface-3:#1f1f1f;--ds-foreground:#fafafa;--ds-muted:#a3a3a3;--ds-subtle:#737373;--ds-border:#262626;--ds-border-strong:#404040;--ds-accent:#3b82f6;--ds-accent-hover:#60a5fa;--ds-accent-fg:#fff;--ds-accent-soft:#172554;--ds-success:#22c55e;--ds-success-soft:#052e16;--ds-warning:#f59e0b;--ds-warning-soft:#422006;--ds-danger:#ef4444;--ds-danger-soft:#450a0a;--ds-info:#3b82f6;--ds-info-soft:#172554;--ds-ring:0 0 0 2px #3b82f6;}}
</style>
<script>
const form = document.querySelector('.newsletter-form');
const submitButton = form.querySelector('.submit-button');
const buttonText = submitButton.querySelector('.button-text');
const buttonLoader = submitButton.querySelector('.button-loader');
const emailInput = form.querySelector('#email');
const consentCheckbox = form.querySelector('#consent');
const successMessage = document.querySelector('.state-message.success');
const errorMessage = document.querySelector('.state-message.error');
const retryButton = errorMessage.querySelector('.retry-button');
// Validation messages
const validationMessages = {
email: {
valueMissing: 'Please enter your email address',
typeMismatch: 'Please enter a valid email address'
},
consent: {
valueMissing: 'Please agree to receive marketing emails'
}
};
// Real-time validation
[emailInput, consentCheckbox].forEach(field => {
field.addEventListener('input', () => validateField(field));
field.addEventListener('blur', () => validateField(field));
});
function validateField(field) {
const errorElement = document.getElementById(`${field.id}-error`);
if (!field.validity.valid) {
let message = '';
for (const key in validationMessages[field.name]) {
if (field.validity[key]) {
message = validationMessages[field.name][key];
break;
}
}
errorElement.textContent = message;
field.setAttribute('aria-invalid', 'true');
} else {
errorElement.textContent = '';
field.setAttribute('aria-invalid', 'false');
}
// Update submit button state
submitButton.disabled = !form.checkValidity();
}
// Form submission
form.addEventListener('submit', async (e) => {
e.preventDefault();
if (!form.checkValidity()) {
[emailInput, consentCheckbox].forEach(validateField);
return;
}
// Show loading state
submitButton.disabled = true;
buttonText.hidden = true;
buttonLoader.hidden = false;
try {
// Simulate API call
await new Promise(resolve => setTimeout(resolve, 2000));
// Show success message
form.hidden = true;
successMessage.hidden = false;
} catch (error) {
// Show error message
form.hidden = true;
errorMessage.hidden = false;
}
});
// Retry button handler
retryButton.addEventListener('click', () => {
form.hidden = false;
errorMessage.hidden = true;
buttonText.hidden = false;
buttonLoader.hidden = true;
submitButton.disabled = !form.checkValidity();
});
</script> # Snippet 14 Newsletter Signup
Snippet 14 Newsletter Signup with responsive design with animations and hover interactions.
**Technology:** vanilla
**Category:** components
**Subcategory:** forms
**Tags:** login, animation, hover-effect, glassmorphism, shadow, responsive, flexbox, center
**Responsive:** Yes 366 lines UTF-8 · LF · Spaces: 2
Continue browsing