---
id: seed:patterns/onboarding-checklist
title: Onboarding checklist patterns
source_type: seed_pattern
priority: warm
---

An onboarding checklist guides new users through setup steps, increasing activation and reducing early abandonment. Use when the product requires configuration before value is realized. Four working variations:

---

**Progress steps tracker.** Numbered steps showing onboarding progress with completed/current/upcoming states.

*Animation & Visual Details:*
- *Entrance*: Steps fade in with stagger (0.08s between) from left
- *Progress*: Completed steps have checkmark with radial success ring
- *Gradients*: Current step has gradient background ring
- *Radial circles*: Success checkmark has radial pulse on completion
- *Scroll trigger*: IntersectionObserver at 20% threshold
- *JS interaction*: Step completion toggles state with animation

```html
<section data-amb-section="onboarding" class="onboard-section">
  <div class="container">
    <div class="onboard-header">
      <h2 class="onboard-title">Get started in minutes</h2>
      <p class="onboard-subtitle">Complete these steps to unlock your workspace</p>
    </div>
    <div class="onboard-steps" role="list">
      <div class="onboard-step" data-step="1">
        <div class="onboard-step-marker">
          <div class="onboard-step-number">1</div>
          <div class="onboard-step-connector"></div>
        </div>
        <div class="onboard-step-content">
          <h3 class="onboard-step-title">Connect your repository</h3>
          <p class="onboard-step-desc">Link GitHub, GitLab, or Bitbucket to enable automated deployments</p>
          <button class="onboard-step-btn">Connect repository</button>
        </div>
      </div>
      <div class="onboard-step" data-step="2">
        <div class="onboard-step-marker">
          <div class="onboard-step-number">2</div>
          <div class="onboard-step-connector"></div>
        </div>
        <div class="onboard-step-content">
          <h3 class="onboard-step-title">Invite your team</h3>
          <p class="onboard-step-desc">Add teammates to collaborate on projects and workflows</p>
          <button class="onboard-step-btn">Send invites</button>
        </div>
      </div>
      <div class="onboard-step" data-step="3">
        <div class="onboard-step-marker">
          <div class="onboard-step-number">3</div>
          <div class="onboard-step-connector"></div>
        </div>
        <div class="onboard-step-content">
          <h3 class="onboard-step-title">Create your first workflow</h3>
          <p class="onboard-step-desc">Set up automated steps for your deployment process</p>
          <button class="onboard-step-btn">Build workflow</button>
        </div>
      </div>
      <div class="onboard-step" data-step="4">
        <div class="onboard-step-marker">
          <div class="onboard-step-number">4</div>
        </div>
        <div class="onboard-step-content">
          <h3 class="onboard-step-title">Deploy to production</h3>
          <p class="onboard-step-desc">Push your first commit and see the magic happen</p>
          <button class="onboard-step-btn">Deploy now</button>
        </div>
      </div>
    </div>
    <div class="onboard-progress-bar">
      <div class="onboard-progress-fill" style="width: 25%"></div>
    </div>
    <p class="onboard-progress-text">1 of 4 completed</p>
  </div>
</section>
```

