fix: scope each EPUB reading theme's CSS to its own class, fixing a stuck-theme freeze - #71
Merged
Merged
Conversation
…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
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.
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/aselectors, not scoped to that theme. epub.js'sThemesmanager creates one
<style>node per theme the first time it'sselected 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/arules), not by whichtheme is actually selected via
rendition.themes.select()(which doescorrectly toggle a class onto
<body>— that part of epub.js was neverthe 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 aruntime 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