Add client-side syntax highlighting for code blocks in lessons - #130
Merged
Conversation
Fenced code blocks in lessons render monochrome even though the renderer has emitted language-tagged markup since ADR-0013. The plan adds a client-side highlighter on the ADR-0016 pattern: pinned self-hosted highlight.js, lazy-loaded, explicit languages only, token colors mapped to the surface-safe theme accents. Refs #128
ADR-0017 closes the seam ADR-0013 left open (the sanitizer keeps the code class for a future syntax highlighter) with the same shape as ADR-0016: the browser upgrades sanitized markup, the server changes nothing. highlight.js is chosen over Prism (needs a bundler or per-language tags for equal coverage) and over any server-side highlighter (would force span classes through the sanitizer and bake presentation into the content-addressed cache). Token colors map to the theme accents already proven surface-safe, so both themes pass AA by construction. Refs #128
The bundle ships self-hosted through org.webjars.npm:highlightjs__cdn-assets, version pinned where Dependabot can see it (highlight.js has had ReDoS advisories). The cdn-assets artifact is deliberate: since v11 the plain highlight.js webjar contains only bundler modules and no browser build. Jar content verified (highlight.min.js with the common languages baked in) and the POM declares zero transitive dependencies, confirmed with dependency:tree. Lesson pages require authentication, so the existing catch-all rule already authorizes the /webjars path. Refs #128
Language-tagged code blocks rendered monochrome even though the renderer has emitted the language-* class since ADR-0013, kept through sanitization for exactly this feature. lesson-highlight.js now closes that seam on the ADR-0017 decision: it loads the self-hosted highlight.js bundle lazily, only when the lesson contains a language-tagged fence other than mermaid, and highlights only blocks whose language the bundle recognizes. Unknown hints and bare fences stay monochrome by design (no auto-detect guessing), and mermaid fences remain the diagram feature's territory. Token colors come from a small hand-written mapping to the theme accent tokens already proven to clear 4.5:1 on the surface background in both themes, so highlighted code follows the design system and any future token retuning with no vendor CSS to fork. The lesson form hints that fences take a language tag. Fixes #128
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## dev #130 +/- ##
=========================================
Coverage 81.71% 81.71%
Complexity 202 202
=========================================
Files 38 38
Lines 864 864
Branches 53 53
=========================================
Hits 706 706
Misses 124 124
Partials 34 34 ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
5 tasks
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
This PR adds client-side syntax highlighting for code blocks in lessons.
It uses a pinned, self-hosted
highlight.jsbundle.Why
Fenced code blocks in lessons are rendered monochrome, even though the
renderer has emitted
code class="language-java"since ADR-0013, wherethe sanitizer deliberately kept the
classoncode"for a futuresyntax highlighter".
This PR adds the highlighter.
How
Client-side highlighting on the same pattern as the Mermaid feature
(ADR-0016: the server keeps shipping sanitized, text-only code blocks
and the browser upgrades them. Decision recorded in
ADR-0017.
docs(plan): implementation plan(
docs/plans/2026-07-31-lesson-syntax-highlighting.md)docs(adr): ADR-0017, highlight.js over Prism, a server-sidehighlighter, or nothing
build(deps): pinnedorg.webjars.npm:highlightjs__cdn-assets11.11.1, zero transitives. The
cdn-assetsartifact is deliberate:since v11 the plain
highlight.jsWebJar ships only bundler modulesand no browser build
feat(frontend):lesson-highlight.jsloads the bundle lazily, onlywhen the lesson contains a
language-*fence other thanlanguage-mermaid, and highlights only languages the bundle knows;unknown hints and bare fences stay monochrome by design (no
auto-detect guessing). Token colors map to the existing surface-safe
accent tokens, so both themes pass WCAG AA with no vendor CSS to fork
Verification
pinning
language-javasurvival through sanitization is untouched.bash fences gain colored tokens in both themes; an unknown-language
fence and a bare fence stay monochrome; a mermaid fence is skipped by
the highlighter and still renders as a diagram; axe (WCAG 2.1 A/AA)
reports zero violations in both themes; no horizontal page scroll.
Note
No sanitizer, renderer, or cache change: the whole diff is one JS
file, one CSS block, one dependency, two template lines, and docs.