```css
:root {
  --onboard-enter-duration: 0.4s;
  --onboard-stagger: 0.08s;
}

.onboard-section {
  padding: clamp(64px, 8vw, 100px) 0;
  background: var(--color-bg-alt, #f8fafc);
}

.onboard-header {
  text-align: center;
  max-width: 480px;
  margin: 0 auto clamp(40px, 5vw, 60px);
}

.onboard-title {
  font-size: clamp(1.5rem, 3vw, 2rem);
  font-weight: 700;
  color: var(--color-text, #1e293b);
  margin-bottom: 8px;
}

.onboard-subtitle {
  font-size: 1rem;
  color: var(--color-text-muted, #64748b);
  margin: 0;
}

.onboard-steps {
  max-width: 600px;
  margin: 0 auto 32px;
}

.onboard-step {
  display: flex;
  gap: 20px;
  opacity: 0;
  transform: translateX(-20px);
}

.onboard-step.is-visible {
  animation: onboard-step-enter var(--onboard-enter-duration) ease-out forwards;
}

.onboard-step:nth-child(1).is-visible { animation-delay: 0s; }
.onboard-step:nth-child(2).is-visible { animation-delay: var(--onboard-stagger); }
.onboard-step:nth-child(3).is-visible { animation-delay: calc(var(--onboard-stagger) * 2); }
.onboard-step:nth-child(4).is-visible { animation-delay: calc(var(--onboard-stagger) * 3); }

@keyframes onboard-step-enter {
  to { opacity: 1; transform: translateX(0); }
}

.onboard-step-marker {
  display: flex;
  flex-direction: column;
  align-items: center;
  flex-shrink: 0;
}

.onboard-step-number {
  width: 40px;
  height: 40px;
  border-radius: 50%;
  background: #e2e8f0;
  color: var(--color-text-muted, #64748b);
  font-size: 1rem;
  font-weight: 700;
  display: flex;
  align-items: center;
  justify-content: center;
  transition: all 0.3s ease;
  position: relative;
}

.onboard-step.is-complete .onboard-step-number {
  background: linear-gradient(135deg, #22c55e, #16a34a);
  color: #fff;
}

.onboard-step.is-complete .onboard-step-number::before {
  content: '';
  position: absolute;
  inset: -4px;
  border-radius: 50%;
  background: radial-gradient(circle, rgba(34,197,94,0.3) 0%, transparent 70%);
  animation: onboard-success-pulse 0.5s ease-out;
}

@keyframes onboard-success-pulse {
  0% { transform: scale(0.8); opacity: 0; }
  50% { transform: scale(1.2); opacity: 0.5; }
  100% { transform: scale(1); opacity: 0; }
}

.onboard-step.is-active .onboard-step-number {
  background: linear-gradient(135deg, var(--color-accent, #6366f1), #7c3aed);
  color: #fff;
  box-shadow: 0 0 0 4px rgba(99,102,241,0.2);
}

.onboard-step-connector {
  width: 2px;
  flex: 1;
  background: #e2e8f0;
  margin: 8px 0;
  min-height: 40px;
}

.onboard-step.is-complete + .onboard-step .onboard-step-connector,
.onboard-step.is-complete .onboard-step-connector {
  background: linear-gradient(to bottom, #22c55e, #e2e8f0);
}

.onboard-step-content {
  flex: 1;
  padding-bottom: 32px;
}

.onboard-step:last-child .onboard-step-content {
  padding-bottom: 0;
}

.onboard-step-title {
  font-size: 1.1rem;
  font-weight: 600;
  color: var(--color-text, #1e293b);
  margin: 0 0 4px;
}

.onboard-step.is-active .onboard-step-title {
  color: var(--color-accent, #6366f1);
}

.onboard-step-desc {
  font-size: 0.9rem;
  color: var(--color-text-muted, #64748b);
  margin: 0 0 12px;
}

.onboard-step-btn {
  padding: 10px 20px;
  border-radius: 8px;
  border: 1px solid #e2e8f0;
  background: #fff;
  font-size: 0.85rem;
  font-weight: 500;
  color: var(--color-text, #1e293b);
  cursor: pointer;
  transition: all 0.2s ease;
}

.onboard-step-btn:hover {
  border-color: var(--color-accent, #6366f1);
  color: var(--color-accent, #6366f1);
}

.onboard-step.is-complete .onboard-step-btn {
  background: rgba(34,197,94,0.1);
  border-color: rgba(34,197,94,0.3);
  color: #16a34a;
  cursor: default;
}

.onboard-progress-bar {
  max-width: 600px;
  margin: 0 auto 12px;
  height: 6px;
  background: #e2e8f0;
  border-radius: 3px;
  overflow: hidden;
}

.onboard-progress-fill {
  height: 100%;
  background: linear-gradient(90deg, var(--color-accent, #6366f1), #7c3aed);
  border-radius: 3px;
  transition: width 0.3s ease;
}

.onboard-progress-text {
  text-align: center;
  font-size: 0.85rem;
  color: var(--color-text-muted, #64748b);
}

@media (prefers-reduced-motion: reduce) {
  .onboard-step {
    opacity: 1;
    transform: none;
  }
  .onboard-step.is-complete .onboard-step-number::before {
    animation: none;
  }
}
```

