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',
|
||||
};
|
||||
|
||||
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 = ({
|
||||
fontFamily,
|
||||
fontSize,
|
||||
lineHeight,
|
||||
justify,
|
||||
hyphenate,
|
||||
themeName,
|
||||
themeMode,
|
||||
}: {
|
||||
fontFamily: string;
|
||||
fontSize: number;
|
||||
lineHeight: number;
|
||||
justify: boolean;
|
||||
hyphenate: boolean;
|
||||
themeName: string;
|
||||
themeMode: string;
|
||||
}) => {
|
||||
const el = document.getElementById("reader-viewport");
|
||||
const style = getComputedStyle(el ?? document.body);
|
||||
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 themeSet = THEME_COLORS[themeName] ?? THEME_COLORS.light;
|
||||
const theme = themeMode === "dark" ? themeSet.dark : themeSet.light;
|
||||
const font = FONT_MAP[fontFamily] ?? '"Literata"';
|
||||
const fontFamilyRule = fontFamily ? `
|
||||
body {
|
||||
font-family: ${font}, serif !important;
|
||||
}
|
||||
body * {
|
||||
font-family: inherit !important;
|
||||
}` : "";
|
||||
|
||||
return `
|
||||
@namespace epub "http://www.idpf.org/2007/ops";
|
||||
html {
|
||||
color-scheme: light dark;
|
||||
background-color: ${bg} !important;
|
||||
color: ${text} !important;
|
||||
font-family: ${font}, serif;
|
||||
font-size: ${fontSize}px;
|
||||
line-height: ${lineHeight};
|
||||
}
|
||||
::selection {
|
||||
background-color: ${selection};
|
||||
${fontFamilyRule}
|
||||
html, body {
|
||||
color: ${theme.fg} !important;
|
||||
background: none !important;
|
||||
}
|
||||
a:link {
|
||||
color: ${link};
|
||||
body * {
|
||||
color: inherit !important;
|
||||
border-color: currentColor !important;
|
||||
background-color: ${theme.bg} !important;
|
||||
}
|
||||
@media (prefers-color-scheme: dark) {
|
||||
a:link {
|
||||
color: ${link};
|
||||
}
|
||||
a:any-link {
|
||||
color: ${theme.link} !important;
|
||||
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 {
|
||||
line-height: ${lineHeight};
|
||||
text-align: ${justify ? "justify" : "start"};
|
||||
-webkit-hyphens: ${hyphenate ? "auto" : "manual"};
|
||||
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;
|
||||
line-height: ${lineHeight} !important;
|
||||
text-align: ${justify ? "justify" : "start"} !important;
|
||||
hyphens: ${hyphenate ? "auto" : "none"};
|
||||
}
|
||||
[align="left"] { text-align: left; }
|
||||
[align="right"] { text-align: right; }
|
||||
@@ -74,32 +163,19 @@ const getCSS = ({
|
||||
[align="justify"] { text-align: justify; }
|
||||
pre {
|
||||
white-space: pre-wrap !important;
|
||||
tab-size: 2;
|
||||
}
|
||||
aside[epub|type~="endnote"],
|
||||
aside[epub|type~="footnote"],
|
||||
aside[epub|type~="note"],
|
||||
aside[epub|type~="rearnote"] {
|
||||
display: none;
|
||||
::selection {
|
||||
background-color: rgba(128, 128, 128, 0.3);
|
||||
}
|
||||
img, svg, video {
|
||||
max-width: 100%;
|
||||
height: auto;
|
||||
display: block;
|
||||
margin: 0.5em auto;
|
||||
}
|
||||
p {
|
||||
orphans: 3;
|
||||
widows: 3;
|
||||
}
|
||||
blockquote {
|
||||
margin: 1em 0;
|
||||
padding-left: 1em;
|
||||
border-left: 3px solid ${link};
|
||||
font-style: italic;
|
||||
}
|
||||
a {
|
||||
color: ${link};
|
||||
text-decoration: underline;
|
||||
html {
|
||||
hanging-punctuation: allow-end last;
|
||||
orphans: 2;
|
||||
widows: 2;
|
||||
}
|
||||
`;
|
||||
};
|
||||
@@ -346,6 +422,8 @@ document.addEventListener("alpine:init", () => {
|
||||
lineHeight: this.lineHeight,
|
||||
justify: this.justify,
|
||||
hyphenate: this.hyphenate,
|
||||
themeName: this.readingTheme,
|
||||
themeMode: this.readingMode,
|
||||
});
|
||||
},
|
||||
restoreDefaults() {
|
||||
|
||||
Reference in New Issue
Block a user