From 27a1bcdbf818fa6b0aa9402afb8534fa62151a3b Mon Sep 17 00:00:00 2001 From: k6G52m4Dz75W <74605402+k6G52m4Dz75W@users.noreply.github.com> Date: Tue, 1 Sep 2026 23:08:55 +0800 Subject: [PATCH 1/3] fix(reader): mobile useBookFonts must not force reader font on body The mobile reader.template.html always emitted an unconditional ':root:not(.vrtl):not(.vltr) body { font-family: var(--readany-font-family) !important }' in baseStyles. Even with useBookFonts enabled (default), this pinned the reader font on body and overrode the book's own font-family set on body, so embedded EPUB fonts never rendered on mobile. Align with the desktop implementation: when useBookFonts is on, emit only a zero-specificity ':where(html)' fallback so body INHERITS the reader font but the book's own font-family (on html, body, or any element) wins where specified. When disabled, force reader font on html/body and every descendant. reader.html is regenerated by build:reader during prebuild, so only the template source changes here. Verified on Android emulator: book fonts now render with useBookFonts enabled. --- .../assets/reader/reader.template.html | 24 +++++++++++++------ 1 file changed, 17 insertions(+), 7 deletions(-) diff --git a/packages/app-expo/assets/reader/reader.template.html b/packages/app-expo/assets/reader/reader.template.html index 87e38df3e..bbcbb91ea 100644 --- a/packages/app-expo/assets/reader/reader.template.html +++ b/packages/app-expo/assets/reader/reader.template.html @@ -1918,11 +1918,22 @@ const ps = Math.round(currentParagraphSpacing * layoutScale); // When useBookFonts is enabled (default), do not force the reader - // font family onto every element so the book's own fonts (e.g. - // embedded @font-face families) render where the book specifies them. - const bodyStarFontOverride = currentUseBookFonts - ? '' - : `:root:not(.vrtl):not(.vltr) body *:not(svg):not(svg *):not(math):not(math *):not(pre):not(pre *):not(code):not(code *):not(kbd):not(kbd *):not(samp):not(samp *) { + // font family onto html/body with !important: the book's own + // font-family (on html, body, or any element) must win where specified, + // so embedded @font-face families render. The reader font stays as a + // zero-specificity fallback via :where(html). Target only html (not + // body): body then INHERITS html's font-family, so when the book sets + // one on html it propagates to body text instead of body being pinned + // to the reader font by a direct rule. When disabled, force the reader + // font on html/body and every descendant. + const readerFontOverride = currentUseBookFonts + ? `:where(html) { + font-family: var(--readany-font-family); +}` + : `html, body { + font-family: var(--readany-font-family) !important; +} +body *:not(svg):not(svg *):not(math):not(math *):not(pre):not(pre *):not(code):not(code *):not(kbd):not(kbd *):not(samp):not(samp *) { font-family: var(--readany-font-family) !important; }`; @@ -1932,12 +1943,11 @@ } :root:not(.vrtl):not(.vltr), :root:not(.vrtl):not(.vltr) body { - font-family: var(--readany-font-family) !important; font-size: ${fSize}px !important; -webkit-text-size-adjust: none; text-size-adjust: none; } - ${bodyStarFontOverride} + ${readerFontOverride} :root:not(.vrtl):not(.vltr) body *:not(svg):not(svg *):not(math):not(math *):not(pre):not(pre *):not(code):not(code *):not(kbd):not(kbd *):not(samp):not(samp *):not(rt):not(rp) { font-size: ${fSize}px !important; } From 1e1e5217fde3d1d122ce458643345d81a893a5f3 Mon Sep 17 00:00:00 2001 From: k6G52m4Dz75W <74605402+k6G52m4Dz75W@users.noreply.github.com> Date: Fri, 11 Sep 2026 23:56:08 +0800 Subject: [PATCH 2/3] fix(reader): stop forcing the app monospace over book code fonts MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The pre/code/kbd/samp monospace rule was unconditional !important on both ends, so with useBookFonts enabled (default) a book's own code font — e.g. an embedded @font-face on pre — never rendered, the same class of bug PR #756 fixed for the body font. The monospace rule now swaps sides with the same toggle: - useBookFonts on: :where(pre, code, kbd, samp) at zero specificity with no !important, so any book declaration wins; without one the monospace chain still applies as the fallback. - useBookFonts off (override): html body :is(pre, code, kbd, samp) with !important. !important alone is not enough: specificity still breaks ties between important author declarations, so a plain 'pre, code, kbd' at (0,0,1) loses to an authored 'body pre { ... !important }' at (0,0,2); (0,0,3) outranks both. Mirrors readest#6047 (code-font cascade hardening) on top of the appearance-branch :where approach. Verified the resolved cascade in headless Chromium across 7 scenarios: book code/body fonts win when honored, fallbacks still apply, and the override beats authored !important rules at equal and higher specificity on both ends. --- packages/app-expo/assets/reader/reader.html | 41 ++++++++++++++----- .../assets/reader/reader.template.html | 17 ++++++-- .../src/components/reader/FoliateViewer.tsx | 18 ++++++-- 3 files changed, 59 insertions(+), 17 deletions(-) diff --git a/packages/app-expo/assets/reader/reader.html b/packages/app-expo/assets/reader/reader.html index 43b01a6a8..d9b05767f 100644 --- a/packages/app-expo/assets/reader/reader.html +++ b/packages/app-expo/assets/reader/reader.html @@ -2093,12 +2093,37 @@ const ps = Math.round(currentParagraphSpacing * layoutScale); // When useBookFonts is enabled (default), do not force the reader - // font family onto every element so the book's own fonts (e.g. - // embedded @font-face families) render where the book specifies them. - const bodyStarFontOverride = currentUseBookFonts - ? '' - : `:root:not(.vrtl):not(.vltr) body *:not(svg):not(svg *):not(math):not(math *):not(pre):not(pre *):not(code):not(code *):not(kbd):not(kbd *):not(samp):not(samp *) { + // font family onto html/body with !important: the book's own + // font-family (on html, body, or any element) must win where specified, + // so embedded @font-face families render. The reader font stays as a + // zero-specificity fallback via :where(html). Target only html (not + // body): body then INHERITS html's font-family, so when the book sets + // one on html it propagates to body text instead of body being pinned + // to the reader font by a direct rule. When disabled, force the reader + // font on html/body and every descendant. + // + // The monospace rule swaps sides with the same toggle: with book fonts + // honored it is zero-specificity (a book's own `pre { font-family: ... }` + // — its embedded code font — wins), and when overriding it needs BOTH + // !important AND higher specificity, because specificity still breaks + // ties between important author declarations: a plain `pre, code, kbd` + // at (0,0,1) loses to an authored `body pre { ... !important }` at + // (0,0,2). `html body :is(...)` at (0,0,3) outranks both. + const readerFontOverride = currentUseBookFonts + ? `:where(html) { + font-family: var(--readany-font-family); +} +:where(pre, code, kbd, samp) { + font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace; +}` + : `html, body { + font-family: var(--readany-font-family) !important; +} +body *:not(svg):not(svg *):not(math):not(math *):not(pre):not(pre *):not(code):not(code *):not(kbd):not(kbd *):not(samp):not(samp *) { font-family: var(--readany-font-family) !important; +} +html body :is(pre, code, kbd, samp) { + font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace !important; }`; const baseStyles = ` @@ -2107,12 +2132,11 @@ } :root:not(.vrtl):not(.vltr), :root:not(.vrtl):not(.vltr) body { - font-family: var(--readany-font-family) !important; font-size: ${fSize}px !important; -webkit-text-size-adjust: none; text-size-adjust: none; } - ${bodyStarFontOverride} + ${readerFontOverride} :root:not(.vrtl):not(.vltr) body *:not(svg):not(svg *):not(math):not(math *):not(pre):not(pre *):not(code):not(code *):not(kbd):not(kbd *):not(samp):not(samp *):not(rt):not(rp) { font-size: ${fSize}px !important; } @@ -2145,9 +2169,6 @@ :root:not(.vrtl):not(.vltr) h6 { line-height: ${lh} !important; } - pre, code, kbd, samp { - font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace !important; - } :root:not(.vrtl):not(.vltr) p { margin-top: ${ps}px !important; margin-bottom: ${ps}px !important; diff --git a/packages/app-expo/assets/reader/reader.template.html b/packages/app-expo/assets/reader/reader.template.html index bbcbb91ea..e8c533cb6 100644 --- a/packages/app-expo/assets/reader/reader.template.html +++ b/packages/app-expo/assets/reader/reader.template.html @@ -1926,15 +1926,29 @@ // one on html it propagates to body text instead of body being pinned // to the reader font by a direct rule. When disabled, force the reader // font on html/body and every descendant. + // + // The monospace rule swaps sides with the same toggle: with book fonts + // honored it is zero-specificity (a book's own `pre { font-family: ... }` + // — its embedded code font — wins), and when overriding it needs BOTH + // !important AND higher specificity, because specificity still breaks + // ties between important author declarations: a plain `pre, code, kbd` + // at (0,0,1) loses to an authored `body pre { ... !important }` at + // (0,0,2). `html body :is(...)` at (0,0,3) outranks both. const readerFontOverride = currentUseBookFonts ? `:where(html) { font-family: var(--readany-font-family); +} +:where(pre, code, kbd, samp) { + font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace; }` : `html, body { font-family: var(--readany-font-family) !important; } body *:not(svg):not(svg *):not(math):not(math *):not(pre):not(pre *):not(code):not(code *):not(kbd):not(kbd *):not(samp):not(samp *) { font-family: var(--readany-font-family) !important; +} +html body :is(pre, code, kbd, samp) { + font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace !important; }`; const baseStyles = ` @@ -1980,9 +1994,6 @@ :root:not(.vrtl):not(.vltr) h6 { line-height: ${lh} !important; } - pre, code, kbd, samp { - font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace !important; - } :root:not(.vrtl):not(.vltr) p { margin-top: ${ps}px !important; margin-bottom: ${ps}px !important; diff --git a/packages/app/src/components/reader/FoliateViewer.tsx b/packages/app/src/components/reader/FoliateViewer.tsx index 59e7ebdbb..1c919187b 100644 --- a/packages/app/src/components/reader/FoliateViewer.tsx +++ b/packages/app/src/components/reader/FoliateViewer.tsx @@ -3474,6 +3474,14 @@ function getRendererStyles(settings: ViewSettings, theme: AppTheme): string { // book sets one on html it propagates to body text instead of body being // pinned to the reader font by a direct rule. When disabled, force the // reader font on html/body and every descendant. + // + // The monospace rule swaps sides with the same toggle: with book fonts + // honored it is zero-specificity (a book's own `pre { font-family: ... }` — + // its embedded code font — wins), and when overriding it needs BOTH + // !important AND higher specificity, because specificity still breaks ties + // between important author declarations: a plain `pre, code, kbd` at (0,0,1) + // loses to an authored `body pre { ... !important }` at (0,0,2). + // `html body :is(...)` at (0,0,3) outranks both. const readerFontOverride = settings.useBookFonts === false ? `html, body { @@ -3482,10 +3490,16 @@ function getRendererStyles(settings: ViewSettings, theme: AppTheme): string { body *:not(svg):not(svg *):not(math):not(math *):not(pre):not(pre *):not(code):not(code *):not(kbd):not(kbd *):not(samp):not(samp *) { font-family: var(--readany-font-family) !important; } +html body :is(pre, code, kbd, samp) { + font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace !important; +} ` : `:where(html) { font-family: var(--readany-font-family); } +:where(pre, code, kbd, samp) { + font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace; +} `; return `${settings.customFontFaceCSS ? `/* Custom font faces */\n${settings.customFontFaceCSS}\n\n` : ""}/* Font styles */ @@ -3510,10 +3524,6 @@ body :not(#__readany_font_size_override):not(svg):not(svg *):not(math):not(math font-size: ${settings.fontSize}px !important; } -pre, code, kbd, samp { - font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace !important; -} - /* Line height for text blocks */ p, div, blockquote, dd, li, span { line-height: ${settings.lineHeight} !important; From 659f749eba6eab67643db378566262dda0e5dc9f Mon Sep 17 00:00:00 2001 From: k6G52m4Dz75W <74605402+k6G52m4Dz75W@users.noreply.github.com> Date: Sat, 12 Sep 2026 00:15:32 +0800 Subject: [PATCH 3/3] =?UTF-8?q?fix(reader):=20OCR=20follow-ups=20=E2=80=94?= =?UTF-8?q?=20vertical=20guard,=20inner-code=20inheritance,=20contract=20t?= =?UTF-8?q?est?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Addresses the 6 findings from the OCR review of the code-font work: - Restore the horizontal-writing guard (:root:not(.vrtl):not(.vltr)) on the forced overrides in the mobile template. The pre-existing rules were scoped to horizontal documents and the vertical branch must keep its own behavior — the unscoped 'html, body' / 'body *' rules from the previous commit leaked into vertical mode (OCR medium). - Inner code/kbd/samp inside a pre now get a zero-specificity 'font-family: inherit' instead of just being excluded from the monospace fallback: the UA stylesheet's own 'code { font-family: monospace }' is a direct declaration, which beats inheritance, so exclusion alone still cut '
' off from the book's font
  declared on pre (OCR low, verified in a real engine). Any author
  declaration on the element still wins the inherit.
