├── src ├── components │ ├── Container │ │ ├── Container.css │ │ └── Container.jsx │ ├── HeaderTitle │ │ ├── HeaderTitle.css │ │ └── HeaderTitle.jsx │ ├── ModeCTA │ │ ├── ModeCTA.css │ │ └── ModeCTA.jsx │ └── Header │ │ ├── Header.css │ │ └── Header.jsx ├── index.js ├── App │ ├── App.css │ └── App.js └── index.css ├── public ├── favicon.ico ├── logo192.png ├── logo512.png ├── robots.txt ├── manifest.json └── index.html ├── readme_images ├── HeaderTitle.png ├── GlobalStyles.png ├── FolderStructure.png └── HeaderTitleStyle.png ├── .gitignore ├── package.json └── README.md /src/components/Container/Container.css: -------------------------------------------------------------------------------- 1 | .Container { 2 | width: 375px; 3 | } -------------------------------------------------------------------------------- /public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Pipe12/dark-mode-example/HEAD/public/favicon.ico -------------------------------------------------------------------------------- /public/logo192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Pipe12/dark-mode-example/HEAD/public/logo192.png -------------------------------------------------------------------------------- /public/logo512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Pipe12/dark-mode-example/HEAD/public/logo512.png -------------------------------------------------------------------------------- /public/robots.txt: -------------------------------------------------------------------------------- 1 | # https://www.robotstxt.org/robotstxt.html 2 | User-agent: * 3 | Disallow: 4 | -------------------------------------------------------------------------------- /readme_images/HeaderTitle.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Pipe12/dark-mode-example/HEAD/readme_images/HeaderTitle.png -------------------------------------------------------------------------------- /readme_images/GlobalStyles.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Pipe12/dark-mode-example/HEAD/readme_images/GlobalStyles.png -------------------------------------------------------------------------------- /readme_images/FolderStructure.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Pipe12/dark-mode-example/HEAD/readme_images/FolderStructure.png -------------------------------------------------------------------------------- /readme_images/HeaderTitleStyle.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Pipe12/dark-mode-example/HEAD/readme_images/HeaderTitleStyle.png -------------------------------------------------------------------------------- /src/components/HeaderTitle/HeaderTitle.css: -------------------------------------------------------------------------------- 1 | .HeaderTitle { 2 | margin: 0; 3 | font-size: 14px; 4 | font-weight: 800; 5 | } -------------------------------------------------------------------------------- /src/components/HeaderTitle/HeaderTitle.jsx: -------------------------------------------------------------------------------- 1 | import './HeaderTitle.css'; 2 | 3 | const HeaderTitle = () =>

Dark Mode Example

