project showing how to use Intersection Observers to animate in certain things when an element appears on screen

This commit is contained in:
Robert McGovern 2022-04-01 03:41:12 +01:00
parent ea94fb7a42
commit ec201ea5d3
10 changed files with 540 additions and 0 deletions

View File

@ -0,0 +1,27 @@
# Basic Responsive Navigation
Nearly every website needs a responsive navigation, but they can be confusing to layout. Its not uncommon for developers to create two separate navigations: one for mobile and one for desktop. But theres a better way! In this video, I'll show you how to create a fully responsive navigation menu with HTML, CSS, and Vanilla JavaScript. Well add aria attributes to achieve a basic accessible navigation.
🔗 Key Links 🔗
- YouTube Video: https://youtu.be/63sxOYm9GwY
- Live Code: https://codinginpublic.dev/projects/responsive-navigation/
---------------------------------------
📹 Related Videos 📹
- Intersection Observer playlist: https://www.youtube.com/playlist?list=PLoqZcxvpWzze3Hkult9jLfCvZlzCYLb8G
- Understanding Clamp(): https://youtu.be/Moely7aPYGE
---------------------------------------
🧰 My Tools 🧰
- VSCode Font/Theme: https://www.youtube.com/watch?v=5uETTXxVj8s
- VSCode Extensions: https://www.youtube.com/watch?v=0ABuzt8X5SA
---------------------------------------
🌐 Connect With Me 🌐
- Website: https://codinginpublic.dev
- Blog: https://chrispennington.blog
- Twitter: https://twitter.com/cpenned

View File

@ -0,0 +1,11 @@
<svg viewBox="0 0 229 161" class="logo" width="120" xmlns="http://www.w3.org/2000/svg" xml:space="preserve"
style="fill-rule:evenodd;clip-rule:evenodd;stroke-linejoin:round;stroke-miterlimit:2">
<path
d="M29.17 82.421c4.003-34.962 33.732-62.157 69.763-62.157 21.047 0 39.944 9.279 52.817 23.964l-.039.022c.343.361.641.685.94 1.027.443.525.877 1.056 1.303 1.592h.008c.265.337.561.717.902 1.16.408.53.788 1.043 1.142 1.536l-.014.001a69.403 69.403 0 0 1 1.953 2.864c.405.634.747 1.192 1.033 1.657l.004-.003a69.94 69.94 0 0 1 6.483 13.895c8.313-5.572 12.656-10.495 11.55-13.947-1.139-3.553-7.885-5.018-18.404-4.604a31.955 31.955 0 0 0-.858-1.576 9.527 9.527 0 0 0-.685-1.029c20.568-.077 34.892 4.541 37.857 13.791 3.148 9.824-7.166 22.842-25.986 35.427-2.834 36.162-33.117 64.668-70.006 64.668-11.228 0-21.844-2.641-31.258-7.334 5.145-13.664 21.098-10.431 67.983-34.016 20.459-10.292 28.647-17.452 31.541-22.172-15.283 9.925-35.78 19.51-59.054 26.968C54.519 141.34 6.329 140.773.597 122.889c-3.221-10.053 6.73-22.526 26.395-35.382a37.974 37.974 0 0 0-.119 1.939c-.03.821-.028 1.608.002 2.331-9.796 6.182-14.118 10.753-12.914 14.509 1.115 3.482 7.616 4.959 17.772 4.627a69.909 69.909 0 0 1-2.895-16.167h.002l-.017-.258a71.154 71.154 0 0 1-.112-3.765c.001-.696.012-1.424.04-2.168.056-1.549.149-2.994.249-4.228l.133-1.49.041-.416h-.004Zm0 0-.001.001-.036.415c-.044.45-.089.95-.133 1.49l-.146 1.647c.075-1.192.181-2.375.315-3.552l.001-.001Z"
style="fill:currentColor" />
<path
d="M34.442 51.095a10.127 10.127 0 0 1-4.596-8.485c0-5.592 4.54-10.132 10.132-10.132 2.837 0 5.403 1.169 7.243 3.05a71.387 71.387 0 0 0-12.779 15.567Z"
style="fill:currentColor" />
<circle cx="173.267" cy="10.132" r="10.132" style="fill:currentColor" />
<circle cx="218.027" cy="100.618" r="10.132" style="fill:currentColor" />
</svg>

After

Width:  |  Height:  |  Size: 1.8 KiB

View File

