fix(ssg): fix white header on dark theme and resolve critters CSP block (#66)

## Summary
- **Critters preload**: Set `preload: false` in `ssgPlugin` to eliminate
`onload="this.media='all'"` event handlers on external stylesheets,
which violated Content Security Policy (`script-src 'self'` /
`require-trusted-types-for 'script'`) and left the compiled stylesheet
permanently blocked in `media="print"`.
- **Dark theme selector retention**: Added `allowRules: [/\.dark/]` to
Critters options so dark theme selectors and palette variables are
preserved during critical CSS inlining.
- **Immediate theme variables**: Initialized `--rb-background`,
`--rb-base`, and `--rb-text` in `THEME_INIT_STYLES` for `:root` and
`.dark` alongside background color to ensure components like `PageHead`
instantly reflect the active theme.

## Verification
- `pnpm typecheck`: Passed (0 errors)
- `pnpm lint`: Passed (0 warnings)
- `pnpm test`: Passed (all suites passed)
- `pnpm build`: Passed (clean SSG generation)
- Local Playwright browser test with dark color scheme verified
`headerBg` is `rgb(20, 20, 20)` and zero CSP inline handler violations.
This commit is contained in:
deytenit authored and GitHub committed 2026-10-24 20:48:59 +03:00
1 parent c1a17a262a
commit 968f41d68d
2 files changed
+6 -4

No files matched your search

+4 -2
View File
@@ -134,10 +134,11 @@ export function ssgPlugin(): RsbuildPlugin {
const Critters = _require('critters') as new (opts?: { const Critters = _require('critters') as new (opts?: {
path?: string; path?: string;
publicPath?: string; publicPath?: string;
preload?: string; preload?: 'body' | 'media' | 'swap' | 'js' | 'js-lazy' | boolean;
inlineFonts?: boolean; inlineFonts?: boolean;
pruneSource?: boolean; pruneSource?: boolean;
reduceInlineStyles?: boolean; reduceInlineStyles?: boolean;
allowRules?: (string | RegExp)[];
}) => { process(html: string): Promise<string> }; }) => { process(html: string): Promise<string> };
const configuredAssetPrefix = const configuredAssetPrefix =
@@ -147,10 +148,11 @@ export function ssgPlugin(): RsbuildPlugin {
const critters = new Critters({ const critters = new Critters({
path: webDistPath, path: webDistPath,
publicPath: configuredAssetPrefix, publicPath: configuredAssetPrefix,
preload: 'media', preload: false,
inlineFonts: false, inlineFonts: false,
pruneSource: false, pruneSource: false,
reduceInlineStyles: false, reduceInlineStyles: false,
allowRules: [/\.dark/],
}); });
for (const entryName of entryNames) { for (const entryName of entryNames) {
+2 -2
View File
@@ -10,7 +10,7 @@ export const THEME_INIT_SCRIPT = `(function(){try{var k='ermnvldmr/ui/lib/theme'
* These align with the main palette in styles.css. * These align with the main palette in styles.css.
*/ */
export const THEME_INIT_STYLES = ` export const THEME_INIT_STYLES = `
:root { background-color: #fafafa; color: #262626; } :root { --rb-background: #fafafa; --rb-base: #f6f6f6; --rb-text: #262626; background-color: #fafafa; color: #262626; }
.dark { background-color: #141414; color: #f6f6f6; } .dark { --rb-background: #141414; --rb-base: #262626; --rb-text: #f6f6f6; background-color: #141414; color: #f6f6f6; }
body { margin: 0; transition: background-color 0.2s ease; } body { margin: 0; transition: background-color 0.2s ease; }
`; `;