YSM // YOUTH SOCCER MARKETING
Websites Resources Blog Contact
Free Tool

Your club newsletter
in two minutes

Paste your stories. Pick a style. Get a branded email you can copy straight into Mailchimp, Gmail, or whatever your club uses. No design skills needed.

Start Building
METRO FC
This Week at Metro FC
Here's what happened this week at the club...
STEP ONE

Paste Your Stories

Drop in your club news. Game results, camp dates, coaching hires — whatever happened this week.

STEP TWO

Pick a Style

Choose from four templates. Your club colors and logo get applied automatically.

STEP THREE

Copy and Send

One click copies the HTML. Paste it into your email tool. Done.

No account needed
No design skills
Works in every email client
100% free

Build Your Newsletter

Set up your club once — it saves for next time.

Step 1

Your Club

Click to upload or drag your logo here

Brand Colors

Step 2

Pick a Style

Bold
Clean
Classic
Dark
Step 3

Write Your Newsletter

Your Stories

Each story becomes a section in your newsletter. Add as many as you need.

Add a photo — drop, click, or paste a URL

Newsletter Footer

Copied to clipboard!
Live Preview
Your Newsletter

Fill in your club details and stories above, then hit Generate Newsletter to see your email here.

' + inner + ''; if (selectedTemplate === 'bold') { html = wrap('' + (introP ? '' : '') + '' + storiesHtml + ctaHtml + '
' + (logoHtml ? '
' + logoHtml + '
' : '') + '

' + escHtml(clubName) + '

' + escHtml(subject) + '

' + introP + '

' + escHtml(contactInfo || clubName) + '

'); } else if (selectedTemplate === 'clean') { html = wrap('' + (introP ? '' : '') + '' + storiesHtml + ctaHtml + '
' + (logoHtml ? '
' + logoHtml + '
' : '') + '

' + escHtml(clubName) + '


' + escHtml(subject) + '

' + introP + '

' + escHtml(contactInfo || clubName) + '

'); } else if (selectedTemplate === 'classic') { html = wrap('' + (introP ? '' : '') + '' + storiesHtml + ctaHtml + '
' + (logoHtml ? '
' + logoHtml + '
' : '') + '

' + escHtml(clubName) + '

' + escHtml(subject) + '

' + introP + '

' + escHtml(contactInfo || clubName) + '

'); } else if (selectedTemplate === 'dark') { html = wrap('' + (introP ? '' : '') + '' + storiesHtml + ctaHtml + '
' + (logoHtml ? '
' + logoHtml + '
' : '') + '

' + escHtml(clubName) + '

' + escHtml(subject) + '

' + introP + '

' + escHtml(contactInfo || clubName) + '

