<< All versions
Skill v1.0.1
currentAutomated scan100/100alinaqi/claude-bootstrap/user-journeys
1 files
──Details
PublishedMay 14, 2026 at 09:48 PM
Content Hashsha256:392d39e23725237f...
Git SHAad6d7161b953
Bump Typepatch
──Files
Files (1 file, 16.6 KB)
SKILL.md16.6 KBactive
SKILL.md · 638 lines · 16.6 KB
version: "1.0.1" name: user-journeys description: User experience flows - journey mapping, UX validation, error recovery when-to-use: When mapping user flows, validating UX, or designing error recovery user-invocable: false effort: medium
User Journeys Skill
For defining and testing real user experiences - not just specs, but actual flows humans take through your application.
Philosophy
Specs test features. Journeys test experiences.
A feature can pass all specs but still deliver a terrible experience. User journeys capture:
- How users actually navigate (not how we think they should)
- Emotional states at each step (frustrated, confused, delighted)
- Recovery from mistakes (users will make them)
- Real-world conditions (slow networks, interruptions, distractions)
Journey Documentation Structure
_project_specs/├── journeys/│ ├── _template.md # Journey template│ ├── critical/ # Must-work journeys (revenue, core value)│ │ ├── signup-to-first-value.md│ │ ├── checkout-purchase.md│ │ └── login-to-dashboard.md│ ├── common/ # Frequent user paths│ │ ├── browse-and-search.md│ │ ├── update-profile.md│ │ └── invite-team-member.md│ └── edge-cases/ # Error recovery, unusual paths│ ├── payment-failure-retry.md│ ├── session-timeout-recovery.md│ └── offline-reconnection.md
Journey Template
markdown
# Journey: [Name]## Overview| Attribute | Value ||-----------|-------|| **Priority** | Critical / High / Medium || **User Type** | New / Returning / Admin || **Frequency** | Daily / Weekly / One-time || **Success Metric** | Conversion rate, time to complete, drop-off rate |## User GoalWhat is the user trying to accomplish? Write from their perspective.> "I want to [goal] so that I can [benefit]."## Preconditions-User state (logged in, has subscription, first visit)-Data state (has items in cart, has team members)-Environment (mobile, desktop, slow connection)## Journey Steps### Step 1: [Entry Point]**User Action:** What the user does**System Response:** What they should see/experience**Success Criteria:**-[ ] Page loads in < 2 seconds-[ ] Primary CTA is immediately visible-[ ] User understands what to do next**Potential Friction:**-Slow load time → Show skeleton/loader-Unclear CTA → A/B test copy variations---### Step 2: [Next Action]**User Action:** ...**System Response:** ...**Success Criteria:**-[ ] ...**Potential Friction:**-...---## Error Scenarios### E1: [Error Name]**Trigger:** What causes this error**User Sees:** Error message/state**Recovery Path:** How user gets back on track**Test:** How to verify recovery works## Metrics to Track-Time to complete journey-Drop-off rate at each step-Error rate and recovery rate-User satisfaction (if surveyed)## E2E Test ReferenceLink to Playwright test: `e2e/tests/journeys/[name].spec.ts`
Critical Journey Examples
Signup to First Value
markdown
# Journey: Signup to First Value## Overview| Attribute | Value ||-----------|-------|| **Priority** | Critical || **User Type** | New || **Frequency** | One-time || **Success Metric** | % reaching "aha moment" within 5 min |## User Goal> "I want to try this product quickly to see if it solves my problem."## Preconditions-First visit to site-No account-Came from landing page or ad## Journey Steps### Step 1: Landing Page**User Action:** Clicks "Get Started Free" or "Try Now"**System Response:** Signup form appears (modal or new page)**Success Criteria:**-[ ] CTA visible above fold-[ ] No distracting elements-[ ] Clear value proposition visible**Potential Friction:**-Too many form fields → Reduce to email + password only-Social login missing → Add Google/GitHub options### Step 2: Account Creation**User Action:** Enters email and password (or uses social login)**System Response:**-Creates account-Sends verification email (don't block on it)-Redirects to onboarding**Success Criteria:**-[ ] Account created in < 3 seconds-[ ] No email verification wall (verify later)-[ ] Clear next step shown**Potential Friction:**-Email already exists → Offer login link-Weak password → Show requirements inline, not after submit### Step 3: Onboarding (Quick Win)**User Action:** Completes 1-2 setup questions**System Response:**-Personalizes experience-Shows progress indicator-Leads to first action**Success Criteria:**-[ ] Max 3 questions-[ ] Skip option available-[ ] < 60 seconds total**Potential Friction:**-Too many questions → User abandons-No skip option → User feels trapped### Step 4: First Value (Aha Moment)**User Action:** Completes core action (creates first X, sees first result)**System Response:**-Celebrates success-Shows value delivered-Suggests next step**Success Criteria:**-[ ] User experiences core value-[ ] Completion feels rewarding-[ ] Clear path to continue## Error Scenarios### E1: Email Already Registered**Trigger:** User tries existing email**User Sees:** "Already have an account? Log in or reset password"**Recovery Path:** Click to login or reset**Test:** `signup-existing-email.spec.ts`### E2: Social Login Fails**Trigger:** OAuth provider error**User Sees:** "Couldn't connect. Try email signup or try again."**Recovery Path:** Email signup form shown as fallback**Test:** `social-login-failure.spec.ts`## Metrics to Track-Signup → First Value: Target < 5 min-Drop-off at each step-Social vs email signup ratio-Skip rate on onboarding
Checkout Purchase
markdown
# Journey: Checkout Purchase## Overview| Attribute | Value ||-----------|-------|| **Priority** | Critical (Revenue) || **User Type** | Any || **Frequency** | Variable || **Success Metric** | Checkout completion rate |## User Goal> "I want to pay quickly and securely without surprises."## Journey Steps### Step 1: Cart Review**User Action:** Views cart before checkout**System Response:**-Shows all items with images, prices-Shows subtotal, taxes, shipping-Clear "Checkout" CTA**Success Criteria:**-[ ] No hidden fees revealed later-[ ] Easy to modify quantities-[ ] Saved items visible### Step 2: Checkout Start**User Action:** Clicks "Checkout"**System Response:**-Shows checkout form or redirect to payment-Progress indicator (Step 1 of 3)-Order summary sidebar**Success Criteria:**-[ ] Guest checkout option-[ ] Express checkout (Apple/Google Pay) prominent-[ ] Form fields pre-filled if logged in### Step 3: Payment**User Action:** Enters payment info**System Response:**-Secure input fields (Stripe/payment provider)-Real-time validation-Clear "Pay $XX" button**Success Criteria:**-[ ] Card validation inline, not after submit-[ ] Multiple payment options-[ ] Security indicators visible### Step 4: Confirmation**User Action:** Submits payment**System Response:**-Processing indicator-Success page with order details-Email confirmation sent**Success Criteria:**-[ ] Confirmation within 5 seconds-[ ] Order number clearly visible-[ ] Next steps clear (shipping, access, etc.)## Error Scenarios### E1: Payment Declined**Trigger:** Card declined by processor**User Sees:** "Payment declined. Please try another card."**Recovery Path:**-Stay on payment step-Pre-fill other fields-Offer alternative payment methods**Test:** `payment-declined-recovery.spec.ts`### E2: Session Timeout During Checkout**Trigger:** User away too long**User Sees:** Cart preserved, re-auth required**Recovery Path:**-Quick login-Return to same checkout step-Cart contents intact**Test:** `checkout-session-timeout.spec.ts`
Journey Testing with Playwright
Journey Test Structure
typescript
// e2e/tests/journeys/signup-to-value.spec.tsimport { test, expect } from '@playwright/test';test.describe('Journey: Signup to First Value', () => {test.describe.configure({ mode: 'serial' }); // Run in ordertest('Step 1: Landing page has clear CTA', async ({ page }) => {await page.goto('/');// CTA visible above fold without scrollingconst cta = page.getByRole('button', { name: /get started|try free/i });await expect(cta).toBeVisible();await expect(cta).toBeInViewport();});test('Step 2: Can create account quickly', async ({ page }) => {await page.goto('/');await page.getByRole('button', { name: /get started/i }).click();// Minimal fieldsawait expect(page.getByLabel('Email')).toBeVisible();await expect(page.getByLabel('Password')).toBeVisible();// Complete signupconst startTime = Date.now();await page.getByLabel('Email').fill('newuser@example.com');await page.getByLabel('Password').fill('SecurePass123!');await page.getByRole('button', { name: /sign up|create/i }).click();// Should reach onboarding quicklyawait expect(page).toHaveURL(/onboarding|welcome|setup/);expect(Date.now() - startTime).toBeLessThan(5000); // < 5 seconds});test('Step 3: Onboarding is skippable', async ({ page }) => {// ... login as new user ...await page.goto('/onboarding');// Skip option existsconst skipButton = page.getByRole('button', { name: /skip/i });await expect(skipButton).toBeVisible();});test('Step 4: Can reach first value in < 5 min', async ({ page }) => {// Full journey timingconst journeyStart = Date.now();// ... complete full journey ...// Verify first value deliveredawait expect(page.getByText(/success|created|done/i)).toBeVisible();// Total time checkconst totalTime = (Date.now() - journeyStart) / 1000 / 60; // minutesexpect(totalTime).toBeLessThan(5);});});
Error Recovery Tests
typescript
// e2e/tests/journeys/checkout-recovery.spec.tsimport { test, expect } from '@playwright/test';test.describe('Journey: Checkout Error Recovery', () => {test('recovers from payment decline gracefully', async ({ page }) => {// Setup: Add item to cart, go to checkoutawait page.goto('/products');await page.getByTestId('add-to-cart').first().click();await page.getByRole('link', { name: 'Checkout' }).click();// Use Stripe test card that declinesconst stripeFrame = page.frameLocator('iframe[name*="stripe"]');await stripeFrame.getByPlaceholder('Card number').fill('4000000000000002');await stripeFrame.getByPlaceholder('MM / YY').fill('12/30');await stripeFrame.getByPlaceholder('CVC').fill('123');await page.getByRole('button', { name: /pay/i }).click();// Verify friendly errorawait expect(page.getByText(/declined|try another/i)).toBeVisible();// Verify still on checkout (not kicked out)await expect(page).toHaveURL(/checkout/);// Verify can try again with different cardawait stripeFrame.getByPlaceholder('Card number').fill('4242424242424242');await page.getByRole('button', { name: /pay/i }).click();// Should succeed nowawait expect(page).toHaveURL(/success|confirmation/);});test('preserves cart after session timeout', async ({ page, context }) => {// Add items to cartawait page.goto('/products');await page.getByTestId('add-to-cart').first().click();// Clear session (simulate timeout)await context.clearCookies();// Return to siteawait page.goto('/cart');// Cart should be preserved (local storage or recovered)await expect(page.getByTestId('cart-item')).toHaveCount(1);});});
User Experience Validation
UX Checklist per Journey Step
markdown
## UX Validation Checklist### Clarity-[ ] User knows where they are (breadcrumbs, progress)-[ ] User knows what to do next (clear CTA)-[ ] User knows what just happened (feedback)### Speed-[ ] Page loads < 2 seconds-[ ] Actions complete < 3 seconds-[ ] Progress shown for longer operations### Forgiveness-[ ] Mistakes are easy to undo-[ ] Errors explain what went wrong-[ ] Recovery path is clear### Accessibility-[ ] Keyboard navigation works-[ ] Screen reader announces changes-[ ] Focus management correct-[ ] Color contrast sufficient### Mobile-[ ] Touch targets >= 44px-[ ] No horizontal scroll-[ ] Forms don't zoom unexpectedly-[ ] Works on slow 3G
Automated UX Checks
typescript
// e2e/utils/ux-validators.tsimport { Page, expect } from '@playwright/test';export async function validatePageLoad(page: Page, maxMs = 2000) {const timing = await page.evaluate(() => {const nav = performance.getEntriesByType('navigation')[0] as PerformanceNavigationTiming;return nav.loadEventEnd - nav.startTime;});expect(timing).toBeLessThan(maxMs);}export async function validateCTAVisible(page: Page, ctaText: RegExp) {const cta = page.getByRole('button', { name: ctaText });await expect(cta).toBeVisible();await expect(cta).toBeInViewport();}export async function validateNoLayoutShift(page: Page) {const cls = await page.evaluate(() => {return new Promise<number>((resolve) => {let clsValue = 0;const observer = new PerformanceObserver((list) => {for (const entry of list.getEntries()) {if (!(entry as any).hadRecentInput) {clsValue += (entry as any).value;}}});observer.observe({ type: 'layout-shift', buffered: true });setTimeout(() => {observer.disconnect();resolve(clsValue);}, 1000);});});expect(cls).toBeLessThan(0.1); // Good CLS score}export async function validateAccessibility(page: Page) {// Check focus visible on interactive elementsconst buttons = page.getByRole('button');const count = await buttons.count();for (let i = 0; i < Math.min(count, 5); i++) {await buttons.nth(i).focus();await expect(buttons.nth(i)).toBeFocused();}}
Journey Metrics Dashboard
Track journey health with these metrics:
typescript
// lib/journey-metrics.tsinterface JourneyMetric {journey: string;step: string;timestamp: Date;duration: number;success: boolean;userId?: string;}// Track in your analytics (PostHog, Mixpanel, etc.)export function trackJourneyStep(metric: JourneyMetric) {analytics.track('journey_step', {journey_name: metric.journey,step_name: metric.step,duration_ms: metric.duration,success: metric.success,});}// Example usage in appconst journeyStart = Date.now();// ... user completes step ...trackJourneyStep({journey: 'signup_to_value',step: 'account_creation',timestamp: new Date(),duration: Date.now() - journeyStart,success: true,});
Common Journey Patterns
Progressive Disclosure Journey
User sees simple view first, complexity revealed as needed.
markdown
Step 1: Show basic options onlyStep 2: "Advanced" expands more optionsStep 3: Expert mode unlocks everything
Guided Setup Journey
Hand-hold new users through initial configuration.
markdown
Step 1: Welcome + single choiceStep 2: Core preferenceStep 3: Optional integrations (skippable)Step 4: First action with guidanceStep 5: Success + remove training wheels
Recovery Journey
User returns after failure or abandonment.
markdown
Step 1: Recognize returning userStep 2: Restore previous stateStep 3: Acknowledge what happenedStep 4: Offer clear path forwardStep 5: Complete original goal
Anti-Patterns
- Happy path only - Test error recovery, not just success
- Spec-driven testing - Test user goals, not features
- Ignoring time - Measure how long journeys take
- Desktop-only - Test mobile journeys separately
- Skipping emotions - Consider user frustration points
- No metrics - Track journey completion and drop-off
- Static journeys - Update as user behavior evolves
Quick Reference
Journey Priorities
| Priority | Criteria | Test Frequency | |
|---|---|---|---|
| Critical | Revenue, core value | Every deploy | |
| High | Daily user actions | Daily | |
| Medium | Weekly features | Weekly | |
| Low | Edge cases | On change |
Package.json Scripts
json
{"scripts": {"test:journeys": "playwright test e2e/tests/journeys/","test:journeys:critical": "playwright test e2e/tests/journeys/critical/","test:journeys:report": "playwright show-report"}}
Journey Documentation Checklist
- [ ] User goal clearly stated
- [ ] All steps documented
- [ ] Success criteria per step
- [ ] Error scenarios covered
- [ ] Recovery paths defined
- [ ] Metrics identified
- [ ] E2E test linked