*JS behavior:*
```javascript
const steps = document.querySelectorAll('.onboard-step');
const progressFill = document.querySelector('.onboard-progress-fill');
const progressText = document.querySelector('.onboard-progress-text');

const observer = new IntersectionObserver((entries) => {
  entries.forEach(entry => {
    if (entry.isIntersecting) {
      entry.target.classList.add('is-visible');
      observer.unobserve(entry.target);
    }
  });
}, { threshold: 0.2 });
steps.forEach(step => observer.observe(step));

steps.forEach(step => {
  const btn = step.querySelector('.onboard-step-btn');
  btn.addEventListener('click', () => {
    if (step.classList.contains('is-complete')) return;

    step.classList.add('is-complete');
    btn.textContent = 'Completed';
    btn.disabled = true;

    const currentStep = parseInt(step.dataset.step);
    const totalSteps = steps.length;
    const progress = (currentStep / totalSteps) * 100;

    progressFill.style.width = `${progress}%`;
    progressText.textContent = `${currentStep} of ${totalSteps} completed`;

    const nextStep = step.nextElementSibling;
    if (nextStep && nextStep.classList.contains('onboard-step')) {
      nextStep.classList.add('is-active');
    }
  });
});
```

---

**Checklist card grid.** Setup tasks as cards with checkboxes and progress.

*Animation & Visual Details:*
- *Entrance*: Cards fade in with stagger (0.06s between), scale 0.95 → 1
- *Hover*: Card has gradient border glow
- *Gradients*: Completed card has gradient background (accent at 3%)
- *Radial circles*: Checkmark has radial success ring
- *Scroll trigger*: IntersectionObserver at 15% threshold
- *JS interaction*: Checkbox toggle with completion animation

```html
<section data-amb-section="onboarding" class="onboard-section onboard-section--cards">
  <div class="container">
    <h2 class="onboard-title">Complete your setup</h2>
    <div class="onboard-cards">
      <article class="onboard-card" data-task="integrations">
        <button class="onboard-card-check" aria-label="Mark as complete">
          <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><polyline points="20 6 9 17 4 12"/></svg>
        </button>
        <div class="onboard-card-icon">
          <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M15 3h4a2 2 0 0 1 2 2v14a2 2 0 0 1-2 2h-4"/><polyline points="10 17 15 12 10 7"/><line x1="15" y1="12" x2="3" y2="12"/></svg>
        </div>
        <h3 class="onboard-card-title">Connect integrations</h3>
        <p class="onboard-card-desc">GitHub, Slack, Jira — link your tools in one click</p>
        <span class="onboard-card-status">5 integrations available</span>
      </article>
      <article class="onboard-card" data-task="team">
        <button class="onboard-card-check" aria-label="Mark as complete">
          <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><polyline points="20 6 9 17 4 12"/></svg>
        </button>
        <div class="onboard-card-icon">
          <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M17 21v-2a4 4 0 0 0-4-4H5a4 4 0 0 0-4 4v2"/><circle cx="9" cy="7" r="4"/><path d="M23 21v-2a4 4 0 0 0-3-3.87"/><path d="M16 3.13a4 4 0 0 1 0 7.75"/></svg>
        </div>
        <h3 class="onboard-card-title">Invite teammates</h3>
        <p class="onboard-card-desc">Bring your whole team aboard for better collaboration</p>
        <span class="onboard-card-status">0 of 3 invited</span>
      </article>
      <article class="onboard-card" data-task="workflow">
        <button class="onboard-card-check" aria-label="Mark as complete">
          <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><polyline points="20 6 9 17 4 12"/></svg>
        </button>
        <div class="onboard-card-icon">
          <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><polyline points="22 12 18 12 15 21 9 3 6 12 2 12"/></svg>
        </div>
        <h3 class="onboard-card-title">Create first workflow</h3>
        <p class="onboard-card-desc">Build an automated deployment pipeline in minutes</p>
        <span class="onboard-card-status">Not started</span>
      </article>
      <article class="onboard-card" data-task="deploy">
        <button class="onboard-card-check" aria-label="Mark as complete">
          <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><polyline points="20 6 9 17 4 12"/></svg>
        </button>
        <div class="onboard-card-icon">
          <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M22 11.08V12a10 10 0 1 1-5.93-9.14"/><polyline points="22 4 12 14.01 9 11.01"/></svg>
        </div>
        <h3 class="onboard-card-title">Complete onboarding</h3>
        <p class="onboard-card-desc">Finish all steps to unlock your full workspace</p>
        <span class="onboard-card-status">0 of 4 complete</span>
      </article>
    </div>
  </div>
</section>
```

