メインコンテンツまでスキップ

Fix: docs pages were unresponsive on first load

· 約3分
工具坞
工具坞团队

On a first visit to any docs page, the content was all there — but clicking a button like "Encrypt" did nothing. No spinner, no error toast, nothing in the console. Reloading the page, or switching language and back, made it work again. The cause turned out to be a subtle mismatch between what the server rendered and what the browser built from it, and it affected every docs page on the site.

What was happening​

The little icons next to the sidebar categories are drawn by a small block of inline CSS. On the server, React escapes special characters in that CSS the same way it escapes any text — > became &gt;, quotes became &quot;. But browsers deliberately do not decode entities inside a <style> tag: its content is raw text. So the CSS that actually reached the page contained literal &gt; sequences, and the browser's version of the page no longer matched what React expected.

When React detects that the server HTML and the client render disagree, it aborts hydration and throws away the server-rendered page, then rebuilds the entire thing in JavaScript. Until that rebuild finishes, none of the page is interactive: the buttons are visible, but clicks land on markup nothing is listening to yet.

On a warm cache the rebuild is fast enough that you never notice. On a cold first visit — especially on a slower connection — the unresponsive window is long enough to click "Encrypt" and get absolutely nothing, with no console output to explain it. That is also why switching to Chinese and back "fixed" it: by then the scripts were cached.

The fix​

Three inline style blocks now emit their CSS verbatim, so the server and the client produce identical markup. The affected spots:

  • Sidebar category icons — the main culprit, present on every docs page.
  • YAML ↔ JSON converter dark-mode highlighting — the same escaping issue was hitting its [data-theme='dark'] rules, so syntax colours fell back to their light values until hydration finished.
  • Yes/No wheel animation — no special characters in its CSS today, but the same pattern, fixed so it cannot bite later.

Verified​

  • Same environment, before vs after: React hydration errors on a docs page dropped from 13 to 0.
  • All 211 unit tests pass, including three new regression tests that assert the style contents survive server rendering (they fail against the old code).
  • A local production build was served and driven in a headless browser: a first visit to /en/docs/crypto/bacon-cipher now accepts input and returns an encoding immediately.