A delayed-newsletter fishbone with editable causes and a separate blank diagram

HTML for this example

Select the code and use your copy shortcut. Changes in this preview are temporary.

<!doctype html>
<html lang="en"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1"><title>Fishbone diagram · a delayed newsletter</title><style>
*{box-sizing:border-box}body{margin:0;background:#f7f4ee;color:#243137;font:16px/1.5 system-ui,sans-serif}main{max-width:1000px;margin:auto;padding:24px}h1{font-size:30px;line-height:1.15;margin:5px 0 10px}h2{font-size:21px;line-height:1.3;margin:0 0 10px}h3{font-size:17px;margin:0 0 10px}p{margin:8px 0 14px}.eyebrow{color:#62655a;font-size:14px;letter-spacing:.05em}.context{max-width:68ch}.toolbar{display:flex;gap:10px;flex-wrap:wrap;margin:18px 0}button,input,textarea{font:inherit}button{min-height:44px;padding:10px 13px;border:1px solid #59665d;border-radius:4px;background:#fff;color:#243137;cursor:pointer}button:hover{background:#f0eee4}button:focus-visible,input:focus-visible,textarea:focus-visible{outline:3px solid #9f561d;outline-offset:3px}.primary{background:#344c41;color:white}.primary:hover{background:#263b31}label{display:block;font-weight:650}input[type=text],textarea{display:block;width:100%;min-width:0;border:1px solid #8d9388;background:#fff;padding:10px;margin:6px 0 15px;border-radius:3px}textarea{resize:vertical;min-height:88px}.diagram{display:grid;grid-template-columns:minmax(0,1fr) 180px;gap:12px;align-items:center;margin:22px 0}.effect{grid-column:2;grid-row:1;border:2px solid #9f561d;padding:14px;background:#fff3e2;position:relative;overflow-wrap:anywhere}.effect:before{content:'';position:absolute;left:-14px;top:calc(50% - 9px);border-top:9px solid transparent;border-bottom:9px solid transparent;border-left:12px solid #9f561d}.effect .label{display:block;color:#79471e;font-size:14px;font-weight:750;margin-bottom:6px}.branches{grid-column:1;grid-row:1;display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:28px 14px;position:relative;align-items:stretch}.branches:before{content:'';position:absolute;left:0;right:-12px;top:50%;height:3px;background:#9f561d}.branch{position:relative;background:#fbfaf6;min-height:170px;padding:12px 10px;border-left:3px solid #6f8171;border-bottom:2px solid #9f561d}.branch:nth-child(n+3){border-bottom:0;border-top:2px solid #9f561d}.branch h3{color:#344c41}.branch ul{list-style:none;padding:0;margin:0;display:grid;gap:9px}.cause{width:100%;text-align:left;border-color:#b7b9ab;border-radius:2px;padding:9px;overflow-wrap:anywhere;line-height:1.4;min-height:54px}.cause[aria-pressed=true]{border:2px solid #9f561d;background:#fff3e2}.cause small{display:block;font-size:13px;color:#656c62;margin-bottom:3px}.empty{color:#73776a;font-style:italic}.question{border-left:5px solid #9f561d;padding:14px 17px;background:white;margin:22px 0}.question p:last-child{margin-bottom:0}.editor{background:#e9eee7;border-top:4px solid #344c41;padding:18px;margin:24px 0}.editor fieldset{border:1px solid #a8b3a4;padding:12px;margin:0 0 15px}.editor legend{font-weight:650}.choices{display:flex;gap:10px 18px;flex-wrap:wrap}.choices label{font-weight:450;display:flex;align-items:center;gap:7px;min-height:44px}.choices input{width:18px;height:18px}.notice{min-height:1.5em;color:#79471e}footer{font-size:15px;color:#60685e;margin:22px 0}[hidden]{display:none!important}@media(max-width:560px){main{padding:14px}h1{font-size:27px}.diagram{display:flex;flex-direction:column;align-items:stretch;gap:18px}.effect{order:-1}.effect:before{display:none}.branches{display:flex;flex-direction:column;gap:14px;padding-left:16px}.branches:before{left:0;right:auto;top:0;bottom:0;width:3px;height:auto}.branch,.branch:nth-child(n+3){min-height:0;border:0;border-left:2px solid #9f561d;padding:12px;background:white}.branch:before{content:'';position:absolute;left:-18px;top:22px;width:16px;height:2px;background:#9f561d}.choices{display:grid;grid-template-columns:1fr}.editor{padding:14px}}
</style></head><body><main>
<p class="eyebrow">FICTIONAL TEAM EXAMPLE</p><h1>Why was the newsletter late?</h1><p class="context" id="discussionNote">Map possible explanations for the missed noon send time. Select a cause to see the question to investigate next. None of these causes has been confirmed.</p>
<div class="toolbar"><button id="filled" type="button">Filled example</button><button id="blank" type="button">Blank diagram</button></div>
<label for="effectInput">Effect to investigate</label><input id="effectInput" type="text" maxlength="240" value="Newsletter sent after the planned noon deadline">
<section class="diagram" aria-label="Possible causes and the effect"><div class="effect"><span class="label">EFFECT</span><strong id="effectText">Newsletter sent after the planned noon deadline</strong></div><div class="branches" id="branches"><section class="branch" id="branchProcess"><h3>Process</h3><ul></ul></section><section class="branch" id="branchInputs"><h3>Inputs</h3><ul></ul></section><section class="branch" id="branchTools"><h3>Tools</h3><ul></ul></section><section class="branch" id="branchPeople"><h3>People and coordination</h3><ul></ul></section></div></section>
<section class="question" aria-labelledby="questionHeading"><h2 id="questionHeading">Next evidence question</h2><p id="selectedCause"></p><p id="evidenceQuestion"></p></section>
<section class="editor" aria-labelledby="editorHeading"><h2 id="editorHeading">Revise a possible cause</h2><p id="editingId">Editing C1</p><label for="causeText">Possible cause wording</label><textarea id="causeText" maxlength="600"></textarea><label for="questionText">Question to investigate</label><textarea id="questionText" maxlength="600"></textarea><fieldset><legend>Branch</legend><div class="choices"><label for="moveProcess"><input id="moveProcess" type="radio" name="branch" value="Process" checked>Process</label><label for="moveInputs"><input id="moveInputs" type="radio" name="branch" value="Inputs">Inputs</label><label for="moveTools"><input id="moveTools" type="radio" name="branch" value="Tools">Tools</label><label for="movePeople"><input id="movePeople" type="radio" name="branch" value="People">People and coordination</label></div></fieldset><div class="toolbar"><button id="newCause" type="button">Write a new cause</button><button id="addCause" class="primary" type="button" hidden>Add this cause</button><button id="removeCause" type="button">Remove selected cause</button></div><p class="notice" id="notice" role="status" aria-live="polite"></p></section>
<footer>Edits stay in this open example. The diagram organizes possibilities; collect evidence before choosing an explanation or action.</footer>
</main><script>
(()=>{'use strict';const $=s=>document.querySelector(s),names={Process:'Process',Inputs:'Inputs',Tools:'Tools',People:'People and coordination'},fixture=[{id:'C1',branch:'Process',text:'Final approval may have arrived after the send cutoff.',question:'When was the review requested, and when did approval arrive?'},{id:'C2',branch:'Inputs',text:'The draft may have been waiting for a working event link.',question:'Which link was missing, and when was it supplied?'},{id:'C3',branch:'Tools',text:'An image change may have disrupted the prepared email layout.',question:'What changed in the draft, and what did the preview show?'},{id:'C4',branch:'People',text:'The backup reviewer may not have known they were needed.',question:'Was backup coverage agreed, and who received the review request?'}];let causes=[],selected=null,nextId=1;
function selectedRow(){return causes.find(c=>c.id===selected)||null;}
function render(){const focusedCause=document.activeElement?.classList.contains('cause')?document.activeElement.id:null;for(const branch of Object.keys(names)){const list=$('#branch'+branch+' ul');list.replaceChildren();const members=causes.filter(c=>c.branch===branch);if(!members.length){const li=document.createElement('li');li.className='empty';li.textContent='No causes yet';list.append(li);}for(const cause of members){const li=document.createElement('li'),button=document.createElement('button'),id=document.createElement('small'),text=document.createElement('span');button.type='button';button.id='cause'+cause.id;button.className='cause';button.dataset.cause=cause.id;button.setAttribute('aria-pressed',String(cause.id===selected));id.textContent=cause.id+' · possible cause';text.textContent=cause.text.trim()||'Cause wording needed';button.append(id,text);button.addEventListener('click',()=>select(cause.id));li.append(button);list.append(li);}}const row=selectedRow();$('#selectedCause').textContent=row?row.id+': '+(row.text.trim()||'Cause wording needed'):'Select a cause, or write a new one.';$('#evidenceQuestion').textContent=row?(row.question.trim()||'Evidence question needed'):'Keep the question beside the possibility it examines.';$('#effectText').textContent=$('#effectInput').value.trim()||'Write one effect to investigate';if(focusedCause)document.getElementById(focusedCause)?.focus({preventScroll:true});}
function select(id){selected=id;const row=selectedRow();$('#editingId').textContent=row?'Editing '+row.id:'New possible cause';$('#causeText').value=row?row.text:'';$('#questionText').value=row?row.question:'';for(const radio of document.querySelectorAll('input[name=branch]'))radio.checked=radio.value===(row?row.branch:'Process');$('#addCause').hidden=!!row;$('#removeCause').hidden=!row;$('#notice').textContent='';render();}
function restore(){causes=fixture.map(c=>({...c}));nextId=5;$('#effectInput').value='Newsletter sent after the planned noon deadline';select('C1');}
$('#effectInput').addEventListener('input',()=>{render();$('#notice').textContent=$('#effectInput').value.trim()?'Effect updated in this open draft.':'Enter an effect before adding a cause.';});
for(const [selector,key]of [['#causeText','text'],['#questionText','question']])$(selector).addEventListener('input',()=>{const row=selectedRow();if(row){row[key]=$(selector).value;render();$('#notice').textContent=row.text.trim()&&row.question.trim()?'Wording updated. The cause remains unconfirmed.':'Finish the cause wording and evidence question.';}else{$('#notice').textContent=$('#causeText').value.trim()&&$('#questionText').value.trim()?'Cause draft changed. Choose Add this cause when ready; the possibility is not confirmed.':'Complete the cause wording and evidence question before adding.';}});
for(const radio of document.querySelectorAll('input[name=branch]'))radio.addEventListener('change',()=>{const row=selectedRow();if(row&&radio.checked){row.branch=radio.value;render();$('#notice').textContent=row.id+' moved to '+names[row.branch]+'. Its evidence question is unchanged.';}});
$('#newCause').addEventListener('click',()=>select(null));$('#addCause').addEventListener('click',()=>{if(!$('#effectInput').value.trim()){$('#notice').textContent='Enter the effect to investigate first.';return;}const text=$('#causeText').value.trim(),question=$('#questionText').value.trim();if(!text||!question){$('#notice').textContent='Enter both a possible cause and its evidence question.';return;}const branch=document.querySelector('input[name=branch]:checked').value,id='C'+nextId++;causes.push({id,branch,text,question});select(id);$('#notice').textContent=id+' added as a possibility, not a confirmed cause.';});
$('#removeCause').addEventListener('click',()=>{const old=selected;causes=causes.filter(c=>c.id!==old);select(causes[0]?.id||null);$('#notice').textContent=old+' removed from this draft. Keep the reason in your investigation notes.';});$('#blank').addEventListener('click',()=>{causes=[];nextId=1;$('#effectInput').value='';select(null);$('#notice').textContent='Blank diagram. Start with one effect, then add a possible cause and question.';});$('#filled').addEventListener('click',restore);restore();})();
</script></body></html>