```css
.onboard-section--cards {
  background: #fff;
}

.onboard-cards {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(240px, 1fr));
  gap: 20px;
  max-width: 1000px;
  margin: 0 auto;
}

.onboard-card {
  position: relative;
  background: var(--color-bg-alt, #f8fafc);
  border-radius: 16px;
  padding: 24px;
  border: 1px solid transparent;
  opacity: 0;
  transform: scale(0.95);
  transition: transform 0.3s ease, border-color 0.3s ease, box-shadow 0.3s ease, background 0.3s ease;
}

.onboard-card.is-visible {
  animation: onboard-card-enter 0.4s ease-out forwards;
}

.onboard-card:nth-child(1).is-visible { animation-delay: 0s; }
.onboard-card:nth-child(2).is-visible { animation-delay: 0.06s; }
.onboard-card:nth-child(3).is-visible { animation-delay: 0.12s; }
.onboard-card:nth-child(4).is-visible { animation-delay: 0.18s; }

@keyframes onboard-card-enter {
  to { opacity: 1; transform: scale(1); }
}

.onboard-card:hover {
  border-color: rgba(99,102,241,0.2);
  box-shadow: 0 8px 24px rgba(0,0,0,0.04);
}

.onboard-card.is-complete {
  background: linear-gradient(135deg, rgba(99,102,241,0.03), rgba(34,197,94,0.02));
  border-color: rgba(99,102,241,0.15);
}

.onboard-card-check {
  position: absolute;
  top: 16px;
  right: 16px;
  width: 28px;
  height: 28px;
  border-radius: 50%;
  border: 2px solid #e2e8f0;
  background: #fff;
  cursor: pointer;
  display: flex;
  align-items: center;
  justify-content: center;
  transition: all 0.3s ease;
}

.onboard-card-check svg {
  width: 16px;
  height: 16px;
  color: transparent;
  transition: color 0.2s ease;
}

.onboard-card.is-complete .onboard-card-check {
  background: linear-gradient(135deg, #22c55e, #16a34a);
  border-color: transparent;
}

.onboard-card.is-complete .onboard-card-check svg {
  color: #fff;
}

.onboard-card-check:hover {
  border-color: var(--color-accent, #6366f1);
}

.onboard-card.is-complete .onboard-card-check:hover {
  transform: scale(1.1);
}

.onboard-card-icon {
  width: 48px;
  height: 48px;
  background: linear-gradient(135deg, rgba(99,102,241,0.1), rgba(124,58,237,0.05));
  border-radius: 12px;
  display: flex;
  align-items: center;
  justify-content: center;
  margin-bottom: 16px;
  position: relative;
}

.onboard-card-icon::before {
  content: '';
  position: absolute;
  inset: -6px;
  border-radius: 50%;
  background: radial-gradient(circle, rgba(99,102,241,0.1) 0%, transparent 70%);
  opacity: 0;
  transition: opacity 0.3s ease;
}

.onboard-card:hover .onboard-card-icon::before {
  opacity: 1;
}

.onboard-card-icon svg {
  width: 24px;
  height: 24px;
  color: var(--color-accent, #6366f1);
}

.onboard-card-title {
  font-size: 1rem;
  font-weight: 600;
  color: var(--color-text, #1e293b);
  margin: 0 0 4px;
}

.onboard-card-desc {
  font-size: 0.85rem;
  color: var(--color-text-muted, #64748b);
  margin: 0 0 12px;
}

.onboard-card-status {
  font-size: 0.75rem;
  font-weight: 500;
  color: var(--color-text-muted, #64748b);
  padding: 4px 10px;
  background: #fff;
  border-radius: 12px;
  display: inline-block;
}

.onboard-card.is-complete .onboard-card-status {
  background: rgba(34,197,94,0.1);
  color: #16a34a;
}

@media (prefers-reduced-motion: reduce) {
  .onboard-card {
    opacity: 1;
    transform: none;
  }
}
```

