Back to Support Wall

JavaScript Widget Embed

Paste this code into your website - no iframe needed!

Widget Settings

<!-- Thank You Wall Widget -->
<div id="support-wall-widget"></div>
<script>
(function() {
  const container = document.getElementById('support-wall-widget');
  const maxMessages = 5;
  const appUrl = 'https://connors-voice-wall.base44.app';
  const dataApiUrl = appUrl + '/functions/getSupportWallData';
  
  const style = document.createElement('style');
  style.textContent = `
    .sw-widget { font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif; background: #f9fafb; min-height: 100vh; padding: 2rem 0; }
    .sw-hero { text-align: center; margin-bottom: 2rem; }
    .sw-title { font-size: 1.875rem; font-weight: 800; color: #0E2C46; margin-bottom: 0.5rem; letter-spacing: -0.02em; }
    .sw-subtitle { color: #6b7280; font-size: 0.875rem; max-width: 28rem; margin: 0 auto 1.25rem; }
    .sw-stats { display: flex; align-items: center; justify-content: center; gap: 1.5rem; margin-bottom: 1rem; flex-wrap: wrap; }
    .sw-stat { display: flex; align-items: center; gap: 0.5rem; color: #6b7280; font-size: 0.875rem; }
    .sw-stat-number { color: #0E2C46; font-weight: 600; }
    .sw-stat-icon { width: 16px; height: 16px; flex-shrink: 0; }
    .sw-btn { background: transparent; color: #EE2825; padding: 0.625rem 1.25rem; border-radius: 9999px; border: 2px solid #EE2825; cursor: pointer; font-weight: 500; display: inline-flex; align-items: center; gap: 0.5rem; text-decoration: none; font-size: 0.875rem; transition: all 0.2s; }
    .sw-btn:hover { background: #EE2825; color: white; }
    .sw-btn-group { display: flex; gap: 0.75rem; align-items: center; justify-content: center; flex-wrap: wrap; }
    .sw-feed { display: flex; flex-direction: column; gap: 1rem; margin-bottom: 1.5rem; }
    .sw-card { border-radius: 1rem; padding: 1.25rem; border: 1px solid rgba(14,44,70,0.06); transition-duration: 0.3s; background: rgba(255,255,255,0.85); backdrop-filter: blur(12px); box-shadow: 0 1px 8px rgba(14,44,70,0.05); }
    .sw-view-all-btn { background: #0E2C46; color: white; padding: 0.875rem 1.5rem; border-radius: 0.5rem; border: none; cursor: pointer; font-weight: 500; display: inline-block; text-align: center; text-decoration: none; font-size: 0.875rem; transition: all 0.2s; width: 100%; }
    .sw-view-all-btn:hover { background: #1a3a5a; }
    .sw-card:hover { box-shadow: 0 10px 24px rgba(14,44,70,0.12); }
    .sw-card-header { display: flex; align-items: flex-start; gap: 0.625rem; margin-bottom: 0.75rem; flex-wrap: wrap; }
    .sw-avatar { width: 2.25rem; height: 2.25rem; border-radius: 50%; background: #0E2C46; display: flex; align-items: center; justify-content: center; color: white; font-weight: 700; font-size: 0.875rem; flex-shrink: 0; }
    .sw-info { flex: 1; }
    .sw-name { font-weight: 600; color: #0E2C46; font-size: 0.875rem; }
    .sw-rank { display: inline-block; background: #0E2C46; color: white; padding: 0.125rem 0.625rem; border-radius: 9999px; font-size: 0.75rem; font-weight: 500; margin-left: 0.5rem; }
    .sw-message { color: #374151; font-size: 0.875rem; line-height: 1.5; white-space: pre-wrap; margin-bottom: 0.75rem; }
    .sw-date { color: #9ca3af; font-size: 0.75rem; }
    .sw-loading { text-align: center; padding: 3rem; color: #9ca3af; }
     .sw-empty { text-align: center; padding: 3rem; color: #9ca3af; }
     .sw-names-section { background: white; border-radius: 1rem; border: 1px solid rgba(14,44,70,0.06); padding: 1.5rem; margin-bottom: 2rem; box-shadow: 0 1px 8px rgba(14,44,70,0.05); }
     .sw-names-title { display: flex; align-items: center; gap: 0.5rem; font-size: 1.125rem; font-weight: 600; color: #0E2C46; margin-bottom: 1rem; }
     .sw-names-icon { width: 20px; height: 20px; }
     .sw-names-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(150px, 1fr)); gap: 0.75rem; }
     .sw-names-item { padding: 0.75rem; background: #f3f4f6; border-radius: 0.5rem; }
     .sw-names-name { font-weight: 500; color: #0E2C46; font-size: 0.875rem; margin: 0; }
     .sw-names-rank { font-size: 0.75rem; color: #6b7280; margin: 0.25rem 0 0 0; }
     .sw-names-empty { color: #9ca3af; font-size: 0.875rem; text-align: center; padding: 1rem; }
     @keyframes fadeIn { from { opacity: 0; transform: translateY(10px); } to { opacity: 1; transform: translateY(0); } }
  `;
  document.head.appendChild(style);
  
  fetch(dataApiUrl, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: '{}' })
    .then(res => res.json())
    .then(result => {
      if (!result.success) throw new Error('Failed to load');
      
      const { comments, stats, endorsements } = result.data;
       const displayComments = comments.slice(0, maxMessages);

       container.innerHTML = `
         <div class="sw-widget">
           <div class="sw-names-section">
             <div class="sw-names-title">
               <svg class="sw-names-icon" fill="none" stroke="currentColor" viewBox="0 0 24 24">
                 <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M17 20h5v-2a3 3 0 00-5.356-1.857M17 20H7m10 0v-2c0-.656-.126-1.283-.356-1.857M7 20H2v-2a3 3 0 015.356-1.857M7 20v-2c0-.656.126-1.283.356-1.857m0 0a5.002 5.002 0 019.288 0M15 7a3 3 0 11-6 0 3 3 0 016 0zm6 3a2 2 0 11-4 0 2 2 0 014 0zM7 10a2 2 0 11-4 0 2 2 0 014 0z"></path>
               </svg>
               <span>Signed by</span>
             </div>
             ${endorsements && endorsements.length > 0 ?
             '<div class="sw-names-grid">' + endorsements.map(sig => `
               <div class="sw-names-item">
                 <p class="sw-names-name">${sig.signer_name}</p>
               </div>
             `).join('') + '</div>' :
             '<p class="sw-names-empty">No signatures yet</p>'
             }
             </div>

             <div class="sw-hero">
             <h2 class="sw-title">Thank You Wall</h2>
             <p class="sw-subtitle">The election is over — thank you to everyone who showed their support</p>
             <div class="sw-stats">
             <div class="sw-stat">
              <svg class="sw-stat-icon" fill="none" stroke="#EE2825" viewBox="0 0 24 24">
                <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M8 12h.01M12 12h.01M16 12h.01M21 12c0 4.418-4.03 8-9 8a9.863 9.863 0 01-4.255-.949L3 20l1.395-3.72C3.512 15.042 3 13.574 3 12c0-4.418 4.03-8 9-8s9 3.582 9 8z"></path>
              </svg>
              <span><span class="sw-stat-number">${stats.messageCount}</span> messages</span>
             </div>
             <div class="sw-stat">
              <svg class="sw-stat-icon" fill="none" stroke="#0E2C46" viewBox="0 0 24 24">
                <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M17 20h5v-2a3 3 0 00-5.356-1.857M17 20H7m10 0v-2c0-.656-.126-1.283-.356-1.857M7 20H2v-2a3 3 0 015.356-1.857M7 20v-2c0-.656.126-1.283.356-1.857m0 0a5.002 5.002 0 019.288 0M15 7a3 3 0 11-6 0 3 3 0 016 0zm6 3a2 2 0 11-4 0 2 2 0 014 0zM7 10a2 2 0 11-4 0 2 2 0 014 0z"></path>
              </svg>
              <span><span class="sw-stat-number">${stats.supporterCount}</span> supporters</span>
             </div>
             </div>
             </div>
          
          <div class="sw-feed">
            ${displayComments.length === 0 ? 
              '<div class="sw-empty">No messages yet. Be the first to leave one!</div>' :
              displayComments.map(comment => `
                <div class="sw-card">
                  <div class="sw-card-header">
                    <div class="sw-avatar">${comment.name.charAt(0).toUpperCase()}</div>
                    <div class="sw-info">
                      <span class="sw-name">${comment.name}<span class="sw-rank">${comment.rank}</span></span>
                    </div>
                  </div>
                  <p class="sw-message">${comment.message}</p>
                  <div class="sw-date">${new Date(comment.created_date).toLocaleDateString('en-US', { month: 'short', day: 'numeric', year: 'numeric' })}</div>
                </div>
              `).join('')
            }
          </div>
          ${displayComments.length > 0 ? '<a href="https://connors-voice-wall.base44.app/SupportWall" target="_blank" class="sw-view-all-btn">View All Messages</a>' : ''}
        </div>
      `;
    })
    .catch(err => {
      container.innerHTML = '<div class="sw-loading">Failed to load support wall</div>';
    });
})();
</script>

Live Preview

See how the widget will look on your website

How to Use

  1. 1.Copy the widget code above
  2. 2.Paste it into your website's HTML where you want the support wall to appear
  3. 3.The widget loads natively on the page - no iframe!
  4. 4.Messages auto-load on page refresh

Benefits vs. iFrame

  • Looks native to your website
  • Can match your site's fonts and colors (customize the CSS)
  • No scrolling or sizing issues
  • Better SEO and accessibility