|
| 1 | +<!DOCTYPE html> |
| 2 | +<html lang="en"> |
| 3 | + |
| 4 | +<head> |
| 5 | + <meta charset="UTF-8"> |
| 6 | + <meta name="viewport" content="width=device-width, initial-scale=1.0"> |
| 7 | + <title>Anatomy of a Friendly Error Message</title> |
| 8 | + <link rel="stylesheet" href="styles.css"> |
| 9 | +</head> |
| 10 | + |
| 11 | +<body> |
| 12 | + <div class="anatomy"> |
| 13 | + <a class="anatomy__back" href="index.html">← Demo</a> |
| 14 | + |
| 15 | + <header class="anatomy__header"> |
| 16 | + <h1>Anatomy of a Friendly Error Message</h1> |
| 17 | + </header> |
| 18 | + |
| 19 | + <div class="anatomy__main"> |
| 20 | + |
| 21 | + <div class="anatomy__left"> |
| 22 | + <!-- Traceback that triggered the error --> |
| 23 | + <div class="anatomy__context"> |
| 24 | + <span class="anatomy__context-label">Error / Trace:</span> |
| 25 | + <div class="anatomy__context-code"> |
| 26 | + <div>Traceback (most recent call last):</div> |
| 27 | + <div> File "main.py", line 2, in <module></div> |
| 28 | + <div> print("Hello, " + nme)</div> |
| 29 | + <div>NameError: name 'nme' is not defined</div> |
| 30 | + </div> |
| 31 | + </div> |
| 32 | + |
| 33 | + <div class="anatomy__card"> |
| 34 | + |
| 35 | + <!-- Stage 1: Title + Summary --> |
| 36 | + <div class="anatomy__zone" data-zone="1"> |
| 37 | + <div class="anatomy__zone-head"> |
| 38 | + <div class="anatomy__zone-badge">1</div> |
| 39 | + <div class="anatomy__zone-tag">Explain & Suggest - Title & Summary</div> |
| 40 | + </div> |
| 41 | + <div class="anatomy__zone-body"> |
| 42 | + <div class="pfem__title">This variable doesn't exist yet</div> |
| 43 | + <div class="pfem__summary">Your code uses the variable "<em>nme</em>", but it hasn't been created yet. |
| 44 | + Check line 2. If you meant to print the text <em>nme</em>, put it in double quotes.</div> |
| 45 | + </div> |
| 46 | + </div> |
| 47 | + |
| 48 | + <!-- Stage 2: Why --> |
| 49 | + <div class="anatomy__zone" data-zone="2"> |
| 50 | + <div class="anatomy__zone-head"> |
| 51 | + <div class="anatomy__zone-badge">2</div> |
| 52 | + <div class="anatomy__zone-tag">Hint & Direct - Why it happened</div> |
| 53 | + </div> |
| 54 | + <div class="anatomy__zone-body"> |
| 55 | + <div class="pfem__why">Without speech marks Python treats <em>nme</em> as a variable, and this variable |
| 56 | + does not exist yet.</div> |
| 57 | + </div> |
| 58 | + </div> |
| 59 | + |
| 60 | + <!-- Stage 3: Steps --> |
| 61 | + <div class="anatomy__zone" data-zone="3"> |
| 62 | + <div class="anatomy__zone-head"> |
| 63 | + <div class="anatomy__zone-badge">3</div> |
| 64 | + <div class="anatomy__zone-tag">Hint & Direct - What to do</div> |
| 65 | + </div> |
| 66 | + <div class="anatomy__zone-body"> |
| 67 | + <ul class="pfem__steps"> |
| 68 | + <li>If it is meant to be text, put speech marks around <em>nme</em>.</li> |
| 69 | + <li>If it is meant to be a variable, make it first (for example: <code>nme = 0</code>).</li> |
| 70 | + <li>Check spelling and capital letters.</li> |
| 71 | + </ul> |
| 72 | + </div> |
| 73 | + </div> |
| 74 | + |
| 75 | + <!-- Stage 4: Patch --> |
| 76 | + <div class="anatomy__zone" data-zone="4"> |
| 77 | + <div class="anatomy__zone-head"> |
| 78 | + <div class="anatomy__zone-badge">4</div> |
| 79 | + <div class="anatomy__zone-tag">Solve - Suggested fix</div> |
| 80 | + </div> |
| 81 | + <div class="anatomy__zone-body"> |
| 82 | + <pre class="pfem__patch">nme = 0 |
| 83 | +print("Hello, " + nme)</pre> |
| 84 | + </div> |
| 85 | + </div> |
| 86 | + |
| 87 | + </div> |
| 88 | + </div> |
| 89 | + |
| 90 | + <div class="anatomy__right"> |
| 91 | + <!-- Stage description --> |
| 92 | + <div class="anatomy__desc"> |
| 93 | + <div class="anatomy__desc-panel" data-zone="1"> |
| 94 | + <div class="anatomy__desc-title">Step 1 - Explain & Suggest</div> |
| 95 | + <div class="anatomy__desc-text">Explain the error in plain English and why the code can't run. Use |
| 96 | + highlighting to draw attention to the relevant line. Suggest which aspect the learner should review - a |
| 97 | + typo, a missing definition, a syntax issue - <em>posed as a suggestion</em>, not a directive.</div> |
| 98 | + </div> |
| 99 | + <div class="anatomy__desc-panel" data-zone="2"> |
| 100 | + <div class="anatomy__desc-title">Step 2a - Hint & Direct (hint)</div> |
| 101 | + <div class="anatomy__desc-text">Provide more specific detail about <strong>why</strong> the error happens. |
| 102 | + Give the learner something concrete to reason about before directing them to act.</div> |
| 103 | + </div> |
| 104 | + <div class="anatomy__desc-panel" data-zone="3"> |
| 105 | + <div class="anatomy__desc-title">Step 2b - Hint & Direct (direct)</div> |
| 106 | + <div class="anatomy__desc-text">Direct the learner - don't suggest. Tell them exactly what to change: |
| 107 | + <em>"You need to change X at line Y."</em> Be specific and action-oriented. |
| 108 | + </div> |
| 109 | + </div> |
| 110 | + <div class="anatomy__desc-panel" data-zone="4"> |
| 111 | + <div class="anatomy__desc-title">Step 3 - Solve</div> |
| 112 | + <div class="anatomy__desc-text">Provide a working <strong>solution</strong> with a brief explanation of what |
| 113 | + it corrects and why. A last resort - enough to unblock without handing the answer over too early.</div> |
| 114 | + </div> |
| 115 | + </div> |
| 116 | + |
| 117 | + <div class="anatomy__controls"> |
| 118 | + <button type="button" class="anatomy__btn anatomy__btn--primary" id="reveal-btn">Next stage |
| 119 | + →</button> |
| 120 | + <button type="button" class="anatomy__btn anatomy__btn--secondary" id="show-all-btn">Show all stages</button> |
| 121 | + <button type="button" class="anatomy__btn anatomy__btn--ghost" id="reset-btn">Reset</button> |
| 122 | + </div> |
| 123 | + </div> |
| 124 | + |
| 125 | + </div> |
| 126 | + </div> |
| 127 | + |
| 128 | + <script> |
| 129 | + const TOTAL = 4; |
| 130 | + let revealed = 1; |
| 131 | + |
| 132 | + const zones = [...document.querySelectorAll('.anatomy__zone')]; |
| 133 | + const descPanels = [...document.querySelectorAll('.anatomy__desc-panel')]; |
| 134 | + const revealBtn = document.getElementById('reveal-btn'); |
| 135 | + const showAllBtn = document.getElementById('show-all-btn'); |
| 136 | + const resetBtn = document.getElementById('reset-btn'); |
| 137 | + |
| 138 | + function update(next, animate = false) { |
| 139 | + revealed = Math.max(1, Math.min(TOTAL, next)); |
| 140 | + |
| 141 | + zones.forEach((z, i) => { |
| 142 | + const n = i + 1; |
| 143 | + const wasRevealed = z.classList.contains('is-revealed'); |
| 144 | + z.classList.toggle('is-revealed', n <= revealed); |
| 145 | + if (animate && n <= revealed && !wasRevealed) { |
| 146 | + z.classList.add('just-revealed'); |
| 147 | + z.addEventListener('animationend', () => z.classList.remove('just-revealed'), { once: true }); |
| 148 | + } |
| 149 | + }); |
| 150 | + |
| 151 | + descPanels.forEach((p) => { |
| 152 | + p.classList.toggle('is-active', parseInt(p.dataset.zone) === revealed); |
| 153 | + }); |
| 154 | + |
| 155 | + revealBtn.disabled = revealed >= TOTAL; |
| 156 | + revealBtn.textContent = revealed >= TOTAL ? 'All stages revealed' : 'Next stage →'; |
| 157 | + showAllBtn.disabled = revealed >= TOTAL; |
| 158 | + } |
| 159 | + |
| 160 | + revealBtn.addEventListener('click', () => update(revealed + 1, true)); |
| 161 | + showAllBtn.addEventListener('click', () => update(TOTAL, true)); |
| 162 | + resetBtn.addEventListener('click', () => update(1)); |
| 163 | + |
| 164 | + update(1); |
| 165 | + </script> |
| 166 | +</body> |
| 167 | + |
| 168 | +</html> |
0 commit comments