├── .gitignore ├── README.md ├── package-lock.json ├── package.json ├── public ├── favicon.ico ├── index.html ├── logo192.png ├── logo512.png ├── manifest.json └── robots.txt └── src ├── App.js ├── data.js ├── final ├── App.js ├── components │ ├── Navbar.js │ └── StyledNavbar.js ├── data.js └── pages │ ├── About.js │ ├── Dashboard.js │ ├── Error.js │ ├── Home.js │ ├── Login.js │ ├── Products.js │ ├── ProtectedRoute.js │ ├── SharedLayout.js │ ├── SharedProductLayout.js │ └── SingleProduct.js ├── index.css ├── index.js └── pages ├── About.js ├── Dashboard.js ├── Error.js ├── Home.js ├── Login.js ├── Products.js └── SingleProduct.js /.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 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # React Router 6 2 | 3 | #### React Course 4 | 5 | [My React Course](https://www.udemy.com/course/react-tutorial-and-projects-course/?referralCode=FEE6A921AF07E2563CEF) 6 | 7 | #### Support 8 | 9 | Find the App Useful? [You can always buy me a coffee](https://www.buymeacoffee.com/johnsmilga) 10 | 11 | #### Run Complete Project 12 | 13 | - index.js 14 | 15 | ```js 16 | // import App from './App'; 17 | import App from './final/App'; 18 | ``` 19 | 20 | #### Docs 21 | 22 | [React Router Docs](https://reactrouter.com/docs/en/v6/getting-started/overview) 23 | 24 | #### Install 25 | 26 | ```sh 27 | npm install react-router-dom@6 28 | ``` 29 | 30 | #### First Pages 31 | 32 | - App.js 33 | 34 | ```js 35 | import { BrowserRouter, Routes, Route } from 'react-router-dom'; 36 | 37 | function App() { 38 | return ( 39 | 40 | 41 | home page} /> 42 | 46 |

testing

