diff --git a/docs/features/dark-mode.mdx b/docs/features/dark-mode.mdx new file mode 100644 index 000000000..83c00138b --- /dev/null +++ b/docs/features/dark-mode.mdx @@ -0,0 +1,116 @@ +--- +id: dark-mode +title: Dark Mode +description: Dark mode support with persistent user preference +--- + +# Dark Mode + +CodeHarborHub includes full dark mode support with persistent user preferences. Your theme choice is automatically saved and remembered across sessions. + +## Features + +✨ **Automatic Theme Detection**: Respects your system preference if you haven't set a preference in CodeHarborHub + +🌓 **Manual Theme Control**: Use the theme toggle button in the navigation bar to switch between light and dark modes + +💾 **Persistent Preferences**: Your theme choice is saved in browser localStorage and restored on your next visit + +⚡ **Smooth Transitions**: Color transitions are animated for a pleasant visual experience + +🎨 **Complete Coverage**: All components and pages support dark mode + +## Using Dark Mode + +### Switching Themes + +1. Look for the theme toggle button in the top-right corner of the navigation bar +2. Click the button to switch between light and dark modes +3. Your preference is automatically saved + +### System Preference + +If you haven't manually set a theme: +- CodeHarborHub will check your system-wide dark mode preference +- If your system is set to dark mode, the site will automatically display in dark mode +- You can override this anytime by using the theme toggle button + +## For Developers + +### Using the Color Mode Hook + +If you're developing components for CodeHarborHub, you can use the `useColorMode` hook to access the current theme: + +```jsx +import useColorMode from '@site/src/hooks/useColorMode'; + +export default function MyComponent() { + const { colorMode, isDark, toggleColorMode } = useColorMode(); + + return ( +
+

Current theme: {colorMode}

+ +
+ ); +} +``` + +### CSS Variables + +Dark mode is applied using the `[data-theme="dark"]` attribute. Use these CSS variables for theming: + +**Light Mode** (`:root`): +```css +--ifm-color-primary: #2e93e2; +--ifm-bg-color: #f8f9fa; +--ifm-text-color: #000; +``` + +**Dark Mode** (`[data-theme="dark"]`): +```css +--ifm-color-primary: #48bf84; +--ifm-bg-color: #1a202c; +--ifm-text-color: #fff; +``` + +### Creating Dark-Mode Aware Components + +```jsx +import styles from './MyComponent.module.css'; + +export default function MyComponent() { + return
Content
; +} +``` + +```css +/* MyComponent.module.css */ +.card { + background: white; + color: black; +} + +[data-theme="dark"] .card { + background: #1a202c; + color: white; +} +``` + +## Browser Compatibility + +Dark mode works on all modern browsers that support CSS custom properties and localStorage: +- Chrome/Edge 49+ +- Firefox 31+ +- Safari 9.1+ +- Opera 36+ + +## Storage + +Your theme preference is stored in the browser's localStorage under the key `theme`. You can manually clear this to reset to system preference: + +```javascript +localStorage.removeItem('theme'); +``` diff --git a/docusaurus.config.js b/docusaurus.config.js index a9e8478ff..5f58951c8 100644 --- a/docusaurus.config.js +++ b/docusaurus.config.js @@ -117,6 +117,11 @@ const config = { themeConfig: /** @type {import('@docusaurus/preset-classic').ThemeConfig} */ ({ + colorMode: { + defaultMode: 'light', + disableSwitch: false, + respectPrefersColorScheme: true, + }, image: "img/codeharborhub-social-card.jpg", announcementBar: { id: "announcementBar", diff --git a/src/components/ThemeToggle.jsx b/src/components/ThemeToggle.jsx new file mode 100644 index 000000000..5876b42cd --- /dev/null +++ b/src/components/ThemeToggle.jsx @@ -0,0 +1,42 @@ +import React from 'react'; +import useColorMode from '@site/src/hooks/useColorMode'; +import styles from './ThemeToggle.module.css'; + +export default function ThemeToggle() { + const { colorMode, toggleColorMode, isDark } = useColorMode(); + + return ( + + ); +} diff --git a/src/components/ThemeToggle.module.css b/src/components/ThemeToggle.module.css new file mode 100644 index 000000000..2e7c2c952 --- /dev/null +++ b/src/components/ThemeToggle.module.css @@ -0,0 +1,49 @@ +.themeToggle { + display: inline-flex; + align-items: center; + justify-content: center; + width: 36px; + height: 36px; + padding: 0; + border: none; + border-radius: 6px; + background: transparent; + color: var(--ifm-text-color); + cursor: pointer; + transition: all 0.2s ease; + font-family: inherit; +} + +.themeToggle:hover { + background: var(--ifm-color-emphasis-200); +} + +.themeToggle:focus-visible { + outline: 2px solid var(--ifm-color-primary); + outline-offset: 2px; +} + +.themeToggle:active { + transform: scale(0.95); +} + +.icon { + width: 20px; + height: 20px; + display: block; +} + +@media (prefers-reduced-motion: no-preference) { + .themeToggle { + transition: background 0.2s cubic-bezier(0.4, 0, 0.2, 1), + color 0.2s cubic-bezier(0.4, 0, 0.2, 1); + } + + .icon { + transition: transform 0.3s cubic-bezier(0.4, 0, 0.2, 1); + } + + .themeToggle:hover .icon { + transform: rotate(15deg); + } +} diff --git a/src/css/custom.css b/src/css/custom.css index 28a0cbd2d..9150e4bd2 100644 --- a/src/css/custom.css +++ b/src/css/custom.css @@ -98,6 +98,19 @@ body { background-image: url("/landing/grid-dark.svg"); } +/* Smooth theme transitions */ +@media (prefers-reduced-motion: no-preference) { + html { + color-scheme: light dark; + transition: background-color 0.3s ease, color 0.3s ease; + } + + body, + :root { + transition: background-color 0.3s ease, color 0.3s ease, border-color 0.3s ease; + } +} + .header-github-link::before { content: ""; width: 24px; diff --git a/src/hooks/useColorMode.js b/src/hooks/useColorMode.js new file mode 100644 index 000000000..54bac306f --- /dev/null +++ b/src/hooks/useColorMode.js @@ -0,0 +1,25 @@ +import { useContext } from 'react'; +import { ColorModeContext } from '@docusaurus/theme-common'; + +export function useColorMode() { + const { colorMode, setColorMode } = useContext(ColorModeContext); + + const toggleColorMode = () => { + setColorMode(colorMode === 'light' ? 'dark' : 'light'); + }; + + const setLight = () => setColorMode('light'); + const setDark = () => setColorMode('dark'); + + return { + colorMode, + setColorMode, + toggleColorMode, + setLight, + setDark, + isDark: colorMode === 'dark', + isLight: colorMode === 'light', + }; +} + +export default useColorMode;