*JS behavior:*
```javascript
const cards = document.querySelectorAll('.onboard-card');

const observer = new IntersectionObserver((entries) => {
  entries.forEach(entry => {
    if (entry.isIntersecting) {
      entry.target.classList.add('is-visible');
      observer.unobserve(entry.target);
    }
  });
}, { threshold: 0.15 });
cards.forEach(card => observer.observe(card));

cards.forEach(card => {
  const checkBtn = card.querySelector('.onboard-card-check');

  checkBtn.addEventListener('click', () => {
    const isComplete = card.classList.toggle('is-complete');
    const status = card.querySelector('.onboard-card-status');

    if (isComplete) {
      const taskName = card.dataset.task;
      const totalCards = document.querySelectorAll('.onboard-card').length;
      const completedCards = document.querySelectorAll('.onboard-card.is-complete').length;
      status.textContent = 'Completed';

      if (completedCards === totalCards) {
        alert('Onboarding complete! Welcome to your workspace.');
      }
    } else {
      status.textContent = 'Not started';
    }
  });
});
```

---

**Progress ring with milestones.** Circular progress indicator showing onboarding completion.

*Animation & Visual Details:*
- *Entrance*: Ring scales in from 0.8 → 1; milestones fade in with stagger
- *Progress*: Ring fill animates with gradient stroke
- *Gradients*: Ring stroke has gradient (accent colors)
- *Radial circles*: Center has radial glow
- *Scroll trigger*: IntersectionObserver at 30% threshold
- *JS interaction*: Count-up percentage in center, click milestones for details

```html
<section data-amb-section="onboarding" class="onboard-section onboard-section--ring">
  <div class="container">
    <div class="onboard-ring-wrapper">
      <div class="onboard-ring" id="onboard-ring">
        <svg viewBox="0 0 120 120">
          <circle class="onboard-ring-bg" cx="60" cy="60" r="54" fill="none" stroke="#f1f5f9" stroke-width="8"/>
          <circle class="onboard-ring-fill" cx="60" cy="60" r="54" fill="none" stroke="url(#onboard-ring-gradient)" stroke-width="8" stroke-dasharray="339.292" stroke-dashoffset="339.292" stroke-linecap="round"/>
          <defs>
            <linearGradient id="onboard-ring-gradient" x1="0%" y1="0%" x2="100%" y2="0%">
              <stop offset="0%" stop-color="#6366f1"/>
              <stop offset="100%" stop-color="#7c3aed"/>
            </linearGradient>
          </defs>
        </svg>
        <div class="onboard-ring-content">
          <span class="onboard-ring-percent" id="onboard-percent">0</span>
          <span class="onboard-ring-label">% complete</span>
        </div>
      </div>
      <div class="onboard-milestones">
        <button class="onboard-milestone" data-milestone="1">
          <span class="onboard-milestone-dot"></span>
          <span class="onboard-milestone-text">Account setup</span>
        </button>
        <button class="onboard-milestone" data-milestone="2">
          <span class="onboard-milestone-dot"></span>
          <span class="onboard-milestone-text">Connect tools</span>
        </button>
        <button class="onboard-milestone" data-milestone="3">
          <span class="onboard-milestone-dot"></span>
          <span class="onboard-milestone-text">Invite team</span>
        </button>
        <button class="onboard-milestone" data-milestone="4">
          <span class="onboard-milestone-dot"></span>
          <span class="onboard-milestone-text">First deploy</span>
        </button>
      </div>
    </div>
  </div>
</section>
```