@ -0,0 +1,2 @@
<svg xmlns="http://www.w3.org/2000/svg" width="32" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="4"> <path stroke-linecap="round" stroke-linejoin="round" d="M4 8h16M4 16h16" />
</svg>

After

Width:  |  Height:  |  Size: 205 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 245 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 183 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 99 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 32 KiB

View File

@ -0,0 +1,168 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="theme-color" content="#050307">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Responsive Navigation</title>
<script src="./nav.js" type="module"></script>
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Nunito+Sans:ital,wght@0,600;0,800;1,600;1,800&display=swap"
rel="stylesheet">
<link rel="stylesheet" href="./style.css">
</head>
<body>
<nav>
<div class="container nav-container">
<a href="/" class="logo-link" aria-label="Go to homepage">
<svg viewBox="0 0 229 161" class="logo" width="120" xmlns="http://www.w3.org/2000/svg"
xml:space="preserve"
style="fill-rule:evenodd;clip-rule:evenodd;stroke-linejoin:round;stroke-miterlimit:2">
<path
d="M29.17 82.421c4.003-34.962 33.732-62.157 69.763-62.157 21.047 0 39.944 9.279 52.817 23.964l-.039.022c.343.361.641.685.94 1.027.443.525.877 1.056 1.303 1.592h.008c.265.337.561.717.902 1.16.408.53.788 1.043 1.142 1.536l-.014.001a69.403 69.403 0 0 1 1.953 2.864c.405.634.747 1.192 1.033 1.657l.004-.003a69.94 69.94 0 0 1 6.483 13.895c8.313-5.572 12.656-10.495 11.55-13.947-1.139-3.553-7.885-5.018-18.404-4.604a31.955 31.955 0 0 0-.858-1.576 9.527 9.527 0 0 0-.685-1.029c20.568-.077 34.892 4.541 37.857 13.791 3.148 9.824-7.166 22.842-25.986 35.427-2.834 36.162-33.117 64.668-70.006 64.668-11.228 0-21.844-2.641-31.258-7.334 5.145-13.664 21.098-10.431 67.983-34.016 20.459-10.292 28.647-17.452 31.541-22.172-15.283 9.925-35.78 19.51-59.054 26.968C54.519 141.34 6.329 140.773.597 122.889c-3.221-10.053 6.73-22.526 26.395-35.382a37.974 37.974 0 0 0-.119 1.939c-.03.821-.028 1.608.002 2.331-9.796 6.182-14.118 10.753-12.914 14.509 1.115 3.482 7.616 4.959 17.772 4.627a69.909 69.909 0 0 1-2.895-16.167h.002l-.017-.258a71.154 71.154 0 0 1-.112-3.765c.001-.696.012-1.424.04-2.168.056-1.549.149-2.994.249-4.228l.133-1.49.041-.416h-.004Zm0 0-.001.001-.036.415c-.044.45-.089.95-.133 1.49l-.146 1.647c.075-1.192.181-2.375.315-3.552l.001-.001Z"
style="fill:currentColor" />
<path
d="M34.442 51.095a10.127 10.127 0 0 1-4.596-8.485c0-5.592 4.54-10.132 10.132-10.132 2.837 0 5.403 1.169 7.243 3.05a71.387 71.387 0 0 0-12.779 15.567Z"
style="fill:currentColor" />
<circle cx="173.267" cy="10.132" r="10.132" style="fill:currentColor" />
<circle cx="218.027" cy="100.618" r="10.132" style="fill:currentColor" />
</svg>
</a>
<div class="nav-wrapper">
<button class="btn btn--menu" id="menu-btn" aria-expanded="false" aria-controls="menu"
aria-label="Open mobile nav">
<svg xmlns="http://www.w3.org/2000/svg" width="32" fill="none" viewBox="0 0 24 24"
stroke="currentColor" stroke-width="4">
<path stroke-linecap="round" stroke-linejoin="round" d="M4 8h16M4 16h16" />
</svg>
</button>
<ul role="menubar" class="nav-links" id="menu">
<li role="none">
<a role="menuitem" href="/" class="nav-link btn">Home</a>
</li>
<li role="none">
<a role="menuitem" href="#" class="nav-link btn">About</a>
</li>
<li role="none">
<a role="menuitem" href="#" class="nav-link btn">Donate</a>
</li>
<li role="none">
<a role="menuitem" href="#" class="nav-link btn btn--accent">Contact</a>
</li>
</ul>
</div>
</div>
</nav>
<header>
<img class="header__img"
srcset="./assets/spaceLg.jpg 1999w, ./assets/spaceMd.jpg 1000w, ./assets/spaceSm.jpg 500w"
src="./assets/space.jpg" alt="View of earth from space" />
<div class="container header-container">
<h1 class="h1 rise">The Next Frontier</h1>
<p class="h3 rise subheading">Explore the universe of tomorrow.</p>
</div>
</header>
<main>
<section class="stars container--sm fade-up" aria-labelledby="stars">
<h2 class="h2" id="stars">Look to the Stars</h2>
<p class="body">Lorem ipsum dolor sit, amet consectetur adipisicing elit. Vitae eligendi quibusdam dolor
illum impedit nisi. Eaque sequi necessitatibus voluptatum porro.</p>
<p class="body">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?</p>
<p class="body">Lorem ipsum dolor sit amet consectetur adipisicing elit. Itaque, temporibus.</p>
</section>
<section class="container" aria-labelledby="cards">
<h2 class="sr-only" id="cards">Reasons to Travel</h2>
<div class="card-container">
<div class="card fade-up">
<svg xmlns="http://www.w3.org/2000/svg" class="card__image fade-up" width="192" fill="currentColor"
viewBox="0 0 256 256">
<rect width="256" height="256" fill="none"></rect>
<path
d="M138.7,175.5l-19.2,52.1a8,8,0,0,1-15,0L85.3,175.5a8.1,8.1,0,0,0-4.8-4.8L28.4,151.5a8,8,0,0,1,0-15l52.1-19.2a8.1,8.1,0,0,0,4.8-4.8l19.2-52.1a8,8,0,0,1,15,0l19.2,52.1a8.1,8.1,0,0,0,4.8,4.8l52.1,19.2a8,8,0,0,1,0,15l-52.1,19.2A8.1,8.1,0,0,0,138.7,175.5Z"
fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round"
stroke-width="16"></path>
<line x1="176" y1="16" x2="176" y2="64" fill="none" stroke="currentColor" stroke-linecap="round"
stroke-linejoin="round" stroke-width="16"></line>
<line x1="200" y1="40" x2="152" y2="40" fill="none" stroke="currentColor" stroke-linecap="round"
stroke-linejoin="round" stroke-width="16"></line>
<line x1="224" y1="72" x2="224" y2="104" fill="none" stroke="currentColor"
stroke-linecap="round" stroke-linejoin="round" stroke-width="16"></line>
<line x1="240" y1="88" x2="208" y2="88" fill="none" stroke="currentColor" stroke-linecap="round"
stroke-linejoin="round" stroke-width="16"></line>
</svg>
<div>
<h3 class="h3">Stars in the Sky</h3>
<p class="body">Lorem ipsum dolor sit amet consectetur adipisicing elit. Harum obcaecati,
deserunt optio possimus atque qui!</p>
</div>
</div>
<div class="card fade-up">
<svg xmlns="http://www.w3.org/2000/svg" class="card__image fade-up" width="192" fill="currentColor"
viewBox="0 0 256 256">
<rect width="256" height="256" fill="none"></rect>
<path d="M88,148a68,68,0,1,1,68,68H76a44,44,0,0,1,0-88,42.5,42.5,0,0,1,14.3,2.4" fill="none"
stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="16">
</path>
<path
d="M47,138.9A64.1,64.1,0,0,1,9.6,94.4h0A68.3,68.3,0,0,0,24,96,64.1,64.1,0,0,0,88,32a68.3,68.3,0,0,0-1.6-14.4h0A64,64,0,0,1,136,80c0,1,0,2-.1,3"
fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round"
stroke-width="16"></path>
</svg>
<div>
<h3 class="h3">Moon Up Above</h3>
<p class="body">Lorem ipsum dolor sit amet consectetur adipisicing elit. Harum obcaecati,
deserunt optio possimus atque qui!</p>
</div>
</div>
<div class="card fade-up">
<svg xmlns="http://www.w3.org/2000/svg" class="card__image fade-up" width="192" fill="currentcolor"
viewBox="0 0 256 256">
<rect width="256" height="256" fill="none"></rect>
<line x1="92.8" y1="59" x2="85.1" y2="40.5" fill="none" stroke="currentcolor"
stroke-linecap="round" stroke-linejoin="round" stroke-width="16"></line>
<line x1="43" y1="108.8" x2="24.5" y2="101.1" fill="none" stroke="currentcolor"
stroke-linecap="round" stroke-linejoin="round" stroke-width="16"></line>
<line x1="213" y1="108.8" x2="231.5" y2="101.1" fill="none" stroke="currentcolor"
stroke-linecap="round" stroke-linejoin="round" stroke-width="16"></line>
<line x1="163.2" y1="59" x2="170.9" y2="40.5" fill="none" stroke="currentcolor"
stroke-linecap="round" stroke-linejoin="round" stroke-width="16"></line>
<line x1="240" y1="160" x2="16" y2="160" fill="none" stroke="currentcolor"
stroke-linecap="round" stroke-linejoin="round" stroke-width="16"></line>
<line x1="208" y1="200" x2="48" y2="200" fill="none" stroke="currentcolor"
stroke-linecap="round" stroke-linejoin="round" stroke-width="16"></line>
<path d="M70.2,160a60,60,0,1,1,115.6,0" fill="none" stroke="currentcolor" stroke-linecap="round"
stroke-linejoin="round" stroke-width="16"></path>
</svg>
<div>
<h3 class="h3">Aint no Sun Allowed</h3>
<p class="body">Lorem ipsum dolor sit amet consectetur adipisicing elit. Harum obcaecati,
deserunt optio possimus atque qui!</p>
</div>
</div>
</div>
</section>
<section class="container--sm moons fade-up" aria-labelledby="moons">
<h3 class="h2" id="moons">Space Travel for Everyone</h3>
<p class="body">Lorem ipsum dolor sit, amet consectetur adipisicing elit. Vitae eligendi quibusdam dolor
illum impedit
nisi. Eaque sequi necessitatibus voluptatum porro.</p>
<p class="body">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?</p>
<p class="body">Lorem ipsum dolor sit amet consectetur adipisicing elit. Itaque, temporibus.</p>
</section>
</main>
</body>
</html>