- Add usebook-fonts-contract.test.ts pinning the rule shapes for both
  toggle states across the template, the generated artifact, and the
  desktop generator — the triplicated recipe previously had no
  automated coverage and could drift silently (OCR low x2).

Verified in headless Chromium: 15 scenarios pass covering both toggle
states, inner-code inheritance, UA-default interception, the vertical
guard, and override-vs-authored-important at equal and higher
specificity.
---
 packages/app-expo/assets/reader/reader.html   | 23 ++++--
 .../assets/reader/reader.template.html        | 23 ++++--
 .../reader/usebook-fonts-contract.test.ts     | 77 +++++++++++++++++++
 .../src/components/reader/FoliateViewer.tsx   |  9 ++-
 4 files changed, 121 insertions(+), 11 deletions(-)
 create mode 100644 packages/app-expo/src/screens/reader/usebook-fonts-contract.test.ts

diff --git a/packages/app-expo/assets/reader/reader.html b/packages/app-expo/assets/reader/reader.html
index d9b05767f..88b1a700e 100644
--- a/packages/app-expo/assets/reader/reader.html
+++ b/packages/app-expo/assets/reader/reader.html
@@ -2108,21 +2108,34 @@
         // !important AND higher specificity, because specificity still breaks
         // ties between important author declarations: a plain `pre, code, kbd`
         // at (0,0,1) loses to an authored `body pre { ... !important }` at
