Skip to content

Instantly share code, notes, and snippets.

@gladchinda
Last active May 4, 2020 17:28
Show Gist options
  • Save gladchinda/2726256c8cd0835e23e5a89e65be272d to your computer and use it in GitHub Desktop.
Save gladchinda/2726256c8cd0835e23e5a89e65be272d to your computer and use it in GitHub Desktop.
import React, { useState, useCallback } from 'react';
import DarkModeToggleSwitch from './DarkModeToggleSwitch';
import ThemeContext, { DARK_THEME, LIGHT_THEME, DarkMode } from './theme';
export default function App () {
const [ dark, setDark ] = useState(DarkMode.getSetting);
const theme = dark ? DARK_THEME : LIGHT_THEME;
const toggleDarkMode = useCallback(function () {
setDark(prevState => {
const newState = !prevState;
DarkMode.updateSetting(newState);
return newState;
});
}, []);
return <>
<div>
<DarkModeToggleSwitch on={dark} toggle={toggleDarkMode}/>
</div>
<ThemeContext.Provider value={theme}>
{/* Render rest of app (consume the theme context) */}
</ThemeContext.Provider>
</>
}
import React from 'react';
import { Sun, Moon } from 'react-feather';
export default function DarkModeToggleSwitch ({ on, toggle }) {
const props = {
type: 'button',
role: 'switch',
onClick: (typeof toggle === 'function') ? toggle : () => {},
'aria-checked': (on = on === true)
};
return <button {...props}>{ on ? <Sun /> : <Moon /> }</button>
}
import React from 'react';
import useTheme from './useTheme';
import ThemeContext from './theme';
export default function App() {
const { theme, darkMode, inheritSystem } = useTheme();
return <>
{/* Render the color scheme controls widget here */}
{/* Use `theme`, `darkMode` and `inheritSystem` as required */}
<ThemeContext.Provider value={theme}>
{/* Render rest of app (consume the theme context) */}
</ThemeContext.Provider>
</>
}
const mq = window.matchMedia('(prefers-color-scheme: dark)');
console.log(`${mq.matches ? 'dark' : 'light'} mode`);
mq.addEventListener('change', function (evt) {
console.log(`${evt.matches ? 'dark' : 'light'} mode`);
});
const mq = window.matchMedia('(prefers-color-scheme: dark)');
const useDarkMode = mq.matches;
// logs true if media query matches, otherwise false
console.log(useDarkMode);
body {color: #333; background: #fff;}
/* Set dark mode styles */
@media (prefers-color-scheme: dark) {
body {color: #fff; background: #333;}
}
import React from 'react';
import { useColorScheme } from 'react-native';
import ThemeContext, { DARK_THEME, LIGHT_THEME } from './theme';
function App () {
const scheme = useColorScheme();
const theme = (scheme === 'dark') ? DARK_THEME : LIGHT_THEME;
return <>
<ThemeContext.Provider value={theme}>
{/* Render rest of app (consume the theme context) */}
</ThemeContext.Provider>
</>
}
import { Appearance } from 'react-native';
const colorScheme = Appearance.getColorScheme();
const useDarkMode = colorScheme === 'dark';
if (useDarkMode) {
// Do some dark mode specific stuff here...
}
import React from 'react';
import useColorScheme from './useColorScheme';
import ThemeContext, { DARK_THEME, LIGHT_THEME } from './theme';
function App () {
const dark = useColorScheme('dark');
const theme = dark ? DARK_THEME : LIGHT_THEME;
return <>
<ThemeContext.Provider value={theme}>
{/* Render rest of app (consume the theme context) */}
</ThemeContext.Provider>
</>
}
import React from 'react';
const DARK_MODE_KEY = 'theme.dark_mode';
const INHERIT_SYSTEM_KEY = 'theme.inherit_system';
const DARK_THEME = { color: '#fff', background: '#333' };
const LIGHT_THEME = { color: '#333', background: '#fff' };
const DarkMode = __themeSettingFactory__(DARK_MODE_KEY);
const InheritSystem = __themeSettingFactory__(INHERIT_SYSTEM_KEY);
function __themeSettingFactory__(settingKey) {
return ({
getSetting: function () {
try {
return JSON.parse(window.localStorage.getItem(settingKey)) === true;
} catch (e) { return false; }
},
updateSetting: function (value) {
try {
window.localStorage.setItem(settingKey, JSON.stringify(value === true));
} catch (e) { }
}
});
}
export { DARK_THEME, LIGHT_THEME, DarkMode, InheritSystem };
export default React.createContext();
import React from 'react';
const DARK_MODE_KEY = 'dark_mode';
const DARK_THEME = { color: '#fff', background: '#333' };
const LIGHT_THEME = { color: '#333', background: '#fff' };
const DarkMode = {
getSetting: function () {
try {
return JSON.parse(window.localStorage.getItem(DARK_MODE_KEY)) === true;
} catch (e) { return false; }
},
updateSetting: function (value) {
try {
window.localStorage.setItem(DARK_MODE_KEY, JSON.stringify(value === true));
} catch (e) {}
}
};
export { DARK_THEME, LIGHT_THEME, DarkMode };
export default React.createContext();
import { useState, useEffect, useRef, useMemo } from 'react';
const COLOR_SCHEMES = ['no-preference', 'dark', 'light'];
const DEFAULT_TARGET_COLOR_SCHEME = 'light';
function resolveTargetColorScheme(scheme) {
if (!(
COLOR_SCHEMES.includes(scheme = String(scheme).toLowerCase()) ||
scheme === 'no-preference'
)) scheme = DEFAULT_TARGET_COLOR_SCHEME;
return scheme;
}
function getCurrentColorScheme() {
const QUERIES = {};
return (getCurrentColorScheme = function() {
for (let scheme of COLOR_SCHEMES) {
const query = QUERIES.hasOwnProperty(scheme)
? QUERIES[scheme]
: (QUERIES[scheme] = matchMedia(`(prefers-color-scheme: ${scheme})`));
if (query.matches) return { query, scheme };
}
})();
}
// Define and export the `useColorScheme` hook
export default function useColorScheme() {}
export default function useColorScheme(targetColorScheme) {
const isMounted = useRef();
const colorScheme = useRef();
const targetScheme = useMemo(() => resolveTargetColorScheme(targetColorScheme), [targetColorScheme]);
const [scheme, setColorScheme] = useState(() => {
const { scheme } = colorScheme.current = getCurrentColorScheme();
return scheme;
});
useEffect(() => {
const { query } = colorScheme.current;
query.addEventListener('change', schemeChangeHandler);
isMounted.current = true;
function schemeChangeHandler(evt) {
if (!evt.matches) {
this.removeEventListener('change', schemeChangeHandler);
const { query, scheme } = colorScheme.current = getCurrentColorScheme();
isMounted.current && setColorScheme(scheme);
query.addEventListener('change', schemeChangeHandler);
}
}
return () => {
const { query } = colorScheme.current;
query.removeEventListener('change', schemeChangeHandler);
isMounted.current = false;
};
}, []);
return scheme === targetScheme;
}
import { useState, useCallback } from 'react';
import useColorScheme from './useColorScheme';
import { DARK_THEME, LIGHT_THEME, DarkMode, InheritSystem } from './theme';
function __stateToggleFactory__(stateUpdater, settingUpdater) {
return function stateToggleFn() {
(typeof stateUpdater === 'function') &&
stateUpdater(prevState => {
const newState = !prevState;
(typeof settingUpdater === 'function') && settingUpdater(newState);
return newState;
});
}
}
export default function useTheme() {
const dark = useColorScheme('dark');
const [darkMode, setDarkMode] = useState(DarkMode.getSetting);
const [inheritSystem, setInheritSystem] = useState(InheritSystem.getSetting);
return {
theme: (inheritSystem ? dark : darkMode) ? DARK_THEME : LIGHT_THEME,
darkMode: {
on: darkMode,
toggle: useCallback(__stateToggleFactory__(setDarkMode, DarkMode.updateSetting), [])
},
inheritSystem: {
on: inheritSystem,
toggle: useCallback(__stateToggleFactory__(setInheritSystem, InheritSystem.updateSetting), [])
}
};
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment