├── README.md ├── assets ├── logomark.svg ├── navigation.svg ├── space.jpg ├── spaceLg.jpg ├── spaceMd.jpg └── spaceSm.jpg ├── index.html ├── nav.js └── style.css /README.md: -------------------------------------------------------------------------------- 1 | # Basic Responsive Navigation 2 | 3 | Nearly every website needs a responsive navigation, but they can be confusing to layout. It’s not uncommon for developers to create two separate navigations: one for mobile and one for desktop. But there’s a better way! In this video, I'll show you how to create a fully responsive navigation menu with HTML, CSS, and Vanilla JavaScript. We’ll add aria attributes to achieve a basic accessible navigation. 4 | 5 | 6 | 🔗 Key Links 🔗 7 | - YouTube Video: https://youtu.be/63sxOYm9GwY 8 | - Live Code: https://codinginpublic.dev/projects/responsive-navigation/ 9 | 10 | --------------------------------------- 11 | 12 | 📹 Related Videos 📹 13 | - Intersection Observer playlist: https://www.youtube.com/playlist?list=PLoqZcxvpWzze3Hkult9jLfCvZlzCYLb8G 14 | - Understanding Clamp(): https://youtu.be/Moely7aPYGE 15 | 16 | --------------------------------------- 17 | 18 | 🧰 My Tools 🧰 19 | - VSCode Font/Theme: https://www.youtube.com/watch?v=5uETTXxVj8s 20 | - VSCode Extensions: https://www.youtube.com/watch?v=0ABuzt8X5SA 21 | 22 | --------------------------------------- 23 | 24 | 🌐 Connect With Me 🌐 25 | - Website: https://codinginpublic.dev 26 | - Blog: https://chrispennington.blog 27 | - Twitter: https://twitter.com/cpenned 28 | -------------------------------------------------------------------------------- /assets/logomark.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /assets/navigation.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /assets/space.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/coding-in-public/responsive-navigation/ef51351663f5a7df2b925a8ea17085c23460a12a/assets/space.jpg -------------------------------------------------------------------------------- /assets/spaceLg.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/coding-in-public/responsive-navigation/ef51351663f5a7df2b925a8ea17085c23460a12a/assets/spaceLg.jpg -------------------------------------------------------------------------------- /assets/spaceMd.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/coding-in-public/responsive-navigation/ef51351663f5a7df2b925a8ea17085c23460a12a/assets/spaceMd.jpg -------------------------------------------------------------------------------- /assets/spaceSm.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/coding-in-public/responsive-navigation/ef51351663f5a7df2b925a8ea17085c23460a12a/assets/spaceSm.jpg -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | Responsive Navigation 9 | 10 | 11 | 12 | 14 | 15 | 16 | 17 | 56 |
57 | View of earth from space 58 |
59 |

The Next Frontier

60 |

Explore the universe of tomorrow.

61 |
62 |
63 |
64 |
65 |

Look to the Stars

66 |

Lorem ipsum dolor sit, amet consectetur adipisicing elit. Vitae eligendi quibusdam dolor illum impedit nisi. Eaque sequi necessitatibus voluptatum porro.

67 |

Lorem ipsum dolor sit, amet consectetur adipisicing elit. Nesciunt fugiat commodi numquam? Obcaecati magni perferendis eligendi laudantium cum perspiciatis minus animi ipsa placeat adipisci repellendus, id culpa minima officiis fugiat quidem nisi sint consectetur eius vero velit sunt quas expedita. Ullam unde optio ad voluptas praesentium animi velit quae dicta?

68 |

Lorem ipsum dolor sit amet consectetur adipisicing elit. Itaque, temporibus.

69 |
70 |
71 |

Reasons to Travel

72 |
73 |
74 | 75 | 76 | 79 | 81 | 83 | 85 | 87 | 88 |
89 |

Stars in the Sky

90 |

Lorem ipsum dolor sit amet consectetur adipisicing elit. Harum obcaecati, deserunt optio possimus atque qui!

91 |
92 |
93 |
94 | 95 | 96 | 98 | 101 | 102 |
103 |

Moon Up Above

104 |

Lorem ipsum dolor sit amet consectetur adipisicing elit. Harum obcaecati, deserunt optio possimus atque qui!

105 |
106 |
107 |
108 | 109 | 110 | 112 | 114 | 116 | 118 | 120 | 122 | 124 | 125 |
126 |

Ain’t no Sun Allowed

127 |

Lorem ipsum dolor sit amet consectetur adipisicing elit. Harum obcaecati, deserunt optio possimus atque qui!

128 |
129 |
130 |
131 |
132 |
133 |

Space Travel for Everyone

134 |

Lorem ipsum dolor sit, amet consectetur adipisicing elit. Vitae eligendi quibusdam dolor illum impedit 135 | nisi. Eaque sequi necessitatibus voluptatum porro.

136 |

Lorem ipsum dolor sit, amet consectetur adipisicing elit. Nesciunt fugiat commodi numquam? Obcaecati 137 | magni perferendis eligendi laudantium cum perspiciatis minus animi ipsa placeat adipisci repellendus, id culpa minima 138 | officiis fugiat quidem nisi sint consectetur eius vero velit sunt quas expedita. Ullam unde optio ad voluptas 139 | praesentium animi velit quae dicta?

140 |

Lorem ipsum dolor sit amet consectetur adipisicing elit. Itaque, temporibus.

141 |
142 |
143 | 144 | -------------------------------------------------------------------------------- /nav.js: -------------------------------------------------------------------------------- 1 | const navBtn = document.querySelector('#menu-btn'); 2 | const nav = document.querySelector('nav'); 3 | const navLinks = document.querySelector('.nav-links'); 4 | 5 | navBtn.addEventListener('click', () => { 6 | navLinks.classList.add('activated'); 7 | const isExpanded = JSON.parse(navBtn.getAttribute('aria-expanded')); 8 | navBtn.setAttribute('aria-expanded', !isExpanded); 9 | !isExpanded && nav.classList.add('active'); 10 | }) 11 | 12 | //INTERSECTION OBSERVER 13 | 14 | const navObs = new IntersectionObserver((entries) => nav.classList.toggle('active', !entries[0].isIntersecting) 15 | , {threshold: .85}) 16 | 17 | navObs.observe(document.querySelector('header')); 18 | 19 | const fadeUpObserver = new IntersectionObserver((entries) => { 20 | entries.forEach(entry => { 21 | if(entry.isIntersecting){ 22 | entry.target.classList.add('faded'); 23 | fadeUpObserver.unobserve(entry.target) 24 | } 25 | }) 26 | }, { 27 | rootMargin: '-15%' 28 | }) 29 | 30 | document.querySelectorAll('.fade-up').forEach(el => { 31 | fadeUpObserver.observe(el); 32 | }) 33 | -------------------------------------------------------------------------------- /style.css: -------------------------------------------------------------------------------- 1 | *, 2 | *::after, 3 | *::before { 4 | box-sizing: border-box; 5 | margin: 0; 6 | padding: 0; 7 | } 8 | 9 | :root { 10 | --bkg: linear-gradient(to top, #0f0915, #050307); 11 | --dark: #050307; 12 | --text: #f9f6fe; 13 | --accent: #4233e4; 14 | } 15 | 16 | body { 17 | font-family: "Nunito Sans", sans-serif; 18 | font-size: clamp(1.1rem, 2vw + 1rem, 1.4rem); 19 | line-height: 1.55; 20 | background: var(--bkg); 21 | color: var(--text); 22 | } 23 | 24 | nav { 25 | position: fixed; 26 | z-index: 10; 27 | width: 100%; 28 | padding-block: 1.2rem; 29 | background-color: transparent; 30 | transition: background-color 800ms cubic-bezier(0.64, 0.04, 0.26, 0.87); 31 | } 32 | 33 | nav.active { 34 | background-color: var(--dark); 35 | } 36 | 37 | .nav-container { 38 | display: flex; 39 | justify-content: space-between; 40 | align-items: center; 41 | } 42 | 43 | .logo { 44 | color: var(--accent); 45 | margin-inline: 0.5rem; 46 | width: clamp(3rem, 10vw, 7.5rem); 47 | display: grid; 48 | place-items: center; 49 | } 50 | 51 | .logo-link:focus-visible, 52 | .nav-link:focus-visible { 53 | outline: 4px solid var(--accent); 54 | outline-offset: 0.2em; 55 | border-radius: 0.5rem; 56 | } 57 | 58 | .nav-links { 59 | display: flex; 60 | align-items: center; 61 | flex-direction: column; 62 | gap: 1.5rem; 63 | transform: translate3d(0, -200%, 0); 64 | position: absolute; 65 | z-index: -1; 66 | top: 3rem; 67 | left: 0; 68 | right: 0; 69 | background-color: var(--dark); 70 | padding: 1.5rem; 71 | border-bottom: 4px solid var(--accent); 72 | text-align: center; 73 | } 74 | 75 | .nav-links.activated { 76 | transition: transform 0.4s cubic-bezier(0.64, 0.04, 0.26, 0.87); 77 | } 78 | 79 | .btn { 80 | color: var(--text); 81 | text-decoration: none; 82 | padding: 0.3rem 1.5rem; 83 | cursor: pointer; 84 | border-radius: 0.5rem; 85 | } 86 | 87 | .btn--accent { 88 | background-color: var(--accent); 89 | padding: 0.3rem 2rem; 90 | } 91 | 92 | .btn--menu { 93 | color: var(--accent); 94 | background-color: transparent; 95 | border: none; 96 | display: grid; 97 | place-items: center; 98 | padding-inline: 1rem; 99 | transition: transform 0.3s cubic-bezier(0.64, 0.04, 0.26, 0.87); 100 | } 101 | 102 | li[role="none"], 103 | .nav-link { 104 | width: 100%; 105 | display: block; 106 | font-size: 1.1rem; 107 | text-transform: uppercase; 108 | } 109 | 110 | .btn--menu[aria-expanded="true"] { 111 | transform: rotate(-180deg); 112 | } 113 | 114 | .btn--menu[aria-expanded="true"] + .nav-links { 115 | transform: translate3d(0, 0, 0); 116 | } 117 | 118 | @media (min-width: 768px) { 119 | .btn--menu { 120 | display: none; 121 | } 122 | .nav-links { 123 | position: static; 124 | transform: translate3d(0, 0, 0); 125 | flex-direction: row; 126 | border: 0; 127 | z-index: 0; 128 | padding: 0; 129 | inset: 0; 130 | background-color: transparent; 131 | } 132 | li[role="none"], 133 | .nav-link { 134 | width: initial; 135 | } 136 | } 137 | 138 | main { 139 | display: grid; 140 | gap: clamp(4rem, 1.45454537rem + 11.636364vw, 8rem); 141 | position: relative; 142 | top: -10vh; 143 | } 144 | 145 | h1 { 146 | font-size: clamp(2.5rem, 5vw + 1rem, 8rem); 147 | font-size: bolder; 148 | line-height: 1.1; 149 | letter-spacing: 0.02em; 150 | } 151 | 152 | .h2 { 153 | font-size: clamp(1.8rem, 4vw + 1rem, 3.5rem); 154 | font-size: bolder; 155 | line-height: 1.1; 156 | color: var(--accent); 157 | } 158 | 159 | .h3 { 160 | font-size: clamp(1.2rem, 3vw + 1rem, 2.2rem); 161 | line-height: 1.1; 162 | color: var(--accent); 163 | } 164 | 165 | .subheading { 166 | color: var(--text); 167 | } 168 | 169 | .sr-only { 170 | position: absolute; 171 | width: 1px; 172 | height: 1px; 173 | padding: 0; 174 | margin: -1px; 175 | overflow: hidden; 176 | clip: rect(0, 0, 0, 0); 177 | white-space: nowrap; 178 | border-width: 0; 179 | } 180 | 181 | header { 182 | min-height: 100vh; 183 | display: grid; 184 | place-items: center; 185 | text-align: center; 186 | position: relative; 187 | } 188 | 189 | .header__img { 190 | position: absolute; 191 | height: 100%; 192 | width: 100%; 193 | object-fit: cover; 194 | } 195 | 196 | .container { 197 | margin-inline: max((100% - 90rem) / 2, 1rem); 198 | } 199 | 200 | .container--sm { 201 | margin-inline: max((100% - 70rem) / 2, 2rem); 202 | } 203 | 204 | .rise { 205 | opacity: 0; 206 | animation: rise 0.8s ease-in-out forwards; 207 | } 208 | 209 | .rise.subheading { 210 | animation: rise 1.2s ease-in-out forwards 0.5s; 211 | } 212 | 213 | @keyframes rise { 214 | 0% { 215 | transform: translateY(2rem); 216 | opacity: 0; 217 | } 218 | 100% { 219 | transform: translateY(0); 220 | opacity: 1; 221 | } 222 | } 223 | 224 | .header-container { 225 | display: grid; 226 | gap: 1rem; 227 | position: relative; 228 | top: -5vh; 229 | } 230 | 231 | .stars { 232 | padding: clamp(1.2rem, 3.5vw, 2.5rem) clamp(1.2rem, 5vw, 3rem) 0; 233 | background-color: var(--dark); 234 | display: grid; 235 | border-radius: 0.5rem; 236 | gap: 1rem; 237 | } 238 | 239 | .moons { 240 | display: grid; 241 | gap: 1rem; 242 | } 243 | 244 | #stars { 245 | text-align: center; 246 | } 247 | 248 | .card-container { 249 | display: flex; 250 | flex-wrap: wrap; 251 | gap: 2.5rem 3.5rem; 252 | } 253 | 254 | .card { 255 | flex: 1 1 100%; 256 | display: grid; 257 | place-items: center; 258 | gap: 1.5rem; 259 | padding: 2.5rem 1.5rem 1.5rem; 260 | border: 5px solid var(--accent); 261 | border-radius: 0.5rem; 262 | } 263 | 264 | @media (min-width: 768px) { 265 | .card { 266 | flex: 1 1 28%; 267 | } 268 | } 269 | 270 | .card__image { 271 | width: clamp(4rem, 10vw, 8rem); 272 | } 273 | 274 | .fade-up { 275 | opacity: 0; 276 | transform: translate3d(0, 5rem, 0); 277 | transition: transform 1s cubic-bezier(0.64, 0.04, 0.26, 0.87), 278 | opacity 0.8s cubic-bezier(0.64, 0.04, 0.26, 0.87); 279 | } 280 | 281 | .fade-up.faded { 282 | opacity: 1; 283 | transform: translate3d(0, 0, 0); 284 | } 285 | 286 | /* Remove all animations, transitions and smooth scroll for people that prefer not to see them */ 287 | @media (prefers-reduced-motion: reduce) { 288 | html:focus-within { 289 | scroll-behavior: auto; 290 | } 291 | 292 | *, 293 | *::before, 294 | *::after { 295 | animation-duration: 0.01ms !important; 296 | animation-iteration-count: 1 !important; 297 | transition-duration: 0.01ms !important; 298 | scroll-behavior: auto !important; 299 | } 300 | } 301 | --------------------------------------------------------------------------------