Add code-block syntax highlighting + full-screen Markdown editor

Add code-block syntax highlighting + full-screen Markdown editor

#16 in tonybierman/dx-blog — merged 2026-05-30

This PR bundles two related editor/reader improvements.


1. Server-side syntax highlighting for code blocks

Fenced code blocks rendered as plain <pre><code> with no token coloring. This adds syntect-based syntax highlighting with a per-block language label, done entirely server-side so it adds zero bytes to the wasm bundle.

Why this architecture

render_markdown/parse_body deliberately run on both the server (SSR) and the wasm client (hydration + the editor's live preview). Calling syntect there would ship its ~1–2 MB of bundled syntaxes to every reader's browser.

Instead, the post body is rendered (with highlighting) on the server and delivered as serialized Segments through the use_server_future the reader already uses — the client reuses those bytes and never runs syntect. Hydration parity is automatic because both sides use the identical server-produced HTML.

Changes

  • syntect in the server feature only (default-fancy, pure-Rust regex — no C toolchain). cargo tree --features web confirms it's absent from the wasm build.
  • src/server/highlight.rs (new): class-based spans (syn- prefix to avoid Tailwind collisions) + a data-lang attribute that drives a CSS corner label. Sanitized via an ammonia builder that permits class/data-lang on pre/code/span (default clean strips them).
  • Segment now derives Serialize/Deserialize; PostDetail.body_segments is server-filled (sqlx(skip)). The reader renders from it instead of re-running the markdown pipeline client-side.
  • Editor live preview keeps its zero-latency client-side render — code shows unhighlighted while typing; the published/reader view is highlighted.
  • assets/highlight.css generated from the bundled base16-ocean.dark theme (via an #[ignore]d generator test in highlight.rs).

2. Full-screen mode for the Markdown editor

Adds a full-screen toggle to the Markdown editor (src/pages/admin/posts.rs).


Testing

  • cargo check on server, web, and default targets — all green
  • cargo tree --features web | grep syntect → empty (no wasm impact)
  • cargo test --features server,sqlite → 19 pass, including new tests asserting syn- classes survive sanitization, the data-lang label is present, unlabeled fences get no label, and non-code prose stays byte-identical to the plain pipeline
  • cargo fmt --check clean

Not yet done: a manual visual check in the browser.

🤖 Generated with Claude Code

Last updated 2026-05-31