47 | 48 | } 49 | /> 50 |
51 |
52 | ); 53 | } 54 | 55 | export default App; 56 | ``` 57 | 58 | #### Components 59 | 60 | - App.js 61 | 62 | ```js 63 | import { BrowserRouter, Routes, Route } from 'react-router-dom'; 64 | import Home from './pages/Home'; 65 | import About from './pages/About'; 66 | import Products from './pages/Products'; 67 | 68 | function App() { 69 | return ( 70 | 71 | 72 | } /> 73 | } /> 74 | } /> 75 | 76 | 77 | ); 78 | } 79 | 80 | export default App; 81 | ``` 82 | 83 | #### Links 84 | 85 | - Home.js, About.js 86 | 87 | ```js 88 | import { Link } from 'react-router-dom'; 89 | 90 | const Home = () => { 91 | return ( 92 |
93 |

Home Page

94 | 95 | About 96 | 97 | 98 |
99 | ); 100 | }; 101 | export default Home; 102 | ``` 103 | 104 | #### Error Page 105 | 106 | - App.js 107 | 108 | ```js 109 | function App() { 110 | return ( 111 | 112 | 113 | } /> 114 | } /> 115 | } /> 116 | } /> 117 | 118 | 119 | ); 120 | } 121 | ``` 122 | 123 | - Error.js 124 | 125 | ```js 126 | import { Link } from 'react-router-dom'; 127 | 128 | const Error = () => { 129 | return ( 130 |
131 |

404

132 |

page not found

133 | back home 134 |
135 | ); 136 | }; 137 | export default Error; 138 | ``` 139 | 140 | #### Nested Pages 141 | 142 | - will refactor few times 143 | 144 | - App.js 145 | 146 | ```js 147 | function App() { 148 | return ( 149 | 150 | 151 | }> 152 | } /> 153 | } /> 154 | } /> 155 | 156 | 157 | 158 | ); 159 | } 160 | ``` 161 | 162 | #### Shared Layout 163 | 164 | - Home.js 165 | 166 | ```js 167 | import { Link, Outlet } from 'react-router-dom'; 168 | 169 | const Home = () => { 170 | return ( 171 |
172 |

Home Page

173 | 174 |
175 | ); 176 | }; 177 | export default Home; 178 | ``` 179 | 180 | #### Navbar 181 | 182 | - Navbar.js 183 | 184 | ```js 185 | import { Link } from 'react-router-dom'; 186 | 187 | const Navbar = () => { 188 | return ( 189 | 194 | ); 195 | }; 196 | export default Navbar; 197 | ``` 198 | 199 | - Home.js 200 | 201 | ```js 202 | import { Link, Outlet } from 'react-router-dom'; 203 | import Navbar from '../components/Navbar'; 204 | const Home = () => { 205 | return ( 206 | <> 207 | 208 |
209 | 210 |
211 | 212 | ); 213 | }; 214 | export default Home; 215 | ``` 216 | 217 | #### Index Routes 218 | 219 | - Index routes render in the parent routes outlet at the parent route's path. 220 | - Index routes match when a parent route matches but none of the other children match. 221 | - Index routes are the default child route for a parent route. 222 | - Index routes render when the user hasn't clicked one of the items in a navigation list yet. 223 | 224 | - copy Home.js content 225 | - SharedLayout.js 226 | 227 | ```js 228 | import { Link, Outlet } from 'react-router-dom'; 229 | import Navbar from '../components/Navbar'; 230 | const SharedLayout = () => { 231 | return ( 232 | <> 233 | 234 |
235 | 236 |
237 | 238 | ); 239 | }; 240 | export default SharedLayout; 241 | ``` 242 | 243 | - Home.js 244 | 245 | ```js 246 | const Home = () => { 247 | return ( 248 |
249 |

Home Page

250 |
251 | ); 252 | }; 253 | export default Home; 254 | ``` 255 | 256 | - App.js 257 | 258 | ```js 259 | function App() { 260 | return ( 261 | 262 | 263 | }> 264 | } /> 265 | } /> 266 | } /> 267 | } /> 268 | 269 | 270 | 271 | ); 272 | } 273 | ``` 274 | 275 | #### NavLink (style) 276 | 277 | - StyledNavbar.js 278 | 279 | ```js 280 | import { NavLink } from 'react-router-dom'; 281 | 282 | ; 292 | ``` 293 | 294 | #### NavLink (className) 295 | 296 | - StyledNavbar.js 297 | 298 | ```js 299 | 307 | ``` 308 | 309 | #### Reading URL Params 310 | 311 | - App.js 312 | 313 | ```js 314 | function App() { 315 | return ( 316 | 317 | 318 | }> 319 | } /> 320 | } /> 321 | } /> 322 | } /> 323 | } /> 324 | 325 | 326 | 327 | ); 328 | } 329 | ``` 330 | 331 | #### Single Product 332 | 333 | - SingleProduct.js 334 | 335 | ```js 336 | import { Link, useParams } from 'react-router-dom'; 337 | import products from '../data'; 338 | const SingleProduct = () => { 339 | const { productId } = useParams(); 340 | 341 | return ( 342 |
343 |

{productId}

344 | back to products 345 |
346 | ); 347 | }; 348 | 349 | export default SingleProduct; 350 | ``` 351 | 352 | #### Products Page 353 | 354 | - Products.js 355 | 356 | ```js 357 | import { Link } from 'react-router-dom'; 358 | import products from '../data'; 359 | const Products = () => { 360 | return ( 361 |
362 |

products

363 |
364 | {products.map((product) => { 365 | return ( 366 |
367 |
{product.name}
368 | more info 369 |
370 | ); 371 | })} 372 |
373 |
374 | ); 375 | }; 376 | 377 | export default Products; 378 | ``` 379 | 380 | #### Single Product 381 | 382 | - SingleProduct.js 383 | 384 | ```js 385 | import { Link, useParams } from 'react-router-dom'; 386 | import products from '../data'; 387 | const SingleProduct = () => { 388 | const { productId } = useParams(); 389 | const product = products.find((product) => product.id === productId); 390 | const { image, name } = product; 391 | 392 | return ( 393 |
394 | {name} 395 |
{name}
396 | back to products 397 |
398 | ); 399 | }; 400 | 401 | export default SingleProduct; 402 | ``` 403 | 404 | #### useNavigate() 405 | 406 | [ (?.) or Optional Chaining Explained](https://youtu.be/PuEGrylM1x8) 407 | 408 | - App.js 409 | 410 | ```js 411 | function App() { 412 | const [user, setUser] = useState(null); 413 | 414 | return ( 415 | 416 | 417 | }> 418 | } /> 419 | } /> 420 | } /> 421 | } /> 422 | } /> 423 | } /> 424 | } /> 425 | 426 | 427 | 428 | ); 429 | } 430 | ``` 431 | 432 | - Login.js 433 | 434 | ```js 435 | import { useState } from 'react'; 436 | import { useNavigate } from 'react-router-dom'; 437 | const Login = ({ setUser }) => { 438 | const [name, setName] = useState(''); 439 | const [email, setEmail] = useState(''); 440 | 441 | const navigate = useNavigate(); 442 | 443 | const handleSubmit = async (e) => { 444 | e.preventDefault(); 445 | if (!name || !email) return; 446 | setUser({ name: name, email: email }); 447 | navigate('/dashboard'); 448 | }; 449 | 450 | ``` 451 | 452 | [ (?.) or Optional Chaining Explained](https://youtu.be/PuEGrylM1x8) 453 | 454 | - Dashboard.js 455 | 456 | ```js 457 | const Dashboard = ({ user }) => { 458 | return ( 459 |
460 |

Hello, {user?.name}

461 |
462 | ); 463 | }; 464 | export default Dashboard; 465 | ``` 466 | 467 | #### Protected Route 468 | 469 | - App.js 470 | 471 | ```js 472 | 476 | 477 | 478 | } 479 | /> 480 | ``` 481 | 482 | - ProtectedRoute.js 483 | 484 | ```js 485 | import { Navigate } from 'react-router-dom'; 486 | 487 | const ProtectedRoute = ({ children, user }) => { 488 | if (!user) { 489 | return ; 490 | } 491 | return children; 492 | }; 493 | 494 | export default ProtectedRoute; 495 | ``` 496 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "react-router", 3 | "version": "0.1.0", 4 | "private": true, 5 | "dependencies": { 6 | "@testing-library/jest-dom": "^5.16.2", 7 | "@testing-library/react": "^12.1.4", 8 | "@testing-library/user-event": "^13.5.0", 9 | "react": "^17.0.2", 10 | "react-dom": "^17.0.2", 11 | "react-router-dom": "^6.2.2", 12 | "react-scripts": "5.0.0", 13 | "web-vitals": "^2.1.4" 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 | -------------------------------------------------------------------------------- /public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/john-smilga/react-router-6-tutorial/7ec32a47862e454f7551a0b111dc569da8ce8d04/public/favicon.ico -------------------------------------------------------------------------------- /public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 12 | 13 | 17 | 18 | 27 | React App 28 | 29 | 30 | 31 |
32 | 42 | 43 | 44 | -------------------------------------------------------------------------------- /public/logo192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/john-smilga/react-router-6-tutorial/7ec32a47862e454f7551a0b111dc569da8ce8d04/public/logo192.png -------------------------------------------------------------------------------- /public/logo512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/john-smilga/react-router-6-tutorial/7ec32a47862e454f7551a0b111dc569da8ce8d04/public/logo512.png -------------------------------------------------------------------------------- /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/robots.txt: -------------------------------------------------------------------------------- 1 | # https://www.robotstxt.org/robotstxt.html 2 | User-agent: * 3 | Disallow: 4 | -------------------------------------------------------------------------------- /src/App.js: -------------------------------------------------------------------------------- 1 | function App() { 2 | return

React Router 6 Tutorial

; 3 | } 4 | 5 | export default App; 6 | -------------------------------------------------------------------------------- /src/data.js: -------------------------------------------------------------------------------- 1 | const products = [ 2 | { 3 | id: 'recZkNf2kwmdBcqd0', 4 | name: 'accent chair', 5 | image: 6 | 'https://res.cloudinary.com/dt2g7mgtv/image/upload/v1681749482/react-comfy-store-products/iuYyO9RP_o_upinxq.jpg', 7 | }, 8 | { 9 | id: 'recEHmzvupvT8ZONH', 10 | name: 'albany sectional', 11 | 12 | image: 13 | 'https://res.cloudinary.com/dt2g7mgtv/image/upload/v1681750782/react-comfy-store-products/product-2_lusrzx.jpg', 14 | }, 15 | { 16 | id: 'rec5NBwZ5zCD9nfF0', 17 | name: 'albany table', 18 | 19 | image: 20 | 'https://res.cloudinary.com/dt2g7mgtv/image/upload/v1681750874/react-comfy-store-products/product-3_znpiqa.jpg', 21 | }, 22 | { 23 | id: 'recd1jIVIEChmiwhe', 24 | name: 'armchair', 25 | 26 | image: 27 | 'https://res.cloudinary.com/dt2g7mgtv/image/upload/v1681750929/react-comfy-store-products/product-4_ebl6q1.jpg', 28 | }, 29 | { 30 | id: 'recoM2MyHJGHLVi5l', 31 | name: 'bar stool', 32 | image: 33 | 'https://res.cloudinary.com/dt2g7mgtv/image/upload/v1681751026/react-comfy-store-products/product-5_n184nu.jpg', 34 | }, 35 | ]; 36 | 37 | export default products; 38 | -------------------------------------------------------------------------------- /src/final/App.js: -------------------------------------------------------------------------------- 1 | import { BrowserRouter, Routes, Route } from 'react-router-dom'; 2 | import { useState } from 'react'; 3 | import Home from './pages/Home'; 4 | import About from './pages/About'; 5 | import Products from './pages/Products'; 6 | import Error from './pages/Error'; 7 | import SharedLayout from './pages/SharedLayout'; 8 | import SingleProduct from './pages/SingleProduct'; 9 | import Dashboard from './pages/Dashboard'; 10 | import Login from './pages/Login'; 11 | import ProtectedRoute from './pages/ProtectedRoute'; 12 | import SharedProductLayout from './pages/SharedProductLayout'; 13 | function App() { 14 | const [user, setUser] = useState(null); 15 | return ( 16 | 17 | 18 | }> 19 | } /> 20 | } /> 21 | 22 | }> 23 | } /> 24 | } /> 25 | 26 | 27 | } /> 28 | 32 | 33 | 34 | } 35 | /> 36 | } /> 37 | 38 | 39 | 40 | ); 41 | } 42 | 43 | export default App; 44 | -------------------------------------------------------------------------------- /src/final/components/Navbar.js: -------------------------------------------------------------------------------- 1 | import { Link } from 'react-router-dom'; 2 | const Navbar = () => { 3 | return ( 4 | 9 | ); 10 | }; 11 | export default Navbar; 12 | -------------------------------------------------------------------------------- /src/final/components/StyledNavbar.js: -------------------------------------------------------------------------------- 1 | import { NavLink } from 'react-router-dom'; 2 | const Navbar = () => { 3 | return ( 4 | 30 | ); 31 | }; 32 | export default Navbar; 33 | -------------------------------------------------------------------------------- /src/final/data.js: -------------------------------------------------------------------------------- 1 | const products = [ 2 | { 3 | id: 'recZkNf2kwmdBcqd0', 4 | name: 'accent chair', 5 | image: 6 | 'https://dl.airtable.com/.attachmentThumbnails/e8bc3791196535af65f40e36993b9e1f/438bd160', 7 | }, 8 | { 9 | id: 'recEHmzvupvT8ZONH', 10 | name: 'albany sectional', 11 | 12 | image: 13 | 'https://dl.airtable.com/.attachmentThumbnails/0be1af59cf889899b5c9abb1e4db38a4/d631ac52', 14 | }, 15 | { 16 | id: 'rec5NBwZ5zCD9nfF0', 17 | name: 'albany table', 18 | 19 | image: 20 | 'https://dl.airtable.com/.attachmentThumbnails/7478483f40a2f56662a87b304bd4e104/707d397f', 21 | }, 22 | { 23 | id: 'recd1jIVIEChmiwhe', 24 | name: 'armchair', 25 | 26 | image: 27 | 'https://dl.airtable.com/.attachmentThumbnails/530c07c5ade5acd9934c8dd334458b86/cf91397f', 28 | }, 29 | { 30 | id: 'recoM2MyHJGHLVi5l', 31 | name: 'bar stool', 32 | image: 33 | 'https://dl.airtable.com/.attachmentThumbnails/a6119fabf7256049cc0e8dbcdf536c9c/b0153f66', 34 | }, 35 | ]; 36 | 37 | export default products; 38 | -------------------------------------------------------------------------------- /src/final/pages/About.js: -------------------------------------------------------------------------------- 1 | import { Link } from 'react-router-dom'; 2 | const About = () => { 3 | return ( 4 |
5 |

About

6 | 7 | Back Home 8 | 9 |
10 | ); 11 | }; 12 | export default About; 13 | -------------------------------------------------------------------------------- /src/final/pages/Dashboard.js: -------------------------------------------------------------------------------- 1 | const Dashboard = ({ user }) => { 2 | return ( 3 |
4 |

Hello, {user?.name}

5 |
6 | ); 7 | }; 8 | export default Dashboard; 9 | -------------------------------------------------------------------------------- /src/final/pages/Error.js: -------------------------------------------------------------------------------- 1 | import { Link } from 'react-router-dom'; 2 | 3 | const Error = () => { 4 | return ( 5 |
6 |

404

7 |

page not found

8 | back home 9 |
10 | ); 11 | }; 12 | export default Error; 13 | -------------------------------------------------------------------------------- /src/final/pages/Home.js: -------------------------------------------------------------------------------- 1 | const Home = () => { 2 | return ( 3 |
4 |

home page

5 |
6 | ); 7 | }; 8 | export default Home; 9 | -------------------------------------------------------------------------------- /src/final/pages/Login.js: -------------------------------------------------------------------------------- 1 | import { useState } from 'react'; 2 | import { useNavigate } from 'react-router-dom'; 3 | const Login = ({ setUser }) => { 4 | const [name, setName] = useState(''); 5 | const [email, setEmail] = useState(''); 6 | 7 | const navigate = useNavigate(); 8 | 9 | const handleSubmit = async (e) => { 10 | e.preventDefault(); 11 | if (!name || !email) return; 12 | setUser({ name: name, email: email }); 13 | navigate('/dashboard'); 14 | }; 15 | 16 | return ( 17 |
18 |
19 |
login
20 |
21 | 24 | setName(e.target.value)} 30 | /> 31 |
32 |
33 | 36 | setEmail(e.target.value)} 42 | /> 43 |
44 | 47 |
48 |
49 | ); 50 | }; 51 | export default Login; 52 | -------------------------------------------------------------------------------- /src/final/pages/Products.js: -------------------------------------------------------------------------------- 1 | import { Link } from 'react-router-dom'; 2 | import products from '../data'; 3 | 4 | const Products = () => { 5 | return ( 6 |
7 |
8 | {products.map((product) => { 9 | return ( 10 |
11 |
{product.name}
12 | more info 13 |
14 | ); 15 | })} 16 |
17 |
18 | ); 19 | }; 20 | 21 | export default Products; 22 | -------------------------------------------------------------------------------- /src/final/pages/ProtectedRoute.js: -------------------------------------------------------------------------------- 1 | import { Navigate } from 'react-router-dom'; 2 | 3 | const ProtectedRoute = ({ children, user }) => { 4 | if (!user) { 5 | return ; 6 | } 7 | return children; 8 | }; 9 | export default ProtectedRoute; 10 | -------------------------------------------------------------------------------- /src/final/pages/SharedLayout.js: -------------------------------------------------------------------------------- 1 | import { Link, Outlet } from 'react-router-dom'; 2 | import Navbar from '../components/Navbar'; 3 | import StyledNavbar from '../components/StyledNavbar'; 4 | const Home = () => { 5 | return ( 6 | <> 7 | 8 | 9 | 10 | ); 11 | }; 12 | export default Home; 13 | -------------------------------------------------------------------------------- /src/final/pages/SharedProductLayout.js: -------------------------------------------------------------------------------- 1 | import { Outlet } from 'react-router-dom'; 2 | const Home = () => { 3 | return ( 4 | <> 5 |

products

6 | 7 | 8 | ); 9 | }; 10 | export default Home; 11 | -------------------------------------------------------------------------------- /src/final/pages/SingleProduct.js: -------------------------------------------------------------------------------- 1 | import { Link, useParams } from 'react-router-dom'; 2 | import products from '../data'; 3 | const SingleProduct = () => { 4 | const { productId } = useParams(); 5 | const product = products.find((product) => product.id === productId); 6 | const { image, name } = product; 7 | return ( 8 |
9 | {name} 10 |
{name}
11 | back to products 12 |
13 | ); 14 | }; 15 | 16 | export default SingleProduct; 17 | -------------------------------------------------------------------------------- /src/index.css: -------------------------------------------------------------------------------- 1 | *, 2 | ::after, 3 | ::before { 4 | box-sizing: border-box; 5 | } 6 | /* fonts */ 7 | /* @import url("https://fonts.googleapis.com/css2?family=Roboto:wght@400;500;600&family=Montserrat&display=swap"); */ 8 | 9 | html { 10 | font-size: 100%; 11 | } /*16px*/ 12 | 13 | :root { 14 | /* colors */ 15 | --primary-100: #e2e0ff; 16 | --primary-200: #c1beff; 17 | --primary-300: #a29dff; 18 | --primary-400: #837dff; 19 | --primary-500: #645cff; 20 | --primary-600: #504acc; 21 | --primary-700: #3c3799; 22 | --primary-800: #282566; 23 | --primary-900: #141233; 24 | 25 | /* grey */ 26 | --grey-50: #f8fafc; 27 | --grey-100: #f1f5f9; 28 | --grey-200: #e2e8f0; 29 | --grey-300: #cbd5e1; 30 | --grey-400: #94a3b8; 31 | --grey-500: #64748b; 32 | --grey-600: #475569; 33 | --grey-700: #334155; 34 | --grey-800: #1e293b; 35 | --grey-900: #0f172a; 36 | /* rest of the colors */ 37 | --black: #222; 38 | --white: #fff; 39 | --red-light: #f8d7da; 40 | --red-dark: #842029; 41 | --green-light: #d1e7dd; 42 | --green-dark: #0f5132; 43 | 44 | /* fonts */ 45 | 46 | --small-text: 0.875rem; 47 | --extra-small-text: 0.7em; 48 | /* rest of the vars */ 49 | --backgroundColor: var(--grey-50); 50 | --textColor: var(--grey-900); 51 | --borderRadius: 0.25rem; 52 | --letterSpacing: 1px; 53 | --transition: 0.3s ease-in-out all; 54 | --max-width: 1120px; 55 | --fixed-width: 600px; 56 | 57 | /* box shadow*/ 58 | --shadow-1: 0 1px 3px 0 rgba(0, 0, 0, 0.1), 0 1px 2px 0 rgba(0, 0, 0, 0.06); 59 | --shadow-2: 0 4px 6px -1px rgba(0, 0, 0, 0.1), 60 | 0 2px 4px -1px rgba(0, 0, 0, 0.06); 61 | --shadow-3: 0 10px 15px -3px rgba(0, 0, 0, 0.1), 62 | 0 4px 6px -2px rgba(0, 0, 0, 0.05); 63 | --shadow-4: 0 20px 25px -5px rgba(0, 0, 0, 0.1), 64 | 0 10px 10px -5px rgba(0, 0, 0, 0.04); 65 | } 66 | 67 | body { 68 | background: var(--backgroundColor); 69 | font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, 70 | Ubuntu, Cantarell, 'Open Sans', 'Helvetica Neue', sans-serif; 71 | font-weight: 400; 72 | line-height: 1.75; 73 | color: var(--textColor); 74 | } 75 | 76 | p { 77 | margin-bottom: 1.5rem; 78 | max-width: 40em; 79 | } 80 | 81 | h1, 82 | h2, 83 | h3, 84 | h4, 85 | h5 { 86 | margin: 0; 87 | margin-bottom: 1.38rem; 88 | font-family: var(--headingFont); 89 | font-weight: 400; 90 | line-height: 1.3; 91 | text-transform: capitalize; 92 | letter-spacing: var(--letterSpacing); 93 | } 94 | 95 | h1 { 96 | margin-top: 0; 97 | font-size: 3.052rem; 98 | } 99 | 100 | h2 { 101 | font-size: 2.441rem; 102 | } 103 | 104 | h3 { 105 | font-size: 1.953rem; 106 | } 107 | 108 | h4 { 109 | font-size: 1.563rem; 110 | } 111 | 112 | h5 { 113 | font-size: 1.25rem; 114 | } 115 | 116 | small, 117 | .text-small { 118 | font-size: var(--small-text); 119 | } 120 | 121 | a { 122 | text-decoration: none; 123 | } 124 | ul { 125 | list-style-type: none; 126 | padding: 0; 127 | } 128 | 129 | .img { 130 | width: 100%; 131 | display: block; 132 | object-fit: cover; 133 | } 134 | /* buttons */ 135 | 136 | .btn { 137 | cursor: pointer; 138 | color: var(--white); 139 | background: var(--primary-500); 140 | border: transparent; 141 | border-radius: var(--borderRadius); 142 | letter-spacing: var(--letterSpacing); 143 | padding: 0.375rem 0.75rem; 144 | box-shadow: var(--shadow-1); 145 | transition: var(--transition); 146 | text-transform: capitalize; 147 | display: inline-block; 148 | } 149 | .btn:hover { 150 | background: var(--primary-700); 151 | box-shadow: var(--shadow-3); 152 | } 153 | .btn-hipster { 154 | color: var(--primary-500); 155 | background: var(--primary-200); 156 | } 157 | .btn-hipster:hover { 158 | color: var(--primary-200); 159 | background: var(--primary-700); 160 | } 161 | .btn-block { 162 | width: 100%; 163 | } 164 | 165 | /* alerts */ 166 | .alert { 167 | padding: 0.375rem 0.75rem; 168 | margin-bottom: 1rem; 169 | border-color: transparent; 170 | border-radius: var(--borderRadius); 171 | } 172 | 173 | .alert-danger { 174 | color: var(--red-dark); 175 | background: var(--red-light); 176 | } 177 | .alert-success { 178 | color: var(--green-dark); 179 | background: var(--green-light); 180 | } 181 | /* form */ 182 | 183 | .form { 184 | width: 90vw; 185 | max-width: 400px; 186 | background: var(--white); 187 | border-radius: var(--borderRadius); 188 | box-shadow: var(--shadow-2); 189 | padding: 2rem 2.5rem; 190 | margin: 3rem 0; 191 | } 192 | .form-label { 193 | display: block; 194 | font-size: var(--small-text); 195 | margin-bottom: 0.5rem; 196 | text-transform: capitalize; 197 | letter-spacing: var(--letterSpacing); 198 | } 199 | .form-input, 200 | .form-textarea { 201 | width: 100%; 202 | padding: 0.375rem 0.75rem; 203 | border-radius: var(--borderRadius); 204 | background: var(--backgroundColor); 205 | border: 1px solid var(--grey-200); 206 | } 207 | 208 | .form-row { 209 | margin-bottom: 1rem; 210 | } 211 | 212 | .form-textarea { 213 | height: 7rem; 214 | } 215 | ::placeholder { 216 | font-family: inherit; 217 | color: var(--grey-400); 218 | } 219 | .form-alert { 220 | color: var(--red-dark); 221 | letter-spacing: var(--letterSpacing); 222 | text-transform: capitalize; 223 | } 224 | /* alert */ 225 | 226 | @keyframes spinner { 227 | to { 228 | transform: rotate(360deg); 229 | } 230 | } 231 | .form h5 { 232 | text-align: center; 233 | } 234 | .form .btn { 235 | margin-top: 0.5rem; 236 | } 237 | .loading { 238 | width: 6rem; 239 | height: 6rem; 240 | border: 5px solid var(--grey-400); 241 | border-radius: 50%; 242 | border-top-color: var(--primary-500); 243 | animation: spinner 0.6s linear infinite; 244 | } 245 | .loading { 246 | margin: 0 auto; 247 | } 248 | /* title */ 249 | 250 | .title { 251 | text-align: center; 252 | } 253 | 254 | .title-underline { 255 | background: var(--primary-500); 256 | width: 7rem; 257 | height: 0.25rem; 258 | margin: 0 auto; 259 | margin-top: -1rem; 260 | } 261 | 262 | .section { 263 | padding: 2rem 0; 264 | width: 90vw; 265 | max-width: var(--max-width); 266 | margin: 0 auto; 267 | } 268 | 269 | .navbar { 270 | width: 90vw; 271 | max-width: var(--max-width); 272 | margin: 0 auto; 273 | display: flex; 274 | gap: 1rem; 275 | } 276 | 277 | .link { 278 | color: var(--grey-500); 279 | } 280 | 281 | .active { 282 | color: var(--primary-500); 283 | } 284 | .products article { 285 | margin-bottom: 1rem; 286 | } 287 | 288 | .products h5 { 289 | margin-bottom: 0; 290 | } 291 | 292 | .product img { 293 | width: 200px; 294 | height: 150px; 295 | } 296 | -------------------------------------------------------------------------------- /src/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import ReactDOM from 'react-dom'; 3 | import './index.css'; 4 | import App from './App'; 5 | // import App from './final/App'; 6 | 7 | ReactDOM.render( 8 | 9 | 10 | , 11 | document.getElementById('root') 12 | ); 13 | -------------------------------------------------------------------------------- /src/pages/About.js: -------------------------------------------------------------------------------- 1 | const About = () => { 2 | return ( 3 |
4 |

About

5 |
6 | ); 7 | }; 8 | export default About; 9 | -------------------------------------------------------------------------------- /src/pages/Dashboard.js: -------------------------------------------------------------------------------- 1 | const Dashboard = () => { 2 | return ( 3 |
4 |

Dashboard

5 |
6 | ); 7 | }; 8 | export default Dashboard; 9 | -------------------------------------------------------------------------------- /src/pages/Error.js: -------------------------------------------------------------------------------- 1 | const Error = () => { 2 | return ( 3 |
4 |

Error

5 |
6 | ); 7 | }; 8 | export default Error; 9 | -------------------------------------------------------------------------------- /src/pages/Home.js: -------------------------------------------------------------------------------- 1 | const Home = () => { 2 | return ( 3 |
4 |

Home Page

5 |
6 | ); 7 | }; 8 | export default Home; 9 | -------------------------------------------------------------------------------- /src/pages/Login.js: -------------------------------------------------------------------------------- 1 | import { useState } from 'react'; 2 | const Login = () => { 3 | const [name, setName] = useState(''); 4 | const [email, setEmail] = useState(''); 5 | 6 | const handleSubmit = async (e) => { 7 | e.preventDefault(); 8 | }; 9 | 10 | return ( 11 |
12 |
13 |
login
14 |
15 | 18 | setName(e.target.value)} 24 | /> 25 |
26 |
27 | 30 | setEmail(e.target.value)} 36 | /> 37 |
38 | 41 |
42 |
43 | ); 44 | }; 45 | export default Login; 46 | -------------------------------------------------------------------------------- /src/pages/Products.js: -------------------------------------------------------------------------------- 1 | const Products = () => { 2 | return ( 3 | <> 4 |
5 |

products

6 |
7 | 8 | ); 9 | }; 10 | 11 | export default Products; 12 | -------------------------------------------------------------------------------- /src/pages/SingleProduct.js: -------------------------------------------------------------------------------- 1 | const SingleProduct = () => { 2 | return ( 3 |
4 |

single product

5 |
6 | ); 7 | }; 8 | 9 | export default SingleProduct; 10 | --------------------------------------------------------------------------------