{"componentChunkName":"component---src-components-posts-layout-index-js","path":"/implementing-dark-mode/","webpackCompilationHash":"6fd724ef40842b910a04","result":{"data":{"mdx":{"id":"43658bb1-e4f8-520e-86ae-5fe72f200715","timeToRead":4,"body":"function _extends() { _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; return _extends.apply(this, arguments); }\n\nfunction _objectWithoutProperties(source, excluded) { if (source == null) return {}; var target = _objectWithoutPropertiesLoose(source, excluded); var key, i; if (Object.getOwnPropertySymbols) { var sourceSymbolKeys = Object.getOwnPropertySymbols(source); for (i = 0; i < sourceSymbolKeys.length; i++) { key = sourceSymbolKeys[i]; if (excluded.indexOf(key) >= 0) continue; if (!Object.prototype.propertyIsEnumerable.call(source, key)) continue; target[key] = source[key]; } } return target; }\n\nfunction _objectWithoutPropertiesLoose(source, excluded) { if (source == null) return {}; var target = {}; var sourceKeys = Object.keys(source); var key, i; for (i = 0; i < sourceKeys.length; i++) { key = sourceKeys[i]; if (excluded.indexOf(key) >= 0) continue; target[key] = source[key]; } return target; }\n\n/* @jsx mdx */\nvar _frontmatter = {\n  \"title\": \"Implementing Dark Mode 🌙\",\n  \"date\": \"2019-12-12\",\n  \"excerpt\": \"I'm going to walk you through how I implemented dark mode on this site. You'll learn how your dark mode preference is read from OS level settings and saved to local storage.\"\n};\n\nvar makeShortcode = function makeShortcode(name) {\n  return function MDXDefaultShortcode(props) {\n    console.warn(\"Component \" + name + \" was not imported, exported, or provided by MDXProvider as global scope\");\n    return mdx(\"div\", props);\n  };\n};\n\nvar layoutProps = {\n  _frontmatter: _frontmatter\n};\nvar MDXLayout = \"wrapper\";\nreturn function MDXContent(_ref) {\n  var components = _ref.components,\n      props = _objectWithoutProperties(_ref, [\"components\"]);\n\n  return mdx(MDXLayout, _extends({}, layoutProps, props, {\n    components: components,\n    mdxType: \"MDXLayout\"\n  }), mdx(\"p\", null, \"I'm going to walk you through how I implemented dark mode on this site.\\nYou'll learn how users' dark mode preference is read from OS level settings and saved to local storage.\"), mdx(\"br\", null), mdx(\"h2\", null, \"useDarkMode\"), mdx(\"p\", null, \"First, let's take a look at the meat & potatoes of this implementation, the useDarkMode() hook.\"), mdx(\"blockquote\", null, mdx(\"p\", {\n    parentName: \"blockquote\"\n  }, \"Full disclosure, this hook we're looking at was written by Gabe Ragland and postes on his site \", mdx(\"a\", {\n    href: \"https://usehooks.com/useDarkMode/\"\n  }, \"useHooks.com\"), \" which has a ton of hook recipes for all kinds of use cases.\")), mdx(\"pre\", null, mdx(\"code\", _extends({\n    parentName: \"pre\"\n  }, {\n    \"className\": \"language-js\"\n  }), \"import { useMedia } from './useMedia'\\nimport { useLocalStorage } from './useLocalStorage'\\n\\nfunction useDarkMode() {\\n  // Uses useLocalStorage hook to persist state through a page refresh.\\n  const [enabledState, setEnabledState] = useLocalStorage('dark-mode-enabled')\\n\\n  // See if user has set a browser or OS preference for dark mode.\\n  const prefersDarkMode = usePrefersDarkMode()\\n\\n  // If enabledState is defined use it, otherwise fallback to prefersDarkMode.\\n  // This allows user to override OS level setting on our website.\\n  const enabled =\\n    typeof enabledState !== 'undefined' ? enabledState : prefersDarkMode\\n\\n  // Return enabled state and setter\\n  return [enabled, setEnabledState]\\n}\\n\\n// Compose our useMedia hook to detect dark mode preference.\\nfunction usePrefersDarkMode() {\\n  return useMedia(['(prefers-color-scheme: dark)'], [true], false)\\n}\\n\\nexport { useDarkMode }\\n\")), mdx(\"p\", null, \"Part of what makes hooks powerful is their shareability and composability.\\nSo here we have our useDarkMode hook. The hook composes a \", mdx(\"a\", {\n    href: \"https://usehooks.com/useLocalStorage/\"\n  }, \"useLocalStorage\"), \"\\nand \", mdx(\"a\", {\n    href: \"https://usehooks.com/useMedia/\"\n  }, \"useMedia\"), \" hook.\"), mdx(\"br\", null), mdx(\"h2\", null, \"useLocalStorage\"), mdx(\"p\", null, \"The useLocalStorage hook is doing just what it sounds like. It's using local storage to store the user's dark mode preference.\"), mdx(\"blockquote\", null, mdx(\"p\", {\n    parentName: \"blockquote\"\n  }, \"Local storage is data stored in the browser that persists even after the browser window has been refreshed or closed altogether.\")), mdx(\"p\", null, \"This hook will allow us to \\\"save\\\" the user's dark mode setting.\\nThe site will \\\"remember\\\" their dark mode setting if they leave and don't come back for a while.\\nTo see this in action, simply refresh this page and you'll notice your dark mode preference was saved.\"), mdx(\"br\", null), mdx(\"p\", null, \"See the full useLocalStorage hook \", mdx(\"a\", {\n    href: \"https://usehooks.com/useLocalStorage/\"\n  }, \"here\"), \".\"), mdx(\"br\", null), mdx(\"h2\", null, \"useMedia\"), mdx(\"p\", null, \"This hook is going to look at your OS level settings to see if you prefer dark mode.\"), mdx(\"blockquote\", null, mdx(\"p\", {\n    parentName: \"blockquote\"\n  }, \"In iOS 13.0 and later, people can choose to adopt a dark system-wide appearance called Dark Mode.\")), mdx(\"p\", null, \"Through a media query, which this hook is using, we can determine if the color scheme preference to dark on your OS.'\"), mdx(\"br\", null), mdx(\"p\", null, \"See the full useMedia hook \", mdx(\"a\", {\n    href: \"https://usehooks.com/useMedia/\"\n  }, \"here\"), \".\"), mdx(\"br\", null), mdx(\"h2\", null, \"initial state and our setter\"), mdx(\"p\", null, \"Finally, we simply return out our initial state and setter function which will allow the user to override the OS level settings.\\nSo the useDarkMode function is going to return true or false based on whether or not dark mode is enabled.\"), mdx(\"br\", null), mdx(\"p\", null, \"Now we can import that hook at the top level and use it to determine if dark mode is enabled.\\nUpon calling this useDarkMode hook, we'll get the value \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"true\"), \" if dark mode is enabled or \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"false\"), \" if it is not.\"), mdx(\"pre\", null, mdx(\"code\", _extends({\n    parentName: \"pre\"\n  }, {\n    \"className\": \"language-js\"\n  }), \"// top level component\\nimport React from 'react'\\nimport { useDarkMode } from '../../hooks/useDarkMode'\\n\\nconst Layout = ({ children }) => {\\n  const [darkMode, setDarkMode] = useDarkMode()\\n\\n  return (\\n    <Layout>\\n      <Toggle onChange={() => setDarkMode(!darkMode)} />\\n      {children}\\n    </Layout>\\n  )\\n}\\n\\nexport default Layout\\n\")), mdx(\"p\", null, \"As you can see, we are providing the ability to toggle dark mode by passing our setter function to the onChange event handler:\"), mdx(\"pre\", null, mdx(\"code\", _extends({\n    parentName: \"pre\"\n  }, {\n    \"className\": \"language-js\"\n  }), \"<Toggle onChange={() => setDarkMode(!darkMode)} />\\n\")), mdx(\"br\", null), mdx(\"h2\", null, \"styled components\"), mdx(\"p\", null, \"At this point we have the ability to read from OS level settings and local storage to determine the initial dark mode state value,\\nand we are providing a way for the user to toggle that value.\"), mdx(\"br\", null), mdx(\"p\", null, \"That's great, but how do now style the site based on that state value?\"), mdx(\"br\", null), mdx(\"p\", null, \"Styled components \\uD83D\\uDC85\"), mdx(\"br\", null), mdx(\"p\", null, \"This is a CSS-in-JS solution and IMO the ideal solution in this case.\"), mdx(\"blockquote\", null, mdx(\"p\", null, mdx(\"a\", {\n    href: \"https://www.styled-components.com/\"\n  }, \"styled-components\"), \" lets you write actual CSS in your JavaScript. This means you can use all the features of CSS you use and love, including (but by far not limited to) media queries, all pseudo-selectors, nesting, etc.\")), mdx(\"br\", null), mdx(\"p\", null, \"Here is an example of a styled component for those unfamiliar:\"), mdx(\"pre\", null, mdx(\"code\", _extends({\n    parentName: \"pre\"\n  }, {\n    \"className\": \"language-js\"\n  }), \"import React from 'react'\\nimport styled from 'styled-components'\\n\\nconst StyledButton = styled.button`\\n  background: transparent;\\n  border-radius: 3px;\\n  border: 2px solid cornflowerblue;\\n  color: cornflowerblue;\\n  margin: 0 1em;\\n  padding: 0.25em 1em;\\n`\\n\\nconst Button = () => {\\n  return <StyledButton>I'm a styled button</StyledButton>\\n}\\n\\nexport default Button\\n\")), mdx(\"p\", null, \"Here we have a Button component. In it we're returning out our styled component which we're defining up top as \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"StyledButton\"), \".\\nThis may look a little weird at first, but you get used to it. We're also importing \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"styled\"), \" which is the default export from styled component.\"), mdx(\"blockquote\", null, mdx(\"p\", {\n    parentName: \"blockquote\"\n  }, \"styled returns a function that accepts a tagged template literal and turns it into a StyledComponent.\")), mdx(\"p\", null, \"So \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"styled\"), \" is a low-level factory you use to create \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"styled.tagname\"), \" helper methods. Here, we are creating a button so we say \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"styled.button\"), \" which will create an actual HTML button element.\\nThis could be styled.div or styled.h1 or you could even pass it a custom component.\"), mdx(\"br\", null), mdx(\"p\", null, \"This StyledButton variable here is now a React component that you can use like any other React component!\"), mdx(\"br\", null), mdx(\"p\", null, \"So that's how styled-components work on a base level, now let's get back to our issue of theming.\"), mdx(\"br\", null), mdx(\"h2\", null, \"ThemeProvider\"), mdx(\"p\", null, \"Styled components exports a helper component called \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"ThemeProvider\"), \".\"), mdx(\"blockquote\", null, mdx(\"p\", {\n    parentName: \"blockquote\"\n  }, \"ThemeProvider allows you to inject a theme into all styled components anywhere beneath it in the component tree, via the context API.\")), mdx(\"p\", null, \"In our top level component, which is the \\\"parent\\\" of all other components, we're going to wrap all of our children in this \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"ThemeProvider\"), \".\"), mdx(\"pre\", null, mdx(\"code\", _extends({\n    parentName: \"pre\"\n  }, {\n    \"className\": \"language-js\"\n  }), \"// our top level component\\nconst Layout = ({ children }) => {\\n  // our fully comprehensive theme\\n  const theme = {\\n    primaryColor: 'cornflowerblue',\\n  }\\n\\n  // styled component accessing the theme prop\\n  const Box = styled.div`\\n    color: ${props => props.theme.primaryColor};\\n  `\\n\\n  return (\\n    <ThemeProvider theme={theme}>\\n      <Box />\\n      {children}\\n    </ThemeProvider>\\n  )\\n}\\n\\nexport default Layout\\n\")), mdx(\"p\", null, \"Now the theme becomes available to all child components via the theme prop. In the above example, you can see we are accessing the theme via props in our Box styled component.\"), mdx(\"br\", null), mdx(\"p\", null, \"We can access the theme inside of any styled component that is a child of the ThemeProvider.\\nSince we wrapped our app in the ThemeProvider at the top level, all styled components are children of the ThemeProvider.\\nThat means any styled component can access the theme via props:\"), mdx(\"pre\", null, mdx(\"code\", _extends({\n    parentName: \"pre\"\n  }, {\n    \"className\": \"language-js\"\n  }), \"// a deeply nested component\\nconst MyDeeplyNestedComponent = () => {\\n  // styled component can access theme via props\\n  const Heading = styled.h1`\\n    color: ${props => props.theme.primaryColor};\\n  `\\n\\n  return (\\n    <Container>\\n      <Heading>Hey, I'm a styled heading</Heading>\\n    </Container>\\n  )\\n}\\n\\nexport default MyDeeplyNestedComponent\\n\")), mdx(\"br\", null), mdx(\"p\", null, \"Now, we can simply build a themes object which houses two separate objects within it, one for our lightTheme styles, and one for darkTheme.\\nWe already have our state set up to determine if dark mode is enabled or not. Now it's just becomes a matter of setting the appropriate theme object based on that state.\"), mdx(\"pre\", null, mdx(\"code\", _extends({\n    parentName: \"pre\"\n  }, {\n    \"className\": \"language-js\"\n  }), \"const Layout = ({ children }) => {\\n  const themes = {\\n    // light theme styles\\n    lightTheme: {\\n      primaryColor: 'cornflowerblue',\\n    },\\n\\n    // dark theme styles\\n    darkTheme: {\\n      primaryColor: '#fff',\\n    },\\n  }\\n\\n  const [darkMode, setDarkMode] = useDarkMode()\\n\\n  const theme = darkMode ? themes.darkTheme : themes.lightTheme\\n\\n  return (\\n    <ThemeProvider theme={theme}>\\n      <Toggle onChange={() => setDarkMode(!darkMode)} />\\n      {children}\\n    </ThemeProvider>\\n  )\\n}\\n\\nexport default Layout\\n\")), mdx(\"br\", null), mdx(\"p\", null, \"Let's look back at our \", mdx(\"strong\", {\n    parentName: \"p\"\n  }, \"deeply nested component\"), \":\"), mdx(\"pre\", null, mdx(\"code\", _extends({\n    parentName: \"pre\"\n  }, {\n    \"className\": \"language-js\"\n  }), \"// a deeply nested component\\nconst MyDeeplyNestedComponent = () => {\\n  // styled component can access theme via props\\n  const Heading = styled.h1`\\n    color: ${props => props.theme.primaryColor};\\n  `\\n\\n  return (\\n    <Container>\\n      <Heading>Hey, I'm a styled heading</Heading>\\n    </Container>\\n  )\\n}\\n\\nexport default MyDeeplyNestedComponent\\n\")), mdx(\"p\", null, \"This is the exact same code, except now, \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"this props.theme.primaryColor\"), \" is going to be either the \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"primaryColor\"), \" stored in the lightTheme object, or the one stored in the darkTheme object if dark mode is enabled.\"), mdx(\"br\", null), mdx(\"p\", null, \"One thing to note is that the theme prop is only available inside of a styled component.\\nOften, you'll run into a situation where you just want to grab something from the theme and pass it along.\\nYou can do this via a React hook called useContext.\"), mdx(\"pre\", null, mdx(\"code\", _extends({\n    parentName: \"pre\"\n  }, {\n    \"className\": \"language-js\"\n  }), \"import React, { useContext } from 'react'\\nimport { ThemeContext } from 'styled-components'\\nimport Button from '../../components/Button'\\n\\nconst MyDeeplyNestedComponent = () => {\\n  const theme = useContext(ThemeContext)\\n\\n  return (\\n    <Container>\\n      <Button color={theme.primaryColor} text=\\\"Hey, I'm a styled button\\\" />\\n    </Container>\\n  )\\n}\\n\\nexport default MyDeeplyNestedComponent\\n\")), mdx(\"p\", null, \"Here, we're grabbing the \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"ThemeContext\"), \" from styled-components and passing it to useContext.\\nThe output of that is going to be what's stored in that theme prop on the \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"ThemeProvider\"), \". We can then grab our color off of the theme object and pass it to a React component.\"), mdx(\"br\", null), mdx(\"p\", null, \"A common use-case for this is icons. For example, I have my icons as React components rendering svg's. They are set up to read their \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"fill\"), \" or \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"stroke\"), \" property from a color prop like so:\"), mdx(\"pre\", null, mdx(\"code\", _extends({\n    parentName: \"pre\"\n  }, {\n    \"className\": \"language-js\"\n  }), \"import React from 'react'\\n\\nconst Arrow = ({ color }) => {\\n  const stroke = color ? color : '#4C51BF'\\n\\n  return (\\n    <svg width=\\\"15\\\" height=\\\"18\\\" xmlns=\\\"http://www.w3.org/2000/svg\\\">\\n      <g stroke={stroke}>\\n        <path d=\\\"M7.5 16.667l6.036-7.12a.878.878 0 000-1.094L7.5 1.333\\\" />\\n      </g>\\n    </svg>\\n  )\\n}\\n\\nexport default Arrow\\n\")), mdx(\"p\", null, \"Now I can useContext, assign it's output to \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"theme\"), \" and then pass along the color I need to the icon's color prop.\"), mdx(\"pre\", null, mdx(\"code\", _extends({\n    parentName: \"pre\"\n  }, {\n    \"className\": \"language-js\"\n  }), \"import React, { useContext } from 'react'\\nimport { ThemeContext } from 'styled-components'\\nimport Arrow from '../../components/Arrow'\\n\\nconst MyDeeplyNestedComponent = () => {\\n  const theme = useContext(ThemeContext)\\n\\n  return (\\n    <Container>\\n      <Arrow color={theme.iconColor} />\\n    </Container>\\n  )\\n}\\n\\nexport default MyDeeplyNestedComponent\\n\")), mdx(\"p\", null, \"I hope you enjoyed this walkthrough and that it was somewhat easy to follow. The above code is obviously abbreviated, especially the theme objects.\\nCheck out the \", mdx(\"a\", {\n    href: \"https://github.com/dillonmorris91/portfolio/tree/master/src\"\n  }, \"source code\"), \" for my site to see the nitty gritty. Feel free to get at me and let me know what you think of this implementation.\"), mdx(\"br\", null), mdx(\"p\", null, \"Thanks so much for reading. Cheers! \\uD83C\\uDF7B\"));\n}\n;\nMDXContent.isMDXComponent = true;","frontmatter":{"title":"Implementing Dark Mode 🌙","date":"December 12, 2019"}}},"pageContext":{"isCreatedByStatefulCreatePages":false,"id":"43658bb1-e4f8-520e-86ae-5fe72f200715","previous":{"id":"c96b71a9-1eb5-5b69-beef-a61932a5b2a0","fields":{"slug":"/theming-contentful/"},"frontmatter":{"title":"Theming with Contentful"}},"next":{"id":"52ff7628-ef2c-50e8-9757-5f67da2322c5","fields":{"slug":"/understanding-closure/"},"frontmatter":{"title":"Understanding Closure"}}}}}