View File

@ -0,0 +1,32 @@
const navBtn = document.querySelector('#menu-btn');
const nav = document.querySelector('nav');
const navLinks = document.querySelector('.nav-links');
navBtn.addEventListener('click', () => {
navLinks.classList.add('activated');
const isExpanded = JSON.parse(navBtn.getAttribute('aria-expanded'));
navBtn.setAttribute('aria-expanded', !isExpanded);
!isExpanded && nav.classList.add('active');
})
//INTERSECTION OBSERVER
const navObs = new IntersectionObserver((entries) => nav.classList.toggle('active', !entries[0].isIntersecting)
, {threshold: .85})
navObs.observe(document.querySelector('header'));
const fadeUpObserver = new IntersectionObserver((entries) => {
entries.forEach(entry => {
if(entry.isIntersecting){
entry.target.classList.add('faded');
fadeUpObserver.unobserve(entry.target)
}
})
}, {
rootMargin: '-15%'
})
document.querySelectorAll('.fade-up').forEach(el => {
fadeUpObserver.observe(el);
})

View File

@ -0,0 +1,300 @@
*,
*::after,
*::before {
box-sizing: border-box;
margin: 0;
padding: 0;
}
:root {
--bkg: linear-gradient(to top, #0f0915, #050307);
--dark: #050307;
--text: #f9f6fe;
--accent: #4233e4;
}
body {
font-family: "Nunito Sans", sans-serif;
font-size: clamp(1.1rem, 2vw + 1rem, 1.4rem);
line-height: 1.55;
background: var(--bkg);
color: var(--text);
}
nav {
position: fixed;
z-index: 10;
width: 100%;
padding-block: 1.2rem;
background-color: transparent;
transition: background-color 800ms cubic-bezier(0.64, 0.04, 0.26, 0.87);
}
nav.active {
background-color: var(--dark);
}
.nav-container {
display: flex;
justify-content: space-between;
align-items: center;
}
.logo {
color: var(--accent);
margin-inline: 0.5rem;
width: clamp(3rem, 10vw, 7.5rem);
display: grid;
place-items: center;
}
.logo-link:focus-visible,
.nav-link:focus-visible {
outline: 4px solid var(--accent);
outline-offset: 0.2em;
border-radius: 0.5rem;
}
.nav-links {
display: flex;
align-items: center;
flex-direction: column;
gap: 1.5rem;
transform: translate3d(0, -200%, 0);
position: absolute;
z-index: -1;
top: 3rem;
left: 0;
right: 0;
background-color: var(--dark);
padding: 1.5rem;
border-bottom: 4px solid var(--accent);
text-align: center;
}
.nav-links.activated {
transition: transform 0.4s cubic-bezier(0.64, 0.04, 0.26, 0.87);
}
.btn {
color: var(--text);
text-decoration: none;
padding: 0.3rem 1.5rem;
cursor: pointer;
border-radius: 0.5rem;
}
.btn--accent {
background-color: var(--accent);
padding: 0.3rem 2rem;
}
.btn--menu {
color: var(--accent);
background-color: transparent;
border: none;
display: grid;
place-items: center;
padding-inline: 1rem;
transition: transform 0.3s cubic-bezier(0.64, 0.04, 0.26, 0.87);
}
li[role="none"],
.nav-link {
width: 100%;
display: block;
font-size: 1.1rem;
text-transform: uppercase;
}
.btn--menu[aria-expanded="true"] {
transform: rotate(-180deg);
}
.btn--menu[aria-expanded="true"] + .nav-links {
transform: translate3d(0, 0, 0);
}
@media (min-width: 768px) {
.btn--menu {
display: none;
}
.nav-links {
position: static;
transform: translate3d(0, 0, 0);
flex-direction: row;
border: 0;
z-index: 0;
padding: 0;
inset: 0;
background-color: transparent;
}
li[role="none"],
.nav-link {
width: initial;
}
}
main {
display: grid;
gap: clamp(4rem, 1.45454537rem + 11.636364vw, 8rem);
position: relative;
top: -10vh;
}
h1 {
font-size: clamp(2.5rem, 5vw + 1rem, 8rem);
font-size: bolder;
line-height: 1.1;
letter-spacing: 0.02em;
}
.h2 {
font-size: clamp(1.8rem, 4vw + 1rem, 3.5rem);
font-size: bolder;
line-height: 1.1;
color: var(--accent);
}
.h3 {
font-size: clamp(1.2rem, 3vw + 1rem, 2.2rem);
line-height: 1.1;
color: var(--accent);
}
.subheading {
color: var(--text);
}
.sr-only {
position: absolute;
width: 1px;
height: 1px;
padding: 0;
margin: -1px;
overflow: hidden;
clip: rect(0, 0, 0, 0);
white-space: nowrap;
border-width: 0;
}
header {
min-height: 100vh;
display: grid;
place-items: center;
text-align: center;
position: relative;
}
.header__img {
position: absolute;
height: 100%;
width: 100%;
object-fit: cover;
}
.container {
margin-inline: max((100% - 90rem) / 2, 1rem);
}
.container--sm {
margin-inline: max((100% - 70rem) / 2, 2rem);
}
.rise {
opacity: 0;
animation: rise 0.8s ease-in-out forwards;
}
.rise.subheading {
animation: rise 1.2s ease-in-out forwards 0.5s;
}
@keyframes rise {
0% {
transform: translateY(2rem);
opacity: 0;
}
100% {
transform: translateY(0);
opacity: 1;
}
}
.header-container {
display: grid;
gap: 1rem;
position: relative;
top: -5vh;
}
.stars {
padding: clamp(1.2rem, 3.5vw, 2.5rem) clamp(1.2rem, 5vw, 3rem) 0;
background-color: var(--dark);
display: grid;
border-radius: 0.5rem;
gap: 1rem;
}
.moons {
display: grid;
gap: 1rem;
}
#stars {
text-align: center;
}
.card-container {
display: flex;
flex-wrap: wrap;
gap: 2.5rem 3.5rem;
}
.card {
flex: 1 1 100%;
display: grid;
place-items: center;
gap: 1.5rem;
padding: 2.5rem 1.5rem 1.5rem;
border: 5px solid var(--accent);
border-radius: 0.5rem;
}
@media (min-width: 768px) {
.card {
flex: 1 1 28%;
}
}
.card__image {
width: clamp(4rem, 10vw, 8rem);
}
.fade-up {
opacity: 0;
transform: translate3d(0, 5rem, 0);
transition: transform 1s cubic-bezier(0.64, 0.04, 0.26, 0.87),
opacity 0.8s cubic-bezier(0.64, 0.04, 0.26, 0.87);
}
.fade-up.faded {
opacity: 1;
transform: translate3d(0, 0, 0);
}
/* Remove all animations, transitions and smooth scroll for people that prefer not to see them */
@media (prefers-reduced-motion: reduce) {
html:focus-within {
scroll-behavior: auto;
}
*,
*::before,
*::after {
animation-duration: 0.01ms !important;
animation-iteration-count: 1 !important;
transition-duration: 0.01ms !important;
scroll-behavior: auto !important;
}
}