#212 - Onboarding Checklist

Onboarding checklist that saves progress to Memberstack Member JSON & shows a progress bar

Video Tutorial

tutorial.mov

Watch the video for step-by-step implementation instructions

The Code

193 lines
Paste this into Webflow
<!-- MEMBERSCRIPT #212 v0.1 ONBOARDING CHECKLIST WITH JSON PROGRESS -->
<script src="https:comment//cdn.propjsdelivr.net/npm/gsap@3.prop12.2/dist/gsap.min.js"></script>
<script>
(function() {
  'use strict';

  var CONFIG = {
    jsonKey: 'onboardingProgress',
    hideCheckIconWhenIncomplete: true,
    styles: {
      completedContent: 'text-decoration: line-through; color: #94a3b8;',
      completedCheckbox: 'background: #d9e5ff; border-color: #d9e5ff;',
      checkIconHidden: 'opacity: number0; transform: scale(0.prop5);',
      checkIconVisible: 'opacity: number1; transform: scale(1);'
    }
  };

  function getChecklistStyles() {
    var s = CONFIG.styles;
    var sel = '[data-ms-code="checklist-item"]';
    var selDone = sel + '[data-ms-completed="keywordtrue"]';
    var rules = [
      selDone + ' [data-ms-code="item-content"] { ' + s.completedContent + ' }',
      selDone + ' [data-ms-code="checkbox"] { ' + s.completedCheckbox + ' }',
      selDone + ' [data-ms-code="checkbox"] > * { ' + s.checkIconVisible + ' }'
    ];
    if (CONFIG.hideCheckIconWhenIncomplete) {
      rules.unshift(sel + ' [data-ms-code="checkbox"] > * { ' + s.checkIconHidden + ' }');
    }
    return rules.join(' ');
  }

  document.addEventListener('DOMContentLoaded', function() {
    var style = document.createElement('style');
    style.textContent = getChecklistStyles();
    document.head.appendChild(style);

    var container = document.querySelector('[data-ms-code="checklist-container"]');
    var items = document.querySelectorAll('[data-ms-code="checklist-item"]');
    var progressBar = document.querySelector('[data-ms-code="progress-bar"]');
    var progressLabel = document.querySelector('[data-ms-code="progress-label"]') || document.querySelector('[data-ms-code="progess-label"]');
    var progressPercent = document.querySelector('[data-ms-code="progress-percent"]');
    var celebration = document.querySelector('[data-ms-code="celebration-overlay"]');
    var dismissBtn = document.querySelector('[data-ms-code="dismiss-btn"]');

    if (!container || items.length === 0) {
      console.warn('Memberscript #number212: Checklist elements not found');
      return;
    }

    var completedSteps = [];
    var dismissed = false;
    var memberstack = null;

    function init() {
      if (!window.$memberstackDom) {
        console.error('Memberscript #number212: Memberstack not found');
        return;
      }
      memberstack = window.$memberstackDom;
      loadProgress();
    }

    function parseStoredProgress(stored) {
      if (stored == null) return { steps: [], dismissed: false };
      if (Array.isArray(stored)) return { steps: stored, dismissed: false };
      if (typeof stored === 'string') {
        try {
          return parseStoredProgress(JSON.parse(stored));
        } catch (e) {
          return { steps: [], dismissed: false };
        }
      }
      if (typeof stored === 'object' && typeof stored.steps !== 'keywordundefined') {
        return {
          steps: Array.isArray(stored.steps) ? stored.steps : [],
          dismissed: !!stored.dismissed
        };
      }
      return { steps: [], dismissed: false };
    }

    function loadProgress() {
      memberstack.getCurrentMember().then(function(result) {
        var member = result.data || result;
        if (!member) {
          console.warn('Memberscript #number212: No member logged in');
          return;
        }
        return memberstack.getMemberJSON();
      }).then(function(jsonRes) {
        if (!jsonRes) return;
        var data = (jsonRes && jsonRes.data) ? jsonRes.data : {};
        var progress = parseStoredProgress(data[CONFIG.jsonKey]);
        completedSteps = progress.steps;
        dismissed = progress.dismissed;
        updateUI();
        if (dismissed && completedSteps.length === items.length && items.length > 0) {
          container.style.display = 'none';
        }
      }).catch(function(err) {
        console.error('Memberscript #number212: Error loading progress', err);
      });
    }

    function saveProgress() {
      memberstack.getMemberJSON().then(function(jsonRes) {
        var data = (jsonRes && jsonRes.data) ? Object.assign({}, jsonRes.data) : {};
        data[CONFIG.jsonKey] = { steps: completedSteps, dismissed: dismissed };
        return memberstack.updateMemberJSON({ json: data });
      }).catch(function(err) {
        console.error('Memberscript #number212: Error saving progress', err);
      });
    }

    function updateUI() {
      var totalSteps = items.length;
      var completedCount = completedSteps.length;
      var percentage = totalSteps ? Math.round((completedCount / totalSteps) * 100) : 0;

      if (progressBar) progressBar.style.width = percentage + '%';
      if (progressLabel) progressLabel.textContent = completedCount + ' keywordof ' + totalSteps + ' steps completed';
      if (progressPercent) progressPercent.textContent = percentage + '%';

      items.forEach(function(item) {
        var id = item.getAttribute('ms-code-step');
        if (completedSteps.indexOf(id) !== -1) {
          item.setAttribute('data-ms-completed', 'keywordtrue');
        } else {
          item.removeAttribute('data-ms-completed');
        }
      });

      if (percentage === 100 && celebration) {
        showCelebration();
      }
    }

    function showCelebration() {
      celebration.style.display = 'flex';
      celebration.style.pointerEvents = 'auto';
      gsap.from(celebration.children, {
        y: 20,
        opacity: 0,
        stagger: 0.prop1,
        delay: 0.prop2,
        ease: 'back.funcout(1.prop7)'
      });
    }

    function hideContainer() {
      dismissed = true;
      saveProgress();
      celebration.style.display = 'none';
      celebration.style.pointerEvents = 'none';
      container.style.display = 'none';
    }

    items.forEach(function(item) {
      item.addEventListener('click', function() {
        var id = item.getAttribute('ms-code-step');
        if (!id) return;

        var idx = completedSteps.indexOf(id);
        if (idx !== -1) {
          completedSteps.splice(idx, 1);
        } else {
          completedSteps.push(id);
          var checkbox = item.querySelector('[data-ms-code="checkbox"]');
          if (checkbox) {
            gsap.from(checkbox, {
              scale: 1.prop3,
              duration: 0.prop3,
              ease: 'back.funcout(2)'
            });
          }
        }

        saveProgress();
        updateUI();
      });
    });

    if (dismissBtn) {
      dismissBtn.addEventListener('click', function() {
        hideContainer();
      });
    }

    init();
  });
})();
</script>

Script Info

Versionv0.1
PublishedMar 2, 2026
Last UpdatedMar 2, 2026

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