Skip to content

fix: scope each EPUB reading theme's CSS to its own class, fixing a stuck-theme freeze - #71

Merged
schmitzkr merged 1 commit into
mainfrom
worktree-epub-theme-css-scope
Sep 8, 2026
Merged

schmitzkr merged 1 commit into
mainfrom
worktree-epub-theme-css-scope

Conversation

@schmitzkr

Copy link
Copy Markdown
Owner

Confirmed on-device repro: open the Display sheet, tap through every
theme in order (Light, Sepia, Dark, Black, Forest), then tap any theme
again — the checkmark keeps moving correctly, but the rendered book page
freezes on whichever theme was showing right before the cycle completed,
no matter what is tapped afterward. Zero JS/Kotlin errors either side.

Root cause: each theme's rules were registered under bare
body/a selectors, not scoped to that theme. epub.js's Themes
manager creates one <style> node per theme the first time it's
selected and never removes or reorders existing ones; with unscoped
selectors every theme's rules stay in effect simultaneously, and which
one visually wins is decided purely by <style> tag order in <head>
(last one wins for equal-specificity body/a rules), not by which
theme is actually selected via rendition.themes.select() (which does
correctly toggle a class onto <body> — that part of epub.js was never
the problem). Once every theme has been visited at least once, the class
toggle keeps working but can never again out-rank whichever theme's node
happens to sit last in <head> — a silent cascade-order bug, not a
runtime one, which is why nothing ever throws.

Scoped every rule to its own theme class (e.g. body.light, .light a)
so at most one theme's rules can ever match the DOM at a time, gated by
the class select() already toggles — a stale, out-of-order <style>
node left over from an earlier visit becomes harmless once its class is
no longer on <body>, regardless of its position in <head>.

Pure JS asset change, no Kotlin touched — verified with node --check,
no local Gradle build run since nothing here would exercise it.

🤖 Generated with Claude Code

…tuck-theme freeze

Confirmed on-device repro: open the Display sheet, tap through every
theme in order (Light, Sepia, Dark, Black, Forest), then tap any theme
again -- the checkmark keeps moving correctly, but the rendered book
page freezes on whichever theme was showing right before the cycle
completed, no matter what is tapped afterward. Zero JS/Kotlin errors
either side.

Root cause: each theme's rules were registered under bare 'body'/'a'
selectors, not scoped to that theme. epub.js's Themes manager creates
one <style> node per theme the first time it is selected and never
removes or reorders existing ones; with unscoped selectors every
theme's rules stay in effect simultaneously, and which one visually
wins is decided purely by <style> tag order in <head> (last one wins
for equal-specificity body/a rules), not by which theme is actually
selected via rendition.themes.select() (which does correctly toggle a
class onto <body> -- that part of epub.js was never the problem). Once
every theme has been visited at least once, the class toggle keeps
working but can never again out-rank whichever theme's node happens to
sit last in <head> -- a silent cascade-order bug, not a runtime one,
which is why nothing ever throws.

Scoped every rule to its own theme class (e.g. 'body.light', '.light a')
so at most one theme's rules can ever match the DOM at a time, gated by
the class select() already toggles -- a stale, out-of-order <style> node
left over from an earlier visit becomes harmless once its class is no
longer on <body>, regardless of its position in <head>.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01M421Z8byhEsWKMq7eWWqDN
@schmitzkr
schmitzkr merged commit 115331c into main Sep 8, 2026
4 checks passed
@schmitzkr
schmitzkr deleted the worktree-epub-theme-css-scope branch September 8, 2026 15:10
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant