Workshop Form · Fillable PDF Preparation — Turn selected HTML controls into named PDF fields and verify edits after saving and reopening in an identified PDF reader.
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>Workshop Form · Fillable PDF Preparation</title><style>:root{--bg:#f4f3f0;--ink:#353b42;--accent:#a85c32;--line:#d7d2ca;--wash:#f7e9dd;}*{box-sizing:border-box}body{margin:0;background:var(--bg);color:var(--ink);font:16px/1.5 Arial,Helvetica,sans-serif}main{max-width:1100px;margin:auto;padding:26px 24px 54px}h1,h2,h3,p{margin:0}h1{font-size:clamp(27px,4vw,39px);line-height:1.15;letter-spacing:-.03em}h2{font-size:23px}h3{font-size:18px}header{margin-bottom:18px}header p{margin-top:8px}.eyebrow{font-size:12px;letter-spacing:.12em;text-transform:uppercase;font-weight:bold;color:var(--accent)}button,input,textarea,select{font:inherit}button{min-height:44px;padding:9px 15px;background:var(--accent);color:#fff;border:1px solid var(--accent);border-radius:6px;cursor:pointer;font-weight:bold}button.secondary{background:#fff;color:var(--accent)}button:disabled{opacity:.45;cursor:default}button[aria-pressed=true]{box-shadow:inset 0 0 0 2px currentColor}input,textarea,select{width:100%;min-height:44px;border:1px solid #919cad;background:#fff;color:var(--ink);border-radius:5px;padding:9px 11px}textarea{resize:vertical;min-height:94px}input[type=checkbox]{width:20px;min-height:20px;vertical-align:middle;margin-right:8px}label{display:block;margin:11px 0 5px;font-size:14px;font-weight:bold}:focus-visible{outline:3px solid #dc8c21;outline-offset:3px}.actions{display:flex;flex-wrap:wrap;gap:9px;margin:14px 0}.panel{background:#fff;border:1px solid var(--line);border-radius:10px;padding:22px;min-width:0}.columns{display:grid;grid-template-columns:1.45fr 1fr;gap:22px}.two{display:grid;grid-template-columns:1fr 1fr;gap:16px}.three{display:grid;grid-template-columns:repeat(3,1fr);gap:12px}.muted{color:#536176;font-size:14px}.notice{margin:14px 0;background:var(--wash);border-left:4px solid var(--accent);padding:13px 16px;overflow-wrap:anywhere}.error{color:#a42d25;font-weight:bold}.below{margin-top:28px;padding-top:22px;border-top:1px solid var(--line)}.below>p{margin:9px 0}.big{font-size:clamp(34px,5vw,56px);line-height:1.15;font-weight:bold}.tag{font-size:12px;padding:4px 8px;border-radius:4px;background:var(--wash)}.row{display:flex;gap:10px;align-items:end}.row>*{min-width:0}.row .unit{width:110px;flex:none}pre{white-space:pre-wrap;overflow-wrap:anywhere;font:13px/1.6 ui-monospace,monospace}table{border-collapse:collapse;width:100%;font-size:14px}th,td{text-align:left;vertical-align:top;padding:9px;border-bottom:1px solid var(--line)}th{font-size:12px;text-transform:uppercase}.scroll{overflow-x:auto}.foot{font-size:14px;color:#536176;margin-top:24px}summary{cursor:pointer;font-weight:bold;min-height:44px;padding:10px 0}details{margin:18px 0}.readonly{font:13px/1.5 ui-monospace,monospace;min-height:170px}.hash{overflow-wrap:anywhere;font:12px/1.6 ui-monospace,monospace}[hidden]{display:none!important}@media(max-width:800px){.columns{grid-template-columns:1fr}}@media(max-width:520px){main{padding:18px 14px 40px}.panel{padding:17px}.two,.three{grid-template-columns:1fr}.actions button{flex-grow:1}td,th{padding:7px}.row .unit{width:90px}}.mode-buttons{display:flex;gap:8px;flex-wrap:wrap;margin:16px 0}.mode-buttons button{flex:1}.paper{background:#fffefb;padding:30px;border:1px solid #d7d2ca;box-shadow:0 5px 18px #423c3010;min-width:0}.paper h2{font:30px Georgia,serif}.paper .booking{border-bottom:2px solid #a85c32;padding-bottom:14px;margin-bottom:15px}.paper p.static-value{padding:10px 0;border-bottom:1px solid #ded9d2;min-height:45px}.field-contract{padding:15px;border-radius:7px;background:#f8eddf;margin-top:18px}.expected-fields{font:15px/1.8 ui-monospace,monospace}.paper label{color:#75513d}#source-code{height:270px}#conversion{height:180px}.long{min-height:135px}</style></head><body><main><header><p class="eyebrow">HTML rehearsal · native PDF checks remain separate</p><h1>Choose what should stay fillable</h1><p>Compare static, all-field and selective contracts against one unchanged workshop source.</p></header><nav class="mode-buttons" aria-label="Conversion contract"><button type="button" id="mode-static" class="secondary">Static</button><button type="button" id="mode-all" class="secondary">All fields</button><button type="button" id="mode-selective" class="secondary">Selective</button></nav><div class="columns"><section class="paper"><div class="booking"><p class="eyebrow">Fictional workshop · source R1</p><h2>Workshop booking</h2><p>Booking ID <strong id="booking-id">WK-01</strong></p></div><p class="muted" id="preview-label">Selective contract · HTML rehearsal, not a PDF</p><label for="attendee">Attendee name</label><input id="attendee" value="Guest A"><p id="static-attendee" class="static-value" hidden>Guest A</p><label for="room-preview">Room</label><select id="room-preview"><option value="Studio">Studio</option><option value="Workshop">Workshop</option></select><p id="static-room" class="static-value" hidden>Studio</p><label for="notes-preview">Notes</label><textarea id="notes-preview" hidden>Bring notebook</textarea><p id="static-notes" class="static-value">Bring notebook</p><div class="actions"><button type="button" id="rehearse">Rehearse Guest B / Workshop edits</button><button type="button" id="reset-preview" class="secondary">Reset rehearsal</button></div><p class="muted" id="rehearsal-status">Source R1 stays unchanged while rehearsal values change.</p></section><aside class="panel"><h2>Expected field contract</h2><p class="big" id="field-count">2 fields</p><pre class="expected-fields" id="field-list">attendee-name
room</pre><p class="field-contract" id="notes-contract">Notes remains static: Bring notebook.</p><h3 style="margin-top:20px">Native workflow</h3><ol><li>Generate separate untouched PDFs for each mode.</li><li>Inspect names and initial values in the field dictionary.</li><li>Edit in an identified reader; save a distinct file.</li><li>Close and reopen that saved path.</li><li>Compare field values with visible content.</li></ol><p class="muted">A visible box or a clickable HTML control does not prove that a PDF field exists or persists.</p></aside></div><section class="below"><h2>Compare entered native observations</h2><p>Enter observations only after inspecting a real saved PDF. This checks your supplied text against the selected rehearsal; it does not inspect a file.</p><div class="two"><div><label for="reader">Actual reader / version and saved edited path</label><input id="reader" placeholder="Blank until a reader and saved path are identified"><label for="observed-fields">Reopened named fields · JSON object of names and values</label><textarea id="observed-fields" class="long" placeholder='Example format only: {"attendee-name":"value","room":"value"}'></textarea></div><div><label for="visible-content">Reopened visible text (transcribed observation)</label><textarea id="visible-content" class="long" placeholder="Blank until observed; include WK-01, visible values and notes"></textarea><div class="actions"><button type="button" id="compare">Compare entered evidence</button></div><p class="notice" id="evidence-result" role="status">No native observations compared.</p></div></div></section><section class="below"><h2>Unchanged source and separate conversion settings</h2><p>Source R1 SHA-256:</p><p class="hash" id="source-hash">71a66a2372a7682bbf9a9c68727acdd8b05f0e1bd576a79d37d0b561c6f1d514</p><label for="source-code">Original HTML source · exact R1 text</label><textarea id="source-code" readonly class="readonly"></textarea><div class="actions"><button type="button" id="select-source" class="secondary">Select source text</button></div><label for="conversion">Selected-mode preparation notes</label><textarea id="conversion" readonly class="readonly"></textarea><div class="actions"><button type="button" id="select-conversion" class="secondary">Select preparation notes</button></div><p id="selection-status" class="muted" role="status"></p><p class="foot">This companion does not create, edit, download or reopen PDFs. “Editable” means named fillable fields, not paragraph reflow. WeasyPrint documentation reference: 70.0; installed converter, inspector and reader versions are untested. Real PDF paths/hashes, dictionary inspection, save operations and reopened appearance remain native evidence. Keep generated and edited PDFs separate; the original source must keep its hash. Reader support varies. No booking submission, availability lookup, signature, real personal data or persistence.</p></section></main><script>'use strict';const $=id=>document.getElementById(id);const sourceText="<!doctype html>\n<html lang=\"en\"><meta charset=\"utf-8\"><title>Workshop WK-01</title>\n<section>\n<h1>Workshop booking</h1>\n<p>Booking ID WK-01</p>\n<label for=\"attendee-name\">Attendee name</label>\n<input id=\"attendee-name\" name=\"attendee-name\" value=\"Guest A\">\n<label for=\"room\">Room</label>\n<select id=\"room\" name=\"room\"><option value=\"Studio\" selected>Studio</option><option value=\"Workshop\">Workshop</option></select>\n<label for=\"notes\">Notes</label>\n<textarea id=\"notes\" name=\"notes\">Bring notebook</textarea>\n</section></html>",sourceHash='71a66a2372a7682bbf9a9c68727acdd8b05f0e1bd576a79d37d0b561c6f1d514';let mode='selective';const names={static:[],all:['attendee-name','room','notes'],selective:['attendee-name','room']};
function values(){return {'attendee-name':$('attendee').value,room:$('room-preview').value,notes:mode==='all'?$('notes-preview').value:'Bring notebook'};}
function invalidateEvidence(){$('evidence-result').textContent='Rehearsal or evidence changed — compare again.';}
function clearEvidence(){$('reader').value='';$('observed-fields').value='';$('visible-content').value='';$('evidence-result').textContent='No native observations compared.';}
function choose(m){mode=m;for(const key of ['static','all','selective'])$('mode-'+key).setAttribute('aria-pressed',String(key===m));$('preview-label').textContent=m[0].toUpperCase()+m.slice(1)+' contract · HTML rehearsal, not a PDF';$('attendee').hidden=m==='static';$('static-attendee').hidden=m!=='static';$('room-preview').hidden=m==='static';$('static-room').hidden=m!=='static';$('notes-preview').hidden=m!=='all';$('static-notes').hidden=m==='all';$('rehearse').disabled=m==='static';$('field-count').textContent=names[m].length+' fields';$('field-list').textContent=names[m].join('\n')||'No named form fields expected';$('notes-contract').textContent=m==='all'?'Notes is a named field; its edited target can change.':'Notes remains static: Bring notebook.';resetPreview();$('conversion').value='CONVERSION CONTRACT — '+m+'\nUnchanged source: R1, SHA-256 '+sourceHash+'\n'+(m==='all'?'Enable pdf_forms=True (or CLI --pdf-forms).':m==='selective'?'Do not enable global pdf_forms. Use a separate conversion stylesheet:\ninput[name="attendee-name"], select[name="room"] { appearance: auto; }\ninput[name="attendee-name"]::before, select[name="room"]::before { visibility: hidden; }\nReview required user-agent style overrides against the installed converter; leave notes outside the selectors.':'No global PDF forms option and no selective form styling.')+'\nExpected field names: '+(names[m].join(', ')||'none')+'\nRecord actual converter/options, generated path/hash, inspector/version and native field names/values. Actual conversion: Not tested.\nRecord reader/version, save operation, saved edited path/hash, closed/reopened field values and visible content separately. Actual persistence: Not tested.\nKeep WK-01 static and verify original source hash unchanged.';}
function resetPreview(){$('attendee').value='Guest A';$('room-preview').value='Studio';$('notes-preview').value='Bring notebook';$('rehearsal-status').textContent='Source R1 stays unchanged while rehearsal values change.';clearEvidence();}
function rehearse(){if(mode==='static')return;$('attendee').value='Guest B';$('room-preview').value='Workshop';$('notes-preview').value=mode==='all'?'Bring sketchbook':'Bring notebook';$('rehearsal-status').textContent='HTML rehearsal changed. No PDF has been generated or saved.';clearEvidence();}
function compare(){let observed;try{observed=JSON.parse($('observed-fields').value);}catch{$('evidence-result').textContent='Enter a valid JSON object from a native field inspection; no result accepted.';return;}if(!observed||Array.isArray(observed)||typeof observed!=='object'){$('evidence-result').textContent='Field observations must be a JSON object of field names and string values.';return;}if(!$('reader').value.trim()||!$('visible-content').value.trim()){$('evidence-result').textContent='Identify the actual reader/version and saved path, and supply observed visible text.';return;}const expected=names[mode],keys=Object.keys(observed);if(keys.length!==expected.length||keys.some(k=>!expected.includes(k))){$('evidence-result').textContent='Field-name mismatch. Expected exactly: '+(expected.join(', ')||'no named fields')+'.';return;}const v=values();if(expected.some(k=>typeof observed[k]!=='string'||observed[k]!==v[k])){$('evidence-result').textContent='Field-value mismatch against the selected rehearsal values.';return;}const required=['WK-01',mode==='static'?'Guest A':v['attendee-name'],mode==='static'?'Studio':v.room,v.notes];if(required.some(s=>!$('visible-content').value.includes(s))){$('evidence-result').textContent='Visible-content mismatch: inspect identifier, attendee, room and notes separately.';return;}$('evidence-result').textContent='Entered dictionary and visible text match the '+mode+' rehearsal. Native source/hash, file and save/reopen verification remain required.';}
for(const m of ['static','all','selective'])$('mode-'+m).addEventListener('click',()=>choose(m));$('rehearse').addEventListener('click',rehearse);$('reset-preview').addEventListener('click',resetPreview);$('compare').addEventListener('click',compare);for(const id of ['attendee','notes-preview','reader','observed-fields','visible-content'])$(id).addEventListener('input',invalidateEvidence);$('room-preview').addEventListener('change',invalidateEvidence);$('source-code').value=sourceText;for(const [button,field] of [['select-source','source-code'],['select-conversion','conversion']])$(button).addEventListener('click',()=>{$(field).focus();$(field).select();$('selection-status').textContent='Text selected. Use your device’s copy command and paste manually.';});globalThis.app={choose,rehearse,compare,values,sourceText,sourceHash,get:()=>mode};choose('selective');</script></body></html>