-        // (0,0,2). `html body :is(...)` at (0,0,3) outranks both.
+        // (0,0,2). The guarded `:root ... body :is(...)` sits at (0,2,2).
+        // Inner code/kbd/samp INSIDE a pre get an explicit `inherit` instead:
+        // the UA stylesheet declares `code { font-family: monospace }` and a
+        // direct declaration (even a UA one) always beats inheritance, so
+        // exclusion alone would still cut `
` off from the book's
+        // font declared on pre. The zero-specificity inherit restores the
+        // chain while any author declaration on the element still wins it.
+        // Standalone inline code keeps the monospace fallback.
+        // Vertical writing (.vrtl/.vltr) keeps its own behavior: the forced
+        // overrides carry the horizontal guard like the pre-existing rules.
         const readerFontOverride = currentUseBookFonts
           ? `:where(html) {
   font-family: var(--readany-font-family);
 }
-:where(pre, code, kbd, samp) {
+:where(pre, :not(pre) > code, :not(pre) > kbd, :not(pre) > samp) {
   font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace;
+}
+:where(pre :is(code, kbd, samp)) {
+  font-family: inherit;
 }`
-          : `html, body {
+          : `:root:not(.vrtl):not(.vltr),
+:root:not(.vrtl):not(.vltr) body {
   font-family: var(--readany-font-family) !important;
 }
-body *:not(svg):not(svg *):not(math):not(math *):not(pre):not(pre *):not(code):not(code *):not(kbd):not(kbd *):not(samp):not(samp *) {
+:root:not(.vrtl):not(.vltr) body *:not(svg):not(svg *):not(math):not(math *):not(pre):not(pre *):not(code):not(code *):not(kbd):not(kbd *):not(samp):not(samp *) {
   font-family: var(--readany-font-family) !important;
 }
-html body :is(pre, code, kbd, samp) {
+:root:not(.vrtl):not(.vltr) body :is(pre, code, kbd, samp) {
   font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace !important;
 }`;
 
diff --git a/packages/app-expo/assets/reader/reader.template.html b/packages/app-expo/assets/reader/reader.template.html
index e8c533cb6..0c0d1795a 100644
--- a/packages/app-expo/assets/reader/reader.template.html
+++ b/packages/app-expo/assets/reader/reader.template.html
@@ -1933,21 +1933,34 @@
         // !important AND higher specificity, because specificity still breaks
         // ties between important author declarations: a plain `pre, code, kbd`
         // at (0,0,1) loses to an authored `body pre { ... !important }` at
-        // (0,0,2). `html body :is(...)` at (0,0,3) outranks both.
+        // (0,0,2). The guarded `:root ... body :is(...)` sits at (0,2,2).
+        // Inner code/kbd/samp INSIDE a pre get an explicit `inherit` instead:
+        // the UA stylesheet declares `code { font-family: monospace }` and a
+        // direct declaration (even a UA one) always beats inheritance, so
+        // exclusion alone would still cut `
` off from the book's
+        // font declared on pre. The zero-specificity inherit restores the
+        // chain while any author declaration on the element still wins it.
+        // Standalone inline code keeps the monospace fallback.
+        // Vertical writing (.vrtl/.vltr) keeps its own behavior: the forced
+        // overrides carry the horizontal guard like the pre-existing rules.
         const readerFontOverride = currentUseBookFonts
           ? `:where(html) {
   font-family: var(--readany-font-family);
 }
-:where(pre, code, kbd, samp) {
+:where(pre, :not(pre) > code, :not(pre) > kbd, :not(pre) > samp) {
   font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace;
+}
+:where(pre :is(code, kbd, samp)) {
+  font-family: inherit;
 }`
-          : `html, body {
+          : `:root:not(.vrtl):not(.vltr),
+:root:not(.vrtl):not(.vltr) body {
   font-family: var(--readany-font-family) !important;
 }
-body *:not(svg):not(svg *):not(math):not(math *):not(pre):not(pre *):not(code):not(code *):not(kbd):not(kbd *):not(samp):not(samp *) {
+:root:not(.vrtl):not(.vltr) body *:not(svg):not(svg *):not(math):not(math *):not(pre):not(pre *):not(code):not(code *):not(kbd):not(kbd *):not(samp):not(samp *) {
   font-family: var(--readany-font-family) !important;
 }
-html body :is(pre, code, kbd, samp) {
+:root:not(.vrtl):not(.vltr) body :is(pre, code, kbd, samp) {
   font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace !important;
 }`;
 
diff --git a/packages/app-expo/src/screens/reader/usebook-fonts-contract.test.ts b/packages/app-expo/src/screens/reader/usebook-fonts-contract.test.ts
new file mode 100644
index 000000000..9459ff4d3
--- /dev/null
+++ b/packages/app-expo/src/screens/reader/usebook-fonts-contract.test.ts
@@ -0,0 +1,77 @@
+import { readFileSync } from "node:fs";
+import { dirname, resolve } from "node:path";
+import { fileURLToPath } from "node:url";
+import { describe, expect, it } from "vitest";
+
+const repositoryRoot = resolve(dirname(fileURLToPath(import.meta.url)), "../../../../..");
+
+function readSource(relativePath: string): string {
+  return readFileSync(resolve(repositoryRoot, relativePath), "utf8");
+}
+
+// The useBookFonts cascade recipe exists in three places that must not drift:
+// the mobile template, its generated artifact, and the desktop generator.
+// The behavior hinges on subtle specificity/!important interactions
+// (zero-specificity :where() fallback vs. (0,0,3)/(0,2,2) forced override),
+// so the contract pins the rule SHAPES per toggle state.
+describe("useBookFonts font cascade contract", () => {
+  const template = readSource("packages/app-expo/assets/reader/reader.template.html");
+  const generated = readSource("packages/app-expo/assets/reader/reader.html");
+  const desktop = readSource("packages/app/src/components/reader/FoliateViewer.tsx");
+
+  it("honors book fonts by default: zero-specificity :where() fallbacks, no !important", () => {
+    for (const [name, source] of [
+      ["template", template],
+      ["generated", generated],
+      ["desktop", desktop],
+    ] as const) {
+      expect(source, name).toMatch(/:where\(html\)\s*\{\s*font-family: var\(--readany-font-family\);/);
+      // Inner code/kbd/samp inside a pre are excluded so they keep inheriting
+      // the book's font from pre instead of getting a direct declaration.
+      expect(source, name).toMatch(
+        /:where\(pre, :not\(pre\) > code, :not\(pre\) > kbd, :not\(pre\) > samp\)\s*\{\s*font-family: ui-monospace[^}]*;/,
+      );
+      // The UA stylesheet declares `code { font-family: monospace }` and any
+      // direct declaration beats inheritance — this zero-specificity inherit
+      // restores the `
` chain to the book's font on pre.
+      expect(source, name).toMatch(
+        /:where\(pre :is\(code, kbd, samp\)\)\s*\{\s*font-family: inherit;/,
+      );
+    }
+    // The honoring branch must not carry !important anywhere.
+    expect(template).toMatch(/currentUseBookFonts\s*\?\s*`:where\(html\)[^`]*`/);
+    expect(template).not.toMatch(/:where\(html\)\s*\{[^}]*!important/);
+    expect(desktop).not.toMatch(/:where\(pre[^)]*\)\s*\{[^}]*!important/);
+  });
+
+  it("forces the reader font when the toggle is off, above authored !important rules", () => {
+    // Desktop: html body :is(...) at (0,0,3) — outranks an authored
+    // `body pre { ... !important }` at (0,0,2), which a bare `pre, code, kbd`
+    // at (0,0,1) would lose to despite its own !important.
+    expect(desktop).toMatch(
+      /html body :is\(pre, code, kbd, samp\)\s*\{\s*font-family: ui-monospace[^}]*!important/,
+    );
+    // Mobile: the same idea guarded to horizontal writing so the vertical
+    // branch keeps its pre-existing behavior (no descendant forcing).
+    expect(template).toMatch(
+      /:root:not\(\.vrtl\):not\(\.vltr\),\s*\n\s*:root:not\(\.vrtl\):not\(\.vltr\) body\s*\{\s*font-family: var\(--readany-font-family\) !important/,
+    );
+    expect(template).toMatch(
+      /:root:not\(\.vrtl\):not\(\.vltr\) body :is\(pre, code, kbd, samp\)\s*\{\s*font-family: ui-monospace[^}]*!important/,
+    );
+    // The forced body-star rule keeps the horizontal guard too.
+    expect(template).toMatch(
+      /:root:not\(\.vrtl\):not\(\.vltr\) body \*:not\(svg\)[^{]*\{\s*font-family: var\(--readany-font-family\) !important/,
+    );
+    // And the generated artifact carries the same shapes.
+    expect(generated).toMatch(/:root:not\(\.vrtl\):not\(\.vltr\) body :is\(pre, code, kbd, samp\)/);
+    expect(generated).toMatch(/:where\(pre, :not\(pre\) > code/);
+  });
+
+  it("emits exactly one unconditional monospace rule in the desktop output", () => {
+    // The legacy unconditional `pre, code, kbd, samp { ... !important }` must
+    // stay gone from the desktop generator — its reintroduction would silently
+    // override book code fonts again.
+    expect(desktop).not.toMatch(/(?` is the dominant code markup);
+  // standalone inline code still gets the fallback.
   const readerFontOverride =
     settings.useBookFonts === false
       ? `html, body {
@@ -3497,9 +3501,12 @@ html body :is(pre, code, kbd, samp) {
       : `:where(html) {
   font-family: var(--readany-font-family);
 }
-:where(pre, code, kbd, samp) {
+:where(pre, :not(pre) > code, :not(pre) > kbd, :not(pre) > samp) {
   font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace;
 }
+:where(pre :is(code, kbd, samp)) {
+  font-family: inherit;
+}
 `;
 
   return `${settings.customFontFaceCSS ? `/* Custom font faces */\n${settings.customFontFaceCSS}\n\n` : ""}/* Font styles */