├── .gitignore ├── README.md ├── package-lock.json ├── package.json ├── public ├── favicon.ico ├── index.html ├── logo192.png ├── logo512.png ├── manifest.json └── robots.txt └── src ├── App.js ├── components ├── Header.js ├── Header.module.scss ├── Layout.js └── Layout.module.scss ├── index.js ├── pages ├── Home.js ├── PageCTA.js ├── PageOne.js ├── PageThree.js └── PageTwo.js └── styles ├── index.scss └── variables.scss /.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 | # Navbar 2 | 3 | A fully responsive and reusable header / navbar component built with React. 4 | 5 | ## CodeFocus Channel 6 | 7 | This project's build walkthrough video can be accessed on the CodeFocus youtube channel. Heres a link: 8 | 9 | ### `npm start` 10 | 11 | Runs the app in the development mode. 12 | Open http://localhost:3000 to view it in the browser. 13 | 14 | The page will reload if you make edits. 15 | You will also see any lint errors in the console. 16 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "navbar", 3 | "version": "0.1.0", 4 | "private": true, 5 | "dependencies": { 6 | "@testing-library/jest-dom": "^5.14.1", 7 | "@testing-library/react": "^11.2.7", 8 | "@testing-library/user-event": "^12.8.3", 9 | "node-sass": "^6.0.1", 10 | "react": "^17.0.2", 11 | "react-dom": "^17.0.2", 12 | "react-icons": "^4.2.0", 13 | "react-router-dom": "^5.2.0", 14 | "react-scripts": "4.0.3", 15 | "web-vitals": "^1.1.2" 16 | }, 17 | "scripts": { 18 | "start": "react-scripts start", 19 | "build": "react-scripts build", 20 | "test": "react-scripts test", 21 | "eject": "react-scripts eject" 22 | }, 23 | "eslintConfig": { 24 | "extends": [ 25 | "react-app", 26 | "react-app/jest" 27 | ] 28 | }, 29 | "browserslist": { 30 | "production": [ 31 | ">0.2%", 32 | "not dead", 33 | "not op_mini all" 34 | ], 35 | "development": [ 36 | "last 1 chrome version", 37 | "last 1 firefox version", 38 | "last 1 safari version" 39 | ] 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CodeFocusChannel/navbar/4f73098fd5fd05e682955e1ea62ca2cc4bcfc40a/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/CodeFocusChannel/navbar/4f73098fd5fd05e682955e1ea62ca2cc4bcfc40a/public/logo192.png -------------------------------------------------------------------------------- /public/logo512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CodeFocusChannel/navbar/4f73098fd5fd05e682955e1ea62ca2cc4bcfc40a/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 | import Layout from "./components/Layout"; 2 | import { Switch, Route } from "react-router-dom"; 3 | import Home from "./pages/Home"; 4 | import PageOne from "./pages/PageOne"; 5 | import PageTwo from "./pages/PageTwo"; 6 | import PageThree from "./pages/PageThree"; 7 | import PageCTA from "./pages/PageCTA"; 8 | 9 | function App() { 10 | return ( 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | ); 31 | } 32 | 33 | export default App; 34 | -------------------------------------------------------------------------------- /src/components/Header.js: -------------------------------------------------------------------------------- 1 | import React, { useEffect, useState } from "react"; 2 | 3 | import { BiMenuAltRight } from "react-icons/bi"; 4 | import { AiOutlineClose } from "react-icons/ai"; 5 | 6 | import classes from "./Header.module.scss"; 7 | import { Link, useHistory } from "react-router-dom"; 8 | 9 | const Header = () => { 10 | const history = useHistory(); 11 | const [menuOpen, setMenuOpen] = useState(false); 12 | const [size, setSize] = useState({ 13 | width: undefined, 14 | height: undefined, 15 | }); 16 | 17 | useEffect(() => { 18 | const handleResize = () => { 19 | setSize({ 20 | width: window.innerWidth, 21 | height: window.innerHeight, 22 | }); 23 | }; 24 | window.addEventListener("resize", handleResize); 25 | 26 | return () => window.removeEventListener("resize", handleResize); 27 | }, []); 28 | 29 | useEffect(() => { 30 | if (size.width > 768 && menuOpen) { 31 | setMenuOpen(false); 32 | } 33 | }, [size.width, menuOpen]); 34 | 35 | const menuToggleHandler = () => { 36 | setMenuOpen((p) => !p); 37 | }; 38 | 39 | const ctaClickHandler = () => { 40 | menuToggleHandler(); 41 | history.push("/page-cta"); 42 | }; 43 | 44 | return ( 45 |
46 |
47 | 48 | navbar 49 | 50 | 74 |
75 | {!menuOpen ? ( 76 | 77 | ) : ( 78 | 79 | )} 80 |
81 |
82 |
83 | ); 84 | }; 85 | 86 | export default Header; 87 | -------------------------------------------------------------------------------- /src/components/Header.module.scss: -------------------------------------------------------------------------------- 1 | @import "./../styles/variables.scss"; 2 | 3 | .header { 4 | background: $dark; 5 | width: 100%; 6 | height: $headerHeight; 7 | padding: 0 1.5rem; 8 | @include breakpoint(md) { 9 | padding: 0 3rem; 10 | } 11 | transition: 0.3s ease all; 12 | 13 | &__content { 14 | overflow: hidden; 15 | color: $light; 16 | margin: 0 auto; 17 | max-width: 1920px; 18 | height: 100%; 19 | display: flex; 20 | align-items: center; 21 | justify-content: space-between; 22 | position: relative; 23 | z-index: 100; 24 | 25 | &__logo { 26 | font-size: 2rem; 27 | font-weight: bold; 28 | text-decoration: none; 29 | color: $light; 30 | } 31 | 32 | &__nav { 33 | top: 0; 34 | right: 100%; 35 | bottom: 0; 36 | width: 100%; 37 | height: 100vh; 38 | position: fixed; 39 | display: flex; 40 | flex-direction: column; 41 | justify-content: center; 42 | align-items: center; 43 | text-align: center; 44 | background: rgba($dark, 0.9); 45 | backdrop-filter: blur(2px); 46 | transform: translate(0); 47 | transition: 0.3s ease transform; 48 | 49 | @include breakpoint(md) { 50 | transform: none; 51 | flex-direction: row; 52 | background: transparent; 53 | width: auto; 54 | height: 100%; 55 | position: static; 56 | } 57 | ul { 58 | list-style: none; 59 | padding: 0; 60 | display: flex; 61 | flex-direction: column; 62 | margin-bottom: $spacing-lg; 63 | @include breakpoint(md) { 64 | flex-direction: row; 65 | align-items: center; 66 | margin-bottom: 0; 67 | margin-right: calc(0.5rem + #{$spacing-md}); 68 | } 69 | li { 70 | &:not(:last-child) { 71 | margin-bottom: $spacing-lg; 72 | @include breakpoint(md) { 73 | margin-bottom: 0; 74 | margin-right: $spacing-md; 75 | } 76 | } 77 | a { 78 | text-decoration: none; 79 | color: inherit; 80 | padding: 0.75rem 1.25rem; 81 | border-radius: $borderRadius; 82 | transition: 0.3s ease all; 83 | 84 | &:hover { 85 | background: rgba($light, 0.1); 86 | } 87 | &:active { 88 | border-radius: calc(#{$borderRadius} + 6px); 89 | background: linear-gradient(rgba($light, 0.1), rgba($light, 0.2)); 90 | } 91 | } 92 | } 93 | } 94 | button { 95 | cursor: pointer; 96 | outline: none; 97 | padding: 0.75rem 1.25rem; 98 | border-radius: $borderRadius; 99 | font-size: 1rem; 100 | font-family: inherit; 101 | background: $primary; 102 | color: $dark; 103 | border: 1px solid transparent; 104 | transition: 0.3s ease all; 105 | 106 | &:hover { 107 | border-color: $primary; 108 | background: rgba($primary, 0.1); 109 | color: $primary; 110 | } 111 | &:active { 112 | border-color: $primary; 113 | background: linear-gradient(rgba($primary, 0.2), rgba($primary, 0.3)); 114 | color: $primary; 115 | } 116 | } 117 | 118 | &.isMenu { 119 | transform: translate(100%); 120 | } 121 | } 122 | 123 | &__toggle { 124 | cursor: pointer; 125 | display: flex; 126 | align-items: center; 127 | font-size: 2rem; 128 | transition: 0.3s ease all; 129 | position: relative; 130 | 131 | &:hover { 132 | color: $primary; 133 | } 134 | 135 | @include breakpoint(md) { 136 | display: none; 137 | } 138 | } 139 | } 140 | } 141 | -------------------------------------------------------------------------------- /src/components/Layout.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import Header from "./Header"; 3 | 4 | import classes from "./Layout.module.scss"; 5 | 6 | const Layout = ({ children }) => { 7 | return ( 8 | <> 9 |
10 |
{children}
11 | 12 | ); 13 | }; 14 | 15 | export default Layout; 16 | -------------------------------------------------------------------------------- /src/components/Layout.module.scss: -------------------------------------------------------------------------------- 1 | @import "./../styles/variables.scss"; 2 | 3 | .container { 4 | height: calc(100vh - #{$headerHeight}); 5 | background: $bg; 6 | display: flex; 7 | justify-content: center; 8 | align-items: center; 9 | 10 | h1 { 11 | padding: 1.5rem 2.5rem; 12 | background: linear-gradient(220deg, $primary, rgba($primary, 0.5)); 13 | border-radius: $borderRadius calc(#{$borderRadius} * 10); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /src/index.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import ReactDOM from "react-dom"; 3 | import "./styles/index.scss"; 4 | import App from "./App"; 5 | import { BrowserRouter as Router } from "react-router-dom"; 6 | 7 | ReactDOM.render( 8 | 9 | 10 | , 11 | document.getElementById("root") 12 | ); 13 | -------------------------------------------------------------------------------- /src/pages/Home.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | 3 | const Home = () => { 4 | return

Home

; 5 | }; 6 | 7 | export default Home; 8 | -------------------------------------------------------------------------------- /src/pages/PageCTA.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | 3 | const PageCTA = () => { 4 | return

CTA Page

; 5 | }; 6 | 7 | export default PageCTA; 8 | -------------------------------------------------------------------------------- /src/pages/PageOne.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | 3 | const PageOne = () => { 4 | return

Page One

; 5 | }; 6 | 7 | export default PageOne; 8 | -------------------------------------------------------------------------------- /src/pages/PageThree.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | 3 | const PageThree = () => { 4 | return

Page Three

; 5 | }; 6 | 7 | export default PageThree; 8 | -------------------------------------------------------------------------------- /src/pages/PageTwo.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | 3 | const PageTwo = () => { 4 | return

Page Two

; 5 | }; 6 | 7 | export default PageTwo; 8 | -------------------------------------------------------------------------------- /src/styles/index.scss: -------------------------------------------------------------------------------- 1 | @import "./variables.scss"; 2 | 3 | * { 4 | box-sizing: border-box; 5 | margin: 0; 6 | } 7 | 8 | body { 9 | width: 100%; 10 | font-family: $Poppins; 11 | color: $dark; 12 | } 13 | 14 | #root { 15 | height: 100%; 16 | } 17 | -------------------------------------------------------------------------------- /src/styles/variables.scss: -------------------------------------------------------------------------------- 1 | @import url("https://fonts.googleapis.com/css2?family=Poppins:wght@400;500;600;700;800&display=swap"); 2 | 3 | $Poppins: "Poppins", sans-serif; 4 | 5 | // colors 6 | $dark: rgb(29, 29, 29); 7 | $light: #fff; 8 | $primary: rgb(162, 162, 246); 9 | $bg: rgb(244, 244, 255); 10 | 11 | // spacing 12 | $spacing-md: 16px; 13 | $spacing-lg: 32px; 14 | 15 | // border radius 16 | $borderRadius: 12px; 17 | 18 | // header height 19 | $headerHeight: 100px; 20 | 21 | @mixin breakpoint($point) { 22 | @if $point == md { 23 | // 768px 24 | @media (min-width: 48em) { 25 | @content; 26 | } 27 | } 28 | } 29 | --------------------------------------------------------------------------------