'); } // Show preview document.getElementById('previewEmpty').style.display = 'none'; const iframe = document.getElementById('previewFrame'); iframe.style.display = 'block'; iframe.srcdoc = html; window._newsletterHTML = html; // Show the "what now" instructions document.getElementById('nextSteps').style.display = 'block'; // Scroll to preview on mobile if (window.innerWidth < 900) { document.querySelector('.preview-col').scrollIntoView({ behavior: 'smooth' }); } } function copyHTML() { if (!window._newsletterHTML) generateNewsletter(); navigator.clipboard.writeText(window._newsletterHTML).then(() => { const el = document.getElementById('copySuccess'); el.style.display = 'inline'; setTimeout(() => { el.style.display = 'none'; }, 2500); }); } // Logo upload document.getElementById('logoUpload').addEventListener('click', function(e) { if (e.target.tagName !== 'IMG') document.getElementById('logoFile').click(); }); function handleLogoFile(input) { if (!input.files[0]) return; const reader = new FileReader(); reader.onload = function(e) { document.getElementById('clubLogo').value = e.target.result; document.getElementById('logoPreview').src = e.target.result; document.getElementById('logoPreview').style.display = 'block'; document.getElementById('logoPlaceholder').style.display = 'none'; document.getElementById('logoUpload').classList.add('has-file'); saveSettings(); }; reader.readAsDataURL(input.files[0]); } function handleLogoDrop(e) { e.preventDefault(); document.getElementById('logoUpload').classList.remove('dragover'); const file = e.dataTransfer.files[0]; if (file && file.type.startsWith('image/')) { const reader = new FileReader(); reader.onload = function(ev) { document.getElementById('clubLogo').value = ev.target.result; document.getElementById('logoPreview').src = ev.target.result; document.getElementById('logoPreview').style.display = 'block'; document.getElementById('logoPlaceholder').style.display = 'none'; document.getElementById('logoUpload').classList.add('has-file'); saveSettings(); }; reader.readAsDataURL(file); } } // Image upload document.addEventListener('click', function(e) { const zone = e.target.closest('.image-upload-zone'); if (zone && !e.target.closest('.story-file') && e.target.tagName !== 'IMG') { zone.querySelector('.story-file').click(); } }); function processFile(file, zone) { if (!file || !file.type.startsWith('image/')) return; const reader = new FileReader(); reader.onload = function(e) { zone.querySelector('.image-preview').src = e.target.result; zone.querySelector('.image-preview').style.display = 'block'; zone.querySelector('.story-image').value = e.target.result; zone.querySelector('.upload-placeholder').style.display = 'none'; zone.classList.add('has-file'); }; reader.readAsDataURL(file); } function handleFileSelect(input) { processFile(input.files[0], input.closest('.image-upload-zone')); } function handleDrop(e, zone) { e.preventDefault(); zone.classList.remove('dragover'); processFile(e.dataTransfer.files[0], zone); } function setImageFromUrl(zone, url) { zone.querySelector('.image-preview').src = url; zone.querySelector('.image-preview').style.display = 'block'; zone.querySelector('.story-image').value = url; zone.querySelector('.upload-placeholder').style.display = 'none'; zone.classList.add('has-file'); } async function extractFromLink(btn) { const item = btn.closest('.story-item'); const url = item.querySelector('.story-link').value.trim(); if (!url) { alert('Enter a story link first.'); return; } btn.textContent = '...'; btn.disabled = true; try { const res = await fetch('/api/og-image?url=' + encodeURIComponent(url)); const data = await res.json(); if (data.image && !item.querySelector('.story-image').value) setImageFromUrl(item.querySelector('.image-upload-zone'), data.image); if (data.title && !item.querySelector('.story-headline').value) item.querySelector('.story-headline').value = data.title; if (data.description && !item.querySelector('.story-body').value) item.querySelector('.story-body').value = data.description; btn.textContent = data.image ? 'Done!' : 'No image'; } catch { btn.textContent = 'Failed'; } setTimeout(() => { btn.textContent = 'Pull'; btn.disabled = false; }, 2000); } function escHtml(str) { const d = document.createElement('div'); d.textContent = str; return d.innerHTML; } // Hamburger var hamburger = document.getElementById('v3Hamburger'); var mobileMenu = document.getElementById('v3MobileMenu'); if (hamburger && mobileMenu) { hamburger.addEventListener('click', function() { var isOpen = hamburger.classList.toggle('open'); mobileMenu.classList.toggle('open'); document.body.classList.toggle('v3-menu-open', isOpen); }); mobileMenu.querySelectorAll('a').forEach(function(link) { link.addEventListener('click', function() { hamburger.classList.remove('open'); mobileMenu.classList.remove('open'); document.body.classList.remove('v3-menu-open'); }); }); } // Scroll reveal const fadeEls = document.querySelectorAll('.fade-in'); const fadeObs = new IntersectionObserver((entries) => { entries.forEach(entry => { if (entry.isIntersecting) { entry.target.classList.add('visible'); fadeObs.unobserve(entry.target); } }); }, { threshold: 0.15 }); fadeEls.forEach(el => fadeObs.observe(el)); // Auto-generate preview on input changes (debounced) let autoGenTimer = null; document.querySelector('.builder').addEventListener('input', function() { clearTimeout(autoGenTimer); autoGenTimer = setTimeout(function() { // Only auto-gen if they've filled in at least a club name or a story const hasClub = document.getElementById('clubName').value.trim(); const hasStory = document.querySelector('.story-headline') && document.querySelector('.story-headline').value.trim(); if (hasClub || hasStory) generateNewsletter(); }, 800); }); init();