From 8556745645cc4fe9178a47adc5bd1d3ae8331243 Mon Sep 17 00:00:00 2001 From: Shijie Sun Date: Mon, 20 Apr 2026 20:14:07 -0400 Subject: [PATCH 1/3] feat(page-controller): enhance dark mode detection Resolve the @TODO in checkDarkMode.ts by adding 3 new detection strategies and expanding data-attribute coverage: - Check data-color-mode, data-bs-theme, data-color-scheme attributes - Read CSS color-scheme property and tag - Inspect background of SPA containers (#app, #root, #__next, etc.) - Detect light text color as a dark-theme signal --- .../page-controller/src/mask/checkDarkMode.ts | 91 +++++++++++++++++-- 1 file changed, 83 insertions(+), 8 deletions(-) diff --git a/packages/page-controller/src/mask/checkDarkMode.ts b/packages/page-controller/src/mask/checkDarkMode.ts index e4cf890..b20fb51 100644 --- a/packages/page-controller/src/mask/checkDarkMode.ts +++ b/packages/page-controller/src/mask/checkDarkMode.ts @@ -15,10 +15,13 @@ function hasDarkModeClass() { } } - // Some sites use data attributes - const darkThemeAttribute = htmlElement.getAttribute('data-theme') - if (darkThemeAttribute?.toLowerCase().includes('dark')) { - return true + // Some sites use data attributes (data-theme, data-color-mode, data-bs-theme, etc.) + const dataAttrs = ['data-theme', 'data-color-mode', 'data-bs-theme', 'data-color-scheme'] + for (const attr of dataAttrs) { + const value = htmlElement.getAttribute(attr) || bodyElement?.getAttribute(attr) + if (value?.toLowerCase().includes('dark')) { + return true + } } return false @@ -87,6 +90,65 @@ function isBackgroundDark() { return false } +/** + * Checks the CSS `color-scheme` property and `` tag. + * @returns {boolean | null} - True/false if deterministic, null if inconclusive. + */ +function getColorSchemePreference(): boolean | null { + // Check + const meta = document.querySelector('meta[name="color-scheme"]') + if (meta) { + const content = meta.content.toLowerCase() + // "dark" or "only dark" → dark; "light dark" is ambiguous so skip + if (content === 'dark' || content === 'only dark') return true + if (content === 'light' || content === 'only light') return false + } + + // Check the computed color-scheme CSS property on :root + const rootStyle = window.getComputedStyle(document.documentElement) + const colorScheme = rootStyle.getPropertyValue('color-scheme').trim().toLowerCase() + if (colorScheme === 'dark' || colorScheme === 'only dark') return true + if (colorScheme === 'light' || colorScheme === 'only light') return false + + return null +} + +/** + * Checks if the text color on the body is light, which implies a dark background. + * @returns {boolean} + */ +function isTextColorLight() { + const bodyStyle = window.getComputedStyle(document.body || document.documentElement) + const textColor = bodyStyle.color + + const rgb = parseRgbColor(textColor) + if (!rgb) return false + + // Light text has high luminance (e.g. white text on dark bg) + const luminance = 0.299 * rgb.r + 0.587 * rgb.g + 0.114 * rgb.b + return luminance > 180 +} + +/** + * Checks the background color of major layout elements (
, #app, #root, etc.). + * Many SPAs render into a container that may have its own dark background while + * remains transparent. + * @returns {boolean} + */ +function isMainContentDark() { + const selectors = ['main', '#app', '#root', '#__next', '[role="main"]'] + for (const selector of selectors) { + const el = document.querySelector(selector) + if (!el) continue + + const style = window.getComputedStyle(el) + if (isColorDark(style.backgroundColor)) { + return true + } + } + return false +} + /** * A comprehensive function to determine if the page is currently in a dark theme. * It combines class checking and background color analysis. @@ -94,18 +156,31 @@ function isBackgroundDark() { */ export function isPageDark() { try { - // Strategy 1: Check for common dark mode classes + // Strategy 1: Check for common dark mode classes and data attributes if (hasDarkModeClass()) { return true } - // Strategy 2: Analyze the computed background color + // Strategy 2: Check CSS color-scheme property and meta tag + const colorScheme = getColorSchemePreference() + if (colorScheme !== null) { + return colorScheme + } + + // Strategy 3: Analyze the computed background color of / if (isBackgroundDark()) { return true } - // @TODO add more checks here, e.g., analyzing text color, - // or checking the background of major layout elements like
or #app. + // Strategy 4: Check background of major layout containers (
, #app, etc.) + if (isMainContentDark()) { + return true + } + + // Strategy 5: Check if text color is light (implies dark background) + if (isTextColorLight()) { + return true + } return false } catch (error) { From cc33297cc48e86614da34cffedfb77080e02e7af Mon Sep 17 00:00:00 2001 From: tageniu Date: Mon, 30 Mar 2026 18:16:15 -0400 Subject: [PATCH 2/3] fix(controller): check html and body data attrs independently Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- packages/page-controller/src/mask/checkDarkMode.ts | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/packages/page-controller/src/mask/checkDarkMode.ts b/packages/page-controller/src/mask/checkDarkMode.ts index b20fb51..92e666c 100644 --- a/packages/page-controller/src/mask/checkDarkMode.ts +++ b/packages/page-controller/src/mask/checkDarkMode.ts @@ -18,8 +18,10 @@ function hasDarkModeClass() { // Some sites use data attributes (data-theme, data-color-mode, data-bs-theme, etc.) const dataAttrs = ['data-theme', 'data-color-mode', 'data-bs-theme', 'data-color-scheme'] for (const attr of dataAttrs) { - const value = htmlElement.getAttribute(attr) || bodyElement?.getAttribute(attr) - if (value?.toLowerCase().includes('dark')) { + const bodyValue = bodyElement?.getAttribute(attr) + const htmlValue = htmlElement.getAttribute(attr) + + if (bodyValue?.toLowerCase().includes('dark') || htmlValue?.toLowerCase().includes('dark')) { return true } } From c6f09375f86bce4f77f6abd05d3c4fec07e32110 Mon Sep 17 00:00:00 2001 From: Simon <10131203+gaomeng1900@users.noreply.github.com> Date: Thu, 2 Apr 2026 17:03:20 +0800 Subject: [PATCH 3/3] fix(controller): `isMainContentDark` too aggressive --- packages/page-controller/src/mask/checkDarkMode.ts | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/packages/page-controller/src/mask/checkDarkMode.ts b/packages/page-controller/src/mask/checkDarkMode.ts index 92e666c..17fa5a4 100644 --- a/packages/page-controller/src/mask/checkDarkMode.ts +++ b/packages/page-controller/src/mask/checkDarkMode.ts @@ -138,15 +138,18 @@ function isTextColorLight() { * @returns {boolean} */ function isMainContentDark() { + const { innerWidth: vw, innerHeight: vh } = window + const minArea = vw * vh * 0.5 + const selectors = ['main', '#app', '#root', '#__next', '[role="main"]'] for (const selector of selectors) { const el = document.querySelector(selector) if (!el) continue - const style = window.getComputedStyle(el) - if (isColorDark(style.backgroundColor)) { - return true - } + const rect = el.getBoundingClientRect() + if (rect.width * rect.height < minArea) continue + + if (isColorDark(window.getComputedStyle(el).backgroundColor)) return true } return false }