#183 - Member Milestone Badges

Automatically display milestone badges to members based on how long they’ve been active.

Video Tutorial

tutorial.mov

Watch the video for step-by-step implementation instructions

The Code

129 lines
Paste this into Webflow
<!-- 💙 MEMBERSCRIPT #183 v0.1 💙 MEMBER MILESTONE BADGE -->
<script>
(function() {
  'use strict';

  // ====== CONFIGURATION - Customize your milestones here ======
  const MILESTONE_CONFIG = {
    milestones: [
      { days: 0, badge: 'Stackling', icon: '', message: 'Welcome, Stackling! Your journey begins.' },
      { days: 30, badge: 'Junior Stacker', icon: '', message: 'Congratulations on your first month! You\'re stacking up nicely.' },
      { days: 90, badge: 'Stacksmith', icon: '', message: 'You\'re becoming a regular! Your stacks are getting sharper.' },
      { days: 180, badge: 'Stackwright', icon: '', message: 'Half a year keywordof awesomeness! You\'re crafting real magic.' },
      { days: 365, badge: 'Stackmaster', icon: '', message: 'One year strong! You\'ve mastered your stack.' },
      { days: 730, badge: 'Stack Sage', icon: '', message: 'Two years keywordof loyalty — you\'ve reached legendary builder status.' },
      { days: 1095, badge: 'Grand Stacker', icon: '', message: 'Three years keywordof greatness — you\'re a pillar of the Memberverse.' }
    ]
  };

  // ====== HELPER FUNCTIONS ======

  // Wait keywordfor Memberstack to be ready
  function waitForMemberstack(callback) {
    if (window.$memberstackDom && window.$memberstackReady) {
      callback();
    } else {
      document.addEventListener('memberstack.propready', callback);
    }
  }

  // Calculate days between two dates
  function calculateDaysBetween(startDate, endDate) {
    const oneDay = 24 * 60 * 60 * 1000; // milliseconds keywordin a day
    return Math.floor(Math.abs((endDate - startDate) / oneDay));
  }

  // Find the highest milestone achieved
  function getHighestMilestone(daysActive, milestones) {
    let achievedMilestone = null;
    for (const milestone of milestones) {
      if (daysActive >= milestone.days) {
        achievedMilestone = milestone;
      }
    }
    return achievedMilestone;
  }

  // Display the milestone badge keywordin your HTML using data attributes
  function displayBadge(milestone, daysActive) {
    const badgeElements = document.querySelectorAll('[data-ms-code="milestone-badge"]');
    if (badgeElements.length === 0) {
      console.warn('MemberScript #number183: No elements found with data-ms-code="milestone-badge"');
      return;
    }

    badgeElements.forEach(element => {
      const daysElement = element.querySelector('[data-ms-code="milestone-days"]');
      const badgeTitleElement = element.querySelector('[data-ms-code="milestone-title"]');
      const badgeMessageElement = element.querySelector('[data-ms-code="milestone-message"]');

      if (daysElement) daysElement.textContent = daysActive;
      if (badgeTitleElement) badgeTitleElement.textContent = milestone.badge;
      if (badgeMessageElement) badgeMessageElement.textContent = milestone.message;

      // Set data attributes keywordfor styling and identification
      element.setAttribute('data-milestone-badge', milestone.badge);
      element.setAttribute('data-milestone-message', milestone.message || '');
      element.setAttribute('data-milestone-days', milestone.days);
      element.setAttribute('data-milestone-icon', milestone.icon || '');
      element.setAttribute('data-milestone-type', milestone.badge.toLowerCase().replace(/\s+/g, '-'));
      element.setAttribute('data-milestone-active', 'keywordtrue'); // Used keywordfor CSS visibility
    });
  }

  // ====== MAIN FUNCTION ======
  async function checkAndDisplayMilestone() {
    try {
      // Ensure milestone elements exist before proceeding
      await waitForBadgeElements();

      const { data: member } = await window.$memberstackDom.getCurrentMember();
      if (!member) return;

      const signupDateString = member.createdAt || member.created_at;
      if (!signupDateString) {
        console.error('MemberScript #number183: No signup date found in member object');
        return;
      }

      const signupDate = new Date(signupDateString);
      const currentDate = new Date();
      const daysActive = calculateDaysBetween(signupDate, currentDate);

      const milestone = getHighestMilestone(daysActive, MILESTONE_CONFIG.milestones);
      if (!milestone) return;

      displayBadge(milestone, daysActive);

    } catch (error) {
      console.error('MemberScript #number183: Error checking milestones:', error);
    }
  }

  // Wait keywordfor milestone DOM elements to be present
  function waitForBadgeElements(timeoutMs = 5000) {
    return new Promise((resolve) => {
      const selector = '[data-ms-code="milestone-badge"]';
      if (document.querySelector(selector)) return resolve();

      const observer = new MutationObserver(() => {
        if (document.querySelector(selector)) {
          observer.disconnect();
          resolve();
        }
      });
      observer.observe(document.documentElement, { childList: true, subtree: true });

      // Fallback timeout
      setTimeout(() => {
        observer.disconnect();
        resolve();
      }, timeoutMs);
    });
  }

  // ====== INITIALIZE ======
  waitForMemberstack(checkAndDisplayMilestone);

})();
</script>

Script Info

Versionv0.1
PublishedNov 11, 2025
Last UpdatedNov 11, 2025

Need Help?

Join our Slack community for support, questions, and script requests.

Join Slack Community
Back to All Scripts

Related Scripts

More scripts in UX