From 304554f74a9c99b50e91792bedac9faf049f0533 Mon Sep 17 00:00:00 2001 From: Praveen Date: Tue, 30 Dec 2025 22:11:49 +0530 Subject: [PATCH 1/4] (02-Counter) UI: modernize counter, add animations and theme toggle --- 02-counter/final/app.js | 27 ++++++- 02-counter/final/index.html | 16 ++-- 02-counter/final/styles.css | 144 ++++++++++++++++++++++++++++++++---- 3 files changed, 162 insertions(+), 25 deletions(-) diff --git a/02-counter/final/app.js b/02-counter/final/app.js index f34827948..537418c7a 100644 --- a/02-counter/final/app.js +++ b/02-counter/final/app.js @@ -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) { @@ -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"); + } + }); +} diff --git a/02-counter/final/index.html b/02-counter/final/index.html index 82f7ea2ba..89f4f5930 100644 --- a/02-counter/final/index.html +++ b/02-counter/final/index.html @@ -11,12 +11,16 @@
-

counter

- 0 -
- - - +

Counter

+
+

A small, delightful counter with accessible controls

+ +
+ 0 +
+ + +
diff --git a/02-counter/final/styles.css b/02-counter/final/styles.css index f7ef04d4a..35ac4ddf1 100644 --- a/02-counter/final/styles.css +++ b/02-counter/final/styles.css @@ -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; @@ -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%); } From be74881b8e80ac4d07501e8d81a79076e93c87c9 Mon Sep 17 00:00:00 2001 From: Praveen Date: Sat, 3 Jan 2026 21:21:14 +0530 Subject: [PATCH 2/4] feat: redesign product pages with modern UI/UX --- 22-products/final/app.js | 74 +++- 22-products/final/index.html | 11 +- 22-products/final/product.html | 18 +- 22-products/final/product.js | 101 ++++-- 22-products/final/styles.css | 637 +++++++++++++++++++++++++-------- 5 files changed, 645 insertions(+), 196 deletions(-) diff --git a/22-products/final/app.js b/22-products/final/app.js index 48c271a71..aa74f65aa 100644 --- a/22-products/final/app.js +++ b/22-products/final/app.js @@ -3,42 +3,82 @@ const url = 'https://www.course-api.com/javascript-store-products'; const productsDOM = document.querySelector('.products-center'); const fetchProducts = async () => { - productsDOM.innerHTML = '
'; + productsDOM.innerHTML = ` +
+
+

Loading products...

+
+ `; try { const resp = await fetch(url); + if (!resp.ok) { + throw new Error('Failed to fetch products'); + } const data = await resp.json(); return data; } catch (error) { - productsDOM.innerHTML = '

there was an error

'; + productsDOM.innerHTML = ` +
+

Oops! Something went wrong

+

+ Unable to load products. Please try again later. +

+
+ `; + console.error('Error fetching products:', error); + return null; } }; const displayProducts = (list) => { + if (!list || list.length === 0) { + productsDOM.innerHTML = ` +
+

No products found

+

+ Please check back later for new products. +

+
+ `; + return; + } + const productList = list .map((product) => { const { id } = product; const { name: title, price } = product.fields; const { url: img } = product.fields.image[0]; - const formatPrice = price / 100; - // id,name,price,img - return ` - ${title} -
-
${title}
- $${formatPrice} -
-
`; + const formatPrice = (price / 100).toFixed(2); + + return ` + + ${title} +
+
${title}
+ $${formatPrice} +
+
+ `; }) .join(''); - productsDOM.innerHTML = `
- ${productList} - -
`; + + productsDOM.innerHTML = ` +
+ ${productList} +
+ `; }; const start = async () => { const data = await fetchProducts(); - displayProducts(data); + if (data) { + displayProducts(data); + } }; -start(); +start(); \ No newline at end of file diff --git a/22-products/final/index.html b/22-products/final/index.html index af8d9d34d..7436e98f9 100644 --- a/22-products/final/index.html +++ b/22-products/final/index.html @@ -4,19 +4,22 @@ - Products Complete + + Products - Modern Store
-

our products

+

Our Products

-
+
+ +
- + \ No newline at end of file diff --git a/22-products/final/product.html b/22-products/final/product.html index 0b5d186b2..68595b010 100644 --- a/22-products/final/product.html +++ b/22-products/final/product.html @@ -4,12 +4,22 @@ - Single Product + + Product Details - Modern Store - back home -
+ - + \ No newline at end of file diff --git a/22-products/final/product.js b/22-products/final/product.js index 0708ebd2e..11023d431 100644 --- a/22-products/final/product.js +++ b/22-products/final/product.js @@ -3,22 +3,55 @@ const url = 'https://www.course-api.com/javascript-store-single-product'; const fetchProduct = async () => { try { - productDOM.innerHTML = '

Loading...

'; - // console.log(window.location.search); + productDOM.innerHTML = ` +
+
+

Loading product details...

+
+ `; + const params = new URLSearchParams(window.location.search); const id = params.get('id'); + if (!id) { + throw new Error('Product ID is missing'); + } + const response = await fetch(`${url}?id=${id}`); + + if (!response.ok) { + throw new Error('Failed to fetch product'); + } + const data = await response.json(); return data; } catch (error) { - productDOM.innerHTML = - '

There was a problem loading the product. Please try again later

'; + productDOM.innerHTML = ` +
+

Unable to load product

+

+ ${error.message || 'Please try again later or go back to browse other products.'} +

+
+ `; + console.error('Error fetching product:', error); + return null; } }; const displayProduct = (product) => { - // company, colors, description, name:title, price, image(url:img) + if (!product || !product.fields) { + productDOM.innerHTML = ` +
+

Product not found

+

+ The product you're looking for doesn't exist. +

+
+ `; + return; + } + const { company, colors, @@ -28,36 +61,54 @@ const displayProduct = (product) => { image, } = product.fields; const { url: img } = image[0]; - document.title = title.toUpperCase(); + const formatPrice = (price / 100).toFixed(2); + + document.title = `${title} - Modern Store`; - // colors + // Colors const colorsList = colors .map((color) => { - return ``; + return ` + + `; }) .join(''); - productDOM.innerHTML = `
- ${title} -
-

${title}

-
${company}
- ${price / 100} -
- ${colorsList} - -
-

- ${description} -

- + productDOM.innerHTML = ` +
+ ${title} +
+

${title}

+
${company}
+ $${formatPrice} +
+ Available Colors: + ${colorsList}
-
`; +

${description}

+ +
+
+ `; }; const start = async () => { const data = await fetchProduct(); - displayProduct(data); + if (data) { + displayProduct(data); + } }; -start(); +start(); \ No newline at end of file diff --git a/22-products/final/styles.css b/22-products/final/styles.css index cfe361180..e9c059430 100644 --- a/22-products/final/styles.css +++ b/22-products/final/styles.css @@ -2,25 +2,29 @@ ::after, ::before { box-sizing: border-box; + margin: 0; + padding: 0; } html { font-size: 100%; -} /*16px*/ + scroll-behavior: smooth; +} :root { - /* colors */ - --primary-100: #e2e0ff; - --primary-200: #c1beff; - --primary-300: #a29dff; - --primary-400: #837dff; - --primary-500: #645cff; - --primary-600: #504acc; - --primary-700: #3c3799; - --primary-800: #282566; - --primary-900: #141233; - - /* grey */ + /* Modern Color Palette */ + --primary-50: #f0f4ff; + --primary-100: #e0e9ff; + --primary-200: #c7d8ff; + --primary-300: #a4bfff; + --primary-400: #819dff; + --primary-500: #6366f1; + --primary-600: #4f46e5; + --primary-700: #4338ca; + --primary-800: #3730a3; + --primary-900: #312e81; + + /* Grey Scale */ --grey-50: #f8fafc; --grey-100: #f1f5f9; --grey-200: #e2e8f0; @@ -31,48 +35,60 @@ html { --grey-700: #334155; --grey-800: #1e293b; --grey-900: #0f172a; - /* rest of the colors */ - --black: #222; - --white: #fff; - --red-light: #f8d7da; - --red-dark: #842029; - --green-light: #d1e7dd; - --green-dark: #0f5132; - - /* fonts */ - --small-text: 0.875rem; - --extra-small-text: 0.7em; - /* rest of the vars */ - --backgroundColor: var(--grey-50); + + /* Accent Colors */ + --black: #0f172a; + --white: #ffffff; + --red-light: #fee2e2; + --red-dark: #dc2626; + --green-light: #dcfce7; + --green-dark: #16a34a; + + /* Design Tokens */ + --backgroundColor: #ffffff; + --surfaceColor: #ffffff; --textColor: var(--grey-900); - --borderRadius: 0.25rem; - --letterSpacing: 1px; - --transition: 0.3s ease-in-out all; - --max-width: 1120px; + --textColorLight: var(--grey-600); + --borderRadius: 1rem; + --borderRadius-sm: 0.5rem; + --letterSpacing: 0.025em; + --transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1); + --transition-fast: all 0.15s ease-out; + --max-width: 1280px; --fixed-width: 600px; - /* box shadow*/ - --shadow-1: 0 1px 3px 0 rgba(0, 0, 0, 0.1), 0 1px 2px 0 rgba(0, 0, 0, 0.06); - --shadow-2: 0 4px 6px -1px rgba(0, 0, 0, 0.1), + /* Modern Shadows */ + --shadow-sm: 0 1px 2px 0 rgba(0, 0, 0, 0.05); + --shadow-md: 0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -1px rgba(0, 0, 0, 0.06); - --shadow-3: 0 10px 15px -3px rgba(0, 0, 0, 0.1), + --shadow-lg: 0 10px 15px -3px rgba(0, 0, 0, 0.1), 0 4px 6px -2px rgba(0, 0, 0, 0.05); - --shadow-4: 0 20px 25px -5px rgba(0, 0, 0, 0.1), + --shadow-xl: 0 20px 25px -5px rgba(0, 0, 0, 0.1), 0 10px 10px -5px rgba(0, 0, 0, 0.04); + --shadow-2xl: 0 25px 50px -12px rgba(0, 0, 0, 0.25); + --shadow-card: 0 1px 3px 0 rgba(0, 0, 0, 0.1), + 0 1px 2px 0 rgba(0, 0, 0, 0.06); + --shadow-card-hover: 0 10px 25px -5px rgba(0, 0, 0, 0.15), + 0 4px 6px -2px rgba(0, 0, 0, 0.05); } body { - background: var(--backgroundColor); - font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, - Ubuntu, Cantarell, 'Open Sans', 'Helvetica Neue', sans-serif; + background: linear-gradient(135deg, #f8fafc 0%, #e2e8f0 100%); + font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Inter', 'Roboto', + 'Oxygen', 'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', + 'Helvetica Neue', sans-serif; font-weight: 400; - line-height: 1.75; + line-height: 1.6; color: var(--textColor); + min-height: 100vh; + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; } p { margin-bottom: 1.5rem; - max-width: 40em; + max-width: 65ch; + color: var(--textColorLight); } h1, @@ -81,42 +97,50 @@ h3, h4, h5 { margin: 0; - margin-bottom: 1.38rem; - font-weight: 400; - line-height: 1.3; - text-transform: capitalize; - letter-spacing: var(--letterSpacing); + margin-bottom: 1rem; + font-weight: 600; + line-height: 1.2; + letter-spacing: -0.025em; + color: var(--textColor); } h1 { margin-top: 0; - font-size: 3.052rem; + font-size: clamp(2.5rem, 5vw, 3.5rem); + font-weight: 700; } h2 { - font-size: 2.441rem; + font-size: clamp(2rem, 4vw, 2.75rem); + font-weight: 700; } h3 { - font-size: 1.953rem; + font-size: clamp(1.5rem, 3vw, 2rem); + font-weight: 600; } h4 { - font-size: 1.563rem; + font-size: 1.5rem; + font-weight: 600; } h5 { - font-size: 1.25rem; + font-size: 1.125rem; + font-weight: 500; } small, .text-small { - font-size: var(--small-text); + font-size: 0.875rem; } a { text-decoration: none; + color: inherit; + transition: var(--transition-fast); } + ul { list-style-type: none; padding: 0; @@ -127,189 +151,510 @@ ul { display: block; object-fit: cover; } -/* buttons */ +/* Buttons */ .btn { cursor: pointer; color: var(--white); - background: var(--primary-500); - border: transparent; - border-radius: var(--borderRadius); + background: linear-gradient(135deg, var(--primary-500) 0%, var(--primary-600) 100%); + border: none; + border-radius: var(--borderRadius-sm); letter-spacing: var(--letterSpacing); - padding: 0.375rem 0.75rem; - box-shadow: var(--shadow-1); + padding: 0.875rem 2rem; + font-size: 1rem; + font-weight: 600; + box-shadow: var(--shadow-md); transition: var(--transition); text-transform: capitalize; - display: inline-block; + display: inline-flex; + align-items: center; + justify-content: center; + gap: 0.5rem; + position: relative; + overflow: hidden; } + +.btn::before { + content: ''; + position: absolute; + top: 0; + left: -100%; + width: 100%; + height: 100%; + background: linear-gradient( + 90deg, + transparent, + rgba(255, 255, 255, 0.2), + transparent + ); + transition: left 0.5s; +} + +.btn:hover::before { + left: 100%; +} + .btn:hover { - background: var(--primary-700); - box-shadow: var(--shadow-3); + background: linear-gradient(135deg, var(--primary-600) 0%, var(--primary-700) 100%); + box-shadow: var(--shadow-lg); + transform: translateY(-2px); } +.btn:active { + transform: translateY(0); + box-shadow: var(--shadow-md); +} + +.btn.home-link { + background: var(--white); + color: var(--primary-600); + border: 2px solid var(--primary-200); + box-shadow: var(--shadow-sm); +} + +.btn.home-link:hover { + background: var(--primary-50); + border-color: var(--primary-400); + box-shadow: var(--shadow-md); + transform: translateY(-2px); +} + +/* Loading Animation */ @keyframes spinner { to { transform: rotate(360deg); } } +@keyframes pulse { + 0%, + 100% { + opacity: 1; + } + 50% { + opacity: 0.5; + } +} + .loading { - width: 6rem; - height: 6rem; - border: 5px solid var(--grey-400); + width: 3.5rem; + height: 3.5rem; + border: 4px solid var(--grey-200); border-radius: 50%; border-top-color: var(--primary-500); - animation: spinner 0.6s linear infinite; + animation: spinner 0.8s linear infinite; + margin: 4rem auto; } -.loading { - margin: 0 auto; - margin-top: 4rem; + +.loading-container { + display: flex; + align-items: center; + justify-content: center; + min-height: 400px; + flex-direction: column; + gap: 1rem; +} + +.loading-text { + color: var(--textColorLight); + font-size: 1rem; } -/* title */ +/* Title Section */ .title { text-align: center; - margin-bottom: 3rem; + margin-bottom: 4rem; + padding-top: 2rem; +} + +.title h2 { + font-size: clamp(2rem, 4vw, 3rem); + font-weight: 700; + background: linear-gradient(135deg, var(--primary-600) 0%, var(--primary-400) 100%); + -webkit-background-clip: text; + -webkit-text-fill-color: transparent; + background-clip: text; + margin-bottom: 1rem; + letter-spacing: -0.02em; } .title-underline { - background: var(--primary-500); - width: 7rem; + background: linear-gradient(90deg, var(--primary-500) 0%, var(--primary-400) 100%); + width: 5rem; height: 0.25rem; margin: 0 auto; - margin-top: -1rem; + border-radius: 2px; + position: relative; +} + +.title-underline::after { + content: ''; + position: absolute; + left: 50%; + transform: translateX(-50%); + width: 2rem; + height: 0.25rem; + background: var(--primary-300); + border-radius: 2px; } -/* -=============== -Products -=============== -*/ + +/* Products Section */ .products { - padding: 5rem 0; + padding: 3rem 0 6rem; + min-height: 100vh; } + .products-center { width: 90vw; - max-width: 1170px; + max-width: var(--max-width); margin: 0 auto; } + .products-container { display: grid; gap: 2rem; + grid-template-columns: 1fr; } -.single-product-img { + +.single-product { + background: var(--surfaceColor); border-radius: var(--borderRadius); - height: 15rem; + overflow: hidden; + box-shadow: var(--shadow-card); + transition: var(--transition); + display: block; + position: relative; + height: 100%; + display: flex; + flex-direction: column; +} + +.single-product::before { + content: ''; + position: absolute; + top: 0; + left: 0; + right: 0; + height: 4px; + background: linear-gradient(90deg, var(--primary-500), var(--primary-400)); + opacity: 0; + transition: var(--transition); +} + +.single-product:hover { + transform: translateY(-8px); + box-shadow: var(--shadow-card-hover); +} + +.single-product:hover::before { + opacity: 1; } + +.single-product-img { + border-radius: var(--borderRadius) var(--borderRadius) 0 0; + height: 280px; + object-fit: cover; + transition: var(--transition); + background: var(--grey-100); +} + +.single-product:hover .single-product-img { + transform: scale(1.05); +} + .single-product footer { - padding: 0.75rem 0; - text-align: center; + padding: 1.5rem; + text-align: left; + display: flex; + flex-direction: column; + gap: 0.5rem; + flex: 1; + justify-content: space-between; } + .single-product .name { - margin-bottom: 0.25rem; + margin-bottom: 0; text-transform: capitalize; - letter-spacing: var(--letterSpacing); - color: var(--grey-500); + letter-spacing: 0.01em; + color: var(--textColor); + font-weight: 600; + font-size: 1.125rem; + line-height: 1.4; + display: -webkit-box; + -webkit-line-clamp: 2; + -webkit-box-orient: vertical; + overflow: hidden; } + .single-product .price { margin-bottom: 0; - color: var(--grey-700); + color: var(--primary-600); font-weight: 700; - font-size: 1rem; - letter-spacing: var(--letterSpacing); + font-size: 1.5rem; + letter-spacing: -0.02em; } -@media screen and (min-width: 768px) { - .products-container { - grid-template-columns: repeat(2, 1fr); - } - .single-product-img { - height: 10rem; - } - .single-product .name { - font-size: 0.85rem; - } - .single-product .price { - font-size: 0.85rem; - } - .single-product { - margin-bottom: 0; - } +/* Error State */ +.error { + text-align: center; + padding: 4rem 2rem; + background: var(--white); + border-radius: var(--borderRadius); + box-shadow: var(--shadow-md); + max-width: 600px; + margin: 4rem auto; } -@media screen and (min-width: 1000px) { - .products-container { - grid-template-columns: repeat(3, 1fr); - } + +.error p { + color: var(--red-dark); + font-size: 1.125rem; + margin-bottom: 1rem; + font-weight: 500; } -.error { - text-align: center; - font-size: 2rem; +/* Single Product Page */ +.product-page { + min-height: 100vh; + padding: 2rem 0; +} + +.home-link-container { + width: 90vw; + max-width: var(--max-width); + margin: 0 auto 3rem; + display: flex; + align-items: center; + gap: 0.5rem; } -/* -=============== -Single Product -=============== -*/ .home-link { - display: block; - width: 150px; - margin: 2rem auto; - text-align: center; + display: inline-flex; + align-items: center; + gap: 0.5rem; + padding: 0.75rem 1.5rem; + font-size: 0.9375rem; } + .product { - padding: 5rem 0; + padding: 0; width: 90vw; - max-width: 1170px; + max-width: var(--max-width); margin: 0 auto; } + .product-loading { text-align: center; + padding: 4rem 2rem; + font-size: 1.25rem; + color: var(--textColorLight); } -.product-wrapper img { - height: 15rem; +.product-wrapper { + background: var(--surfaceColor); border-radius: var(--borderRadius); + overflow: hidden; + box-shadow: var(--shadow-xl); + display: grid; + grid-template-columns: 1fr; + gap: 0; } -.product-color { - display: inline-block; - width: 1rem; - height: 1rem; - border-radius: 50%; - background: #222; - margin: 0.5rem 0.5rem 0.25rem 0; +.product-wrapper img { + width: 100%; + height: 100%; + min-height: 400px; + object-fit: cover; + background: var(--grey-100); } .product-info { - padding-top: 1rem; + padding: 2.5rem; + display: flex; + flex-direction: column; + gap: 1.5rem; } + .product-info h3 { - margin: 0; - margin-bottom: 0.25rem; + font-size: clamp(1.75rem, 4vw, 2.5rem); + font-weight: 700; + margin-bottom: 0; + line-height: 1.2; + color: var(--textColor); } + .product-info h5 { - color: var(--grey-500); + color: var(--textColorLight); margin: 0; - margin-bottom: 0.25rem; + font-size: 1rem; + font-weight: 500; + text-transform: uppercase; + letter-spacing: 0.1em; } -.product span { - color: var(--grey-800); + +.product-info .price { + color: var(--primary-600); + font-weight: 700; + font-size: 2.5rem; + letter-spacing: -0.02em; + margin: 0; +} + +.colors { + display: flex; + align-items: center; + gap: 0.75rem; + flex-wrap: wrap; +} + +.colors-label { + font-weight: 600; + color: var(--textColor); + font-size: 0.9375rem; + margin-right: 0.5rem; } -.product p { +.product-color { + display: inline-block; + width: 2.5rem; + height: 2.5rem; + border-radius: 50%; + background: #222; margin: 0; - margin-bottom: 1.25rem; + cursor: pointer; + border: 3px solid var(--white); + box-shadow: var(--shadow-md); + transition: var(--transition-fast); + position: relative; +} + +.product-color:hover { + transform: scale(1.15); + box-shadow: var(--shadow-lg); +} + +.product-color::after { + content: ''; + position: absolute; + inset: -4px; + border-radius: 50%; + border: 2px solid transparent; + transition: var(--transition-fast); +} + +.product-color:active::after { + border-color: var(--primary-500); +} + +.product-info p { + margin: 0; + font-size: 1.0625rem; + line-height: 1.8; + color: var(--textColorLight); +} + +.product-info .btn { + margin-top: 0.5rem; + padding: 1rem 2.5rem; + font-size: 1.0625rem; + width: 100%; + max-width: 300px; +} + +/* Responsive Design */ +@media screen and (min-width: 640px) { + .products-container { + grid-template-columns: repeat(2, 1fr); + } + + .single-product-img { + height: 320px; + } } -@media screen and (min-width: 768px) { +@media screen and (min-width: 1024px) { + .products-container { + grid-template-columns: repeat(3, 1fr); + gap: 2.5rem; + } + + .single-product-img { + height: 350px; + } + .product-wrapper { - display: grid; grid-template-columns: 1fr 1fr; - column-gap: 2rem; + gap: 0; } + .product-wrapper img { - height: 100%; - max-height: 500px; + min-height: 600px; + border-radius: var(--borderRadius) 0 0 var(--borderRadius); + } + + .product-info { + padding: 3.5rem; + justify-content: center; + } +} + +@media screen and (min-width: 1280px) { + .products-container { + grid-template-columns: repeat(4, 1fr); } } + +/* Smooth Animations */ +@keyframes fadeIn { + from { + opacity: 0; + transform: translateY(20px); + } + to { + opacity: 1; + transform: translateY(0); + } +} + +.products-container > * { + animation: fadeIn 0.6s ease-out backwards; +} + +.products-container > *:nth-child(1) { + animation-delay: 0.1s; +} + +.products-container > *:nth-child(2) { + animation-delay: 0.2s; +} + +.products-container > *:nth-child(3) { + animation-delay: 0.3s; +} + +.products-container > *:nth-child(4) { + animation-delay: 0.4s; +} + +.products-container > *:nth-child(n + 5) { + animation-delay: 0.5s; +} + +/* Focus States for Accessibility */ +.btn:focus-visible, +.single-product:focus-visible, +.product-color:focus-visible { + outline: 3px solid var(--primary-500); + outline-offset: 2px; +} + +/* Print Styles */ +@media print { + body { + background: white; + } + + .btn, + .home-link { + display: none; + } +} \ No newline at end of file From 324c514e253f96560df55b3260d3eb1d9a417121 Mon Sep 17 00:00:00 2001 From: Praveen Date: Sun, 4 Jan 2026 23:40:37 +0530 Subject: [PATCH 3/4] modernize dark mode toggle with switch and enhanced UI --- 19-dark-mode/final/app.js | 19 ++- 19-dark-mode/final/index.html | 17 ++- 19-dark-mode/final/styles.css | 277 +++++++++++++++++++++++++++++----- 3 files changed, 270 insertions(+), 43 deletions(-) diff --git a/19-dark-mode/final/app.js b/19-dark-mode/final/app.js index 461f6b185..76d8187b4 100644 --- a/19-dark-mode/final/app.js +++ b/19-dark-mode/final/app.js @@ -1,10 +1,23 @@ -const toggleBtn = document.querySelector('.btn'); +const toggleSwitch = document.querySelector('#theme-toggle'); const articlesContainer = document.querySelector('.articles'); -toggleBtn.addEventListener('click', () => { +// Check for saved theme preference or default to light mode +const currentTheme = localStorage.getItem('theme') || 'light'; +if (currentTheme === 'dark') { + document.documentElement.classList.add('dark-theme'); + toggleSwitch.checked = true; +} + +// Toggle theme and save preference +toggleSwitch.addEventListener('change', () => { document.documentElement.classList.toggle('dark-theme'); + const theme = document.documentElement.classList.contains('dark-theme') + ? 'dark' + : 'light'; + localStorage.setItem('theme', theme); }); +// Display articles const articlesData = articles .map((article) => { const { title, date, length, snippet } = article; @@ -22,4 +35,4 @@ const articlesData = articles }) .join(''); -articlesContainer.innerHTML = articlesData; +articlesContainer.innerHTML = articlesData; \ No newline at end of file diff --git a/19-dark-mode/final/index.html b/19-dark-mode/final/index.html index e10831d53..e7bc06a1e 100644 --- a/19-dark-mode/final/index.html +++ b/19-dark-mode/final/index.html @@ -4,7 +4,8 @@ - Dark Mode + + Dark Mode - overreacted @@ -12,13 +13,17 @@
- - - +
@@ -33,4 +38,4 @@ - + \ No newline at end of file diff --git a/19-dark-mode/final/styles.css b/19-dark-mode/final/styles.css index 1ca6cd8da..821e4a3a8 100644 --- a/19-dark-mode/final/styles.css +++ b/19-dark-mode/final/styles.css @@ -5,17 +5,30 @@ Global Styles */ :root { - --clr-bcg: #fff; - --clr-font: #282c35; - --clr-primary: #d23669; + --clr-bcg: #ffffff; + --clr-font: #1e293b; + --clr-primary: #6366f1; + --clr-primary-light: #818cf8; + --clr-primary-dark: #4f46e5; --clr-grey: #64748b; + --clr-grey-light: #94a3b8; + --clr-border: #e2e8f0; + --clr-surface: #ffffff; + --clr-surface-hover: #f8fafc; + --transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1); } .dark-theme { - --clr-bcg: #282c35; - --clr-font: #fff; - --clr-primary: #ffa7c4; - --clr-grey: #cbd5e1; + --clr-bcg: #0f172a; + --clr-font: #f1f5f9; + --clr-primary: #818cf8; + --clr-primary-light: #a5b4fc; + --clr-primary-dark: #6366f1; + --clr-grey: #94a3b8; + --clr-grey-light: #cbd5e1; + --clr-border: #334155; + --clr-surface: #1e293b; + --clr-surface-hover: #334155; } *, @@ -25,14 +38,18 @@ Global Styles padding: 0; box-sizing: border-box; } + body { - font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, - Ubuntu, Cantarell, 'Open Sans', 'Helvetica Neue', sans-serif; + font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Inter', 'Roboto', + Oxygen, Ubuntu, Cantarell, 'Open Sans', 'Helvetica Neue', sans-serif; background: var(--clr-bcg); color: var(--clr-font); - line-height: 1.5; - font-size: 0.875rem; - transition: all 0.3s linear; + line-height: 1.6; + font-size: 1rem; + transition: var(--transition); + min-height: 100vh; + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; } /* @@ -42,52 +59,244 @@ Navbar */ .nav-center { width: 90vw; - max-width: 600px; + max-width: 800px; margin: 0 auto; display: flex; justify-content: space-between; align-items: center; - padding: 2rem 0; + padding: 2.5rem 0; + border-bottom: 1px solid var(--clr-border); + margin-bottom: 3rem; } + .nav-center span { - font-size: 2.5rem; - text-transform: capitalize; - letter-spacing: 2px; + font-size: clamp(1.75rem, 4vw, 2.5rem); + font-weight: 700; + letter-spacing: -0.025em; + background: linear-gradient(135deg, var(--clr-primary) 0%, var(--clr-primary-light) 100%); + -webkit-background-clip: text; + -webkit-text-fill-color: transparent; + background-clip: text; } -.btn { - background: var(--clr-primary); - color: var(--clr-bcg); - padding: 0.25rem 0.5rem; - border-radius: 5px; - border-color: transparent; + +/* Switch Toggle */ +.toggle-container { + display: flex; + align-items: center; + gap: 0.75rem; +} + +.toggle-label { + font-size: 0.9375rem; + font-weight: 500; + color: var(--clr-grey); text-transform: capitalize; - transition: all 0.3s linear; - font-weight: bold; - letter-spacing: 2px; + letter-spacing: 0.025em; + user-select: none; +} + +/* Modern Switch Toggle */ +.switch { + position: relative; + display: inline-block; + width: 60px; + height: 32px; +} + +.switch input { + opacity: 0; + width: 0; + height: 0; +} + +.slider { + position: absolute; cursor: pointer; + top: 0; + left: 0; + right: 0; + bottom: 0; + background-color: var(--clr-grey-light); + transition: var(--transition); + border-radius: 34px; + box-shadow: inset 0 2px 4px rgba(0, 0, 0, 0.1); +} + +.slider:before { + position: absolute; + content: ''; + height: 24px; + width: 24px; + left: 4px; + bottom: 4px; + background-color: white; + transition: var(--transition); + border-radius: 50%; + box-shadow: 0 2px 4px rgba(0, 0, 0, 0.2); } +input:checked + .slider { + background: linear-gradient(135deg, var(--clr-primary) 0%, var(--clr-primary-light) 100%); + box-shadow: 0 0 0 3px rgba(99, 102, 241, 0.1), inset 0 2px 4px rgba(0, 0, 0, 0.1); +} + +input:checked + .slider:before { + transform: translateX(28px); + box-shadow: 0 2px 6px rgba(0, 0, 0, 0.3); +} + +input:focus-visible + .slider { + outline: 3px solid var(--clr-primary); + outline-offset: 2px; +} + +.slider:active:before { + width: 28px; +} + +input:checked + .slider:active:before { + width: 28px; +} + +/* +=============== +Articles +=============== +*/ .articles { - padding: 5rem 0; + padding: 2rem 0 5rem; width: 90vw; - max-width: 600px; + max-width: 800px; margin: 0 auto; } + .post { - margin-bottom: 3rem; + margin-bottom: 4rem; + padding: 2rem; + background: var(--clr-surface); + border-radius: 1rem; + border: 1px solid var(--clr-border); + transition: var(--transition); +} + +.post:hover { + border-color: var(--clr-primary); + box-shadow: 0 4px 12px rgba(99, 102, 241, 0.1); + transform: translateY(-2px); +} + +.post:last-child { + margin-bottom: 0; } .post h2 { color: var(--clr-primary); text-transform: capitalize; - letter-spacing: 2px; - font-size: 1.75rem; + letter-spacing: -0.02em; + font-size: clamp(1.5rem, 3vw, 2rem); + font-weight: 700; + margin-bottom: 1rem; + line-height: 1.3; + transition: var(--transition); } + +.post:hover h2 { + color: var(--clr-primary-light); +} + .post-info { - margin-bottom: 0.75rem; - font-style: italic; + margin-bottom: 1.25rem; + font-style: normal; color: var(--clr-grey); + font-size: 0.9375rem; + display: flex; + align-items: center; + gap: 1rem; + flex-wrap: wrap; } + .post-info span { - margin-right: 0.5rem; + margin-right: 0; + display: inline-flex; + align-items: center; + gap: 0.5rem; + padding: 0.375rem 0.75rem; + background: var(--clr-surface-hover); + border-radius: 0.5rem; + font-weight: 500; +} + +.post-info span::before { + content: ''; + width: 4px; + height: 4px; + background: var(--clr-primary); + border-radius: 50%; + display: inline-block; +} + +.post p { + color: var(--clr-font); + font-size: 1.0625rem; + line-height: 1.8; + margin: 0; +} + +/* Responsive Design */ +@media screen and (max-width: 640px) { + .nav-center { + padding: 1.5rem 0; + margin-bottom: 2rem; + } + + .toggle-label { + display: none; + } + + .post { + padding: 1.5rem; + margin-bottom: 2.5rem; + } + + .articles { + padding: 1rem 0 3rem; + } +} + +/* Smooth page transitions */ +@keyframes fadeIn { + from { + opacity: 0; + transform: translateY(10px); + } + to { + opacity: 1; + transform: translateY(0); + } +} + +.post { + animation: fadeIn 0.5s ease-out backwards; +} + +.post:nth-child(1) { + animation-delay: 0.1s; +} + +.post:nth-child(2) { + animation-delay: 0.2s; +} + +.post:nth-child(3) { + animation-delay: 0.3s; +} + +.post:nth-child(4) { + animation-delay: 0.4s; +} + +/* Focus states for accessibility */ +*:focus-visible { + outline: 2px solid var(--clr-primary); + outline-offset: 2px; } From 4273d4bd398fd5cde9ccab8c0f9244f1dbcc7354 Mon Sep 17 00:00:00 2001 From: Praveen Date: Thu, 8 Jan 2026 23:16:14 +0530 Subject: [PATCH 4/4] Transformed animations for buttons and icons (navbar) --- 04-navbar/final/app.js | 128 ++++++++++-- 04-navbar/final/index.html | 102 ++++++++-- 04-navbar/final/styles.css | 397 ++++++++++++++++++++++++++++++++++--- 3 files changed, 568 insertions(+), 59 deletions(-) diff --git a/04-navbar/final/app.js b/04-navbar/final/app.js index 128fc4f64..4c84a8590 100644 --- a/04-navbar/final/app.js +++ b/04-navbar/final/app.js @@ -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", + }); + } + } + }); }); diff --git a/04-navbar/final/index.html b/04-navbar/final/index.html index 21758f36a..0c1e53ba0 100644 --- a/04-navbar/final/index.html +++ b/04-navbar/final/index.html @@ -3,71 +3,137 @@ - Navbar + Modern Navbar -