Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 24 additions & 3 deletions 02-counter/final/app.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ let count = 0;
// select value and buttons
const value = document.querySelector("#value");
const btns = document.querySelectorAll(".btn");
const themeToggle = document.querySelector("#theme-toggle");
const body = document.body;

btns.forEach(function (btn) {
btn.addEventListener("click", function (e) {
Expand All @@ -16,14 +18,33 @@ btns.forEach(function (btn) {
}

if (count > 0) {
value.style.color = "green";
value.style.color = "var(--clr-green-dark)";
}
if (count < 0) {
value.style.color = "red";
value.style.color = "var(--clr-red-dark)";
}
if (count === 0) {
value.style.color = "#222";
value.style.color = "var(--clr-primary-2)";
}
value.textContent = count;
// animate value change
value.classList.remove("pop");
// reflow to restart animation
void value.offsetWidth;
value.classList.add("pop");
});
});

// theme toggle: toggles alternate palette on body[data-theme="alt"]
if (themeToggle) {
themeToggle.addEventListener("click", function () {
const isAlt = body.getAttribute("data-theme") === "alt";
if (isAlt) {
body.removeAttribute("data-theme");
themeToggle.setAttribute("aria-pressed", "false");
} else {
body.setAttribute("data-theme", "alt");
themeToggle.setAttribute("aria-pressed", "true");
}
});
}
16 changes: 10 additions & 6 deletions 02-counter/final/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -11,12 +11,16 @@
<body>
<main>
<div class="container">
<h1>counter</h1>
<span id="value">0</span>
<div class="button-container">
<button class="btn decrease">decrease</button>
<button class="btn reset">reset</button>
<button class="btn increase">increase</button>
<h1>Counter</h1>
<div class="top-row">
<p class="subtitle">A small, delightful counter with accessible controls</p>
<button id="theme-toggle" class="theme-toggle" aria-pressed="false" aria-label="Toggle color palette">Theme</button>
</div>
<span id="value" aria-live="polite">0</span>
<div class="button-container" role="group" aria-label="counter controls">
<button class="btn decrease" aria-label="decrease">-</button>
<button class="btn reset" aria-label="reset">Reset</button>
<button class="btn increase" aria-label="increase">+</button>
</div>
</div>
</main>
Expand Down
144 changes: 128 additions & 16 deletions 02-counter/final/styles.css
Original file line number Diff line number Diff line change
Expand Up @@ -68,10 +68,11 @@ Global Styles
}
body {
font-family: var(--ff-secondary);
background: var(--clr-grey-10);
background: linear-gradient(135deg, var(--clr-primary-9), var(--clr-primary-10));
color: var(--clr-grey-1);
line-height: 1.5;
font-size: 0.875rem;
font-size: 0.95rem;
-webkit-font-smoothing:antialiased;
}
ul {
list-style-type: none;
Expand Down Expand Up @@ -164,27 +165,138 @@ main {
}
.container {
text-align: center;
background: var(--clr-white);
padding: 2.25rem 2rem;
border-radius: 12px;
box-shadow: var(--light-shadow);
width: min(90%, 420px);
transform: translateY(-1%);
}

/* top row: subtitle + theme toggle */
.top-row {
display: flex;
gap: 0.75rem;
align-items: center;
justify-content: center;
margin-bottom: 0.25rem;
}
.theme-toggle {
background: transparent;
border: 1px solid var(--clr-grey-8);
color: var(--clr-grey-2);
padding: 0.35rem 0.6rem;
border-radius: 999px;
cursor: pointer;
font-size: 0.825rem;
}
.theme-toggle[aria-pressed="true"] {
background: var(--clr-primary-9);
color: var(--clr-white);
border-color: transparent;
}
/* value */
#value {
font-size: 6rem;
font-weight: bold;
display: block;
font-size: 4.5rem;
font-weight: 800;
color: var(--clr-primary-2);
margin: 0.5rem 0 1rem;
letter-spacing: -0.02em;
text-shadow: 0 4px 18px rgba(32, 45, 70, 0.08);
}

/* animation when value changes */
.pop {
animation: pop 360ms cubic-bezier(0.22, 1, 0.36, 1);
}
@keyframes pop {
0% { transform: scale(1); }
40% { transform: scale(1.18); }
100% { transform: scale(1); }
}

/* subtitle */
.subtitle {
color: var(--clr-grey-5);
margin-top: 0.25rem;
margin-bottom: 0.5rem;
font-size: 0.95rem;
}

/* buttons */
.btn {
text-transform: uppercase;
background: transparent;
color: var(--clr-black);
padding: 0.375rem 0.75rem;
letter-spacing: var(--spacing);
text-transform: none;
color: var(--clr-white);
padding: 0.5rem 1rem;
letter-spacing: 0.02em;
display: inline-block;
transition: var(--transition);
font-size: 0.875rem;
border: 2px solid var(--clr-black);
font-size: 1rem;
border: 0;
cursor: pointer;
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.2);
border-radius: var(--radius);
margin: 0.5rem;
border-radius: 8px;
margin: 0.25rem;
min-width: 84px;
}
.btn:focus {
outline: 3px solid rgba(32, 124, 229, 0.18);
outline-offset: 2px;
}
.btn.increase {
background: linear-gradient(180deg, var(--clr-green-light), var(--clr-green-dark));
box-shadow: 0 8px 20px rgba(46, 125, 50, 0.18);
}
.btn.decrease {
background: linear-gradient(180deg, var(--clr-red-light), var(--clr-red-dark));
box-shadow: 0 8px 20px rgba(211, 47, 47, 0.18);
}
.btn.reset {
background: linear-gradient(180deg, var(--clr-primary-8), var(--clr-primary-5));
color: var(--clr-grey-1);
box-shadow: 0 6px 16px rgba(10, 102, 194, 0.12);
}
.btn:hover {
color: var(--clr-white);
background: var(--clr-black);
transform: translateY(-3px);
filter: brightness(1.02);
}

