diff --git a/docs/.vitepress/theme/components/ThemeDropdown.vue b/docs/.vitepress/theme/components/ThemeDropdown.vue index e7f2d6994..cc65fd9e7 100644 --- a/docs/.vitepress/theme/components/ThemeDropdown.vue +++ b/docs/.vitepress/theme/components/ThemeDropdown.vue @@ -3,7 +3,7 @@ import { ref, computed, onMounted, onUnmounted } from 'vue' import { useTheme } from '../themes/themeHandler' import type { DisplayMode } from '../themes/types' -const { mode, setMode, state, amoledEnabled, setAmoledEnabled } = useTheme() +const { mode, setMode, state, amoledEnabled, setAmoledEnabled, monochromeEnabled, setMonochromeEnabled } = useTheme() const isOpen = ref(false) const dropdownRef = ref(null) @@ -13,20 +13,25 @@ interface ModeChoice { label: string icon: string isAmoled?: boolean + isMonochrome?: boolean } const modeChoices: ModeChoice[] = [ { mode: 'light', label: 'Light', icon: 'i-ph-sun-duotone' }, { mode: 'dark', label: 'Dark', icon: 'i-ph-moon-duotone' }, - { mode: 'dark', label: 'AMOLED', icon: 'i-ph-moon-stars-duotone', isAmoled: true } + { mode: 'dark', label: 'AMOLED', icon: 'i-ph-moon-stars-duotone', isAmoled: true }, + { mode: 'dark', label: 'Monochrome', icon: 'i-ph-circle-half-tilt-duotone', isMonochrome: true } ] const currentChoice = computed(() => { const current = (mode && (mode as any).value) ? (mode as any).value : 'light' + if (current === 'dark' && monochromeEnabled.value) { + return modeChoices[3] // Monochrome option + } if (current === 'dark' && amoledEnabled.value) { return modeChoices[2] // AMOLED option } - return modeChoices.find(choice => choice.mode === current && !choice.isAmoled) || modeChoices[0] + return modeChoices.find(choice => choice.mode === current && !choice.isAmoled && !choice.isMonochrome) || modeChoices[0] }) const toggleDropdown = () => { @@ -34,22 +39,31 @@ const toggleDropdown = () => { } const selectMode = (choice: ModeChoice) => { - if (choice.isAmoled) { + if (choice.isMonochrome) { + setMode('dark') + setAmoledEnabled(false) + setMonochromeEnabled(true) + } else if (choice.isAmoled) { setMode('dark') setAmoledEnabled(true) + setMonochromeEnabled(false) } else { setMode(choice.mode) setAmoledEnabled(false) + setMonochromeEnabled(false) } isOpen.value = false } const isActiveChoice = (choice: ModeChoice) => { const current = (mode && (mode as any).value) ? (mode as any).value : 'light' + if (choice.isMonochrome) { + return current === 'dark' && monochromeEnabled.value + } if (choice.isAmoled) { return current === 'dark' && amoledEnabled.value } - return choice.mode === current && !choice.isAmoled && !amoledEnabled.value + return choice.mode === current && !choice.isAmoled && !choice.isMonochrome && !amoledEnabled.value && !monochromeEnabled.value } const handleClickOutside = (event: MouseEvent) => { diff --git a/docs/.vitepress/theme/style.scss b/docs/.vitepress/theme/style.scss index 6c1b54432..8ca7d8560 100644 --- a/docs/.vitepress/theme/style.scss +++ b/docs/.vitepress/theme/style.scss @@ -138,17 +138,13 @@ */ :root { --vp-home-hero-name-color: transparent; - --vp-home-hero-name-background: -webkit-linear-gradient( - 120deg, - #c4b5fd 30%, - #7bc5e4 - ); + --vp-home-hero-name-background: -webkit-linear-gradient(120deg, + #c4b5fd 30%, + #7bc5e4); - --vp-home-hero-image-background-image: linear-gradient( - -45deg, - #c4b5fd 50%, - #47caff 50% - ); + --vp-home-hero-image-background-image: linear-gradient(-45deg, + #c4b5fd 50%, + #47caff 50%); --vp-home-hero-image-filter: blur(44px); } @@ -223,6 +219,7 @@ animation: nprogress-spinner 400ms linear infinite; } } + .nprogress-custom-parent { overflow: hidden; position: relative; @@ -253,7 +250,7 @@ } } -#VPContent strong > a { +#VPContent strong>a { font-weight: bold; } @@ -358,4 +355,20 @@ mask-size: 100% 100%; background-color: currentColor; color: inherit; +} + +/* Monochrome Specifics */ +html.monochrome { + filter: grayscale(100%); + + img, + video, + iframe { + filter: grayscale(100%); + } + + ::selection { + background-color: #333; + color: #fff; + } } \ No newline at end of file diff --git a/docs/.vitepress/theme/themes/themeHandler.ts b/docs/.vitepress/theme/themes/themeHandler.ts index 9016cec48..6294a0475 100644 --- a/docs/.vitepress/theme/themes/themeHandler.ts +++ b/docs/.vitepress/theme/themes/themeHandler.ts @@ -21,6 +21,7 @@ import { themeRegistry } from './configs' const STORAGE_KEY_THEME = 'vitepress-theme-name' const STORAGE_KEY_MODE = 'vitepress-display-mode' const STORAGE_KEY_AMOLED = 'vitepress-amoled-enabled' +const STORAGE_KEY_MONOCHROME = 'vitepress-monochrome-enabled' export class ThemeHandler { private state = ref({ @@ -29,6 +30,7 @@ export class ThemeHandler { theme: null }) private amoledEnabled = ref(false) + private monochromeEnabled = ref(false) constructor() { this.initializeTheme() @@ -41,6 +43,7 @@ export class ThemeHandler { const savedTheme = localStorage.getItem(STORAGE_KEY_THEME) || 'color-swarm' const savedMode = localStorage.getItem(STORAGE_KEY_MODE) as DisplayMode | null const savedAmoled = localStorage.getItem(STORAGE_KEY_AMOLED) === 'true' + const savedMonochrome = localStorage.getItem(STORAGE_KEY_MONOCHROME) === 'true' if (themeRegistry[savedTheme]) { this.state.value.currentTheme = savedTheme @@ -49,6 +52,7 @@ export class ThemeHandler { // Set amoled preference this.amoledEnabled.value = savedAmoled + this.monochromeEnabled.value = savedMonochrome // Set mode if (savedMode) { @@ -66,8 +70,8 @@ export class ThemeHandler { if (!localStorage.getItem(STORAGE_KEY_MODE)) { this.state.value.currentMode = e.matches ? 'dark' : 'light' this.applyTheme() - } - else { + } + else { this.applyTheme() } }) @@ -80,11 +84,15 @@ export class ThemeHandler { // Is this the WORST fix of all time??? const root = document.documentElement - const bgColor = currentMode === 'dark' && this.amoledEnabled.value ? '#000000' : currentMode === 'dark' ? '#1A1A1A' : '#f8fafc' + const isMonochrome = currentMode === 'dark' && this.monochromeEnabled.value + + const bgColor = isMonochrome ? '#000000' : currentMode === 'dark' && this.amoledEnabled.value ? '#000000' : currentMode === 'dark' ? '#1A1A1A' : '#f8fafc' root.style.setProperty('--vp-c-bg', bgColor) - const bgAltColor = currentMode === 'dark' && this.amoledEnabled.value ? '#000000' : currentMode === 'dark' ? '#171717' : '#eef2f5' + + const bgAltColor = isMonochrome ? '#000000' : currentMode === 'dark' && this.amoledEnabled.value ? '#000000' : currentMode === 'dark' ? '#171717' : '#eef2f5' root.style.setProperty('--vp-c-bg-alt', bgAltColor) - const bgElvColor = currentMode === 'dark' && this.amoledEnabled.value ? 'rgba(0, 0, 0, 0.9)' : currentMode === 'dark' ? '#1a1a1acc' : 'rgba(255, 255, 255, 0.8)' + + const bgElvColor = isMonochrome ? 'rgba(0, 0, 0, 0.9)' : currentMode === 'dark' && this.amoledEnabled.value ? 'rgba(0, 0, 0, 0.9)' : currentMode === 'dark' ? '#1a1a1acc' : 'rgba(255, 255, 255, 0.8)' root.style.setProperty('--vp-c-bg-elv', bgElvColor) this.applyDOMClasses(currentMode) @@ -99,17 +107,22 @@ export class ThemeHandler { private applyDOMClasses(mode: DisplayMode) { const root = document.documentElement - + // Remove all mode classes - root.classList.remove('dark', 'light', 'amoled') - + root.classList.remove('dark', 'light', 'amoled', 'monochrome') + // Add current mode class root.classList.add(mode) - + // Add amoled class if enabled in dark mode if (mode === 'dark' && this.amoledEnabled.value) { root.classList.add('amoled') } + + // Add monochrome class if enabled in dark mode + if (mode === 'dark' && this.monochromeEnabled.value) { + root.classList.add('monochrome') + } } private applyCSSVariables(colors: ModeColors, theme: Theme) { @@ -127,16 +140,32 @@ export class ThemeHandler { let bgColor = colors.bg let bgAltColor = colors.bgAlt let bgElvColor = colors.bgElv - + + const isMonochrome = this.state.value.currentMode === 'dark' && this.monochromeEnabled.value + if (this.state.value.currentMode === 'dark' && this.amoledEnabled.value) { bgColor = '#000000' bgAltColor = '#000000' bgElvColor = 'rgba(0, 0, 0, 0.9)' } - // Apply brand colors only if theme specifies them - // Otherwise, remove inline styles to let ColorPicker CSS take effect - if (colors.brand && (colors.brand[1] || colors.brand[2] || colors.brand[3] || colors.brand.soft)) { + if (isMonochrome) { + bgColor = '#000000' + bgAltColor = '#000000' + bgElvColor = 'rgba(0, 0, 0, 0.9)' + } + + // Apply brand colors only if theme specifies them OR if monochrome to override + if (isMonochrome) { + root.style.setProperty('--vp-c-brand-1', '#d4d4d4') + root.style.setProperty('--vp-c-brand-2', '#a3a3a3') + root.style.setProperty('--vp-c-brand-3', '#737373') + root.style.setProperty('--vp-c-brand-soft', '#525252') + + root.style.setProperty('--vp-c-text-1', '#ffffff') + root.style.setProperty('--vp-c-text-2', '#a3a3a3') + root.style.setProperty('--vp-c-text-3', '#737373') + } else if (colors.brand && (colors.brand[1] || colors.brand[2] || colors.brand[3] || colors.brand.soft)) { if (colors.brand[1]) root.style.setProperty('--vp-c-brand-1', colors.brand[1]) if (colors.brand[2]) root.style.setProperty('--vp-c-brand-2', colors.brand[2]) if (colors.brand[3]) root.style.setProperty('--vp-c-brand-3', colors.brand[3]) @@ -158,11 +187,12 @@ export class ThemeHandler { } // Apply text colors - always set them to ensure proper theme switching - if (colors.text) { + // Except whenever Monochrome is active, we handled text colors above + if (!isMonochrome && colors.text) { if (colors.text[1]) root.style.setProperty('--vp-c-text-1', colors.text[1]) if (colors.text[2]) root.style.setProperty('--vp-c-text-2', colors.text[2]) if (colors.text[3]) root.style.setProperty('--vp-c-text-3', colors.text[3]) - } else { + } else if (!isMonochrome) { // Remove inline styles if theme doesn't specify text colors // This allows CSS variables from style.scss to take effect root.style.removeProperty('--vp-c-text-1') @@ -284,7 +314,7 @@ export class ThemeHandler { this.state.value.theme = themeRegistry[themeName] localStorage.setItem(STORAGE_KEY_THEME, themeName) this.applyTheme() - + // Force re-apply ColorPicker colors if theme doesn't specify brand colors this.ensureColorPickerColors() } @@ -297,13 +327,27 @@ export class ThemeHandler { public toggleMode() { const currentMode = this.state.value.currentMode - + // Toggle between light and dark const newMode: DisplayMode = currentMode === 'light' ? 'dark' : 'light' - + this.setMode(newMode) } + public setMonochromeEnabled(enabled: boolean) { + this.monochromeEnabled.value = enabled + localStorage.setItem(STORAGE_KEY_MONOCHROME, enabled.toString()) + this.applyTheme() + } + + public getMonochromeEnabled() { + return this.monochromeEnabled.value + } + + public isMonochromeMode() { + return this.state.value.currentMode === 'dark' && this.monochromeEnabled.value + } + public setAmoledEnabled(enabled: boolean) { this.amoledEnabled.value = enabled localStorage.setItem(STORAGE_KEY_AMOLED, enabled.toString()) @@ -403,6 +447,9 @@ export function useTheme() { amoledEnabled: handler.getAmoledEnabledRef(), setAmoledEnabled: (enabled: boolean) => handler.setAmoledEnabled(enabled), toggleAmoled: () => handler.toggleAmoled(), + monochromeEnabled: handler.getMonochromeEnabled(), + setMonochromeEnabled: (enabled: boolean) => handler.setMonochromeEnabled(enabled), + isMonochromeMode: () => handler.isMonochromeMode(), state } } \ No newline at end of file