4 | 5 | export default HeaderTitle; -------------------------------------------------------------------------------- /src/components/Container/Container.jsx: -------------------------------------------------------------------------------- 1 | import './Container.css' 2 | 3 | const Container = ({ children }) => { 4 | return ( 5 |
6 | { children } 7 |
8 | ) 9 | } 10 | 11 | export default Container; 12 | -------------------------------------------------------------------------------- /src/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import ReactDOM from 'react-dom'; 3 | import './index.css'; 4 | import App from './App/App'; 5 | 6 | ReactDOM.render( 7 | 8 | 9 | , 10 | document.getElementById('root') 11 | ); 12 | -------------------------------------------------------------------------------- /src/App/App.css: -------------------------------------------------------------------------------- 1 | .App { 2 | border: 1px solid purple; 3 | 4 | height: 100vh; 5 | width: 100vw; 6 | box-sizing: border-box; 7 | 8 | display: grid; 9 | justify-content: center; 10 | align-content: center; 11 | 12 | background-color: var(--dark-mode-background); 13 | } 14 | -------------------------------------------------------------------------------- /src/components/ModeCTA/ModeCTA.css: -------------------------------------------------------------------------------- 1 | .ModeCTA { 2 | background-color: transparent; 3 | border: none; 4 | color: var(--dark-mode-text); 5 | font-size: 12px; 6 | display: flex; 7 | align-items: center; 8 | cursor: pointer; 9 | padding: 0; 10 | } 11 | 12 | .ModeCTA .MoonIcon { 13 | margin-right: 8px; 14 | } -------------------------------------------------------------------------------- /src/components/ModeCTA/ModeCTA.jsx: -------------------------------------------------------------------------------- 1 | import { BsMoon } from 'react-icons/bs'; 2 | import "./ModeCTA.css" 3 | 4 | const ModeCTA = () => { 5 | return ( 6 | 10 | ) 11 | } 12 | 13 | export default ModeCTA; 14 | -------------------------------------------------------------------------------- /src/components/Header/Header.css: -------------------------------------------------------------------------------- 1 | .Header { 2 | background-color: var(--dark-mode-element); 3 | color: var(--dark-mode-text); 4 | height: 80px; 5 | display: flex; 6 | align-items: center; 7 | justify-content: space-between; 8 | padding: 0 24px; 9 | box-shadow: 0px 2px 10px 0px var(--dark-mode-shadow); 10 | } 11 | -------------------------------------------------------------------------------- /src/App/App.js: -------------------------------------------------------------------------------- 1 | import Container from '../components/Container/Container'; 2 | import Header from '../components/Header/Header'; 3 | import './App.css'; 4 | 5 | function App() { 6 | return ( 7 |
8 | 9 |
10 | 11 |
12 | ); 13 | } 14 | 15 | export default App; 16 | -------------------------------------------------------------------------------- /src/components/Header/Header.jsx: -------------------------------------------------------------------------------- 1 | import HeaderTitle from '../HeaderTitle/HeaderTitle'; 2 | import ModeCTA from '../ModeCTA/ModeCTA'; 3 | import './Header.css'; 4 | 5 | const Header = ({ children }) => { 6 | return ( 7 |
8 | 9 | 10 |
11 | ) 12 | } 13 | 14 | export default Header; 15 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | /node_modules 5 | /.pnp 6 | .pnp.js 7 | 8 | # testing 9 | /coverage 10 | 11 | # production 12 | /build 13 | 14 | # misc 15 | .DS_Store 16 | .env.local 17 | .env.development.local 18 | .env.test.local 19 | .env.production.local 20 | 21 | npm-debug.log* 22 | yarn-debug.log* 23 | yarn-error.log* 24 | -------------------------------------------------------------------------------- /public/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "short_name": "React App", 3 | "name": "Create React App Sample", 4 | "icons": [ 5 | { 6 | "src": "favicon.ico", 7 | "sizes": "64x64 32x32 24x24 16x16", 8 | "type": "image/x-icon" 9 | }, 10 | { 11 | "src": "logo192.png", 12 | "type": "image/png", 13 | "sizes": "192x192" 14 | }, 15 | { 16 | "src": "logo512.png", 17 | "type": "image/png", 18 | "sizes": "512x512" 19 | } 20 | ], 21 | "start_url": ".", 22 | "display": "standalone", 23 | "theme_color": "#000000", 24 | "background_color": "#ffffff" 25 | } 26 | -------------------------------------------------------------------------------- /public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 12 | 13 | 14 | React App 15 | 16 | 17 |
18 | 19 | 20 | -------------------------------------------------------------------------------- /src/index.css: -------------------------------------------------------------------------------- 1 | :root { 2 | --dark-blue: hsl(209, 23%, 22%); 3 | --darker-blue: hsl(207, 26%, 17%); 4 | --darkest-blue: hsl(200, 15%, 8%); 5 | --thirty-darkest-blue: hsla(200, 15%, 8%, .3); 6 | --thirty-dark-gray: hsla(0, 0%, 52%, .3); 7 | --dark-gray: hsl(0, 0%, 52%); 8 | --very-light-gray: hsl(0, 0%, 98%); 9 | 10 | --dark-mode-background: var(--darker-blue); 11 | --dark-mode-element: var(--dark-blue); 12 | --dark-mode-text: white; 13 | --dark-mode-input: var(--dark-blue); 14 | --dark-mode-shadow: var(--thirty-darkest-blue); 15 | 16 | --light-mode-background: var(--very-light-gray); 17 | --light-mode-element: white; 18 | --light-mode-text: var(--darkest-blue); 19 | --light-mode-input: var(--dark-gray); 20 | --light-mode-shadow: var(--thirty-dark-gray); 21 | } 22 | 23 | body { 24 | margin: 0; 25 | font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen', 26 | 'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue', 27 | sans-serif; 28 | } 29 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "dark-mode-example", 3 | "version": "0.1.0", 4 | "private": true, 5 | "dependencies": { 6 | "@testing-library/jest-dom": "^5.11.4", 7 | "@testing-library/react": "^11.1.0", 8 | "@testing-library/user-event": "^12.1.10", 9 | "react": "^17.0.2", 10 | "react-dom": "^17.0.2", 11 | "react-icons": "^4.2.0", 12 | "react-scripts": "4.0.3", 13 | "web-vitals": "^1.0.1" 14 | }, 15 | "scripts": { 16 | "start": "react-scripts start", 17 | "build": "react-scripts build", 18 | "test": "react-scripts test", 19 | "eject": "react-scripts eject" 20 | }, 21 | "eslintConfig": { 22 | "extends": [ 23 | "react-app", 24 | "react-app/jest" 25 | ] 26 | }, 27 | "browserslist": { 28 | "production": [ 29 | ">0.2%", 30 | "not dead", 31 | "not op_mini all" 32 | ], 33 | "development": [ 34 | "last 1 chrome version", 35 | "last 1 firefox version", 36 | "last 1 safari version" 37 | ] 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Dark mode usando Styled-components 2 | 3 | ## Table of contents 4 | 5 | - [Objetivo general](#objetivo-general) 6 | - [Introducción a Styled-components](#introducción-a-styled-components) 7 | - [Ventajas de usar Styled-components](ventajas-de-usar-styled-components) 8 | - [Desventajas de usar Styled-components](#desventajas-de-usar-styled-components) 9 | - [Instalación y algunos ejemplos](#instalación-y-algunos-ejemplos) 10 | - [Instalación](#instalación) 11 | - [Crear un componente](#crear-un-componente) 12 | - [Crear estilos globales](#crear-estilos-globales) 13 | - [Proveer los themes a toda la App](#proveer-los-themes-a-toda-la-App) 14 | - [Como usar este repositorio](#como-usar-este-repositorio) 15 | 16 | ## Objetivo general 17 | 18 | Implementar la funcionalidad de Dark Mode usando Styled-components en React. 19 | 20 | ## Introducción a Styled-components 21 | 22 | Styled-components es la repuesta a la búsqueda de como podemos ensanchar CSS para darle estilos a el sistema de componentes de React. 23 | 24 | ### Ventajas de usar Styled-components 25 | 26 | * Automatic critical CSS: Styled-components mantiene un rastreo de cuales componentes son renderezados en la pagina y le inyecta sus estilos y nada mas de manera autmatica. 27 | 28 | * No mas Bugs por nombres de class: Styled-components genera nombres de clase únicos para los estilos. Nunca mas te preocuparas por duplicación, superposición o errores de escritura. 29 | 30 | * Facil de remover del CSS: es difícil saber donde se uso un nombre de clase fue usado dentro del código. Styled-components makes esto obvio, como cada pedazo de CSS es atado a un componente especifico. 31 | 32 | * Simple dynamic styling: Adaptar los estilos del componente basado en las props o el thema global es simple e intuitivo sin la necesidad de manejar muchas clases. 33 | 34 | * Mantenimiento menos doloroso: No tendrás que buscar a través que line a código esta afectando el component. 35 | 36 | * Automatic vendor prefixing: Escribe tu CSS normal y permite que Styled-components haga el resto. 37 | 38 | Tienes todos estos beneficios mientras escribes CSS de toda la vida, solo separado en piezas individuales. 39 | 40 | ### Desventajas de usar Styled-components 41 | 42 | En mi concepto. Si tienes muchos componentes similares como muchos componentes botón cuando estas en el navegador buscando cual es. Los hash generados en las clases no generan ninguna información extra para saber cual es el componente especifico. 43 | 44 | ## Instalación y algunos ejemplos 45 | 46 | ### Instalación 47 | 48 | Instalar styled-components solo toma una linea de código : 49 | 50 | ```bash 51 | # with npm 52 | npm install --save styled-components 53 | 54 | # with yarn 55 | yarn add styled-components 56 | ``` 57 | 58 | ### Crear un componente 59 | 60 | Estructura de carpetas 61 | 62 | ![Header Title HTML](./readme_images/FolderStructure.png) 63 | 64 | Crear estilos StyledHeaderTitle.js 65 | 66 | ```javaScript 67 | import styled from 'styled-components'; 68 | 69 | const StyledHeaderTitle = styled.h2` 70 | margin: 0; 71 | font-size: 14px; 72 | font-weight: 800; 73 | ` 74 | export default StyledHeaderTitle; 75 | ``` 76 | 77 | Crear el componente de React HeaderTitle.jsx 78 | 79 | ```javaScript 80 | import StyledHeaderTitle from './StyledHeaderTitle'; 81 | 82 | const HeaderTitle = () => { 83 | return ( 84 | 85 | Dark Mode Example 86 | 87 | ) 88 | } 89 | 90 | export default HeaderTitle; 91 | ``` 92 | Como se ve en el navegador 93 | 94 | ![Header Title HTML](./readme_images/HeaderTitle.png) 95 | 96 | ![Header Title HTML](./readme_images/HeaderTitleStyle.png) 97 | 98 | ### Crear estilos globales 99 | 100 | Crear los estilos globales en GlobalStyles.js 101 | 102 | ```javaScript 103 | import { createGlobalStyle } from "styled-components"; 104 | 105 | const GlobalStyles = createGlobalStyle` 106 | :root { 107 | --dark-blue: hsl(209, 23%, 22%); 108 | --darker-blue: hsl(207, 26%, 17%); 109 | --darkest-blue: hsl(200, 15%, 8%); 110 | --thirty-darkest-blue: hsla(200, 15%, 8%, .3); 111 | --thirty-dark-gray: hsla(0, 0%, 52%, .3); 112 | --dark-gray: hsl(0, 0%, 52%); 113 | --very-light-gray: hsl(0, 0%, 98%); 114 | 115 | --dark-mode-background: var(--darker-blue); 116 | --dark-mode-element: var(--dark-blue); 117 | --dark-mode-text: white; 118 | --dark-mode-input: var(--dark-blue); 119 | --dark-mode-shadow: var(--thirty-darkest-blue); 120 | 121 | --light-mode-background: var(--very-light-gray); 122 | --light-mode-element: white; 123 | --light-mode-text: var(--darkest-blue); 124 | --light-mode-input: var(--dark-gray); 125 | --light-mode-shadow: var(--thirty-dark-gray); 126 | } 127 | 128 | body { 129 | margin: 0; 130 | font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen', 131 | 'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue', 132 | sans-serif; 133 | } 134 | ` 135 | 136 | export default GlobalStyles; 137 | ``` 138 | 139 | Como se ve en el navegador. 140 | 141 | ![Header Title HTML](./readme_images/GlobalStyles.png) 142 | 143 | ### Proveer los themes a toda la App 144 | 145 | Crear los themes en la App ./src/utils/themes.js 146 | 147 | ```javaScript 148 | export const lightTheme = { 149 | backgroundBody: '--light-mode-background', 150 | text: '--light-mode-text', 151 | backgroundElement: '--light-mode-element', 152 | input: '--light-mode-input', 153 | shadow: '--light-mode-shadow' 154 | } 155 | 156 | export const darkTheme = { 157 | backgroundBody: '--dark-mode-background', 158 | text: '--dark-mode-text', 159 | backgroundElement: '--dark-mode-element', 160 | input: '--dark-mode-input', 161 | shadow: '--dark-mode-shadow' 162 | } 163 | ``` 164 | Proveer themes a la App 165 | 166 | ```javaScript 167 | import { useState } from 'react'; 168 | import StyledApp from './StyledApp'; 169 | import Container from '../components/Container/Container'; 170 | import Header from '../components/Header/Header'; 171 | import { ThemeProvider } from 'styled-components'; 172 | import { darkTheme, lightTheme } from '../utils/themes'; 173 | 174 | function App() { 175 | 176 | const [theme, setTheme] = useState('dark'); 177 | 178 | const toggleTheme = () => { 179 | if (theme === 'dark') { 180 | setTheme('light'); 181 | } else { 182 | setTheme('dark'); 183 | } 184 | } 185 | 186 | return ( 187 | 188 | 189 | 190 |
191 | 192 | 193 | 194 | ); 195 | } 196 | 197 | export default App; 198 | ``` 199 | Aplicandolo en el componente 200 | 201 | ```javaScript 202 | .App { 203 | border: 1px solid purple; 204 | 205 | height: 100vh; 206 | width: 100vw; 207 | box-sizing: border-box; 208 | 209 | display: grid; 210 | justify-content: center; 211 | align-content: center; 212 | 213 | background-color: var(--dark-mode-background); 214 | } 215 | ``` 216 | 217 | ## Como usar este repositorio 218 | 219 | Este repo tiene 6 ramas y en cada se realiza un paso para lograr implementar la funcionalidad de Dark Mode. 220 | 221 | * master 222 | * 01-install-styled-components 223 | * 02-convert-all-components-to-styled-components 224 | * 03-create-global-styles 225 | * 04-create-and-provide-themes 226 | * 05-apply-themes-to-components 227 | * 06-manage-mode-with-button 228 | 229 | Master es la rama inicial y puedes pasar de rama a rama y ver cada paso en la implementación. en la rama 06-manage-mode-with-button puedes ver la implementación terminada. 230 | --------------------------------------------------------------------------------