DOJ-4798: add workbook-generate (per-course interactive HTML workbook)#27
Conversation
…mplate
Voice-neutral IDTK surface that turns a course's text classes into one
standalone, accessible, interactive HTML workbook (Rise/Typeform-style),
held to the editorial quality bar of interactive-HTML explainers.
- commands/workbook-generate.md — spec resolution (sibling/remote/graceful-
degrade), per-course discovery + ordering, content->component mapping,
overlay invocation, ${CLAUDE_PLUGIN_ROOT} defensive note
- skills/workbook-generate/SKILL.md — IDT-owned component catalog + mapping
rules + accessibility contract + consumer-spec consumption rules
- assets/templates/workbook/workbook-base.html — self-contained template:
design-system CSS layer (type/spacing/radius/shadow scales, 1.5px borders,
panels+elevation, mono eyebrows, editorial headings), vanilla-JS engine,
full component kit (progressive disclosure, inline SVG, annotated code,
quiz/free-text, comparison table, term->glossary)
State is in-memory only (persistence deferred to V2). No CDN runtime deps.
The consumer's instruction-bundle-spec.yaml themes + optionally tunes the
catalog; the library itself stays in IDT base, voice-neutral. CI lint green.
Created by Claude Code on behalf of @daniel
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
|
@dojo-code-reviewer review |
There was a problem hiding this comment.
✅ Approved
Approved — 0 blockers, 1 P2, 3 P3. Confidence: 3/5.
🟡 P2 — Major
assets/templates/workbook/workbook-base.html:512— 🟡 P2 (major) — Global keydown event listeners for Enter and arrow keys will capture events even when focus is inside active controls like buttons, links, or tabs. This triggers accidental slide navigation when a student presses Enter to expand an accordion, activate a tab, or choose a quiz option. Restricting keydown listener execution to exclude interactions with focused active components prevents overlapping navigation actions.
if (e.target.closest("textarea, input, button, select, a, [role='tab']")) return;
🔵 P3 — Minor
assets/templates/workbook/workbook-base.html:450— 🔵 P3 (minor) — Generating a workbook with zero step elements causes a runtime error when trying to access steps[idx] in show(0) or when dividing by zero in progress calculations. Adding an early-return guard when steps is empty prevents JS execution errors.
if (steps.length === 0) return;
assets/templates/workbook/workbook-base.html:548— 🔵 P3 (minor) — Double-clicking the code copy button before the 1200ms timeout resets its text captures 'Copied' as the original label. When the timer triggers, this leaves the button perpetually stuck showing 'Copied'. Ignoring click events when the button is already in the copied state eliminates this race condition.
btn.addEventListener("click", function () {
if (btn.textContent === "Copied") return;
assets/templates/workbook/workbook-base.html:559— 🔵 P3 (minor) — Selecting glossary definitions with unescaped attribute selectors fails with a DOMException if a glossary term contains special characters like double quotes or parentheses. Iterating through dt elements and validating their data-term attribute prevents unexpected runtime CSS selector syntax errors.
function glossaryEntry(term) {
var dts = document.querySelectorAll(".wb-glossary dt[data-term]");
for (var i = 0; i < dts.length; i++) {
if (dts[i].getAttribute("data-term") === term) return dts[i];
}
return null;
}
Total findings: 1 compliance, 3 business context (4 total)
ℹ️ Inline anchoring failed for the comments below; they are listed here instead.
assets/templates/workbook/workbook-base.html:450— 🔵 P3 (minor) — Generating a workbook with zero step elements causes a runtime error when trying to access steps[idx] in show(0) or when dividing by zero in progress calculations. Adding an early-return guard when steps is empty prevents JS execution errors.
if (steps.length === 0) return;
assets/templates/workbook/workbook-base.html:512— 🟡 P2 (major) — Global keydown event listeners for Enter and arrow keys will capture events even when focus is inside active controls like buttons, links, or tabs. This triggers accidental slide navigation when a student presses Enter to expand an accordion, activate a tab, or choose a quiz option. Restricting keydown listener execution to exclude interactions with focused active components prevents overlapping navigation actions.
if (e.target.closest("textarea, input, button, select, a, [role='tab']")) return;
assets/templates/workbook/workbook-base.html:548— 🔵 P3 (minor) — Double-clicking the code copy button before the 1200ms timeout resets its text captures 'Copied' as the original label. When the timer triggers, this leaves the button perpetually stuck showing 'Copied'. Ignoring click events when the button is already in the copied state eliminates this race condition.
btn.addEventListener("click", function () {
if (btn.textContent === "Copied") return;
assets/templates/workbook/workbook-base.html:559— 🔵 P3 (minor) — Selecting glossary definitions with unescaped attribute selectors fails with a DOMException if a glossary term contains special characters like double quotes or parentheses. Iterating through dt elements and validating their data-term attribute prevents unexpected runtime CSS selector syntax errors.
function glossaryEntry(term) {
var dts = document.querySelectorAll(".wb-glossary dt[data-term]");
for (var i = 0; i < dts.length; i++) {
if (dts[i].getAttribute("data-term") === term) return dts[i];
}
return null;
}
Folds the strongest, reliability-preserving patterns from the html-effectiveness deep-dive into the workbook base + skill: - clipboard copy now has a file:// fallback (execCommand + off-screen textarea) — fixes a silent no-op when the workbook opens as a local file - inline code diff variant (struck removed lines + added lines) - bar chart with precomputed coords + recolored salient datum (.is-peak) - parametric "knob": slider -> CSS var -> live preview — the reliable way to deliver "felt" interactivity without bespoke per-concept JS - skill: documents the new components, the semantic color language (accent=current / good=success / bad=error), the chart-highlight + diff mapping heuristics; promotes the knob to first-class, leaves live-simulation/state-visualizer as the fragile stretch tier Verified in preview: all new components render + work, zero console errors. CI lint green. Created by Claude Code on behalf of @daniel Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
There was a problem hiding this comment.
✅ Approved
Approved — 0 blockers, 1 P2, 1 P3. Confidence: 3/5.
🟡 P2 — Major
assets/templates/workbook/workbook-base.html:499— 🟡 P2 (major) — In stepped mode, the global document keydown listener intercepts all "Enter" key presses to advance the workbook step unless the event target matches"textarea, input". Because the interactive workbook components (accordions, tabs, quiz options, code copy buttons, etc.) are built using<button>or<a>elements, pressing "Enter" to activate them will trigger step navigation as well. This severely violates keyboard accessibility guidelines.
To fix this, update the focus check to exclude all standard interactive elements from triggering page navigation on Enter.
if (e.target.matches("button, a, textarea, input, select, option, [role='button'], [role='tab']")) return;
🔵 P3 — Minor
assets/templates/workbook/workbook-base.html:543— 🔵 P3 (minor) — Complete the roving tabindex implementation for the tabbed interface. Currently, all tabs in the list are standard focusable<button>elements without explicittabindexattributes. This means keyboard users must Tab through every single tab in the list sequentially, which violates the ARIA Tab Panel design pattern.
Updating unselected tabs to tabindex="-1" and the active tab to tabindex="0" ensures the tablist is navigated via arrow keys as intended and acts as a single tab stop.
Total findings: 1 compliance, 1 business context (2 total)
ℹ️ The findings below could not be anchored to a diff line (line not part of any hunk); they are listed here instead.
assets/templates/workbook/workbook-base.html:499— 🟡 P2 (major) — In stepped mode, the global document keydown listener intercepts all "Enter" key presses to advance the workbook step unless the event target matches"textarea, input". Because the interactive workbook components (accordions, tabs, quiz options, code copy buttons, etc.) are built using<button>or<a>elements, pressing "Enter" to activate them will trigger step navigation as well. This severely violates keyboard accessibility guidelines.
To fix this, update the focus check to exclude all standard interactive elements from triggering page navigation on Enter.
if (e.target.matches("button, a, textarea, input, select, option, [role='button'], [role='tab']")) return;
| }); | ||
| }); | ||
|
|
||
| // ---- Tabs (roving) ---- |
There was a problem hiding this comment.
🔵 P3 (minor) — Complete the roving tabindex implementation for the tabbed interface. Currently, all tabs in the list are standard focusable <button> elements without explicit tabindex attributes. This means keyboard users must Tab through every single tab in the list sequentially, which violates the ARIA Tab Panel design pattern.
Updating unselected tabs to tabindex="-1" and the active tab to tabindex="0" ensures the tablist is navigated via arrow keys as intended and acts as a single tab stop.
…+ export Per the html-effectiveness thesis, interactivity here serves display + felt understanding + closing the loop — never capturing answers the artifact can't persist or act on. So: - remove the multiple-choice quiz and free-text input (dead ends with no persistence and no export) - add predict-and-reveal: a "think first, then reveal the model answer" self-check (retrieval + immediate feedback, no capture, no grading) - add export / copy-as-prompt: a first-class loop-closer that copies a ready-made prompt to the clipboard (file:// fallback) so the learner takes work back to Claude or a mentor - consolidate the clipboard helper; document "what interactivity is for", the new components, and the capture-to-nowhere anti-pattern in the skill Verified in preview: predict-and-reveal toggles, export copies + flashes, no quiz/free-text remain, zero console errors. CI lint green. Created by Claude Code on behalf of @daniel Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
There was a problem hiding this comment.
🔴 Changes Requested
Changes requested — 2 blockers, 1 P2, 1 P3. Confidence: 1/5.
🔴 P1 — Blockers
assets/templates/workbook/workbook-base.html:516— 🔴 P1 (blocker) — Intercepting keydowns globally ondocumentin stepped mode intercepts standard keyboard controls for buttons, links, and tabs. For example, pressing "Enter" on the focused "Next" button triggers both the keydown and the click events, causing the workbook to skip two steps at a time. It also prevents accordion triggers or "Reveal" buttons from being activated by the keyboard, navigating the page instead. Exclude active interactive elements from the global listener.
if (e.target.closest("button, a, input, textarea, [role='tab'], [role='button']")) return;
assets/templates/workbook/workbook-base.html:575— 🔴 P1 (blocker) — Reading.innerTexton a hidden DOM element (thepreelement has thehiddenattribute, which usesdisplay: none) returns an empty string""in modern browsers. This causes the "Copy as prompt" button to copy nothing to the clipboard. Use.textContentinstead to ensure the hidden prompt text is copied correctly.
if (payload) copyToClipboard(payload.textContent, btn, "Copied ✓");
🟡 P2 — Major
assets/templates/workbook/workbook-base.html:564— 🟡 P2 (major) — Copying the rawinnerTextof a code diff block copies both deleted (.diff-del) and added (.diff-add) lines, resulting in malformed/unusable code in the clipboard. Clean the diff before copying by removing elements with the.diff-delclass from a cloned node to copy only the final "after" state of the code.
document.querySelectorAll(".wb-code__copy").forEach(function (btn) {
btn.addEventListener("click", function () {
var code = btn.parentElement.querySelector("code");
if (code) {
var text = code.innerText;
if (code.querySelector(".diff-del")) {
var clone = code.cloneNode(true);
clone.querySelectorAll(".diff-del").forEach(function (el) { el.remove(); });
text = clone.innerText;
}
copyToClipboard(text, btn);
}
});
});
🔵 P3 — Minor
assets/templates/workbook/workbook-base.html:506— 🔵 P3 (minor) — Calculating progress by dividing bysteps.lengthyieldsNaNorInfinityif the workbook contains zero steps. Avoid potential rendering errors or invalid inline style values by checkingsteps.lengthdefensively.
setProgress(steps.length > 0 ? ((idx + 1) / steps.length) * 100 : 0);
Total findings: 2 security, 1 compliance, 1 business context (4 total)
ℹ️ Inline anchoring failed for the comments below; they are listed here instead.
assets/templates/workbook/workbook-base.html:516— 🔴 P1 (blocker) — Intercepting keydowns globally ondocumentin stepped mode intercepts standard keyboard controls for buttons, links, and tabs. For example, pressing "Enter" on the focused "Next" button triggers both the keydown and the click events, causing the workbook to skip two steps at a time. It also prevents accordion triggers or "Reveal" buttons from being activated by the keyboard, navigating the page instead. Exclude active interactive elements from the global listener.
if (e.target.closest("button, a, input, textarea, [role='tab'], [role='button']")) return;
assets/templates/workbook/workbook-base.html:575— 🔴 P1 (blocker) — Reading.innerTexton a hidden DOM element (thepreelement has thehiddenattribute, which usesdisplay: none) returns an empty string""in modern browsers. This causes the "Copy as prompt" button to copy nothing to the clipboard. Use.textContentinstead to ensure the hidden prompt text is copied correctly.
if (payload) copyToClipboard(payload.textContent, btn, "Copied ✓");
assets/templates/workbook/workbook-base.html:564— 🟡 P2 (major) — Copying the rawinnerTextof a code diff block copies both deleted (.diff-del) and added (.diff-add) lines, resulting in malformed/unusable code in the clipboard. Clean the diff before copying by removing elements with the.diff-delclass from a cloned node to copy only the final "after" state of the code.
document.querySelectorAll(".wb-code__copy").forEach(function (btn) {
btn.addEventListener("click", function () {
var code = btn.parentElement.querySelector("code");
if (code) {
var text = code.innerText;
if (code.querySelector(".diff-del")) {
var clone = code.cloneNode(true);
clone.querySelectorAll(".diff-del").forEach(function (el) { el.remove(); });
text = clone.innerText;
}
copyToClipboard(text, btn);
}
});
});
assets/templates/workbook/workbook-base.html:506— 🔵 P3 (minor) — Calculating progress by dividing bysteps.lengthyieldsNaNorInfinityif the workbook contains zero steps. Avoid potential rendering errors or invalid inline style values by checkingsteps.lengthdefensively.
setProgress(steps.length > 0 ? ((idx + 1) / steps.length) * 100 : 0);
…ytelling
Reworks the workbook flow + navigation from design review:
- default flow is now `doc` — a navigable document CHUNKED BY MODULE ("lesson"),
one module shown at a time, switched from the TOC (the Rise lesson model).
scroll-snap dropped; plain smooth scroll within a lesson. `stepped` mode kept
for screen-recording.
- navigation: sticky sidebar on desktop; on mobile, a slim sticky bar + a
collapsible "Contents" menu (was a 441px wall of links pushing content down).
- light-touch storytelling: an arc indicator ("Module N of M") + a forward-hook
Next control (action line + an OPTIONAL teaser line that is a voice slot —
empty in the base, filled by the consumer's voice overlay).
- fluid hero type (clamp) + overflow-wrap on headings for small screens.
- command + skill updated for the doc/stepped flow, mobile nav, arc + hook slots.
Verified in preview: chunk switching, mobile collapsible menu, forward-hook +
arc render, no horizontal overflow, zero console errors. CI lint green.
Created by Claude Code on behalf of @daniel
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
There was a problem hiding this comment.
🔴 Changes Requested
Changes requested — 1 blocker, 1 P2, 2 P3. Confidence: 1/5.
🔴 P1 — Blockers
assets/templates/workbook/workbook-base.html:672— 🔴 P1 (blocker) — CallinginnerTexton a hidden element (the payload pre-tag has thehiddenattribute) will return an empty string ("") in all major browsers becauseinnerTextis aware of CSS layout and returns empty for hidden nodes. This will result in the user copying an empty string to their clipboard when clicking "Copy as prompt". UsetextContentinstead, which is not style-aware and successfully retrieves the hidden payload.
if (payload) copyToClipboard(payload.textContent, btn, "Copied ✓");
🟡 P2 — Major
assets/templates/workbook/workbook-base.html:493— 🟡 P2 (major) — The global keydown listener in stepped mode only ignorestextareaandinputtargets. If a user presses 'Enter' or 'ArrowLeft'/'ArrowRight' while focused on other interactive elements (such as accordions, tabs, buttons, or links), the keydown event will bubble up to the document and trigger step navigation. This causes extremely disruptive behavior during step-level interactions. Exclude all interactive elements usingcloseston the target.
if (e.target.closest("textarea, input, button, select, a, [role='tab'], [role='button']")) return;
🔵 P3 — Minor
assets/templates/workbook/workbook-base.html:32— 🔵 P3 (minor) —--wb-accent-softis hardcoded to a dark gray RGB value (rgba(43,43,43,0.08)). If a consumer specifies a custom brand color for--wb-accent, the soft background will remain gray instead of adapting to the brand palette. Utilizing CSScolor-mixallows this soft variant to dynamically derive from the primary accent color across all themes.
--wb-accent-soft: color-mix(in srgb, var(--wb-accent) 8%, transparent);
assets/templates/workbook/workbook-base.html:518— 🔵 P3 (minor) — Performingdocument.querySelectordirectly ona.getAttribute("href")will throw aDOMExceptionand crash the script if the link is external or an invalid selector (e.g. not starting with#). Ensure we safely guard the selection by verifying that the href is an internal hash link, and retrieve it viadocument.getElementByIdto ensure robustness.
a.addEventListener("click", function (e) {
var href = a.getAttribute("href");
if (!href || href.charAt(0) !== '#') return;
var target = document.getElementById(href.slice(1));
if (!target) return;
Total findings: 1 security, 1 compliance, 2 business context (4 total)
ℹ️ Inline anchoring failed for the comments below; they are listed here instead.
assets/templates/workbook/workbook-base.html:672— 🔴 P1 (blocker) — CallinginnerTexton a hidden element (the payload pre-tag has thehiddenattribute) will return an empty string ("") in all major browsers becauseinnerTextis aware of CSS layout and returns empty for hidden nodes. This will result in the user copying an empty string to their clipboard when clicking "Copy as prompt". UsetextContentinstead, which is not style-aware and successfully retrieves the hidden payload.
if (payload) copyToClipboard(payload.textContent, btn, "Copied ✓");
assets/templates/workbook/workbook-base.html:493— 🟡 P2 (major) — The global keydown listener in stepped mode only ignorestextareaandinputtargets. If a user presses 'Enter' or 'ArrowLeft'/'ArrowRight' while focused on other interactive elements (such as accordions, tabs, buttons, or links), the keydown event will bubble up to the document and trigger step navigation. This causes extremely disruptive behavior during step-level interactions. Exclude all interactive elements usingcloseston the target.
if (e.target.closest("textarea, input, button, select, a, [role='tab'], [role='button']")) return;
assets/templates/workbook/workbook-base.html:32— 🔵 P3 (minor) —--wb-accent-softis hardcoded to a dark gray RGB value (rgba(43,43,43,0.08)). If a consumer specifies a custom brand color for--wb-accent, the soft background will remain gray instead of adapting to the brand palette. Utilizing CSScolor-mixallows this soft variant to dynamically derive from the primary accent color across all themes.
--wb-accent-soft: color-mix(in srgb, var(--wb-accent) 8%, transparent);
assets/templates/workbook/workbook-base.html:518— 🔵 P3 (minor) — Performingdocument.querySelectordirectly ona.getAttribute("href")will throw aDOMExceptionand crash the script if the link is external or an invalid selector (e.g. not starting with#). Ensure we safely guard the selection by verifying that the href is an internal hash link, and retrieve it viadocument.getElementByIdto ensure robustness.
a.addEventListener("click", function (e) {
var href = a.getAttribute("href");
if (!href || href.charAt(0) !== '#') return;
var target = document.getElementById(href.slice(1));
if (!target) return;
…bustness Greptile first-pass findings on PR #27: - P1 (blocker): "Copy as prompt" copied an empty string — innerText returns "" for the hidden payload <pre>. Use textContent (style-agnostic, portable). - P2 (major): stepped-mode global keydown hijacked Enter/arrows while focus was on a control (double-advance; blocked accordion/tab/reveal activation). Guard with closest() over interactive elements so they activate natively. - P3: --wb-accent-soft was hardcoded gray; derive from --wb-accent via color-mix so the soft tint tracks the consumer's brand. - P3: TOC click did querySelector(href) (throws on non-hash/external href); guard to internal hash + getElementById. Verified in preview: export copies the non-empty payload, color-mix resolves, TOC chunk-switching intact, zero console errors. CI lint green. Created by Claude Code on behalf of @daniel Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
There was a problem hiding this comment.
✅ Approved
Approved — 0 blockers, 2 P2, 1 P3. Confidence: 3/5.
🟡 P2 — Major
assets/templates/workbook/workbook-base.html:483— 🟡 P2 (major) — The table of contents links are completely non-functional in stepped mode because the event listener to prevent default anchor behavior and update the active step is only registered in thedocmode branch. Clicking a link in stepped mode will trigger the browser's default hash navigation, which fails to scroll or focus because the target step hasdisplay: noneset.
Registering a click handler for navLinks inside the stepped mode block that maps the clicked module or step to the correct index and calls show(idx) will make navigation work seamlessly in both modes.
assets/templates/workbook/workbook-base.html:629— 🟡 P2 (major) — The tabs component lacks full keyboard roving tabindex support and automatic activation when navigating via arrow keys. Right now, keyboard users must Tab through every individual tab button sincetabindexis not managed, and arrow key focus does not trigger panel selection or updatearia-selectedstatus.
Updating tabindex dynamically (active tab is 0, inactive tabs are -1) and automatically selecting the focused tab during arrow key navigation satisfies standard WAI-ARIA guidelines and ensures a high-quality, accessible experience.
// ---- Tabs (roving & automatic activation) ----
document.querySelectorAll(".wb-tabs").forEach(function (group) {
var tabs = Array.prototype.slice.call(group.querySelectorAll('[role="tab"]'));
function select(tab) {
tabs.forEach(function (t) {
var sel = t === tab;
t.setAttribute("aria-selected", String(sel));
t.setAttribute("tabindex", sel ? "0" : "-1");
var panel = document.getElementById(t.getAttribute("aria-controls"));
if (panel) panel.hidden = !sel;
});
}
tabs.forEach(function (t, i) {
t.setAttribute("tabindex", t.getAttribute("aria-selected") === "true" ? "0" : "-1");
t.addEventListener("click", function () { select(t); });
t.addEventListener("keydown", function (e) {
var nextTab;
if (e.key === "ArrowRight") { nextTab = tabs[(i + 1) % tabs.length]; }
else if (e.key === "ArrowLeft") { nextTab = tabs[(i - 1 + tabs.length) % tabs.length]; }
if (nextTab) {
nextTab.focus();
select(nextTab);
}
});
});
});
🔵 P3 — Minor
assets/templates/workbook/workbook-base.html:253— 🔵 P3 (minor) — Inline SVGs sharing the duplicate marker IDwb-arrowwill conflict when multiple flow diagrams are generated on the same page. The browser will only use the first defined marker, which can cause rendering bugs if custom colors or styles are applied in later diagrams.
Incorporating a unique suffix (e.g., wb-arrow-{{id}}) dynamically during generation inside the workbook-generate command will prevent ID clashing.
Total findings: 2 compliance, 1 business context (3 total)
ℹ️ The findings below could not be anchored to a diff line (line not part of any hunk); they are listed here instead.
assets/templates/workbook/workbook-base.html:629— 🟡 P2 (major) — The tabs component lacks full keyboard roving tabindex support and automatic activation when navigating via arrow keys. Right now, keyboard users must Tab through every individual tab button sincetabindexis not managed, and arrow key focus does not trigger panel selection or updatearia-selectedstatus.
Updating tabindex dynamically (active tab is 0, inactive tabs are -1) and automatically selecting the focused tab during arrow key navigation satisfies standard WAI-ARIA guidelines and ensures a high-quality, accessible experience.
// ---- Tabs (roving & automatic activation) ----
document.querySelectorAll(".wb-tabs").forEach(function (group) {
var tabs = Array.prototype.slice.call(group.querySelectorAll('[role="tab"]'));
function select(tab) {
tabs.forEach(function (t) {
var sel = t === tab;
t.setAttribute("aria-selected", String(sel));
t.setAttribute("tabindex", sel ? "0" : "-1");
var panel = document.getElementById(t.getAttribute("aria-controls"));
if (panel) panel.hidden = !sel;
});
}
tabs.forEach(function (t, i) {
t.setAttribute("tabindex", t.getAttribute("aria-selected") === "true" ? "0" : "-1");
t.addEventListener("click", function () { select(t); });
t.addEventListener("keydown", function (e) {
var nextTab;
if (e.key === "ArrowRight") { nextTab = tabs[(i + 1) % tabs.length]; }
else if (e.key === "ArrowLeft") { nextTab = tabs[(i - 1 + tabs.length) % tabs.length]; }
if (nextTab) {
nextTab.focus();
select(nextTab);
}
});
});
});
| <button class="wb-btn" id="wb-next" type="button">Next →</button> | ||
| </div> | ||
| </div> | ||
| </main> |
There was a problem hiding this comment.
🟡 P2 (major) — The table of contents links are completely non-functional in stepped mode because the event listener to prevent default anchor behavior and update the active step is only registered in the doc mode branch. Clicking a link in stepped mode will trigger the browser's default hash navigation, which fails to scroll or focus because the target step has display: none set.
Registering a click handler for navLinks inside the stepped mode block that maps the clicked module or step to the correct index and calls show(idx) will make navigation work seamlessly in both modes.
| .wb-recap { text-align: center; padding: var(--wb-sp-8) 0; } | ||
| .wb-recap__badge { font-size: 2.5rem; color: var(--wb-good); } | ||
| .wb-recap h2 { font-size: 1.9rem; letter-spacing: var(--wb-h-tracking); } | ||
| .wb-recap[hidden] { display: none; } |
There was a problem hiding this comment.
🔵 P3 (minor) — Inline SVGs sharing the duplicate marker ID wb-arrow will conflict when multiple flow diagrams are generated on the same page. The browser will only use the first defined marker, which can cause rendering bugs if custom colors or styles are applied in later diagrams.
Incorporating a unique suffix (e.g., wb-arrow-{{id}}) dynamically during generation inside the workbook-generate command will prevent ID clashing.
Summary
Adds the IDTK
workbook-generatesurface — a voice-neutral command + skill that turns a course's text classes into one standalone, accessible, interactive HTML workbook (Rise/Typeform-style continuous flow), held to the editorial quality bar of interactive-HTML explainers like html-effectiveness.Student-facing sibling of
course-visualize(per-course HTML). Distinct fromslides-generate(video briefs). Per-course by default;--scope modulenarrows.Files (3 new)
commands/workbook-generate.md— spec resolution (sibling repo → remote → graceful-degrade), per-course discovery + ordering oftext-*.md, content→component mapping, overlay invocation,${CLAUDE_PLUGIN_ROOT}defensive note.skills/workbook-generate/SKILL.md— the IDT-owned, voice-neutral component catalog + content-shape→component mapping rules + accessibility contract + consumer-spec consumption (the consumer YAML themes/tunes; it never defines the library).assets/templates/workbook/workbook-base.html— self-contained template: design-system CSS layer (type/spacing/radius/shadow scales, 1.5px borders, panels + elevation, mono eyebrows, editorial heading weight + negative tracking, ~680px measure, 300ms SVG transitions), vanilla-JS engine, full component kit — progressive disclosure (accordion/tabs/reveal), inline SVG (flow/comparison/chart/figure), annotated code, multiple-choice + free-text, comparison table, term→glossary micro-interaction, course-nav TOC + progress.Constraints honored
<link>+system-uifallback).localStorage/postMessage/iframe (persistence deferred to V2).instruction-bundle-spec.yaml.prefers-reduced-motion, ARIA landmarks/progressbar, focus-visible rings, AA contrast, text+icon quiz feedback.Test plan
python3 scripts/ci/check_frontmatter.py && check_agent_references.py && check_json_schemas.py→ all greenLinear
Part of DOJ-4798 (spike result of DOJ-4783). Paired with dojo-academy PR (workbooks spec preferences). Works today via graceful defaults even before that lands.
Created by Claude Code on behalf of @daniel
🤖 Generated with Claude Code