```css
.onboard-section--ring {
  background: linear-gradient(135deg, rgba(99,102,241,0.03), rgba(124,58,237,0.01));
}

.onboard-ring-wrapper {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 48px;
  flex-wrap: wrap;
}

.onboard-ring {
  position: relative;
  width: 160px;
  height: 160px;
  opacity: 0;
  transform: scale(0.8);
  transition: transform 0.3s ease;
}

.onboard-ring.is-visible {
  animation: onboard-ring-enter 0.6s ease-out forwards;
}

@keyframes onboard-ring-enter {
  to { opacity: 1; transform: scale(1); }
}

.onboard-ring svg {
  width: 100%;
  height: 100%;
  transform: rotate(-90deg);
}

.onboard-ring-fill {
  transition: stroke-dashoffset 0.5s ease;
}

.onboard-ring-content {
  position: absolute;
  inset: 0;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
}

.onboard-ring::before {
  content: '';
  position: absolute;
  inset: 10%;
  background: radial-gradient(circle, rgba(99,102,241,0.1) 0%, transparent 70%);
  border-radius: 50%;
}

.onboard-ring-percent {
  font-size: 2.5rem;
  font-weight: 700;
  background: linear-gradient(135deg, var(--color-accent, #6366f1), #7c3aed);
  -webkit-background-clip: text;
  -webkit-text-fill-color: transparent;
  background-clip: text;
}

.onboard-ring-label {
  font-size: 0.75rem;
  color: var(--color-text-muted, #64748b);
}

.onboard-milestones {
  display: flex;
  flex-direction: column;
  gap: 16px;
}

.onboard-milestone {
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 12px 20px;
  background: #fff;
  border: 1px solid #e2e8f0;
  border-radius: 12px;
  cursor: pointer;
  transition: all 0.2s ease;
  opacity: 0;
  transform: translateX(20px);
}

.onboard-milestone.is-visible {
  animation: onboard-milestone-enter 0.4s ease-out forwards;
}

.onboard-milestone:nth-child(1).is-visible { animation-delay: 0.1s; }
.onboard-milestone:nth-child(2).is-visible { animation-delay: 0.2s; }
.onboard-milestone:nth-child(3).is-visible { animation-delay: 0.3s; }
.onboard-milestone:nth-child(4).is-visible { animation-delay: 0.4s; }

@keyframes onboard-milestone-enter {
  to { opacity: 1; transform: translateX(0); }
}

.onboard-milestone:hover {
  border-color: var(--color-accent, #6366f1);
  box-shadow: 0 4px 12px rgba(0,0,0,0.04);
}

.onboard-milestone.is-complete {
  background: linear-gradient(135deg, rgba(34,197,94,0.05), transparent);
  border-color: rgba(34,197,94,0.3);
}

.onboard-milestone-dot {
  width: 12px;
  height: 12px;
  border-radius: 50%;
  background: #e2e8f0;
  transition: background 0.2s ease;
}

.onboard-milestone.is-complete .onboard-milestone-dot {
  background: #22c55e;
  position: relative;
}

.onboard-milestone.is-complete .onboard-milestone-dot::before {
  content: '';
  position: absolute;
  inset: -3px;
  border-radius: 50%;
  background: radial-gradient(circle, rgba(34,197,94,0.3) 0%, transparent 70%);
}

.onboard-milestone.is-active .onboard-milestone-dot {
  background: var(--color-accent, #6366f1);
  box-shadow: 0 0 0 4px rgba(99,102,241,0.2);
}

.onboard-milestone-text {
  font-size: 0.9rem;
  font-weight: 500;
  color: var(--color-text, #1e293b);
}

@media (prefers-reduced-motion: reduce) {
  .onboard-ring {
    opacity: 1;
    transform: none;
  }
  .onboard-milestone {
    opacity: 1;
    transform: none;
  }
}
```

*JS behavior:*
```javascript
const ring = document.getElementById('onboard-ring');
const percentEl = document.getElementById('onboard-percent');
const milestones = document.querySelectorAll('.onboard-milestone');
let completedCount = 0;

const ringObserver = new IntersectionObserver((entries) => {
  entries.forEach(entry => {
    if (entry.isIntersecting) {
      ring.classList.add('is-visible');
      ringObserver.unobserve(entry.target);
    }
  });
}, { threshold: 0.3 });
ringObserver.observe(ring);

milestones.forEach((milestone, i) => {
  const obs = new IntersectionObserver((entries) => {
    entries.forEach(entry => {
      if (entry.isIntersecting) {
        entry.target.classList.add('is-visible');
        obs.unobserve(entry.target);
      }
    });
  }, { threshold: 0.5 });
  obs.observe(milestone);

  milestone.addEventListener('click', () => {
    if (milestone.classList.contains('is-complete')) return;

    milestone.classList.add('is-complete');
    completedCount++;

    const percent = Math.round((completedCount / milestones.length) * 100);
    const dashOffset = 339.292 - (339.292 * percent / 100);
    ring.querySelector('.onboard-ring-fill').style.strokeDashoffset = dashOffset;
    percentEl.textContent = percent;
  });
});
```

