Newsletter Signup Component
Newsletter Signup Component
Section titled “Newsletter Signup Component”The NewsletterSignup component provides an email subscription form with two visual variants, honeypot spam protection, and full accessibility support.
Features
Section titled “Features”- ✅ Two visual variants (inline, footer)
- ✅ Honeypot spam protection
- ✅ Accessible form controls (ARIA labels, keyboard navigation)
- ✅ Real-time validation
- ✅ Success/error states
- ✅ Mobile responsive
- ✅ API integration with
/api/newsletter-subscribe
Basic Usage
Section titled “Basic Usage”---import NewsletterSignup from '@rejected-media/podcast-framework-core/components/NewsletterSignup.astro';---
<NewsletterSignup />This creates an inline newsletter signup form with default styling.
Optional Props
Section titled “Optional Props”variant
Section titled “variant”Type: 'inline' | 'footer'
Default: 'inline'
Visual variant of the newsletter form.
Variants:
inline- Centered, standalone form (max-width: 500px)footer- Footer-optimized layout (max-width: 500px, left-aligned)
<!-- Inline variant (default) --><NewsletterSignup variant="inline" />
<!-- Footer variant --><NewsletterSignup variant="footer" />placeholder
Section titled “placeholder”Type: string
Default: "Your email address"
Email input placeholder text.
<NewsletterSignup placeholder="Enter your email..." />buttonText
Section titled “buttonText”Type: string
Default: "Subscribe"
Submit button text.
<NewsletterSignup buttonText="Sign Up" />Complete Example
Section titled “Complete Example”---import NewsletterSignup from '@rejected-media/podcast-framework-core/components/NewsletterSignup.astro';---
<div class="bg-blue-50 rounded-lg p-8 text-center"> <h2 class="text-2xl font-bold mb-3">Never Miss an Episode</h2> <p class="text-gray-600 mb-6"> Get weekly episodes delivered to your inbox </p>
<NewsletterSignup variant="inline" buttonText="Subscribe Now" /></div>Visual Variants
Section titled “Visual Variants”Inline Variant
Section titled “Inline Variant”Best for dedicated sections or call-to-action areas:
<section class="py-12 bg-gray-100"> <div class="max-w-2xl mx-auto px-4 text-center"> <h2 class="text-3xl font-bold mb-4">Stay Updated</h2> <p class="text-lg text-gray-600 mb-8"> Join our community and get notified about new episodes </p> <NewsletterSignup variant="inline" /> </div></section>Footer Variant
Section titled “Footer Variant”Designed for use in the Footer component:
<Footer siteName="My Podcast" showNewsletter={true}> <NewsletterSignup slot="newsletter" variant="footer" /></Footer>Spam Protection
Section titled “Spam Protection”The component includes a honeypot field to prevent bot submissions:
<!-- Hidden from users, but visible to bots --><input type="text" name="website" style="display:none" tabindex="-1" aria-hidden="true" autocomplete="off"/>How it works:
- Hidden from human users (display: none)
- Bots often fill all fields, including hidden ones
- If
websitefield has a value, submission is silently rejected - Bot thinks it succeeded (prevents revealing the honeypot)
Form States
Section titled “Form States”The component handles three states:
1. Default State
Section titled “1. Default State”┌─────────────────────────────────┐│ [[email protected]] [Subscribe]│└─────────────────────────────────┘2. Submitting State
Section titled “2. Submitting State”┌─────────────────────────────────┐│ [[email protected]] [Subscribing...]│└─────────────────────────────────┘(Button disabled)3. Success State
Section titled “3. Success State”┌─────────────────────────────────┐│ Thanks for subscribing! ✓ │└─────────────────────────────────┘(Form hidden, success message shown)4. Error State
Section titled “4. Error State”┌─────────────────────────────────┐│ [[email protected]] [Subscribe]││ ⚠ Something went wrong... │└─────────────────────────────────┘API Integration
Section titled “API Integration”The component submits to /api/newsletter-subscribe:
// POST /api/newsletter-subscribe{}
// Response (success){ "message": "Thanks for subscribing!", "success": true}
// Response (error){ "message": "Email already subscribed", "success": false}Implementation:
import type { APIRoute } from 'astro';import { NewsletterService } from '@rejected-media/podcast-framework-core';
export const POST: APIRoute = async ({ request }) => { const { email } = await request.json();
const service = new NewsletterService({ apiKey: import.meta.env.CONVERTKIT_API_KEY, formId: import.meta.env.CONVERTKIT_FORM_ID });
const result = await service.subscribe(email);
return new Response(JSON.stringify(result), { status: result.success ? 200 : 400, headers: { 'Content-Type': 'application/json' } });};Styling
Section titled “Styling”CSS Custom Properties
Section titled “CSS Custom Properties”The component uses theme variables:
--color-primary /* Button background */Custom Styles
Section titled “Custom Styles”Override with CSS:
<NewsletterSignup />
<style> .newsletter-signup input[type="email"] { border-color: #3b82f6; }
.newsletter-signup button { background: linear-gradient(to right, #3b82f6, #8b5cf6); }</style>Accessibility
Section titled “Accessibility”ARIA Labels
Section titled “ARIA Labels”<label for="email-inline" class="sr-only"> Email address</label><input type="email" id="email-inline" aria-required="true" aria-describedby="newsletter-status-inline"/><div id="newsletter-status-inline" role="status" aria-live="polite"></div>Keyboard Navigation
Section titled “Keyboard Navigation”- Tab - Focus email input
- Tab - Focus submit button
- Enter - Submit form (from either input or button)
Screen Readers
Section titled “Screen Readers”- Input has descriptive label (visually hidden)
- Status updates announced via
aria-live="polite" - Required field indicated with
aria-required="true"
Responsive Design
Section titled “Responsive Design”Desktop
Section titled “Desktop”┌────────────────────────────────────────┐│ [[email protected]...] [Subscribe] │└────────────────────────────────────────┘Mobile (< 640px)
Section titled “Mobile (< 640px)”┌────────────────────────┐│ [[email protected]...] │├────────────────────────┤│ [Subscribe] │└────────────────────────┘Inputs stack vertically on mobile for better usability.
Performance
Section titled “Performance”- Bundle Size: ~2 KB (including validation logic)
- JavaScript: Required for form submission
- CSS: Inline, scoped styles
- Network: Single API call on submit
Error Handling
Section titled “Error Handling”The component handles multiple error scenarios:
// Network errorcatch (error) { statusDiv.textContent = 'Network error. Please check your connection and try again.'; statusDiv.className = 'newsletter-status error';}
// Server errorif (!response.ok) { const data = await response.json(); statusDiv.textContent = data.message || 'Something went wrong. Please try again.'; statusDiv.className = 'newsletter-status error';}
// Honeypot triggered (bot detected)if (website) { // Silently succeed to not reveal honeypot statusDiv.textContent = 'Thanks for subscribing!'; statusDiv.className = 'newsletter-status success';}Customization Examples
Section titled “Customization Examples”Example 1: Custom Colors
Section titled “Example 1: Custom Colors”<NewsletterSignup />
<style is:global> .newsletter-signup input[type="email"]:focus { border-color: #10b981; box-shadow: 0 0 0 3px rgba(16, 185, 129, 0.1); }
.newsletter-signup button { background: #10b981; }
.newsletter-signup button:hover { background: #059669; }</style>Example 2: With Analytics Tracking
Section titled “Example 2: With Analytics Tracking”<NewsletterSignup />
<script> document.addEventListener('DOMContentLoaded', () => { const form = document.querySelector('.newsletter-form');
form?.addEventListener('submit', (e) => { // Track newsletter signups if (window.gtag) { gtag('event', 'newsletter_signup', { event_category: 'engagement', event_label: 'newsletter_form' }); } }); });</script>Example 3: Multi-step Form
Section titled “Example 3: Multi-step Form”---// Show signup after user reads 3 episodesconst shouldShowNewsletter = checkUserEngagement();---
{shouldShowNewsletter && ( <div class="fixed bottom-4 right-4 bg-white shadow-2xl rounded-lg p-6 max-w-md"> <button class="absolute top-2 right-2 text-gray-400 hover:text-gray-600"> × </button> <h3 class="text-xl font-bold mb-2">Enjoying the show?</h3> <p class="text-gray-600 mb-4">Get episodes delivered to your inbox</p> <NewsletterSignup variant="inline" /> </div>)}Troubleshooting
Section titled “Troubleshooting”Form not submitting
Section titled “Form not submitting”Check that the API route exists:
# Should existsrc/pages/api/newsletter-subscribe.tsEndpoint returns 404
Section titled “Endpoint returns 404”Verify the endpoint path in the component matches your API route:
// Component expects:fetch('/api/newsletter-subscribe', { ... })
// API route should be at:src/pages/api/newsletter-subscribe.tsConvertKit integration not working
Section titled “ConvertKit integration not working”Check environment variables:
CONVERTKIT_API_KEY="your-api-key"CONVERTKIT_FORM_ID="your-form-id"Honeypot incorrectly triggered
Section titled “Honeypot incorrectly triggered”Ensure the honeypot field is properly hidden:
<input type="text" name="website" style="display:none" <!-- Must be display:none --> tabindex="-1" autocomplete="off"/>Related Components
Section titled “Related Components”- Footer - Uses NewsletterSignup in newsletter slot
- Server Services - NewsletterService for ConvertKit integration
Next Steps
Section titled “Next Steps”- Server Services - Set up NewsletterService
- Configuration - Configure ConvertKit
- Deployment - Set production env vars