fix(reader): rewrite getCSS with concrete theme colors and aggressive !important
Studied grimmory-tools/grimmory's ebook-reader style.service.ts and adopted
their approach:
- Replace CSS custom property resolution (getComputedStyle) with a hardcoded
THEME_COLORS map containing concrete fg/bg/link values for all 18 themes
in both light and dark modes. No more variable resolution failures.
- Font family now targets body + body * with !important, overriding book CSS
on every element (matching grimmory's approach).
- Colors use grimmory's aggressive pattern:
html, body { color: ... !important; background: none !important; }
body * { color: inherit !important; background-color: ... !important; }
This forces reading theme colors on ALL book elements, overriding inline
styles and book stylesheets.
- Line height uses !important on p, li, blockquote, dd to override book CSS.
- Removed fragile getComputedStyle calls entirely. getCSS() now receives
themeName and themeMode parameters for direct color lookup.
This commit is contained in:
+125
-47
@@ -15,58 +15,147 @@ const FONT_MAP: Record<string, string> = {
|
|||||||
"comic-sans": '"Comic Sans MS", "Comic Sans", cursive',
|
"comic-sans": '"Comic Sans MS", "Comic Sans", cursive',
|
||||||
};
|
};
|
||||||
|
|
||||||
|
interface ThemeColors {
|
||||||
|
fg: string;
|
||||||
|
bg: string;
|
||||||
|
link: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
const THEME_COLORS: Record<string, { light: ThemeColors; dark: ThemeColors }> = {
|
||||||
|
light: {
|
||||||
|
light: { fg: "#1a1a1a", bg: "#fafafa", link: "#0066cc" },
|
||||||
|
dark: { fg: "#e8e8e8", bg: "#1a1a1a", link: "#60a5fa" },
|
||||||
|
},
|
||||||
|
paper: {
|
||||||
|
light: { fg: "#2d2d2d", bg: "#fdfbf7", link: "#0284c7" },
|
||||||
|
dark: { fg: "#ebe5dd", bg: "#1c1917", link: "#38bdf8" },
|
||||||
|
},
|
||||||
|
sepia: {
|
||||||
|
light: { fg: "#4a3728", bg: "#f4ecd8", link: "#8b4513" },
|
||||||
|
dark: { fg: "#d4c4a8", bg: "#2b2420", link: "#d97706" },
|
||||||
|
},
|
||||||
|
parchment: {
|
||||||
|
light: { fg: "#5c4033", bg: "#f0e6d3", link: "#92400e" },
|
||||||
|
dark: { fg: "#e8dcc8", bg: "#3d2b1f", link: "#b45309" },
|
||||||
|
},
|
||||||
|
warm: {
|
||||||
|
light: { fg: "#2b2b2b", bg: "#faf8f0", link: "#d97706" },
|
||||||
|
dark: { fg: "#f5e6d3", bg: "#2d2416", link: "#fbbf24" },
|
||||||
|
},
|
||||||
|
candlelight: {
|
||||||
|
light: { fg: "#3d2b1f", bg: "#fef3c7", link: "#b45309" },
|
||||||
|
dark: { fg: "#fde68a", bg: "#451a03", link: "#fb923c" },
|
||||||
|
},
|
||||||
|
azure: {
|
||||||
|
light: { fg: "#262d48", bg: "#cedef5", link: "#2d53e5" },
|
||||||
|
dark: { fg: "#babee1", bg: "#282e47", link: "#0ea5e9" },
|
||||||
|
},
|
||||||
|
sky: {
|
||||||
|
light: { fg: "#1e3a5f", bg: "#e0f2fe", link: "#0284c7" },
|
||||||
|
dark: { fg: "#bae6fd", bg: "#0c4a6e", link: "#0ea5e9" },
|
||||||
|
},
|
||||||
|
arctic: {
|
||||||
|
light: { fg: "#1e3a5f", bg: "#f0f9ff", link: "#0284c7" },
|
||||||
|
dark: { fg: "#bfdbfe", bg: "#1e293b", link: "#38bdf8" },
|
||||||
|
},
|
||||||
|
frost: {
|
||||||
|
light: { fg: "#334155", bg: "#f8fafc", link: "#38bdf8" },
|
||||||
|
dark: { fg: "#e0f2fe", bg: "#0f172a", link: "#7dd3fc" },
|
||||||
|
},
|
||||||
|
dusk: {
|
||||||
|
light: { fg: "#3d2914", bg: "#fef3e2", link: "#ea580c" },
|
||||||
|
dark: { fg: "#f5d5b8", bg: "#2d1f14", link: "#fb923c" },
|
||||||
|
},
|
||||||
|
sunset: {
|
||||||
|
light: { fg: "#4a1d1d", bg: "#fff7ed", link: "#f97316" },
|
||||||
|
dark: { fg: "#fed7aa", bg: "#431407", link: "#fb923c" },
|
||||||
|
},
|
||||||
|
twilight: {
|
||||||
|
light: { fg: "#4c1d95", bg: "#f5f3ff", link: "#8b5cf6" },
|
||||||
|
dark: { fg: "#ddd6fe", bg: "#2e1065", link: "#a78bfa" },
|
||||||
|
},
|
||||||
|
forest: {
|
||||||
|
light: { fg: "#1a2e1a", bg: "#e8efe8", link: "#15803d" },
|
||||||
|
dark: { fg: "#c8dcc8", bg: "#1a2e1a", link: "#22c55e" },
|
||||||
|
},
|
||||||
|
moss: {
|
||||||
|
light: { fg: "#14532d", bg: "#dcfce7", link: "#22c55e" },
|
||||||
|
dark: { fg: "#86efac", bg: "#052e16", link: "#4ade80" },
|
||||||
|
},
|
||||||
|
slate: {
|
||||||
|
light: { fg: "#334155", bg: "#f8fafc", link: "#475569" },
|
||||||
|
dark: { fg: "#e2e8f0", bg: "#1e293b", link: "#94a3b8" },
|
||||||
|
},
|
||||||
|
oled: {
|
||||||
|
light: { fg: "#000000", bg: "#ffffff", link: "#0066cc" },
|
||||||
|
dark: { fg: "#ffffff", bg: "#000000", link: "#3b82f6" },
|
||||||
|
},
|
||||||
|
solarized: {
|
||||||
|
light: { fg: "#657b83", bg: "#fdf6e3", link: "#268bd2" },
|
||||||
|
dark: { fg: "#839496", bg: "#002b36", link: "#268bd2" },
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
const getCSS = ({
|
const getCSS = ({
|
||||||
fontFamily,
|
fontFamily,
|
||||||
fontSize,
|
fontSize,
|
||||||
lineHeight,
|
lineHeight,
|
||||||
justify,
|
justify,
|
||||||
hyphenate,
|
hyphenate,
|
||||||
|
themeName,
|
||||||
|
themeMode,
|
||||||
}: {
|
}: {
|
||||||
fontFamily: string;
|
fontFamily: string;
|
||||||
fontSize: number;
|
fontSize: number;
|
||||||
lineHeight: number;
|
lineHeight: number;
|
||||||
justify: boolean;
|
justify: boolean;
|
||||||
hyphenate: boolean;
|
hyphenate: boolean;
|
||||||
|
themeName: string;
|
||||||
|
themeMode: string;
|
||||||
}) => {
|
}) => {
|
||||||
const el = document.getElementById("reader-viewport");
|
const themeSet = THEME_COLORS[themeName] ?? THEME_COLORS.light;
|
||||||
const style = getComputedStyle(el ?? document.body);
|
const theme = themeMode === "dark" ? themeSet.dark : themeSet.light;
|
||||||
const bg = style.getPropertyValue("--reader-bg").trim() || "#fafafa";
|
|
||||||
const text = style.getPropertyValue("--reader-text").trim() || "#1a1a1a";
|
|
||||||
const link = style.getPropertyValue("--reader-link").trim() || "#0066cc";
|
|
||||||
const selection =
|
|
||||||
style.getPropertyValue("--reader-selection").trim() || "#b3d9ff";
|
|
||||||
const font = FONT_MAP[fontFamily] ?? '"Literata"';
|
const font = FONT_MAP[fontFamily] ?? '"Literata"';
|
||||||
|
const fontFamilyRule = fontFamily ? `
|
||||||
|
body {
|
||||||
|
font-family: ${font}, serif !important;
|
||||||
|
}
|
||||||
|
body * {
|
||||||
|
font-family: inherit !important;
|
||||||
|
}` : "";
|
||||||
|
|
||||||
return `
|
return `
|
||||||
@namespace epub "http://www.idpf.org/2007/ops";
|
@namespace epub "http://www.idpf.org/2007/ops";
|
||||||
html {
|
html {
|
||||||
color-scheme: light dark;
|
color-scheme: light dark;
|
||||||
background-color: ${bg} !important;
|
|
||||||
color: ${text} !important;
|
|
||||||
font-family: ${font}, serif;
|
|
||||||
font-size: ${fontSize}px;
|
font-size: ${fontSize}px;
|
||||||
line-height: ${lineHeight};
|
line-height: ${lineHeight};
|
||||||
}
|
}
|
||||||
::selection {
|
${fontFamilyRule}
|
||||||
background-color: ${selection};
|
html, body {
|
||||||
|
color: ${theme.fg} !important;
|
||||||
|
background: none !important;
|
||||||
}
|
}
|
||||||
a:link {
|
body * {
|
||||||
color: ${link};
|
color: inherit !important;
|
||||||
|
border-color: currentColor !important;
|
||||||
|
background-color: ${theme.bg} !important;
|
||||||
}
|
}
|
||||||
@media (prefers-color-scheme: dark) {
|
a:any-link {
|
||||||
a:link {
|
color: ${theme.link} !important;
|
||||||
color: ${link};
|
text-decoration-color: color-mix(in srgb, currentColor 20%, transparent);
|
||||||
}
|
text-underline-offset: .1em;
|
||||||
|
}
|
||||||
|
svg, img {
|
||||||
|
background-color: transparent !important;
|
||||||
|
}
|
||||||
|
aside[epub|type~="footnote"] {
|
||||||
|
display: none;
|
||||||
}
|
}
|
||||||
p, li, blockquote, dd {
|
p, li, blockquote, dd {
|
||||||
line-height: ${lineHeight};
|
line-height: ${lineHeight} !important;
|
||||||
text-align: ${justify ? "justify" : "start"};
|
text-align: ${justify ? "justify" : "start"} !important;
|
||||||
-webkit-hyphens: ${hyphenate ? "auto" : "manual"};
|
hyphens: ${hyphenate ? "auto" : "none"};
|
||||||
hyphens: ${hyphenate ? "auto" : "manual"};
|
|
||||||
-webkit-hyphenate-limit-before: 3;
|
|
||||||
-webkit-hyphenate-limit-after: 2;
|
|
||||||
-webkit-hyphenate-limit-lines: 2;
|
|
||||||
hanging-punctuation: allow-end last;
|
|
||||||
widows: 2;
|
|
||||||
}
|
}
|
||||||
[align="left"] { text-align: left; }
|
[align="left"] { text-align: left; }
|
||||||
[align="right"] { text-align: right; }
|
[align="right"] { text-align: right; }
|
||||||
@@ -74,32 +163,19 @@ const getCSS = ({
|
|||||||
[align="justify"] { text-align: justify; }
|
[align="justify"] { text-align: justify; }
|
||||||
pre {
|
pre {
|
||||||
white-space: pre-wrap !important;
|
white-space: pre-wrap !important;
|
||||||
|
tab-size: 2;
|
||||||
}
|
}
|
||||||
aside[epub|type~="endnote"],
|
::selection {
|
||||||
aside[epub|type~="footnote"],
|
background-color: rgba(128, 128, 128, 0.3);
|
||||||
aside[epub|type~="note"],
|
|
||||||
aside[epub|type~="rearnote"] {
|
|
||||||
display: none;
|
|
||||||
}
|
}
|
||||||
img, svg, video {
|
img, svg, video {
|
||||||
max-width: 100%;
|
max-width: 100%;
|
||||||
height: auto;
|
height: auto;
|
||||||
display: block;
|
|
||||||
margin: 0.5em auto;
|
|
||||||
}
|
}
|
||||||
p {
|
html {
|
||||||
orphans: 3;
|
hanging-punctuation: allow-end last;
|
||||||
widows: 3;
|
orphans: 2;
|
||||||
}
|
widows: 2;
|
||||||
blockquote {
|
|
||||||
margin: 1em 0;
|
|
||||||
padding-left: 1em;
|
|
||||||
border-left: 3px solid ${link};
|
|
||||||
font-style: italic;
|
|
||||||
}
|
|
||||||
a {
|
|
||||||
color: ${link};
|
|
||||||
text-decoration: underline;
|
|
||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
};
|
};
|
||||||
@@ -346,6 +422,8 @@ document.addEventListener("alpine:init", () => {
|
|||||||
lineHeight: this.lineHeight,
|
lineHeight: this.lineHeight,
|
||||||
justify: this.justify,
|
justify: this.justify,
|
||||||
hyphenate: this.hyphenate,
|
hyphenate: this.hyphenate,
|
||||||
|
themeName: this.readingTheme,
|
||||||
|
themeMode: this.readingMode,
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
restoreDefaults() {
|
restoreDefaults() {
|
||||||
|
|||||||
Reference in New Issue
Block a user