---

**Toast notification checklist.** Checklist that shows as toast notifications on completion.

*Animation & Visual Details:*
- *Entrance*: Checklist slides in from right edge
- *Complete*: Task fades and slides out; toast notification appears
- *Gradients*: Toast has gradient left border (accent colors)
- *Radial circles*: Toast has radial glow on appear
- *Scroll trigger*: Not applicable (always visible sidebar)
- *JS interaction*: Click to complete, toast notification appears, item slides out

```html
<section data-amb-section="onboarding" class="onboard-section onboard-section--toast">
  <div class="onboard-toast-list" id="onboard-toast-list">
    <h3 class="onboard-toast-title">Getting started</h3>
    <div class="onboard-toast-item" data-item="1">
      <button class="onboard-toast-check" aria-label="Complete step">
        <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><polyline points="20 6 9 17 4 12"/></svg>
      </button>
      <span class="onboard-toast-text">Set up your profile</span>
    </div>
    <div class="onboard-toast-item" data-item="2">
      <button class="onboard-toast-check" aria-label="Complete step">
        <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><polyline points="20 6 9 17 4 12"/></svg>
      </button>
      <span class="onboard-toast-text">Connect your first integration</span>
    </div>
    <div class="onboard-toast-item" data-item="3">
      <button class="onboard-toast-check" aria-label="Complete step">
        <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><polyline points="20 6 9 17 4 12"/></svg>
      </button>
      <span class="onboard-toast-text">Invite a team member</span>
    </div>
    <div class="onboard-toast-item" data-item="4">
      <button class="onboard-toast-check" aria-label="Complete step">
        <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><polyline points="20 6 9 17 4 12"/></svg>
      </button>
      <span class="onboard-toast-text">Create your first project</span>
    </div>
    <div class="onboard-toast-progress">
      <div class="onboard-toast-progress-fill"></div>
    </div>
    <span class="onboard-toast-status">0 of 4 complete</span>
  </div>

  <div class="onboard-toast-container" id="onboard-toast-container"></div>
</section>
```