/* button container spacing */
.button-container {
display: flex;
gap: 0.5rem;
justify-content: center;
align-items: center;
flex-wrap: wrap;
}

@media screen and (min-width: 800px) {
.container {
padding: 3rem 3.5rem;
}
#value {
font-size: 6rem;
}
.btn {
min-width: 110px;
font-size: 1.05rem;
}
}

/* alternate palette applied when body[data-theme="alt"] */
body[data-theme="alt"] {
--clr-primary-1: hsl(285, 40%, 12%);
--clr-primary-2: hsl(285, 55%, 28%);
--clr-primary-3: hsl(285, 55%, 38%);
--clr-primary-4: hsl(285, 60%, 48%);
--clr-primary-5: hsl(285, 65%, 58%);
--clr-primary-6: hsl(285, 75%, 68%);
--clr-primary-7: hsl(285, 80%, 76%);
--clr-primary-8: hsl(285, 85%, 83%);
--clr-primary-9: hsl(285, 90%, 90%);
--clr-primary-10: hsl(285, 95%, 96%);
--clr-green-dark: hsl(190, 70%, 30%);
--clr-green-light: hsl(190, 70%, 60%);
--clr-red-dark: hsl(10, 70%, 35%);
--clr-red-light: hsl(10, 70%, 62%);
}
128 changes: 114 additions & 14 deletions 04-navbar/final/app.js
Original file line number Diff line number Diff line change
@@ -1,20 +1,120 @@
// classList - shows/gets all classes
// contains - checks classList for specific class
// add - add class
// remove - remove class
// toggle - toggles class
// Enhanced Navbar Functionality

const navToggle = document.querySelector(".nav-toggle");
const links = document.querySelector(".links");
const mobileOverlay = document.querySelector(".mobile-overlay");
const navbar = document.querySelector(".navbar");
const navLinks = document.querySelectorAll(".nav-link");
const body = document.body;

navToggle.addEventListener("click", function () {
// console.log(links.classList);
// console.log(links.classList.contains("random"));
// console.log(links.classList.contains("links"));
// if (links.classList.contains("show-links")) {
// links.classList.remove("show-links");
// } else {
// links.classList.add("show-links");
// }
// Toggle mobile menu
function toggleMenu() {
const isOpen = links.classList.contains("show-links");

links.classList.toggle("show-links");
mobileOverlay.classList.toggle("show");
navToggle.classList.toggle("active");

// Update aria attributes for accessibility
navToggle.setAttribute("aria-expanded", !isOpen);

// Prevent body scroll when menu is open (mobile)
if (!isOpen) {
body.style.overflow = "hidden";
} else {
body.style.overflow = "";
}
}

// Close mobile menu
function closeMenu() {
links.classList.remove("show-links");
mobileOverlay.classList.remove("show");
navToggle.classList.remove("active");
navToggle.setAttribute("aria-expanded", "false");
body.style.overflow = "";
}

// Event Listeners
navToggle.addEventListener("click", toggleMenu);

// Close menu when clicking overlay
mobileOverlay.addEventListener("click", closeMenu);

// Close menu when clicking a link (mobile)
navLinks.forEach((link) => {
link.addEventListener("click", () => {
if (window.innerWidth < 800) {
closeMenu();
}
});
});

// Handle window resize
let resizeTimer;
window.addEventListener("resize", () => {
clearTimeout(resizeTimer);
resizeTimer = setTimeout(() => {
if (window.innerWidth >= 800) {
closeMenu();
}
}, 250);
});

// Add scroll effect to navbar
let lastScroll = 0;
window.addEventListener("scroll", () => {
const currentScroll = window.pageYOffset;

if (currentScroll > 50) {
navbar.classList.add("scrolled");
} else {
navbar.classList.remove("scrolled");
}

lastScroll = currentScroll;
});

// Set active link based on current page
function setActiveLink() {
const currentPath = window.location.pathname;
const currentPage = currentPath.split("/").pop() || "index.html";

navLinks.forEach((link) => {
link.classList.remove("active");
const linkPath = link.getAttribute("href");

if (linkPath === currentPage ||
(currentPage === "" && linkPath === "index.html")) {
link.classList.add("active");
}
});
}

// Initialize active link on load
setActiveLink();

// Close menu on Escape key press
document.addEventListener("keydown", (e) => {
if (e.key === "Escape" && links.classList.contains("show-links")) {
closeMenu();
navToggle.focus(); // Return focus to toggle button
}
});

// Smooth scroll for anchor links
document.querySelectorAll('a[href^="#"]').forEach((anchor) => {
anchor.addEventListener("click", function (e) {
const href = this.getAttribute("href");
if (href !== "#" && href.startsWith("#")) {
e.preventDefault();
const target = document.querySelector(href);
if (target) {
target.scrollIntoView({
behavior: "smooth",
block: "start",
});
}
}
});
});
Loading