diff --git a/apps/readest-app/src/__tests__/app/reader/components/SectionInfo.test.tsx b/apps/readest-app/src/__tests__/app/reader/components/SectionInfo.test.tsx
index 9d74d524..60bf2313 100644
--- a/apps/readest-app/src/__tests__/app/reader/components/SectionInfo.test.tsx
+++ b/apps/readest-app/src/__tests__/app/reader/components/SectionInfo.test.tsx
@@ -68,3 +68,27 @@ describe('SectionInfo notch mask', () => {
expect(notch.classList.contains('bg-base-100')).toBe(false);
});
});
+
+describe('SectionInfo contrast against the page (#4901)', () => {
+ // A light-mode PDF shown under a dark theme keeps its white page, but the
+ // running header text was themed for the dark UI (neutral-content, light)
+ // and became unreadable on the white page. mix-blend-mode: difference makes
+ // the text invert against whatever is actually behind it (the page or the
+ // themed margin), so it stays legible over any background.
+ it('blends the section title against the background in non-eink mode', () => {
+ const { container } = render();
+ const info = container.querySelector('.sectioninfo') as HTMLElement;
+
+ expect(info.classList.contains('mix-blend-difference')).toBe(true);
+ expect(info.classList.contains('text-white/75')).toBe(true);
+ // The theme-fixed neutral color is what made it unreadable on a white page.
+ expect(info.classList.contains('text-neutral-content')).toBe(false);
+ });
+
+ it('does not blend in eink mode (base-content text on the e-ink page)', () => {
+ const { container } = render();
+ const info = container.querySelector('.sectioninfo') as HTMLElement;
+
+ expect(info.classList.contains('mix-blend-difference')).toBe(false);
+ });
+});
diff --git a/apps/readest-app/src/__tests__/components/ProgressBar.test.tsx b/apps/readest-app/src/__tests__/components/ProgressBar.test.tsx
index 653eb147..dcf98809 100644
--- a/apps/readest-app/src/__tests__/components/ProgressBar.test.tsx
+++ b/apps/readest-app/src/__tests__/components/ProgressBar.test.tsx
@@ -286,3 +286,51 @@ describe('ProgressBar — sticky progress bar', () => {
expect(container.querySelector('.sticky-progress-bar')).toBeNull();
});
});
+
+describe('ProgressBar — contrast against the page (#4901)', () => {
+ // A light-mode PDF under a dark theme keeps its white page while the footer
+ // progress text stayed themed for the dark UI (neutral-content, light) and
+ // became unreadable over the white page. Blending the text spans against the
+ // real backdrop keeps the page number and remaining-pages text legible on
+ // any background. StatusInfo (clock/battery) is intentionally left alone — it
+ // manages its own blend against the battery glyph.
+ it('blends the progress and remaining text against the background in non-eink mode', () => {
+ currentViewSettings = {
+ ...baseSettings,
+ isEink: false,
+ showRemainingPages: true,
+ showRemainingTime: false,
+ progressInfoMode: 'all',
+ } as ViewSettings;
+ currentProgress = makeProgress(2, 5);
+ currentBookData = { isFixedLayout: false };
+ currentRenderer = { page: 1, pages: 4 };
+
+ const { container } = renderProgressBar();
+
+ const progress = container.querySelector('.progress-info') as HTMLElement;
+ const remaining = container.querySelector('.remaining-info') as HTMLElement;
+ expect(progress.classList.contains('mix-blend-difference')).toBe(true);
+ expect(progress.classList.contains('text-white/75')).toBe(true);
+ expect(remaining.classList.contains('mix-blend-difference')).toBe(true);
+ expect(remaining.classList.contains('text-white/75')).toBe(true);
+ });
+
+ it('does not blend in eink mode', () => {
+ currentViewSettings = {
+ ...baseSettings,
+ isEink: true,
+ showRemainingPages: true,
+ showRemainingTime: false,
+ progressInfoMode: 'all',
+ } as ViewSettings;
+ currentProgress = makeProgress(2, 5);
+ currentBookData = { isFixedLayout: false };
+ currentRenderer = { page: 1, pages: 4 };
+
+ const { container } = renderProgressBar();
+
+ const progress = container.querySelector('.progress-info') as HTMLElement;
+ expect(progress.classList.contains('mix-blend-difference')).toBe(false);
+ });
+});
diff --git a/apps/readest-app/src/app/reader/components/ProgressBar.tsx b/apps/readest-app/src/app/reader/components/ProgressBar.tsx
index 40ed2b21..8ca61c4b 100644
--- a/apps/readest-app/src/app/reader/components/ProgressBar.tsx
+++ b/apps/readest-app/src/app/reader/components/ProgressBar.tsx
@@ -287,6 +287,9 @@ const ProgressBar: React.FC = ({
'remaining-info whitespace-nowrap text-start',
!stickyBarActive && 'flex-1',
showStatusInfo && 'overflow-hidden',
+ // Keep the text legible on any backdrop (e.g. a light PDF page
+ // under a dark theme, #4901); blend it against what's behind.
+ !isEink && 'text-white/75 mix-blend-difference',
)}
>
{viewSettings.showRemainingTime ? (
@@ -346,6 +349,9 @@ const ProgressBar: React.FC = ({
className={clsx(
'progress-info items-center overflow-hidden whitespace-nowrap text-end tabular-nums',
!stickyBarActive && 'flex-1',
+ // Keep the page number legible on any backdrop (e.g. a light PDF
+ // page under a dark theme, #4901); blend it against what's behind.
+ !isEink && 'text-white/75 mix-blend-difference',
)}
>
{(progressBarMode === 'all' || progressBarMode.includes('progress')) && (
diff --git a/apps/readest-app/src/app/reader/components/SectionInfo.tsx b/apps/readest-app/src/app/reader/components/SectionInfo.tsx
index 7cc4938c..f4d35a85 100644
--- a/apps/readest-app/src/app/reader/components/SectionInfo.tsx
+++ b/apps/readest-app/src/app/reader/components/SectionInfo.tsx
@@ -75,7 +75,13 @@ const SectionInfo: React.FC = ({