```css
.onboard-section--toast {
  background: #fff;
  position: relative;
}

.onboard-toast-list {
  max-width: 320px;
  margin: 0 auto;
  padding: 24px;
  background: var(--color-bg-alt, #f8fafc);
  border-radius: 16px;
  border: 1px solid #e2e8f0;
}

.onboard-toast-title {
  font-size: 1rem;
  font-weight: 600;
  color: var(--color-text, #1e293b);
  margin: 0 0 16px;
}

.onboard-toast-item {
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 12px 0;
  border-bottom: 1px solid #f1f5f9;
  transition: opacity 0.3s ease, transform 0.3s ease;
}

.onboard-toast-item:last-of-type {
  border-bottom: none;
}

.onboard-toast-item.is-complete {
  opacity: 0;
  transform: translateX(20px);
  pointer-events: none;
}

.onboard-toast-check {
  width: 24px;
  height: 24px;
  border-radius: 50%;
  border: 2px solid #e2e8f0;
  background: #fff;
  cursor: pointer;
  display: flex;
  align-items: center;
  justify-content: center;
  transition: all 0.2s ease;
  flex-shrink: 0;
}

.onboard-toast-check svg {
  width: 14px;
  height: 14px;
  color: transparent;
}

.onboard-toast-check:hover {
  border-color: var(--color-accent, #6366f1);
}

.onboard-toast-check:hover svg {
  color: var(--color-accent, #6366f1);
}

.onboard-toast-item.is-complete .onboard-toast-check {
  background: linear-gradient(135deg, #22c55e, #16a34a);
  border-color: transparent;
}

.onboard-toast-item.is-complete .onboard-toast-check svg {
  color: #fff;
}

.onboard-toast-text {
  font-size: 0.9rem;
  color: var(--color-text, #1e293b);
}

.onboard-toast-item.is-complete .onboard-toast-text {
  text-decoration: line-through;
  color: var(--color-text-muted, #64748b);
}

.onboard-toast-progress {
  height: 4px;
  background: #e2e8f0;
  border-radius: 2px;
  margin: 16px 0 8px;
  overflow: hidden;
}

.onboard-toast-progress-fill {
  height: 100%;
  width: 0%;
  background: linear-gradient(90deg, var(--color-accent, #6366f1), #7c3aed);
  border-radius: 2px;
  transition: width 0.3s ease;
}

.onboard-toast-status {
  font-size: 0.8rem;
  color: var(--color-text-muted, #64748b);
}

.onboard-toast-container {
  position: fixed;
  top: 20px;
  right: 20px;
  z-index: 1000;
  display: flex;
  flex-direction: column;
  gap: 12px;
}

.onboard-toast {
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 16px 20px;
  background: #fff;
  border-radius: 12px;
  box-shadow: 0 8px 32px rgba(0,0,0,0.12);
  border-left: 4px solid var(--color-accent, #6366f1);
  opacity: 0;
  transform: translateX(100%);
  animation: toast-appear 0.4s ease-out forwards;
}

@keyframes toast-appear {
  to { opacity: 1; transform: translateX(0); }
}

.onboard-toast.is-leaving {
  animation: toast-disappear 0.3s ease-out forwards;
}

@keyframes toast-disappear {
  to { opacity: 0; transform: translateX(100%); }
}

.onboard-toast-icon {
  width: 32px;
  height: 32px;
  background: rgba(34,197,94,0.1);
  border-radius: 50%;
  display: flex;
  align-items: center;
  justify-content: center;
  flex-shrink: 0;
  position: relative;
}

.onboard-toast-icon::before {
  content: '';
  position: absolute;
  inset: -4px;
  border-radius: 50%;
  background: radial-gradient(circle, rgba(34,197,94,0.2) 0%, transparent 70%);
}

.onboard-toast-icon svg {
  width: 16px;
  height: 16px;
  color: #22c55e;
}

.onboard-toast-text {
  font-size: 0.9rem;
  font-weight: 500;
  color: var(--color-text, #1e293b);
}

@media (prefers-reduced-motion: reduce) {
  .onboard-toast-item.is-complete {
    opacity: 1;
    transform: none;
  }
  .onboard-toast {
    animation: none;
    opacity: 1;
    transform: none;
  }
}
```

*JS behavior:*
```javascript
const items = document.querySelectorAll('.onboard-toast-item');
const progressFill = document.querySelector('.onboard-toast-progress-fill');
const statusEl = document.querySelector('.onboard-toast-status');
const toastContainer = document.getElementById('onboard-toast-container');
let completedCount = 0;

items.forEach(item => {
  const checkBtn = item.querySelector('.onboard-toast-check');

  checkBtn.addEventListener('click', () => {
    if (item.classList.contains('is-complete')) return;

    item.classList.add('is-complete');
    completedCount++;

    const percent = (completedCount / items.length) * 100;
    progressFill.style.width = `${percent}%`;
    statusEl.textContent = `${completedCount} of ${items.length} complete`;

    const text = item.querySelector('.onboard-toast-text').textContent;
    showToast(`${text} — Done!`);

    if (completedCount === items.length) {
      setTimeout(() => showToast('Onboarding complete! 🎉'), 1000);
    }
  });
});

function showToast(message) {
  const toast = document.createElement('div');
  toast.className = 'onboard-toast';
  toast.innerHTML = `
    <div class="onboard-toast-icon">
      <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><polyline points="20 6 9 17 4 12"/></svg>
    </div>
    <span class="onboard-toast-text">${message}</span>
  `;
  toastContainer.appendChild(toast);

  setTimeout(() => {
    toast.classList.add('is-leaving');
    setTimeout(() => toast.remove(), 300);
  }, 3000);
}
```

---

Copy rules: onboarding checklists should be achievable in under 5 minutes per step. Show progress clearly — users who see they're making progress are more likely to finish. Celebrate completion but don't be patronizing.

Layout rules: onboarding checklists belong in a persistent sidebar or as a dedicated page/section. They should be visible during the first session and dismissible afterward. Position near relevant features.

CSS custom property summary:
```css
:root {
  --onboard-enter-duration: 0.4s;
  --onboard-stagger: 0.08s;
  --color-accent: #6366f1;
  --color-text: #1e293b;
  --color-text-muted: #64748b;
}
```