One letter at a time · phonics plan — Plan explicit printed-letter-to-sound word building from teacher-confirmed correspondences,with one-change words and a check that…
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>One letter at a time · phonics plan</title><style>
*{box-sizing:border-box}body{margin:0;background:#eff6ed;color:#294935;font:16px/1.5 system-ui,sans-serif}main{max-width:1120px;margin:auto;padding:30px}h1{font:clamp(31px,4vw,45px)/1.15 Georgia,serif;letter-spacing:-.025em;margin:6px 0 12px}h2{font:27px/1.2 Georgia,serif;margin:0 0 14px}h3{font-size:17px;margin:0 0 9px}p{margin:0 0 12px}.eyebrow{font-size:11px;font-weight:750;letter-spacing:.13em;text-transform:uppercase;color:#5a8064}.small{font-size:13px;color:#5d7765}.chain{display:grid;grid-template-columns:1fr 42px 1fr 42px 1fr;gap:12px;align-items:center;background:#fffdf4;border:1px solid #cfdbbe;border-radius:12px;padding:24px;margin-top:25px;text-align:center}.word{font:750 43px/1.5 ui-monospace,Consolas,monospace;letter-spacing:.05em}.word em{font-style:normal;border-bottom:4px solid #bd792d;background:#fae6b8;border-radius:4px}.arrow{font-size:30px;color:#8ba078}.change-notes{display:grid;grid-template-columns:1fr 1fr;gap:20px;margin:17px 0 25px}.change-notes div{padding:14px 18px;border-left:4px solid #8da46b;background:#e3edd9;font-size:14px}.lesson{display:grid;grid-template-columns:1fr 1fr;gap:24px}.panel{background:#fffef8;border:1px solid #cfdbbe;border-radius:11px;padding:23px}.stepbuttons{display:flex;gap:8px;flex-wrap:wrap;margin-bottom:18px}button{font:700 14px system-ui;min-height:44px;padding:10px 16px;border:1px solid #4c7553;border-radius:6px;background:#4c7553;color:white;cursor:pointer}button.secondary{background:#fffef8;color:#4c7553}button[aria-pressed=true]{background:#294f34;color:white}button:disabled{opacity:.45;cursor:default}:focus-visible{outline:3px solid #bd792d;outline-offset:3px}.slots{display:flex;gap:10px;margin:20px 0}.slots button{font:700 38px ui-monospace,Consolas,monospace;padding:8px;min-height:73px;flex:1;background:white;color:#294935;border:2px solid #a9bd96}.slots button[aria-pressed=true]{border-color:#bd792d;box-shadow:0 0 0 2px #f1d3a7}.letterbank{display:flex;gap:8px;margin:13px 0 18px;flex-wrap:wrap}.letterbank button{font-size:23px;flex:1;background:#e4edd9;color:#294935;border-color:#b9cba5;min-width:44px}.feedback{padding:14px;background:#e6efdc;border-left:4px solid #8da46b;margin-top:16px;min-height:75px}.feedback p{font-size:14px;margin:4px 0}.correspondences{display:flex;gap:8px;flex-wrap:wrap;margin:18px 0}.sound{background:#f6ecd6;padding:9px 14px;border-radius:5px;font-size:18px}.timetable{margin-top:26px}.timing-row{display:grid;grid-template-columns:85px 1fr 75px;gap:14px;padding:16px 0;border-top:1px solid #dae3ce;align-items:start}.timing-row strong{display:block}.timing-row p{font-size:14px;margin:4px 0}.range{font-size:13px;color:#56734e;font-weight:700}.duration input{margin:0;text-align:center}.duration{font-size:12px}label{font-size:13px;font-weight:650;display:block}input,textarea{display:block;width:100%;border:1px solid #9eb28f;border-radius:5px;font:16px/1.5 system-ui;padding:9px 11px;background:white;color:#294935;margin:5px 0 13px}textarea{min-height:85px;resize:vertical}.total{font-size:28px;font-weight:750}.error{color:#a63729;min-height:22px;font-size:14px}.row{display:flex;justify-content:space-between;gap:15px;align-items:center;flex-wrap:wrap}details{border-top:1px solid #cfdbbe;padding-top:16px;margin-top:24px}summary{cursor:pointer;font-weight:700;min-height:44px}.check{display:flex;gap:9px;align-items:flex-start;font-weight:500}.check input{width:20px;height:20px;flex-shrink:0;margin:2px 0 0}.observations{display:grid;grid-template-columns:1fr 1fr;gap:0 20px}.footer{font-size:13px;color:#5d7765;margin:25px 0}[hidden]{display:none!important}@media(max-width:740px){main{padding:23px 16px}.lesson,.observations{grid-template-columns:1fr}.chain{padding:18px 13px;gap:7px;grid-template-columns:1fr 20px 1fr 20px 1fr}.word{font-size:31px}.arrow{font-size:23px}.change-notes{grid-template-columns:1fr;gap:12px}.panel{padding:20px}.timing-row{grid-template-columns:60px 1fr 66px;gap:10px}.timing-row p{font-size:13px}}@media(max-width:380px){main{padding:20px 12px}.word{font-size:27px}.chain{padding:16px 8px}.timing-row{grid-template-columns:1fr 65px}.range{grid-column:1/-1}.stepbuttons button{flex:1;padding:9px}.panel{padding:17px}}
</style></head><body><main><p class="eyebrow">Original illustrative teacher plan · 35 minutes</p><h1>Change one printed letter.</h1><p>Model the change, guide the next one, then record building and reading separately.</p><section class="chain" aria-label="Original word chain"><div><p class="small">Source</p><div class="word">ma<em>p</em></div></div><div class="arrow" aria-hidden="true">→</div><div><p class="small">Teacher model</p><div class="word">ma<em>t</em></div></div><div class="arrow" aria-hidden="true">→</div><div><p class="small">Guided target</p><div class="word"><em>s</em>at</div></div></section><div class="change-notes"><div><strong>map → mat</strong><br>Final p /p/ → t /t/. Initial m and middle a stay.</div><div><strong>mat → sat</strong><br>Initial m /m/ → s /s/. Middle a and final t stay.</div></div><div class="lesson"><section class="panel"><h2>Printed-letter workbench</h2><div class="stepbuttons"><button type="button" id="model" class="secondary" aria-pressed="false">Model · map → mat</button><button type="button" id="guided" aria-pressed="true">Guide · mat → sat</button></div><p id="build-prompt">Change the initial m to s. Keep a and t.</p><p class="small">Choose a position, then choose its printed letter.</p><div id="slots" class="slots"><button type="button" id="slot-0" aria-pressed="true" aria-label="Initial letter, m">m</button><button type="button" id="slot-1" aria-pressed="false" aria-label="Middle letter, a">a</button><button type="button" id="slot-2" aria-pressed="false" aria-label="Final letter, t">t</button></div><div class="letterbank"><button type="button" id="letter-m">m</button><button type="button" id="letter-a">a</button><button type="button" id="letter-p">p</button><button type="button" id="letter-t">t</button><button type="button" id="letter-s">s</button></div><div class="row"><button type="button" id="check-build">Check guided change</button><button type="button" id="reset-build" class="secondary">Reset this step</button></div><div class="feedback" role="status"><strong id="build-result">Current word: mat</strong><p id="build-feedback">A completed tile arrangement does not establish a reading response.</p></div><button type="button" id="reveal-target" class="secondary" hidden style="margin-top:14px">Review independent target · teacher</button><p id="target-review" class="small" hidden></p></section><section class="panel"><h2>Confirm the teaching inputs</h2><p class="small">Supplied correspondences for the sample's stated US practice pronunciation:</p><div class="correspondences"><span class="sound">m /m/</span><span class="sound">a /æ/</span><span class="sound">p /p/</span><span class="sound">t /t/</span><span class="sound">s /s/</span></div><label for="context">Learner context / modeling procedure<input id="context" placeholder="Teacher to confirm"></label><label class="check" for="known"><input type="checkbox" id="known">Teacher has confirmed the correspondences and context needed for this independent attempt.</label><details id="teacher-setup"><summary>Teacher setup · independent target card</summary><p class="small">Choose a new three-letter word using only the confirmed letters m, a, p, t, s. The page does not choose a word or infer readiness.</p><label for="independent-target">Teacher-selected target · blank until supplied<input id="independent-target" maxlength="3" autocomplete="off" spellcheck="false"></label><button type="button" id="prepare-independent">Prepare independent attempt</button><p id="target-error" class="error" role="alert"></p></details><p class="small" style="margin-top:17px">For independent work, close the teacher setup, give the teacher-approved prompt, and keep the target card hidden until the construction is recorded. No audio recording, speech capture, or automatic reading grade is provided.</p></section></div><section class="panel timetable"><div class="row"><h2>Editable lesson timetable</h2><div id="total" class="total">35 minutes</div></div><p class="small">Materials: separate printed source/target cards and letter cards m, a, p, t, s; a visible teacher modeling area and individual work space. Adapt arrangements to the actual learners.</p><div id="timing-rows"></div><p id="timing-error" class="error" role="alert"></p><button type="button" id="restore-times" class="secondary">Restore 5 / 7 / 10 / 8 / 5</button></section><section class="panel" style="margin-top:25px"><h2>Actual observation record · starts blank</h2><p class="small">Record after teaching. A copied model, a built word, and a reading response are separate evidence.</p><div class="observations"><label for="prompt-given">Prompt actually given<textarea id="prompt-given"></textarea></label><label for="letters-placed">Letters placed in order<textarea id="letters-placed"></textarea></label><label for="reading">Reading response<textarea id="reading"></textarea></label><label for="support">Support used<textarea id="support"></textarea></label><label for="next-step">Teacher next step<textarea id="next-step"></textarea></label></div></section><footer class="footer">This is a teacher planning and local tile companion, not a student-only copy or evidence of learner decoding. Independent target choice, pronunciation modeling, classroom suitability, and reading judgments belong to the teacher. Printed letters remain visible: this is phonics, not an oral-only sound exercise. No native document export, student-data collection, diagnosis, promised gains, or persistent notes. Resetting the tile step or timetable leaves manually entered actual observations unchanged.</footer></main><script>
'use strict';const $=id=>document.getElementById(id);const letters=['m','a','p','t','s'];let stage='guided',word=['m','a','t'],position=0,target='',recorded=false;const stages={model:{source:'map',target:'mat',prompt:'Change final p to t. Keep m and a.',position:2},guided:{source:'mat',target:'sat',prompt:'Change the initial m to s. Keep a and t.',position:0}};
const blocks=[['Review','Check the teacher-selected correspondences; note which need instruction.',5],['Model','Show map → mat. Point to final p /p/ → t /t/ and keep m /m/, a /æ/ visible.',7],['Guided practice','Guide mat → sat. Ask which printed position changes to s /s/.',10],['Independent attempt','Use a new teacher-supplied target from confirmed correspondences; keep its card hidden.',8],['Check and review','Compare actual construction and reading separately; choose the next practice from evidence.',5]];
function planTimes(values){if(values.some(v=>!/^\d+$/.test(String(v).trim())||!Number.isSafeInteger(Number(v))||+v<=0||+v>180))return{error:'Enter whole positive minutes from 1 to 180 for every phase.'};let start=0;const ranges=values.map(v=>{let end=start+Number(v),range=start+'–'+end+' min';start=end;return range;});return{total:start,ranges};}
function timing(){const r=planTimes(blocks.map((_,i)=>$('minutes-'+i).value));$('total').textContent=r.error?'Pending':r.total+' minutes';$('timing-error').textContent=r.error||(r.total===35?'':'Current total differs from the original 35-minute plan by '+Math.abs(r.total-35)+' minutes. Review the planned lesson length.');blocks.forEach((_,i)=>$('range-'+i).textContent=r.error?'Not calculated':r.ranges[i]);}
function validateTarget(text,confirmed){const t=text.trim().toLowerCase();if(!confirmed)return{error:'Confirm the teacher-selected correspondences and context first.'};if(!/^[mapts]{3}$/.test(t))return{error:'Supply a three-letter teacher-selected word using only m, a, p, t, s.'};if(['map','mat','sat'].includes(t))return{error:'Choose a new teacher-selected word, different from the modeled chain.'};return{target:t};}
function checkWord(type,current){if(current.some(c=>!c))return{title:'Incomplete construction.',message:'Place a printed letter in all three positions.'};const text=current.join('');if(type==='independent')return{title:'Construction recorded: '+text,message:'Reading response remains to be supplied by the teacher. Review the hidden target only after recording.'};const data=stages[type],changes=[...data.source].map((c,i)=>c!==current[i]?i:-1).filter(i=>i>=0);if(text===data.target)return{title:text+' · one-letter change matches.',message:type==='model'?'Only final p /p/ changed to t /t/. Initial m and middle a stayed.':'Only initial m /m/ changed to s /s/. Middle a and final t stayed.'};return{title:'Current word: '+text+' · revise the change.',message:current[1]!=='a'?'Middle a should stay in place. Point back to the source and target cards.':changes.length!==1?'Change exactly one printed letter; keep the other two in their positions.':'Check the changed position against the supplied target card.'};}
function renderWord(){for(let i=0;i<3;i++){$('slot-'+i).textContent=word[i]||'—';$('slot-'+i).setAttribute('aria-pressed',String(position===i));$('slot-'+i).setAttribute('aria-label',['Initial','Middle','Final'][i]+' letter, '+(word[i]||'blank'));}$('build-prompt').textContent=stage==='independent'?'Build the new word supplied by your teacher. The target card stays hidden.':stages[stage].prompt;for(const s of['model','guided'])$(s).setAttribute('aria-pressed',String(stage===s));$('check-build').textContent=stage==='independent'?'Record independent construction':'Check '+stage+' change';$('reveal-target').hidden=stage!=='independent'||!recorded;}
function clearFeedback(){recorded=false;$('build-result').textContent='Current word: '+word.map(c=>c||'—').join('');$('build-feedback').textContent='Edited construction · not checked or recorded.';$('target-review').hidden=true;renderWord();}
function setStage(next){stage=next;word=[...stages[next].source];position=stages[next].position;clearFeedback();}
for(let i=0;i<3;i++)$('slot-'+i).addEventListener('click',()=>{position=i;renderWord();});for(const letter of letters)$('letter-'+letter).addEventListener('click',()=>{word[position]=letter;clearFeedback();});for(const s of['model','guided'])$(s).addEventListener('click',()=>setStage(s));$('check-build').addEventListener('click',()=>{if(stage==='independent'&&!$('known').checked){$('build-result').textContent='Independent attempt held.';$('build-feedback').textContent='Teacher confirmation was removed; prepare the target again.';return;}const r=checkWord(stage,word);$('build-result').textContent=r.title;$('build-feedback').textContent=r.message;recorded=stage==='independent'&&word.every(Boolean);renderWord();});$('reset-build').addEventListener('click',()=>{word=stage==='independent'?['','','']:[...stages[stage].source];clearFeedback();});$('prepare-independent').addEventListener('click',()=>{const r=validateTarget($('independent-target').value,$('known').checked);$('target-error').textContent=r.error||'';if(r.error)return;target=r.target;stage='independent';word=['','',''];position=0;$('teacher-setup').open=false;clearFeedback();});for(const id of['independent-target','known']){$(id).addEventListener('input',()=>{recorded=false;target='';$('target-review').hidden=true;if(stage==='independent'){stage='guided';word=['m','a','t'];clearFeedback();}});$(id).addEventListener('change',()=>{if(!$('known').checked&&stage==='independent'){stage='guided';word=['m','a','t'];target='';clearFeedback();}});}$('reveal-target').addEventListener('click',()=>{if(!recorded||!target)return;$('target-review').hidden=false;$('target-review').textContent='Teacher target: '+target+'. Recorded construction: '+word.join('')+'. '+(word.join('')===target?'Printed construction matches.':'Compare each printed position with the target.')+' Reading response is a separate observation.';});
blocks.forEach((b,i)=>{let row=document.createElement('div');row.className='timing-row';let range=document.createElement('div');range.id='range-'+i;range.className='range';row.appendChild(range);let text=document.createElement('div'),heading=document.createElement('strong'),activity=document.createElement('p');heading.textContent=b[0];activity.textContent=b[1];text.appendChild(heading);text.appendChild(activity);row.appendChild(text);let label=document.createElement('label');label.className='duration';label.setAttribute('for','minutes-'+i);label.textContent='Minutes';let input=document.createElement('input');input.id='minutes-'+i;input.value=String(b[2]);input.setAttribute('inputmode','numeric');input.setAttribute('aria-label',b[0]+' minutes');input.addEventListener('input',timing);label.appendChild(input);row.appendChild(label);$('timing-rows').appendChild(row);});$('restore-times').addEventListener('click',()=>{blocks.forEach((b,i)=>$('minutes-'+i).value=String(b[2]));timing();});renderWord();timing();
</script></body></html>