diff --git a/1-color-flipper/final/app.js b/01-color-flipper/final/app.js
similarity index 100%
rename from 1-color-flipper/final/app.js
rename to 01-color-flipper/final/app.js
diff --git a/1-color-flipper/final/hex.html b/01-color-flipper/final/hex.html
similarity index 100%
rename from 1-color-flipper/final/hex.html
rename to 01-color-flipper/final/hex.html
diff --git a/1-color-flipper/final/hex.js b/01-color-flipper/final/hex.js
similarity index 100%
rename from 1-color-flipper/final/hex.js
rename to 01-color-flipper/final/hex.js
diff --git a/1-color-flipper/final/index.html b/01-color-flipper/final/index.html
similarity index 100%
rename from 1-color-flipper/final/index.html
rename to 01-color-flipper/final/index.html
diff --git a/1-color-flipper/final/styles.css b/01-color-flipper/final/styles.css
similarity index 100%
rename from 1-color-flipper/final/styles.css
rename to 01-color-flipper/final/styles.css
diff --git a/1-color-flipper/setup/app.js b/01-color-flipper/setup/app.js
similarity index 100%
rename from 1-color-flipper/setup/app.js
rename to 01-color-flipper/setup/app.js
diff --git a/1-color-flipper/setup/hex.html b/01-color-flipper/setup/hex.html
similarity index 100%
rename from 1-color-flipper/setup/hex.html
rename to 01-color-flipper/setup/hex.html
diff --git a/1-color-flipper/setup/hex.js b/01-color-flipper/setup/hex.js
similarity index 100%
rename from 1-color-flipper/setup/hex.js
rename to 01-color-flipper/setup/hex.js
diff --git a/1-color-flipper/setup/index.html b/01-color-flipper/setup/index.html
similarity index 100%
rename from 1-color-flipper/setup/index.html
rename to 01-color-flipper/setup/index.html
diff --git a/1-color-flipper/setup/styles.css b/01-color-flipper/setup/styles.css
similarity index 100%
rename from 1-color-flipper/setup/styles.css
rename to 01-color-flipper/setup/styles.css
diff --git a/2-counter/final/app.js b/02-counter/final/app.js
similarity index 100%
rename from 2-counter/final/app.js
rename to 02-counter/final/app.js
diff --git a/2-counter/final/index.html b/02-counter/final/index.html
similarity index 100%
rename from 2-counter/final/index.html
rename to 02-counter/final/index.html
diff --git a/2-counter/final/styles.css b/02-counter/final/styles.css
similarity index 100%
rename from 2-counter/final/styles.css
rename to 02-counter/final/styles.css
diff --git a/16-ES6-slider/setup/app.js b/02-counter/setup/app.js
similarity index 100%
rename from 16-ES6-slider/setup/app.js
rename to 02-counter/setup/app.js
diff --git a/2-counter/setup/index.html b/02-counter/setup/index.html
similarity index 100%
rename from 2-counter/setup/index.html
rename to 02-counter/setup/index.html
diff --git a/2-counter/setup/styles.css b/02-counter/setup/styles.css
similarity index 100%
rename from 2-counter/setup/styles.css
rename to 02-counter/setup/styles.css
diff --git a/03-reviews/final/app.js b/03-reviews/final/app.js
new file mode 100644
index 000000000..b7aacba1f
--- /dev/null
+++ b/03-reviews/final/app.js
@@ -0,0 +1,84 @@
+// local reviews data
+const reviews = [
+ {
+ id: 1,
+ name: 'susan smith',
+ job: 'web developer',
+ img: 'https://www.course-api.com/images/people/person-1.jpeg',
+ text: "I'm baby meggings twee health goth +1. Bicycle rights tumeric chartreuse before they sold out chambray pop-up. Shaman humblebrag pickled coloring book salvia hoodie, cold-pressed four dollar toast everyday carry",
+ },
+ {
+ id: 2,
+ name: 'anna johnson',
+ job: 'web designer',
+ img: 'https://www.course-api.com/images/people/person-2.jpeg',
+ text: 'Helvetica artisan kinfolk thundercats lumbersexual blue bottle. Disrupt glossier gastropub deep v vice franzen hell of brooklyn twee enamel pin fashion axe.photo booth jean shorts artisan narwhal.',
+ },
+ {
+ id: 3,
+ name: 'peter jones',
+ job: 'intern',
+ img: 'https://www.course-api.com/images/people/person-4.jpeg',
+ text: 'Sriracha literally flexitarian irony, vape marfa unicorn. Glossier tattooed 8-bit, fixie waistcoat offal activated charcoal slow-carb marfa hell of pabst raclette post-ironic jianbing swag.',
+ },
+ {
+ id: 4,
+ name: 'bill anderson',
+ job: 'the boss',
+ img: 'https://www.course-api.com/images/people/person-3.jpeg',
+ text: 'Edison bulb put a bird on it humblebrag, marfa pok pok heirloom fashion axe cray stumptown venmo actually seitan. VHS farm-to-table schlitz, edison bulb pop-up 3 wolf moon tote bag street art shabby chic. ',
+ },
+];
+// select items
+const img = document.getElementById('person-img');
+const author = document.getElementById('author');
+const job = document.getElementById('job');
+const info = document.getElementById('info');
+
+const prevBtn = document.querySelector('.prev-btn');
+const nextBtn = document.querySelector('.next-btn');
+const randomBtn = document.querySelector('.random-btn');
+
+// set starting item
+let currentItem = 0;
+
+// load initial item
+window.addEventListener('DOMContentLoaded', function () {
+ const item = reviews[currentItem];
+ img.src = item.img;
+ author.textContent = item.name;
+ job.textContent = item.job;
+ info.textContent = item.text;
+});
+
+// show person based on item
+function showPerson(person) {
+ const item = reviews[person];
+ img.src = item.img;
+ author.textContent = item.name;
+ job.textContent = item.job;
+ info.textContent = item.text;
+}
+// show next person
+nextBtn.addEventListener('click', function () {
+ currentItem++;
+ if (currentItem > reviews.length - 1) {
+ currentItem = 0;
+ }
+ showPerson(currentItem);
+});
+// show prev person
+prevBtn.addEventListener('click', function () {
+ currentItem--;
+ if (currentItem < 0) {
+ currentItem = reviews.length - 1;
+ }
+ showPerson(currentItem);
+});
+// show random person
+randomBtn.addEventListener('click', function () {
+ console.log('hello');
+
+ currentItem = Math.floor(Math.random() * reviews.length);
+ showPerson(currentItem);
+});
diff --git a/3-reviews/final/index.html b/03-reviews/final/index.html
similarity index 100%
rename from 3-reviews/final/index.html
rename to 03-reviews/final/index.html
diff --git a/3-reviews/final/person-1.jpeg b/03-reviews/final/person-1.jpeg
similarity index 100%
rename from 3-reviews/final/person-1.jpeg
rename to 03-reviews/final/person-1.jpeg
diff --git a/3-reviews/final/styles.css b/03-reviews/final/styles.css
similarity index 100%
rename from 3-reviews/final/styles.css
rename to 03-reviews/final/styles.css
diff --git a/03-reviews/setup/app.js b/03-reviews/setup/app.js
new file mode 100644
index 000000000..b7aacba1f
--- /dev/null
+++ b/03-reviews/setup/app.js
@@ -0,0 +1,84 @@
+// local reviews data
+const reviews = [
+ {
+ id: 1,
+ name: 'susan smith',
+ job: 'web developer',
+ img: 'https://www.course-api.com/images/people/person-1.jpeg',
+ text: "I'm baby meggings twee health goth +1. Bicycle rights tumeric chartreuse before they sold out chambray pop-up. Shaman humblebrag pickled coloring book salvia hoodie, cold-pressed four dollar toast everyday carry",
+ },
+ {
+ id: 2,
+ name: 'anna johnson',
+ job: 'web designer',
+ img: 'https://www.course-api.com/images/people/person-2.jpeg',
+ text: 'Helvetica artisan kinfolk thundercats lumbersexual blue bottle. Disrupt glossier gastropub deep v vice franzen hell of brooklyn twee enamel pin fashion axe.photo booth jean shorts artisan narwhal.',
+ },
+ {
+ id: 3,
+ name: 'peter jones',
+ job: 'intern',
+ img: 'https://www.course-api.com/images/people/person-4.jpeg',
+ text: 'Sriracha literally flexitarian irony, vape marfa unicorn. Glossier tattooed 8-bit, fixie waistcoat offal activated charcoal slow-carb marfa hell of pabst raclette post-ironic jianbing swag.',
+ },
+ {
+ id: 4,
+ name: 'bill anderson',
+ job: 'the boss',
+ img: 'https://www.course-api.com/images/people/person-3.jpeg',
+ text: 'Edison bulb put a bird on it humblebrag, marfa pok pok heirloom fashion axe cray stumptown venmo actually seitan. VHS farm-to-table schlitz, edison bulb pop-up 3 wolf moon tote bag street art shabby chic. ',
+ },
+];
+// select items
+const img = document.getElementById('person-img');
+const author = document.getElementById('author');
+const job = document.getElementById('job');
+const info = document.getElementById('info');
+
+const prevBtn = document.querySelector('.prev-btn');
+const nextBtn = document.querySelector('.next-btn');
+const randomBtn = document.querySelector('.random-btn');
+
+// set starting item
+let currentItem = 0;
+
+// load initial item
+window.addEventListener('DOMContentLoaded', function () {
+ const item = reviews[currentItem];
+ img.src = item.img;
+ author.textContent = item.name;
+ job.textContent = item.job;
+ info.textContent = item.text;
+});
+
+// show person based on item
+function showPerson(person) {
+ const item = reviews[person];
+ img.src = item.img;
+ author.textContent = item.name;
+ job.textContent = item.job;
+ info.textContent = item.text;
+}
+// show next person
+nextBtn.addEventListener('click', function () {
+ currentItem++;
+ if (currentItem > reviews.length - 1) {
+ currentItem = 0;
+ }
+ showPerson(currentItem);
+});
+// show prev person
+prevBtn.addEventListener('click', function () {
+ currentItem--;
+ if (currentItem < 0) {
+ currentItem = reviews.length - 1;
+ }
+ showPerson(currentItem);
+});
+// show random person
+randomBtn.addEventListener('click', function () {
+ console.log('hello');
+
+ currentItem = Math.floor(Math.random() * reviews.length);
+ showPerson(currentItem);
+});
diff --git a/3-reviews/setup/index.html b/03-reviews/setup/index.html
similarity index 100%
rename from 3-reviews/setup/index.html
rename to 03-reviews/setup/index.html
diff --git a/3-reviews/setup/person-1.jpeg b/03-reviews/setup/person-1.jpeg
similarity index 100%
rename from 3-reviews/setup/person-1.jpeg
rename to 03-reviews/setup/person-1.jpeg
diff --git a/3-reviews/setup/styles.css b/03-reviews/setup/styles.css
similarity index 100%
rename from 3-reviews/setup/styles.css
rename to 03-reviews/setup/styles.css
diff --git a/4-navbar/final/app.js b/04-navbar/final/app.js
similarity index 100%
rename from 4-navbar/final/app.js
rename to 04-navbar/final/app.js
diff --git a/4-navbar/final/index.html b/04-navbar/final/index.html
similarity index 100%
rename from 4-navbar/final/index.html
rename to 04-navbar/final/index.html
diff --git a/4-navbar/final/logo.svg b/04-navbar/final/logo.svg
similarity index 100%
rename from 4-navbar/final/logo.svg
rename to 04-navbar/final/logo.svg
diff --git a/4-navbar/final/styles.css b/04-navbar/final/styles.css
similarity index 100%
rename from 4-navbar/final/styles.css
rename to 04-navbar/final/styles.css
diff --git a/4-navbar/final/utils.txt b/04-navbar/final/utils.txt
similarity index 100%
rename from 4-navbar/final/utils.txt
rename to 04-navbar/final/utils.txt
diff --git a/4-navbar/setup/app.js b/04-navbar/setup/app.js
similarity index 100%
rename from 4-navbar/setup/app.js
rename to 04-navbar/setup/app.js
diff --git a/4-navbar/setup/index.html b/04-navbar/setup/index.html
similarity index 100%
rename from 4-navbar/setup/index.html
rename to 04-navbar/setup/index.html
diff --git a/4-navbar/setup/logo.svg b/04-navbar/setup/logo.svg
similarity index 100%
rename from 4-navbar/setup/logo.svg
rename to 04-navbar/setup/logo.svg
diff --git a/4-navbar/setup/styles.css b/04-navbar/setup/styles.css
similarity index 100%
rename from 4-navbar/setup/styles.css
rename to 04-navbar/setup/styles.css
diff --git a/4-navbar/setup/utils.txt b/04-navbar/setup/utils.txt
similarity index 100%
rename from 4-navbar/setup/utils.txt
rename to 04-navbar/setup/utils.txt
diff --git a/5-sidebar/final/app.js b/05-sidebar/final/app.js
similarity index 100%
rename from 5-sidebar/final/app.js
rename to 05-sidebar/final/app.js
diff --git a/5-sidebar/final/index.html b/05-sidebar/final/index.html
similarity index 100%
rename from 5-sidebar/final/index.html
rename to 05-sidebar/final/index.html
diff --git a/5-sidebar/final/logo.svg b/05-sidebar/final/logo.svg
similarity index 100%
rename from 5-sidebar/final/logo.svg
rename to 05-sidebar/final/logo.svg
diff --git a/5-sidebar/final/styles.css b/05-sidebar/final/styles.css
similarity index 100%
rename from 5-sidebar/final/styles.css
rename to 05-sidebar/final/styles.css
diff --git a/5-sidebar/final/utils.txt b/05-sidebar/final/utils.txt
similarity index 100%
rename from 5-sidebar/final/utils.txt
rename to 05-sidebar/final/utils.txt
diff --git a/17-stripe-submenus/setup/app.js b/05-sidebar/setup/app.js
similarity index 100%
rename from 17-stripe-submenus/setup/app.js
rename to 05-sidebar/setup/app.js
diff --git a/5-sidebar/setup/index.html b/05-sidebar/setup/index.html
similarity index 100%
rename from 5-sidebar/setup/index.html
rename to 05-sidebar/setup/index.html
diff --git a/5-sidebar/setup/logo.svg b/05-sidebar/setup/logo.svg
similarity index 100%
rename from 5-sidebar/setup/logo.svg
rename to 05-sidebar/setup/logo.svg
diff --git a/5-sidebar/setup/styles.css b/05-sidebar/setup/styles.css
similarity index 95%
rename from 5-sidebar/setup/styles.css
rename to 05-sidebar/setup/styles.css
index 2971968ed..cc36f5499 100644
--- a/5-sidebar/setup/styles.css
+++ b/05-sidebar/setup/styles.css
@@ -3,7 +3,7 @@
Fonts
===============
*/
-@import url("https://fonts.googleapis.com/css?family=Open+Sans|Roboto:400,700&display=swap");
+@import url('https://fonts.googleapis.com/css?family=Open+Sans|Roboto:400,700&display=swap');
/*
===============
@@ -43,8 +43,8 @@ Variables
--clr-green-dark: hsl(125, 67%, 44%);
--clr-green-light: hsl(125, 71%, 66%);
--clr-black: #222;
- --ff-primary: "Roboto", sans-serif;
- --ff-secondary: "Open Sans", sans-serif;
+ --ff-primary: 'Roboto', sans-serif;
+ --ff-secondary: 'Open Sans', sans-serif;
--transition: all 0.3s linear;
--spacing: 0.1rem;
--radius: 0.25rem;
@@ -247,7 +247,7 @@ Sidebar
row-gap: 1rem;
box-shadow: var(--clr-red-dark);
transition: var(--transition);
- transform: translate(-100%);
+ /* transform: translate(-100%); */
}
.show-sidebar {
transform: translate(0);
diff --git a/5-sidebar/setup/utils.txt b/05-sidebar/setup/utils.txt
similarity index 100%
rename from 5-sidebar/setup/utils.txt
rename to 05-sidebar/setup/utils.txt
diff --git a/6-modal/final/app.js b/06-modal/final/app.js
similarity index 100%
rename from 6-modal/final/app.js
rename to 06-modal/final/app.js
diff --git a/6-modal/final/hero.jpeg b/06-modal/final/hero.jpeg
similarity index 100%
rename from 6-modal/final/hero.jpeg
rename to 06-modal/final/hero.jpeg
diff --git a/6-modal/final/index.html b/06-modal/final/index.html
similarity index 100%
rename from 6-modal/final/index.html
rename to 06-modal/final/index.html
diff --git a/6-modal/final/logo.svg b/06-modal/final/logo.svg
similarity index 100%
rename from 6-modal/final/logo.svg
rename to 06-modal/final/logo.svg
diff --git a/6-modal/final/styles.css b/06-modal/final/styles.css
similarity index 100%
rename from 6-modal/final/styles.css
rename to 06-modal/final/styles.css
diff --git a/6-modal/setup/app.js b/06-modal/setup/app.js
similarity index 100%
rename from 6-modal/setup/app.js
rename to 06-modal/setup/app.js
diff --git a/6-modal/setup/hero.jpeg b/06-modal/setup/hero.jpeg
similarity index 100%
rename from 6-modal/setup/hero.jpeg
rename to 06-modal/setup/hero.jpeg
diff --git a/6-modal/setup/index.html b/06-modal/setup/index.html
similarity index 100%
rename from 6-modal/setup/index.html
rename to 06-modal/setup/index.html
diff --git a/6-modal/setup/logo.svg b/06-modal/setup/logo.svg
similarity index 100%
rename from 6-modal/setup/logo.svg
rename to 06-modal/setup/logo.svg
diff --git a/6-modal/setup/styles.css b/06-modal/setup/styles.css
similarity index 100%
rename from 6-modal/setup/styles.css
rename to 06-modal/setup/styles.css
diff --git a/7-questions/final/app.js b/07-questions/final/app.js
similarity index 100%
rename from 7-questions/final/app.js
rename to 07-questions/final/app.js
diff --git a/7-questions/final/index.html b/07-questions/final/index.html
similarity index 100%
rename from 7-questions/final/index.html
rename to 07-questions/final/index.html
diff --git a/7-questions/final/logo.svg b/07-questions/final/logo.svg
similarity index 100%
rename from 7-questions/final/logo.svg
rename to 07-questions/final/logo.svg
diff --git a/7-questions/final/styles.css b/07-questions/final/styles.css
similarity index 100%
rename from 7-questions/final/styles.css
rename to 07-questions/final/styles.css
diff --git a/7-questions/setup/app.js b/07-questions/setup/app.js
similarity index 100%
rename from 7-questions/setup/app.js
rename to 07-questions/setup/app.js
diff --git a/7-questions/setup/index.html b/07-questions/setup/index.html
similarity index 100%
rename from 7-questions/setup/index.html
rename to 07-questions/setup/index.html
diff --git a/7-questions/setup/logo.svg b/07-questions/setup/logo.svg
similarity index 100%
rename from 7-questions/setup/logo.svg
rename to 07-questions/setup/logo.svg
diff --git a/7-questions/setup/styles.css b/07-questions/setup/styles.css
similarity index 100%
rename from 7-questions/setup/styles.css
rename to 07-questions/setup/styles.css
diff --git a/8-menu/final/1-basic.js b/08-menu/final/1-basic.js
similarity index 100%
rename from 8-menu/final/1-basic.js
rename to 08-menu/final/1-basic.js
diff --git a/8-menu/final/2-naive.txt b/08-menu/final/2-naive.txt
similarity index 100%
rename from 8-menu/final/2-naive.txt
rename to 08-menu/final/2-naive.txt
diff --git a/8-menu/final/app.js b/08-menu/final/app.js
similarity index 100%
rename from 8-menu/final/app.js
rename to 08-menu/final/app.js
diff --git a/8-menu/final/images/item-1.jpeg b/08-menu/final/images/item-1.jpeg
similarity index 100%
rename from 8-menu/final/images/item-1.jpeg
rename to 08-menu/final/images/item-1.jpeg
diff --git a/8-menu/final/images/item-10.jpeg b/08-menu/final/images/item-10.jpeg
similarity index 100%
rename from 8-menu/final/images/item-10.jpeg
rename to 08-menu/final/images/item-10.jpeg
diff --git a/8-menu/final/images/item-2.jpeg b/08-menu/final/images/item-2.jpeg
similarity index 100%
rename from 8-menu/final/images/item-2.jpeg
rename to 08-menu/final/images/item-2.jpeg
diff --git a/8-menu/final/images/item-3.jpeg b/08-menu/final/images/item-3.jpeg
similarity index 100%
rename from 8-menu/final/images/item-3.jpeg
rename to 08-menu/final/images/item-3.jpeg
diff --git a/8-menu/final/images/item-4.jpeg b/08-menu/final/images/item-4.jpeg
similarity index 100%
rename from 8-menu/final/images/item-4.jpeg
rename to 08-menu/final/images/item-4.jpeg
diff --git a/8-menu/final/images/item-5.jpeg b/08-menu/final/images/item-5.jpeg
similarity index 100%
rename from 8-menu/final/images/item-5.jpeg
rename to 08-menu/final/images/item-5.jpeg
diff --git a/8-menu/final/images/item-6.jpeg b/08-menu/final/images/item-6.jpeg
similarity index 100%
rename from 8-menu/final/images/item-6.jpeg
rename to 08-menu/final/images/item-6.jpeg
diff --git a/8-menu/final/images/item-7.jpeg b/08-menu/final/images/item-7.jpeg
similarity index 100%
rename from 8-menu/final/images/item-7.jpeg
rename to 08-menu/final/images/item-7.jpeg
diff --git a/8-menu/final/images/item-8.jpeg b/08-menu/final/images/item-8.jpeg
similarity index 100%
rename from 8-menu/final/images/item-8.jpeg
rename to 08-menu/final/images/item-8.jpeg
diff --git a/8-menu/final/images/item-9.jpeg b/08-menu/final/images/item-9.jpeg
similarity index 100%
rename from 8-menu/final/images/item-9.jpeg
rename to 08-menu/final/images/item-9.jpeg
diff --git a/8-menu/final/index.html b/08-menu/final/index.html
similarity index 100%
rename from 8-menu/final/index.html
rename to 08-menu/final/index.html
diff --git a/8-menu/final/logo.svg b/08-menu/final/logo.svg
similarity index 100%
rename from 8-menu/final/logo.svg
rename to 08-menu/final/logo.svg
diff --git a/8-menu/final/menu-item.jpeg b/08-menu/final/menu-item.jpeg
similarity index 100%
rename from 8-menu/final/menu-item.jpeg
rename to 08-menu/final/menu-item.jpeg
diff --git a/8-menu/final/styles.css b/08-menu/final/styles.css
similarity index 100%
rename from 8-menu/final/styles.css
rename to 08-menu/final/styles.css
diff --git a/8-menu/setup/1-basic.js b/08-menu/setup/1-basic.js
similarity index 100%
rename from 8-menu/setup/1-basic.js
rename to 08-menu/setup/1-basic.js
diff --git a/8-menu/setup/2-naive.txt b/08-menu/setup/2-naive.txt
similarity index 100%
rename from 8-menu/setup/2-naive.txt
rename to 08-menu/setup/2-naive.txt
diff --git a/8-menu/setup/app.js b/08-menu/setup/app.js
similarity index 100%
rename from 8-menu/setup/app.js
rename to 08-menu/setup/app.js
diff --git a/8-menu/setup/images/item-1.jpeg b/08-menu/setup/images/item-1.jpeg
similarity index 100%
rename from 8-menu/setup/images/item-1.jpeg
rename to 08-menu/setup/images/item-1.jpeg
diff --git a/8-menu/setup/images/item-10.jpeg b/08-menu/setup/images/item-10.jpeg
similarity index 100%
rename from 8-menu/setup/images/item-10.jpeg
rename to 08-menu/setup/images/item-10.jpeg
diff --git a/8-menu/setup/images/item-2.jpeg b/08-menu/setup/images/item-2.jpeg
similarity index 100%
rename from 8-menu/setup/images/item-2.jpeg
rename to 08-menu/setup/images/item-2.jpeg
diff --git a/8-menu/setup/images/item-3.jpeg b/08-menu/setup/images/item-3.jpeg
similarity index 100%
rename from 8-menu/setup/images/item-3.jpeg
rename to 08-menu/setup/images/item-3.jpeg
diff --git a/8-menu/setup/images/item-4.jpeg b/08-menu/setup/images/item-4.jpeg
similarity index 100%
rename from 8-menu/setup/images/item-4.jpeg
rename to 08-menu/setup/images/item-4.jpeg
diff --git a/8-menu/setup/images/item-5.jpeg b/08-menu/setup/images/item-5.jpeg
similarity index 100%
rename from 8-menu/setup/images/item-5.jpeg
rename to 08-menu/setup/images/item-5.jpeg
diff --git a/8-menu/setup/images/item-6.jpeg b/08-menu/setup/images/item-6.jpeg
similarity index 100%
rename from 8-menu/setup/images/item-6.jpeg
rename to 08-menu/setup/images/item-6.jpeg
diff --git a/8-menu/setup/images/item-7.jpeg b/08-menu/setup/images/item-7.jpeg
similarity index 100%
rename from 8-menu/setup/images/item-7.jpeg
rename to 08-menu/setup/images/item-7.jpeg
diff --git a/8-menu/setup/images/item-8.jpeg b/08-menu/setup/images/item-8.jpeg
similarity index 100%
rename from 8-menu/setup/images/item-8.jpeg
rename to 08-menu/setup/images/item-8.jpeg
diff --git a/8-menu/setup/images/item-9.jpeg b/08-menu/setup/images/item-9.jpeg
similarity index 100%
rename from 8-menu/setup/images/item-9.jpeg
rename to 08-menu/setup/images/item-9.jpeg
diff --git a/8-menu/setup/index.html b/08-menu/setup/index.html
similarity index 100%
rename from 8-menu/setup/index.html
rename to 08-menu/setup/index.html
diff --git a/8-menu/setup/logo.svg b/08-menu/setup/logo.svg
similarity index 100%
rename from 8-menu/setup/logo.svg
rename to 08-menu/setup/logo.svg
diff --git a/8-menu/setup/menu-item.jpeg b/08-menu/setup/menu-item.jpeg
similarity index 100%
rename from 8-menu/setup/menu-item.jpeg
rename to 08-menu/setup/menu-item.jpeg
diff --git a/8-menu/setup/styles.css b/08-menu/setup/styles.css
similarity index 100%
rename from 8-menu/setup/styles.css
rename to 08-menu/setup/styles.css
diff --git a/9-video/final/app.js b/09-video/final/app.js
similarity index 100%
rename from 9-video/final/app.js
rename to 09-video/final/app.js
diff --git a/9-video/final/index.html b/09-video/final/index.html
similarity index 100%
rename from 9-video/final/index.html
rename to 09-video/final/index.html
diff --git a/9-video/final/logo.svg b/09-video/final/logo.svg
similarity index 100%
rename from 9-video/final/logo.svg
rename to 09-video/final/logo.svg
diff --git a/9-video/final/preloader.gif b/09-video/final/preloader.gif
similarity index 100%
rename from 9-video/final/preloader.gif
rename to 09-video/final/preloader.gif
diff --git a/9-video/final/styles.css b/09-video/final/styles.css
similarity index 100%
rename from 9-video/final/styles.css
rename to 09-video/final/styles.css
diff --git a/9-video/final/video.mp4 b/09-video/final/video.mp4
similarity index 100%
rename from 9-video/final/video.mp4
rename to 09-video/final/video.mp4
diff --git a/9-video/setup/app.js b/09-video/setup/app.js
similarity index 100%
rename from 9-video/setup/app.js
rename to 09-video/setup/app.js
diff --git a/9-video/setup/index.html b/09-video/setup/index.html
similarity index 100%
rename from 9-video/setup/index.html
rename to 09-video/setup/index.html
diff --git a/9-video/setup/logo.svg b/09-video/setup/logo.svg
similarity index 100%
rename from 9-video/setup/logo.svg
rename to 09-video/setup/logo.svg
diff --git a/9-video/setup/preloader.gif b/09-video/setup/preloader.gif
similarity index 100%
rename from 9-video/setup/preloader.gif
rename to 09-video/setup/preloader.gif
diff --git a/9-video/setup/styles.css b/09-video/setup/styles.css
similarity index 100%
rename from 9-video/setup/styles.css
rename to 09-video/setup/styles.css
diff --git a/9-video/setup/video.mp4 b/09-video/setup/video.mp4
similarity index 100%
rename from 9-video/setup/video.mp4
rename to 09-video/setup/video.mp4
diff --git a/16-ES6-slider/VIDEO-URL.md b/16-ES6-slider/VIDEO-URL.md
deleted file mode 100644
index 2e3b24f45..000000000
--- a/16-ES6-slider/VIDEO-URL.md
+++ /dev/null
@@ -1,4 +0,0 @@
-(JS Video)[https://youtu.be/V26mqoNncO4]
-
-
-(HTML&CSS Video) [https://youtu.be/1I19G1jSkvw]
diff --git a/16-ES6-slider/final/app.js b/16-ES6-slider/final/app.js
deleted file mode 100644
index b889bee9b..000000000
--- a/16-ES6-slider/final/app.js
+++ /dev/null
@@ -1,74 +0,0 @@
-import data from "./data.js";
-const container = document.querySelector(".slide-container");
-const nextBtn = document.querySelector(".next-btn");
-const prevBtn = document.querySelector(".prev-btn");
-// if length is 1 hide buttons
-if (data.length === 1) {
- nextBtn.style.display = "none";
- prevBtn.style.display = "none";
-}
-// if length is 2, add copies of slides
-let people = [...data];
-if (data.length === 2) {
- people = [...data, ...data];
-}
-container.innerHTML = people
- .map((person, slideIndex) => {
- const { img, name, job, text } = person;
- let position = "next";
- if (slideIndex === 0) {
- position = "active";
- }
- if (slideIndex === people.length - 1) {
- position = "last";
- }
- if (data.length <= 1) {
- position = "active";
- }
- return `
-
- ${name}
- ${job}
-
- ${text}
-
-
-
-
- `;
- })
- .join("");
-
-const startSlider = (type) => {
- // get all three slides active,last next
- const active = document.querySelector(".active");
- const last = document.querySelector(".last");
- let next = active.nextElementSibling;
- if (!next) {
- next = container.firstElementChild;
- }
- active.classList.remove(["active"]);
- last.classList.remove(["last"]);
- next.classList.remove(["next"]);
-
- if (type === "prev") {
- active.classList.add("next");
- last.classList.add("active");
- next = last.previousElementSibling;
- if (!next) {
- next = container.lastElementChild;
- }
- next.classList.remove(["next"]);
- next.classList.add("last");
- return;
- }
- active.classList.add("last");
- last.classList.add("next");
- next.classList.add("active");
-};
-nextBtn.addEventListener("click", () => {
- startSlider();
-});
-prevBtn.addEventListener("click", () => {
- startSlider("prev");
-});
diff --git a/16-counter/final/app-class.js b/16-counter/final/app-class.js
new file mode 100644
index 000000000..4149d7a45
--- /dev/null
+++ b/16-counter/final/app-class.js
@@ -0,0 +1,44 @@
+function getElement(selection) {
+ const element = document.querySelector(selection);
+ if (element) {
+ return element;
+ }
+ throw new Error(
+ `Please check "${selection}" selector, no such element exists`
+ );
+}
+
+class Counter {
+ constructor(element, value) {
+ this.counter = element;
+ this.value = value;
+ this.resetBtn = element.querySelector('.reset');
+ this.increaseBtn = element.querySelector('.increase');
+ this.decreaseBtn = element.querySelector('.decrease');
+ this.valueDOM = element.querySelector('.value');
+ this.valueDOM.textContent = this.value;
+ // bind this to all function
+ this.increase = this.increase.bind(this);
+ this.decrease = this.decrease.bind(this);
+ this.reset = this.reset.bind(this);
+
+ this.increaseBtn.addEventListener('click', this.increase);
+ this.decreaseBtn.addEventListener('click', this.decrease);
+ this.resetBtn.addEventListener('click', this.reset);
+ }
+ increase() {
+ this.value++;
+ this.valueDOM.textContent = this.value;
+ }
+ decrease() {
+ this.value--;
+ this.valueDOM.textContent = this.value;
+ }
+ reset() {
+ this.value = 0;
+ this.valueDOM.textContent = this.value;
+ }
+}
+
+const firstCounter = new Counter(getElement('.first-counter'), 100);
+const secondCounter = new Counter(getElement('.second-counter'), 200);
diff --git a/16-counter/final/app-proto.js b/16-counter/final/app-proto.js
new file mode 100644
index 000000000..bf0e256bf
--- /dev/null
+++ b/16-counter/final/app-proto.js
@@ -0,0 +1,43 @@
+function getElement(selection) {
+ const element = document.querySelector(selection);
+ if (element) {
+ return element;
+ }
+ throw new Error(
+ `Please check "${selection}" selector, no such element exists`
+ );
+}
+
+function Counter(element, value) {
+ this.counter = element;
+ this.value = value;
+ this.resetBtn = element.querySelector('.reset');
+ this.increaseBtn = element.querySelector('.increase');
+ this.decreaseBtn = element.querySelector('.decrease');
+ this.valueDOM = element.querySelector('.value');
+ this.valueDOM.textContent = this.value;
+ // bind this to all function
+ this.increase = this.increase.bind(this);
+ this.decrease = this.decrease.bind(this);
+ this.reset = this.reset.bind(this);
+
+ this.increaseBtn.addEventListener('click', this.increase);
+ this.decreaseBtn.addEventListener('click', this.decrease);
+ this.resetBtn.addEventListener('click', this.reset);
+}
+
+Counter.prototype.increase = function () {
+ this.value++;
+ this.valueDOM.textContent = this.value;
+};
+Counter.prototype.decrease = function () {
+ this.value--;
+ this.valueDOM.textContent = this.value;
+};
+Counter.prototype.reset = function () {
+ this.value = 0;
+ this.valueDOM.textContent = this.value;
+};
+
+const firstCounter = new Counter(getElement('.first-counter'), 100);
+const secondCounter = new Counter(getElement('.second-counter'), 200);
diff --git a/16-counter/final/index.html b/16-counter/final/index.html
new file mode 100644
index 000000000..9876ea47f
--- /dev/null
+++ b/16-counter/final/index.html
@@ -0,0 +1,32 @@
+
+
+
+
+
+ Counter
+
+
+
+
+
+
first counter
+
0
+
+ decrease
+ reset
+ increase
+
+
+
+
+
second counter
+
0
+
+ decrease
+ reset
+ increase
+
+
+
+
+
diff --git a/16-counter/final/styles.css b/16-counter/final/styles.css
new file mode 100644
index 000000000..072652eb1
--- /dev/null
+++ b/16-counter/final/styles.css
@@ -0,0 +1,193 @@
+/*
+===============
+Fonts
+===============
+*/
+@import url('https://fonts.googleapis.com/css?family=Open+Sans|Roboto:400,700&display=swap');
+
+/*
+===============
+Variables
+===============
+*/
+
+:root {
+ /* dark shades of primary color*/
+ --clr-primary-1: hsl(205, 86%, 17%);
+ --clr-primary-2: hsl(205, 77%, 27%);
+ --clr-primary-3: hsl(205, 72%, 37%);
+ --clr-primary-4: hsl(205, 63%, 48%);
+ /* primary/main color */
+ --clr-primary-5: hsl(205, 78%, 60%);
+ /* lighter shades of primary color */
+ --clr-primary-6: hsl(205, 89%, 70%);
+ --clr-primary-7: hsl(205, 90%, 76%);
+ --clr-primary-8: hsl(205, 86%, 81%);
+ --clr-primary-9: hsl(205, 90%, 88%);
+ --clr-primary-10: hsl(205, 100%, 96%);
+ /* darkest grey - used for headings */
+ --clr-grey-1: hsl(209, 61%, 16%);
+ --clr-grey-2: hsl(211, 39%, 23%);
+ --clr-grey-3: hsl(209, 34%, 30%);
+ --clr-grey-4: hsl(209, 28%, 39%);
+ /* grey used for paragraphs */
+ --clr-grey-5: hsl(210, 22%, 49%);
+ --clr-grey-6: hsl(209, 23%, 60%);
+ --clr-grey-7: hsl(211, 27%, 70%);
+ --clr-grey-8: hsl(210, 31%, 80%);
+ --clr-grey-9: hsl(212, 33%, 89%);
+ --clr-grey-10: hsl(210, 36%, 96%);
+ --clr-white: #fff;
+ --clr-red-dark: hsl(360, 67%, 44%);
+ --clr-red-light: hsl(360, 71%, 66%);
+ --clr-green-dark: hsl(125, 67%, 44%);
+ --clr-green-light: hsl(125, 71%, 66%);
+ --clr-black: #222;
+ --ff-primary: 'Roboto', sans-serif;
+ --ff-secondary: 'Open Sans', sans-serif;
+ --transition: all 0.3s linear;
+ --spacing: 0.1rem;
+ --radius: 0.25rem;
+ --light-shadow: 0 5px 15px rgba(0, 0, 0, 0.1);
+ --dark-shadow: 0 5px 15px rgba(0, 0, 0, 0.2);
+ --max-width: 1170px;
+ --fixed-width: 620px;
+}
+/*
+===============
+Global Styles
+===============
+*/
+
+*,
+::after,
+::before {
+ margin: 0;
+ padding: 0;
+ box-sizing: border-box;
+}
+body {
+ font-family: var(--ff-secondary);
+ background: var(--clr-grey-10);
+ color: var(--clr-grey-1);
+ line-height: 1.5;
+ font-size: 0.875rem;
+}
+ul {
+ list-style-type: none;
+}
+a {
+ text-decoration: none;
+}
+h1,
+h2,
+h3,
+h4 {
+ letter-spacing: var(--spacing);
+ text-transform: capitalize;
+ line-height: 1.25;
+ margin-bottom: 0.75rem;
+ font-family: var(--ff-primary);
+}
+h1 {
+ font-size: 3rem;
+}
+h2 {
+ font-size: 2rem;
+}
+h3 {
+ font-size: 1.25rem;
+}
+h4 {
+ font-size: 0.875rem;
+}
+p {
+ margin-bottom: 1.25rem;
+ color: var(--clr-grey-5);
+}
+@media screen and (min-width: 800px) {
+ h1 {
+ font-size: 4rem;
+ }
+ h2 {
+ font-size: 2.5rem;
+ }
+ h3 {
+ font-size: 1.75rem;
+ }
+ h4 {
+ font-size: 1rem;
+ }
+ body {
+ font-size: 1rem;
+ }
+ h1,
+ h2,
+ h3,
+ h4 {
+ line-height: 1;
+ }
+}
+/* global classes */
+
+/* section */
+.section {
+ padding: 5rem 0;
+}
+
+.section-center {
+ width: 90vw;
+ margin: 0 auto;
+ max-width: 1170px;
+}
+@media screen and (min-width: 992px) {
+ .section-center {
+ width: 95vw;
+ }
+}
+main {
+ min-height: 100vh;
+ display: grid;
+ place-items: center;
+}
+
+/*
+===============
+Counter
+===============
+*/
+
+.container {
+ text-align: center;
+ margin: 3rem auto;
+ background: var(--clr-white);
+ border-radius: var(--radius);
+ width: 90vw;
+ max-width: var(--fixed-width);
+ padding: 2rem;
+}
+.value {
+ font-size: 4rem;
+ font-weight: bold;
+ margin-bottom: 1.5rem;
+ display: inline-block;
+}
+.btn {
+ text-transform: uppercase;
+ background: transparent;
+ color: var(--clr-black);
+ padding: 0.375rem 0.75rem;
+ letter-spacing: var(--spacing);
+ display: inline-block;
+ transition: var(--transition);
+ font-size: 0.875rem;
+ border: 2px solid var(--clr-black);
+ cursor: pointer;
+ box-shadow: 0 1px 3px rgba(0, 0, 0, 0.2);
+ border-radius: var(--radius);
+ margin: 0.5rem;
+}
+.btn:hover {
+ color: var(--clr-white);
+ background: var(--clr-black);
+}
diff --git a/2-counter/setup/app.js b/16-counter/starter/app.js
similarity index 100%
rename from 2-counter/setup/app.js
rename to 16-counter/starter/app.js
diff --git a/16-counter/starter/index.html b/16-counter/starter/index.html
new file mode 100644
index 000000000..ba7a237db
--- /dev/null
+++ b/16-counter/starter/index.html
@@ -0,0 +1,13 @@
+
+
+
+
+
+ Counter Starter
+
+
+
+ OOP counter project starter
+
+
+
diff --git a/16-counter/starter/styles.css b/16-counter/starter/styles.css
new file mode 100644
index 000000000..072652eb1
--- /dev/null
+++ b/16-counter/starter/styles.css
@@ -0,0 +1,193 @@
+/*
+===============
+Fonts
+===============
+*/
+@import url('https://fonts.googleapis.com/css?family=Open+Sans|Roboto:400,700&display=swap');
+
+/*
+===============
+Variables
+===============
+*/
+
+:root {
+ /* dark shades of primary color*/
+ --clr-primary-1: hsl(205, 86%, 17%);
+ --clr-primary-2: hsl(205, 77%, 27%);
+ --clr-primary-3: hsl(205, 72%, 37%);
+ --clr-primary-4: hsl(205, 63%, 48%);
+ /* primary/main color */
+ --clr-primary-5: hsl(205, 78%, 60%);
+ /* lighter shades of primary color */
+ --clr-primary-6: hsl(205, 89%, 70%);
+ --clr-primary-7: hsl(205, 90%, 76%);
+ --clr-primary-8: hsl(205, 86%, 81%);
+ --clr-primary-9: hsl(205, 90%, 88%);
+ --clr-primary-10: hsl(205, 100%, 96%);
+ /* darkest grey - used for headings */
+ --clr-grey-1: hsl(209, 61%, 16%);
+ --clr-grey-2: hsl(211, 39%, 23%);
+ --clr-grey-3: hsl(209, 34%, 30%);
+ --clr-grey-4: hsl(209, 28%, 39%);
+ /* grey used for paragraphs */
+ --clr-grey-5: hsl(210, 22%, 49%);
+ --clr-grey-6: hsl(209, 23%, 60%);
+ --clr-grey-7: hsl(211, 27%, 70%);
+ --clr-grey-8: hsl(210, 31%, 80%);
+ --clr-grey-9: hsl(212, 33%, 89%);
+ --clr-grey-10: hsl(210, 36%, 96%);
+ --clr-white: #fff;
+ --clr-red-dark: hsl(360, 67%, 44%);
+ --clr-red-light: hsl(360, 71%, 66%);
+ --clr-green-dark: hsl(125, 67%, 44%);
+ --clr-green-light: hsl(125, 71%, 66%);
+ --clr-black: #222;
+ --ff-primary: 'Roboto', sans-serif;
+ --ff-secondary: 'Open Sans', sans-serif;
+ --transition: all 0.3s linear;
+ --spacing: 0.1rem;
+ --radius: 0.25rem;
+ --light-shadow: 0 5px 15px rgba(0, 0, 0, 0.1);
+ --dark-shadow: 0 5px 15px rgba(0, 0, 0, 0.2);
+ --max-width: 1170px;
+ --fixed-width: 620px;
+}
+/*
+===============
+Global Styles
+===============
+*/
+
+*,
+::after,
+::before {
+ margin: 0;
+ padding: 0;
+ box-sizing: border-box;
+}
+body {
+ font-family: var(--ff-secondary);
+ background: var(--clr-grey-10);
+ color: var(--clr-grey-1);
+ line-height: 1.5;
+ font-size: 0.875rem;
+}
+ul {
+ list-style-type: none;
+}
+a {
+ text-decoration: none;
+}
+h1,
+h2,
+h3,
+h4 {
+ letter-spacing: var(--spacing);
+ text-transform: capitalize;
+ line-height: 1.25;
+ margin-bottom: 0.75rem;
+ font-family: var(--ff-primary);
+}
+h1 {
+ font-size: 3rem;
+}
+h2 {
+ font-size: 2rem;
+}
+h3 {
+ font-size: 1.25rem;
+}
+h4 {
+ font-size: 0.875rem;
+}
+p {
+ margin-bottom: 1.25rem;
+ color: var(--clr-grey-5);
+}
+@media screen and (min-width: 800px) {
+ h1 {
+ font-size: 4rem;
+ }
+ h2 {
+ font-size: 2.5rem;
+ }
+ h3 {
+ font-size: 1.75rem;
+ }
+ h4 {
+ font-size: 1rem;
+ }
+ body {
+ font-size: 1rem;
+ }
+ h1,
+ h2,
+ h3,
+ h4 {
+ line-height: 1;
+ }
+}
+/* global classes */
+
+/* section */
+.section {
+ padding: 5rem 0;
+}
+
+.section-center {
+ width: 90vw;
+ margin: 0 auto;
+ max-width: 1170px;
+}
+@media screen and (min-width: 992px) {
+ .section-center {
+ width: 95vw;
+ }
+}
+main {
+ min-height: 100vh;
+ display: grid;
+ place-items: center;
+}
+
+/*
+===============
+Counter
+===============
+*/
+
+.container {
+ text-align: center;
+ margin: 3rem auto;
+ background: var(--clr-white);
+ border-radius: var(--radius);
+ width: 90vw;
+ max-width: var(--fixed-width);
+ padding: 2rem;
+}
+.value {
+ font-size: 4rem;
+ font-weight: bold;
+ margin-bottom: 1.5rem;
+ display: inline-block;
+}
+.btn {
+ text-transform: uppercase;
+ background: transparent;
+ color: var(--clr-black);
+ padding: 0.375rem 0.75rem;
+ letter-spacing: var(--spacing);
+ display: inline-block;
+ transition: var(--transition);
+ font-size: 0.875rem;
+ border: 2px solid var(--clr-black);
+ cursor: pointer;
+ box-shadow: 0 1px 3px rgba(0, 0, 0, 0.2);
+ border-radius: var(--radius);
+ margin: 0.5rem;
+}
+.btn:hover {
+ color: var(--clr-white);
+ background: var(--clr-black);
+}
diff --git a/17-gallery/final/app-class.js b/17-gallery/final/app-class.js
new file mode 100644
index 000000000..543252d71
--- /dev/null
+++ b/17-gallery/final/app-class.js
@@ -0,0 +1,98 @@
+function getElement(selection) {
+ const element = document.querySelector(selection);
+ if (element) {
+ return element;
+ }
+ throw new Error(
+ `Please check "${selection}" selector, no such element exists`
+ );
+}
+
+class Gallery {
+ constructor(element) {
+ this.container = element;
+ this.list = [...element.querySelectorAll('.img')];
+ // target
+ this.modal = getElement('.modal');
+ this.modalImg = getElement('.main-img');
+ this.imageName = getElement('.image-name');
+ this.modalImages = getElement('.modal-images');
+ this.closeBtn = getElement('.close-btn');
+ this.nextBtn = getElement('.next-btn');
+ this.prevBtn = getElement('.prev-btn');
+ // self ref
+ // let self = this;
+ // bind functions
+ // this.openModal = this.openModal.bind(this);
+ this.closeModal = this.closeModal.bind(this);
+ this.nextImage = this.nextImage.bind(this);
+ this.prevImage = this.prevImage.bind(this);
+ this.chooseImage = this.chooseImage.bind(this);
+ // container event
+ this.container.addEventListener(
+ 'click',
+ function (e) {
+ // self.openModal();
+ if (e.target.classList.contains('img')) {
+ this.openModal(e.target, this.list);
+ }
+ }.bind(this)
+ );
+ }
+ openModal(selectedImage, list) {
+ this.setMainImage(selectedImage);
+ this.modalImages.innerHTML = list
+ .map(function (image) {
+ return ` `;
+ })
+ .join('');
+ this.modal.classList.add('open');
+ this.closeBtn.addEventListener('click', this.closeModal);
+ this.nextBtn.addEventListener('click', this.nextImage);
+ this.prevBtn.addEventListener('click', this.prevImage);
+ this.modalImages.addEventListener('click', this.chooseImage);
+ }
+
+ setMainImage(selectedImage) {
+ this.modalImg.src = selectedImage.src;
+ this.imageName.textContent = selectedImage.title;
+ }
+
+ closeModal() {
+ this.modal.classList.remove('open');
+ this.closeBtn.removeEventListener('click', this.closeModal);
+ this.nextBtn.removeEventListener('click', this.nextImage);
+ this.prevBtn.removeEventListener('click', this.prevImage);
+ this.modalImages.removeEventListener('click', this.chooseImage);
+ }
+ nextImage() {
+ const selected = this.modalImages.querySelector('.selected');
+ const next =
+ selected.nextElementSibling || this.modalImages.firstElementChild;
+ selected.classList.remove('selected');
+ next.classList.add('selected');
+ this.setMainImage(next);
+ }
+ prevImage() {
+ const selected = this.modalImages.querySelector('.selected');
+ const prev =
+ selected.previousElementSibling || this.modalImages.lastElementChild;
+ selected.classList.remove('selected');
+ prev.classList.add('selected');
+ this.setMainImage(prev);
+ }
+ chooseImage(e) {
+ if (e.target.classList.contains('modal-img')) {
+ const selected = this.modalImages.querySelector('.selected');
+ selected.classList.remove('selected');
+
+ this.setMainImage(e.target);
+ e.target.classList.add('selected');
+ }
+ }
+}
+
+const nature = new Gallery(getElement('.nature'));
+const city = new Gallery(getElement('.city'));
diff --git a/17-gallery/final/app.js b/17-gallery/final/app.js
new file mode 100644
index 000000000..9260baba2
--- /dev/null
+++ b/17-gallery/final/app.js
@@ -0,0 +1,96 @@
+function getElement(selection) {
+ const element = document.querySelector(selection);
+ if (element) {
+ return element;
+ }
+ throw new Error(
+ `Please check "${selection}" selector, no such element exists`
+ );
+}
+
+function Gallery(element) {
+ this.container = element;
+ this.list = [...element.querySelectorAll('.img')];
+ // target
+ this.modal = getElement('.modal');
+ this.modalImg = getElement('.main-img');
+ this.imageName = getElement('.image-name');
+ this.modalImages = getElement('.modal-images');
+ this.closeBtn = getElement('.close-btn');
+ this.nextBtn = getElement('.next-btn');
+ this.prevBtn = getElement('.prev-btn');
+ // self ref
+ // let self = this;
+ // bind functions
+ // this.openModal = this.openModal.bind(this);
+ this.closeModal = this.closeModal.bind(this);
+ this.nextImage = this.nextImage.bind(this);
+ this.prevImage = this.prevImage.bind(this);
+ this.chooseImage = this.chooseImage.bind(this);
+ // container event
+ this.container.addEventListener(
+ 'click',
+ function (e) {
+ // self.openModal();
+ if (e.target.classList.contains('img')) {
+ this.openModal(e.target, this.list);
+ }
+ }.bind(this)
+ );
+}
+
+Gallery.prototype.openModal = function (selectedImage, list) {
+ this.setMainImage(selectedImage);
+ this.modalImages.innerHTML = list
+ .map(function (image) {
+ return ` `;
+ })
+ .join('');
+ this.modal.classList.add('open');
+ this.closeBtn.addEventListener('click', this.closeModal);
+ this.nextBtn.addEventListener('click', this.nextImage);
+ this.prevBtn.addEventListener('click', this.prevImage);
+ this.modalImages.addEventListener('click', this.chooseImage);
+};
+
+Gallery.prototype.setMainImage = function (selectedImage) {
+ this.modalImg.src = selectedImage.src;
+ this.imageName.textContent = selectedImage.title;
+};
+
+Gallery.prototype.closeModal = function () {
+ this.modal.classList.remove('open');
+ this.closeBtn.removeEventListener('click', this.closeModal);
+ this.nextBtn.removeEventListener('click', this.nextImage);
+ this.prevBtn.removeEventListener('click', this.prevImage);
+ this.modalImages.removeEventListener('click', this.chooseImage);
+};
+Gallery.prototype.nextImage = function () {
+ const selected = this.modalImages.querySelector('.selected');
+ const next =
+ selected.nextElementSibling || this.modalImages.firstElementChild;
+ selected.classList.remove('selected');
+ next.classList.add('selected');
+ this.setMainImage(next);
+};
+Gallery.prototype.prevImage = function () {
+ const selected = this.modalImages.querySelector('.selected');
+ const prev =
+ selected.previousElementSibling || this.modalImages.lastElementChild;
+ selected.classList.remove('selected');
+ prev.classList.add('selected');
+ this.setMainImage(prev);
+};
+Gallery.prototype.chooseImage = function (e) {
+ if (e.target.classList.contains('modal-img')) {
+ const selected = this.modalImages.querySelector('.selected');
+ selected.classList.remove('selected');
+
+ this.setMainImage(e.target);
+ e.target.classList.add('selected');
+ }
+};
+const nature = new Gallery(getElement('.nature'));
+const city = new Gallery(getElement('.city'));
diff --git a/17-gallery/final/images/city-1.jpeg b/17-gallery/final/images/city-1.jpeg
new file mode 100644
index 000000000..b11412922
Binary files /dev/null and b/17-gallery/final/images/city-1.jpeg differ
diff --git a/17-gallery/final/images/city-2.jpeg b/17-gallery/final/images/city-2.jpeg
new file mode 100644
index 000000000..238853350
Binary files /dev/null and b/17-gallery/final/images/city-2.jpeg differ
diff --git a/17-gallery/final/images/city-3.jpeg b/17-gallery/final/images/city-3.jpeg
new file mode 100644
index 000000000..cb3f547d7
Binary files /dev/null and b/17-gallery/final/images/city-3.jpeg differ
diff --git a/17-gallery/final/images/city-4.jpeg b/17-gallery/final/images/city-4.jpeg
new file mode 100644
index 000000000..33252b006
Binary files /dev/null and b/17-gallery/final/images/city-4.jpeg differ
diff --git a/17-gallery/final/images/city-5.jpeg b/17-gallery/final/images/city-5.jpeg
new file mode 100644
index 000000000..10faa1fec
Binary files /dev/null and b/17-gallery/final/images/city-5.jpeg differ
diff --git a/17-gallery/final/images/nature-1.jpeg b/17-gallery/final/images/nature-1.jpeg
new file mode 100644
index 000000000..ce22c7777
Binary files /dev/null and b/17-gallery/final/images/nature-1.jpeg differ
diff --git a/17-gallery/final/images/nature-2.jpeg b/17-gallery/final/images/nature-2.jpeg
new file mode 100644
index 000000000..dbaf2c3ae
Binary files /dev/null and b/17-gallery/final/images/nature-2.jpeg differ
diff --git a/17-gallery/final/images/nature-3.jpeg b/17-gallery/final/images/nature-3.jpeg
new file mode 100644
index 000000000..4d0133d0b
Binary files /dev/null and b/17-gallery/final/images/nature-3.jpeg differ
diff --git a/17-gallery/final/index.html b/17-gallery/final/index.html
new file mode 100644
index 000000000..f599d6360
--- /dev/null
+++ b/17-gallery/final/index.html
@@ -0,0 +1,115 @@
+
+
+
+
+
+ Gallery
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
image name
+
+
+
+
+
+
+
+
+
diff --git a/17-gallery/final/styles.css b/17-gallery/final/styles.css
new file mode 100644
index 000000000..7abcc9795
--- /dev/null
+++ b/17-gallery/final/styles.css
@@ -0,0 +1,268 @@
+/*
+===============
+Fonts
+===============
+*/
+@import url('https://fonts.googleapis.com/css?family=Open+Sans|Roboto:400,700&display=swap');
+
+/*
+===============
+Variables
+===============
+*/
+
+:root {
+ /* dark shades of primary color*/
+ --clr-primary-1: hsl(205, 86%, 17%);
+ --clr-primary-2: hsl(205, 77%, 27%);
+ --clr-primary-3: hsl(205, 72%, 37%);
+ --clr-primary-4: hsl(205, 63%, 48%);
+ /* primary/main color */
+ --clr-primary-5: hsl(205, 78%, 60%);
+ /* lighter shades of primary color */
+ --clr-primary-6: hsl(205, 89%, 70%);
+ --clr-primary-7: hsl(205, 90%, 76%);
+ --clr-primary-8: hsl(205, 86%, 81%);
+ --clr-primary-9: hsl(205, 90%, 88%);
+ --clr-primary-10: hsl(205, 100%, 96%);
+ /* darkest grey - used for headings */
+ --clr-grey-1: hsl(209, 61%, 16%);
+ --clr-grey-2: hsl(211, 39%, 23%);
+ --clr-grey-3: hsl(209, 34%, 30%);
+ --clr-grey-4: hsl(209, 28%, 39%);
+ /* grey used for paragraphs */
+ --clr-grey-5: hsl(210, 22%, 49%);
+ --clr-grey-6: hsl(209, 23%, 60%);
+ --clr-grey-7: hsl(211, 27%, 70%);
+ --clr-grey-8: hsl(210, 31%, 80%);
+ --clr-grey-9: hsl(212, 33%, 89%);
+ --clr-grey-10: hsl(210, 36%, 96%);
+ --clr-white: #fff;
+ --clr-red-dark: hsl(360, 67%, 44%);
+ --clr-red-light: hsl(360, 71%, 66%);
+ --clr-green-dark: hsl(125, 67%, 44%);
+ --clr-green-light: hsl(125, 71%, 66%);
+ --clr-black: #222;
+ --ff-primary: 'Roboto', sans-serif;
+ --ff-secondary: 'Open Sans', sans-serif;
+ --transition: all 0.3s linear;
+ --spacing: 0.1rem;
+ --radius: 0.25rem;
+ --light-shadow: 0 5px 15px rgba(0, 0, 0, 0.1);
+ --dark-shadow: 0 5px 15px rgba(0, 0, 0, 0.4);
+ --max-width: 1170px;
+ --fixed-width: 620px;
+}
+/*
+===============
+Global Styles
+===============
+*/
+
+*,
+::after,
+::before {
+ margin: 0;
+ padding: 0;
+ box-sizing: border-box;
+}
+body {
+ font-family: var(--ff-secondary);
+ background: var(--clr-white);
+ color: var(--clr-grey-1);
+ line-height: 1.5;
+ font-size: 0.875rem;
+}
+ul {
+ list-style-type: none;
+}
+a {
+ text-decoration: none;
+}
+h1,
+h2,
+h3,
+h4 {
+ letter-spacing: var(--spacing);
+ text-transform: capitalize;
+ line-height: 1.25;
+ margin-bottom: 0.75rem;
+ font-family: var(--ff-primary);
+}
+h1 {
+ font-size: 3rem;
+}
+h2 {
+ font-size: 2rem;
+}
+h3 {
+ font-size: 1.25rem;
+}
+h4 {
+ font-size: 0.875rem;
+}
+p {
+ margin-bottom: 1.25rem;
+ color: var(--clr-grey-5);
+}
+@media screen and (min-width: 800px) {
+ h1 {
+ font-size: 4rem;
+ }
+ h2 {
+ font-size: 2.5rem;
+ }
+ h3 {
+ font-size: 1.75rem;
+ }
+ h4 {
+ font-size: 1rem;
+ }
+ body {
+ font-size: 1rem;
+ }
+ h1,
+ h2,
+ h3,
+ h4 {
+ line-height: 1;
+ }
+}
+/* global classes */
+
+/* section */
+.section {
+ padding: 5rem 0;
+ width: 90vw;
+ margin: 0 auto;
+ max-width: 1170px;
+ display: grid;
+ gap: 2rem;
+}
+@media screen and (min-width: 768px) {
+ .section {
+ display: grid;
+ grid-template-columns: 1fr 1fr;
+ }
+}
+@media screen and (min-width: 992px) {
+ .section {
+ grid-template-columns: 1fr 1fr 1fr;
+ width: 95vw;
+ }
+ .prev-btn {
+ left: -4.5rem;
+ }
+ .next-btn {
+ right: -4.5rem;
+ }
+}
+.img {
+ width: 100%;
+ height: 100%;
+ object-fit: cover;
+ border-radius: var(--radius);
+ cursor: pointer;
+ transition: var(--transition);
+ box-shadow: var(--light-shadow);
+ height: 15rem;
+}
+.img:hover {
+ box-shadow: var(--dark-shadow);
+}
+
+/*
+===============
+Modal
+===============
+*/
+
+.modal {
+ position: fixed;
+ z-index: -1;
+ left: 0;
+ top: 0;
+ width: 100%;
+ height: 100%;
+ background: rgb(0, 0, 0);
+ transition: var(--transition);
+ opacity: 0;
+ min-height: 100vh;
+ overflow: scroll;
+}
+.modal.open {
+ opacity: 1;
+ z-index: 999;
+}
+.close-btn {
+ position: absolute;
+ top: 1rem;
+ right: 1rem;
+ font-size: 3rem;
+ color: var(--clr-white);
+ background: transparent;
+ border-color: transparent;
+ cursor: pointer;
+}
+.next-btn,
+.prev-btn {
+ position: absolute;
+ top: 10rem;
+ transform: translateY(-50%);
+ background: transparent;
+ font-size: 2rem;
+ color: var(--clr-white);
+ border-color: transparent;
+ cursor: pointer;
+}
+.prev-btn {
+ left: -2.5rem;
+}
+.next-btn {
+ right: -2.5rem;
+}
+@media screen and (min-width: 768px) {
+ .prev-btn {
+ left: -4rem;
+ }
+ .next-btn {
+ right: -4rem;
+ }
+}
+
+.modal-content {
+ width: 80%;
+ max-width: var(--max-width);
+ margin: 0 auto;
+ margin-top: 10rem;
+ position: relative;
+}
+.main-img {
+ width: 100%;
+ height: 20rem;
+ object-fit: cover;
+ border-radius: var(--radius);
+}
+.image-name {
+ text-align: center;
+ margin: 1rem 0 3rem;
+ color: var(--clr-white);
+}
+.modal-images {
+ display: grid;
+ grid-template-columns: repeat(5, 1fr);
+}
+.modal-img {
+ width: 100%;
+ height: 5rem;
+ object-fit: cover;
+ opacity: 0.5;
+ transition: var(--transition);
+ cursor: pointer;
+}
+.modal-img:hover {
+ opacity: 1;
+}
+.modal-img.selected {
+ opacity: 1;
+}
diff --git a/17-gallery/starter/app.js b/17-gallery/starter/app.js
new file mode 100644
index 000000000..dd302352f
--- /dev/null
+++ b/17-gallery/starter/app.js
@@ -0,0 +1,9 @@
+function getElement(selection) {
+ const element = document.querySelector(selection);
+ if (element) {
+ return element;
+ }
+ throw new Error(
+ `Please check "${selection}" selector, no such element exists`
+ );
+}
diff --git a/17-gallery/starter/images/city-1.jpeg b/17-gallery/starter/images/city-1.jpeg
new file mode 100644
index 000000000..b11412922
Binary files /dev/null and b/17-gallery/starter/images/city-1.jpeg differ
diff --git a/17-gallery/starter/images/city-2.jpeg b/17-gallery/starter/images/city-2.jpeg
new file mode 100644
index 000000000..238853350
Binary files /dev/null and b/17-gallery/starter/images/city-2.jpeg differ
diff --git a/17-gallery/starter/images/city-3.jpeg b/17-gallery/starter/images/city-3.jpeg
new file mode 100644
index 000000000..cb3f547d7
Binary files /dev/null and b/17-gallery/starter/images/city-3.jpeg differ
diff --git a/17-gallery/starter/images/city-4.jpeg b/17-gallery/starter/images/city-4.jpeg
new file mode 100644
index 000000000..33252b006
Binary files /dev/null and b/17-gallery/starter/images/city-4.jpeg differ
diff --git a/17-gallery/starter/images/city-5.jpeg b/17-gallery/starter/images/city-5.jpeg
new file mode 100644
index 000000000..10faa1fec
Binary files /dev/null and b/17-gallery/starter/images/city-5.jpeg differ
diff --git a/17-gallery/starter/images/nature-1.jpeg b/17-gallery/starter/images/nature-1.jpeg
new file mode 100644
index 000000000..ce22c7777
Binary files /dev/null and b/17-gallery/starter/images/nature-1.jpeg differ
diff --git a/17-gallery/starter/images/nature-2.jpeg b/17-gallery/starter/images/nature-2.jpeg
new file mode 100644
index 000000000..dbaf2c3ae
Binary files /dev/null and b/17-gallery/starter/images/nature-2.jpeg differ
diff --git a/17-gallery/starter/images/nature-3.jpeg b/17-gallery/starter/images/nature-3.jpeg
new file mode 100644
index 000000000..4d0133d0b
Binary files /dev/null and b/17-gallery/starter/images/nature-3.jpeg differ
diff --git a/17-gallery/starter/index.html b/17-gallery/starter/index.html
new file mode 100644
index 000000000..b17887809
--- /dev/null
+++ b/17-gallery/starter/index.html
@@ -0,0 +1,19 @@
+
+
+
+
+
+ Gallery Starter
+
+
+
+
+
+
+ gallery project starter
+
+
+
diff --git a/17-gallery/starter/styles.css b/17-gallery/starter/styles.css
new file mode 100644
index 000000000..7abcc9795
--- /dev/null
+++ b/17-gallery/starter/styles.css
@@ -0,0 +1,268 @@
+/*
+===============
+Fonts
+===============
+*/
+@import url('https://fonts.googleapis.com/css?family=Open+Sans|Roboto:400,700&display=swap');
+
+/*
+===============
+Variables
+===============
+*/
+
+:root {
+ /* dark shades of primary color*/
+ --clr-primary-1: hsl(205, 86%, 17%);
+ --clr-primary-2: hsl(205, 77%, 27%);
+ --clr-primary-3: hsl(205, 72%, 37%);
+ --clr-primary-4: hsl(205, 63%, 48%);
+ /* primary/main color */
+ --clr-primary-5: hsl(205, 78%, 60%);
+ /* lighter shades of primary color */
+ --clr-primary-6: hsl(205, 89%, 70%);
+ --clr-primary-7: hsl(205, 90%, 76%);
+ --clr-primary-8: hsl(205, 86%, 81%);
+ --clr-primary-9: hsl(205, 90%, 88%);
+ --clr-primary-10: hsl(205, 100%, 96%);
+ /* darkest grey - used for headings */
+ --clr-grey-1: hsl(209, 61%, 16%);
+ --clr-grey-2: hsl(211, 39%, 23%);
+ --clr-grey-3: hsl(209, 34%, 30%);
+ --clr-grey-4: hsl(209, 28%, 39%);
+ /* grey used for paragraphs */
+ --clr-grey-5: hsl(210, 22%, 49%);
+ --clr-grey-6: hsl(209, 23%, 60%);
+ --clr-grey-7: hsl(211, 27%, 70%);
+ --clr-grey-8: hsl(210, 31%, 80%);
+ --clr-grey-9: hsl(212, 33%, 89%);
+ --clr-grey-10: hsl(210, 36%, 96%);
+ --clr-white: #fff;
+ --clr-red-dark: hsl(360, 67%, 44%);
+ --clr-red-light: hsl(360, 71%, 66%);
+ --clr-green-dark: hsl(125, 67%, 44%);
+ --clr-green-light: hsl(125, 71%, 66%);
+ --clr-black: #222;
+ --ff-primary: 'Roboto', sans-serif;
+ --ff-secondary: 'Open Sans', sans-serif;
+ --transition: all 0.3s linear;
+ --spacing: 0.1rem;
+ --radius: 0.25rem;
+ --light-shadow: 0 5px 15px rgba(0, 0, 0, 0.1);
+ --dark-shadow: 0 5px 15px rgba(0, 0, 0, 0.4);
+ --max-width: 1170px;
+ --fixed-width: 620px;
+}
+/*
+===============
+Global Styles
+===============
+*/
+
+*,
+::after,
+::before {
+ margin: 0;
+ padding: 0;
+ box-sizing: border-box;
+}
+body {
+ font-family: var(--ff-secondary);
+ background: var(--clr-white);
+ color: var(--clr-grey-1);
+ line-height: 1.5;
+ font-size: 0.875rem;
+}
+ul {
+ list-style-type: none;
+}
+a {
+ text-decoration: none;
+}
+h1,
+h2,
+h3,
+h4 {
+ letter-spacing: var(--spacing);
+ text-transform: capitalize;
+ line-height: 1.25;
+ margin-bottom: 0.75rem;
+ font-family: var(--ff-primary);
+}
+h1 {
+ font-size: 3rem;
+}
+h2 {
+ font-size: 2rem;
+}
+h3 {
+ font-size: 1.25rem;
+}
+h4 {
+ font-size: 0.875rem;
+}
+p {
+ margin-bottom: 1.25rem;
+ color: var(--clr-grey-5);
+}
+@media screen and (min-width: 800px) {
+ h1 {
+ font-size: 4rem;
+ }
+ h2 {
+ font-size: 2.5rem;
+ }
+ h3 {
+ font-size: 1.75rem;
+ }
+ h4 {
+ font-size: 1rem;
+ }
+ body {
+ font-size: 1rem;
+ }
+ h1,
+ h2,
+ h3,
+ h4 {
+ line-height: 1;
+ }
+}
+/* global classes */
+
+/* section */
+.section {
+ padding: 5rem 0;
+ width: 90vw;
+ margin: 0 auto;
+ max-width: 1170px;
+ display: grid;
+ gap: 2rem;
+}
+@media screen and (min-width: 768px) {
+ .section {
+ display: grid;
+ grid-template-columns: 1fr 1fr;
+ }
+}
+@media screen and (min-width: 992px) {
+ .section {
+ grid-template-columns: 1fr 1fr 1fr;
+ width: 95vw;
+ }
+ .prev-btn {
+ left: -4.5rem;
+ }
+ .next-btn {
+ right: -4.5rem;
+ }
+}
+.img {
+ width: 100%;
+ height: 100%;
+ object-fit: cover;
+ border-radius: var(--radius);
+ cursor: pointer;
+ transition: var(--transition);
+ box-shadow: var(--light-shadow);
+ height: 15rem;
+}
+.img:hover {
+ box-shadow: var(--dark-shadow);
+}
+
+/*
+===============
+Modal
+===============
+*/
+
+.modal {
+ position: fixed;
+ z-index: -1;
+ left: 0;
+ top: 0;
+ width: 100%;
+ height: 100%;
+ background: rgb(0, 0, 0);
+ transition: var(--transition);
+ opacity: 0;
+ min-height: 100vh;
+ overflow: scroll;
+}
+.modal.open {
+ opacity: 1;
+ z-index: 999;
+}
+.close-btn {
+ position: absolute;
+ top: 1rem;
+ right: 1rem;
+ font-size: 3rem;
+ color: var(--clr-white);
+ background: transparent;
+ border-color: transparent;
+ cursor: pointer;
+}
+.next-btn,
+.prev-btn {
+ position: absolute;
+ top: 10rem;
+ transform: translateY(-50%);
+ background: transparent;
+ font-size: 2rem;
+ color: var(--clr-white);
+ border-color: transparent;
+ cursor: pointer;
+}
+.prev-btn {
+ left: -2.5rem;
+}
+.next-btn {
+ right: -2.5rem;
+}
+@media screen and (min-width: 768px) {
+ .prev-btn {
+ left: -4rem;
+ }
+ .next-btn {
+ right: -4rem;
+ }
+}
+
+.modal-content {
+ width: 80%;
+ max-width: var(--max-width);
+ margin: 0 auto;
+ margin-top: 10rem;
+ position: relative;
+}
+.main-img {
+ width: 100%;
+ height: 20rem;
+ object-fit: cover;
+ border-radius: var(--radius);
+}
+.image-name {
+ text-align: center;
+ margin: 1rem 0 3rem;
+ color: var(--clr-white);
+}
+.modal-images {
+ display: grid;
+ grid-template-columns: repeat(5, 1fr);
+}
+.modal-img {
+ width: 100%;
+ height: 5rem;
+ object-fit: cover;
+ opacity: 0.5;
+ transition: var(--transition);
+ cursor: pointer;
+}
+.modal-img:hover {
+ opacity: 1;
+}
+.modal-img.selected {
+ opacity: 1;
+}
diff --git a/17-stripe-submenus/VIDEO-URL.md b/17-stripe-submenus/VIDEO-URL.md
deleted file mode 100644
index 0847e059e..000000000
--- a/17-stripe-submenus/VIDEO-URL.md
+++ /dev/null
@@ -1,2 +0,0 @@
-[JS Video](https://youtu.be/ypH_XA7PYBI)
-
diff --git a/18-numbers/final/README.md b/18-numbers/final/README.md
new file mode 100644
index 000000000..c64762a5f
--- /dev/null
+++ b/18-numbers/final/README.md
@@ -0,0 +1,43 @@
+## Numbers Project
+
+#### Structure (HTML)
+
+- section
+ - article
+ - span.number data-value="value" (0)
+ - p (text)
+
+#### Logic (JS)
+
+- select all span's with .number
+- iterate over and log each span
+- create updateCount function
+- accept el as argument
+- invoke and pass each span el in iteration
+
+```js
+const updateCount = (el) => {
+ const value = parseInt(el.dataset.value);
+ const increment = Math.ceil(value / 1000);
+ let initialValue = 0;
+};
+```
+
+```js
+const updateCount = (el) => {
+ const value = parseInt(el.dataset.value);
+ const increment = Math.ceil(value / 1000);
+ let initialValue = 0;
+
+ const increaseCount = setInterval(() => {
+ initialValue += increment;
+
+ if (initialValue > value) {
+ el.textContent = `${value}+`;
+ clearInterval(increaseCount);
+ return;
+ }
+ el.textContent = `${initialValue}+`;
+ }, 1);
+};
+```
diff --git a/18-numbers/final/app.js b/18-numbers/final/app.js
new file mode 100644
index 000000000..b3ef2e2cb
--- /dev/null
+++ b/18-numbers/final/app.js
@@ -0,0 +1,25 @@
+const items = [...document.querySelectorAll('.number')];
+
+const updateCount = (el) => {
+ const value = parseInt(el.dataset.value);
+ const increment = Math.ceil(value / 1000);
+ // const increment = 1;
+ let initialValue = 0;
+
+ const increaseCount = setInterval(() => {
+ initialValue += increment;
+
+ if (initialValue > value) {
+ el.textContent = `${value}+`;
+ clearInterval(increaseCount);
+ return;
+ }
+
+ el.textContent = `${initialValue}+`;
+ }, 1);
+ // console.log(increaseCount);
+};
+
+items.forEach((item) => {
+ updateCount(item);
+});
diff --git a/18-numbers/final/index.html b/18-numbers/final/index.html
new file mode 100644
index 000000000..251271636
--- /dev/null
+++ b/18-numbers/final/index.html
@@ -0,0 +1,33 @@
+
+
+
+
+
+
+ Numbers Starter
+
+
+
+
+
+
+ 0
+ Succeeded Projects
+
+
+
+
+ 0
+ Working Hours Spent
+
+
+
+
+ 0
+ Happy Clients
+
+
+
+
+
+
diff --git a/18-numbers/final/styles.css b/18-numbers/final/styles.css
new file mode 100644
index 000000000..bd484059a
--- /dev/null
+++ b/18-numbers/final/styles.css
@@ -0,0 +1,178 @@
+*,
+::after,
+::before {
+ box-sizing: border-box;
+}
+
+:root {
+ --primary-100: hsl(21, 94%, 87%);
+ --primary-200: hsl(21, 80%, 74%);
+ --primary-300: hsl(21, 65%, 59%);
+ --primary-400: hsl(21, 57%, 50%);
+ /* primary/main color */
+ --primary-500: hsl(21, 62%, 45%);
+ --primary-600: hsl(21, 77%, 34%);
+ --primary-700: hsl(21, 81%, 29%);
+ --primary-800: hsl(21, 84%, 25%);
+ --primary-900: hsl(21, 91%, 17%);
+
+ /* grey */
+ --grey-50: #f8fafc;
+ --grey-100: #f1f5f9;
+ --grey-200: #e2e8f0;
+ --grey-300: #cbd5e1;
+ --grey-400: #94a3b8;
+ --grey-500: #64748b;
+ --grey-600: #475569;
+ --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;
+
+ --smallText: 0.7em;
+ /* rest of the vars */
+ --backgroundColor: var(--grey-50);
+ --textColor: var(--grey-900);
+ --borderRadius: 0.25rem;
+ --letterSpacing: 1px;
+ --transition: 0.3s ease-in-out all;
+ --max-width: 1120px;
+ --fixed-width: 600px;
+ --fluid-width: 90vw;
+ /* 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),
+ 0 2px 4px -1px rgba(0, 0, 0, 0.06);
+ --shadow-3: 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),
+ 0 10px 10px -5px rgba(0, 0, 0, 0.04);
+}
+
+body {
+ background: var(--backgroundColor);
+ font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen,
+ Ubuntu, Cantarell, 'Open Sans', 'Helvetica Neue', sans-serif;
+ font-weight: 400;
+ line-height: 1.75;
+ color: var(--textColor);
+}
+
+p {
+ margin-bottom: 1.5rem;
+ max-width: 40em;
+}
+
+h1,
+h2,
+h3,
+h4,
+h5 {
+ margin: 0;
+ margin-bottom: 1.38rem;
+ font-family: var(--headingFont);
+ font-weight: 400;
+ line-height: 1.3;
+ text-transform: capitalize;
+ letter-spacing: var(--letterSpacing);
+}
+
+h1 {
+ margin-top: 0;
+ font-size: 3.052rem;
+}
+
+h2 {
+ font-size: 2.441rem;
+}
+
+h3 {
+ font-size: 1.953rem;
+}
+
+h4 {
+ font-size: 1.563rem;
+}
+
+h5 {
+ font-size: 1.25rem;
+}
+
+small,
+.text-small {
+ font-size: var(--smallText);
+}
+
+a {
+ text-decoration: none;
+}
+ul {
+ list-style-type: none;
+ padding: 0;
+}
+
+.img {
+ width: 100%;
+ display: block;
+ object-fit: cover;
+}
+/*
+===============
+Numbers
+===============
+*/
+
+section {
+ padding: 5rem 0;
+ width: 90vw;
+ max-width: 1170px;
+ margin: 0 auto;
+ display: grid;
+ gap: 2rem;
+}
+
+@media screen and (min-width: 768px) {
+ section {
+ grid-template-columns: 1fr 1fr;
+ }
+}
+@media screen and (min-width: 992px) {
+ section {
+ grid-template-columns: 1fr 1fr 1fr;
+ }
+}
+
+article {
+ background: var(--white);
+ padding: 1rem 2rem;
+ border-radius: var(--borderRadius);
+ border-bottom: 4px solid transparent;
+}
+
+article span {
+ font-size: 4rem;
+ font-weight: 700;
+ line-height: 1.25;
+}
+
+article p {
+ margin: 0;
+ text-transform: capitalize;
+ font-size: 1.5rem;
+}
+
+article:nth-of-type(1) {
+ border-bottom-color: #f59e0b;
+}
+article:nth-of-type(2) {
+ border-bottom-color: #14b8a6;
+}
+article:nth-of-type(3) {
+ border-bottom-color: #a855f7;
+}
diff --git a/18-numbers/starter/README.md b/18-numbers/starter/README.md
new file mode 100644
index 000000000..c64762a5f
--- /dev/null
+++ b/18-numbers/starter/README.md
@@ -0,0 +1,43 @@
+## Numbers Project
+
+#### Structure (HTML)
+
+- section
+ - article
+ - span.number data-value="value" (0)
+ - p (text)
+
+#### Logic (JS)
+
+- select all span's with .number
+- iterate over and log each span
+- create updateCount function
+- accept el as argument
+- invoke and pass each span el in iteration
+
+```js
+const updateCount = (el) => {
+ const value = parseInt(el.dataset.value);
+ const increment = Math.ceil(value / 1000);
+ let initialValue = 0;
+};
+```
+
+```js
+const updateCount = (el) => {
+ const value = parseInt(el.dataset.value);
+ const increment = Math.ceil(value / 1000);
+ let initialValue = 0;
+
+ const increaseCount = setInterval(() => {
+ initialValue += increment;
+
+ if (initialValue > value) {
+ el.textContent = `${value}+`;
+ clearInterval(increaseCount);
+ return;
+ }
+ el.textContent = `${initialValue}+`;
+ }, 1);
+};
+```
diff --git a/18-numbers/starter/app.js b/18-numbers/starter/app.js
new file mode 100644
index 000000000..9e399f610
--- /dev/null
+++ b/18-numbers/starter/app.js
@@ -0,0 +1 @@
+console.log('numbers project');
diff --git a/18-numbers/starter/index.html b/18-numbers/starter/index.html
new file mode 100644
index 000000000..3d1305757
--- /dev/null
+++ b/18-numbers/starter/index.html
@@ -0,0 +1,14 @@
+
+
+
+
+
+
+ Numbers Starter
+
+
+
+ numbers project starter
+
+
+
diff --git a/18-numbers/starter/styles.css b/18-numbers/starter/styles.css
new file mode 100644
index 000000000..bd484059a
--- /dev/null
+++ b/18-numbers/starter/styles.css
@@ -0,0 +1,178 @@
+*,
+::after,
+::before {
+ box-sizing: border-box;
+}
+
+:root {
+ --primary-100: hsl(21, 94%, 87%);
+ --primary-200: hsl(21, 80%, 74%);
+ --primary-300: hsl(21, 65%, 59%);
+ --primary-400: hsl(21, 57%, 50%);
+ /* primary/main color */
+ --primary-500: hsl(21, 62%, 45%);
+ --primary-600: hsl(21, 77%, 34%);
+ --primary-700: hsl(21, 81%, 29%);
+ --primary-800: hsl(21, 84%, 25%);
+ --primary-900: hsl(21, 91%, 17%);
+
+ /* grey */
+ --grey-50: #f8fafc;
+ --grey-100: #f1f5f9;
+ --grey-200: #e2e8f0;
+ --grey-300: #cbd5e1;
+ --grey-400: #94a3b8;
+ --grey-500: #64748b;
+ --grey-600: #475569;
+ --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;
+
+ --smallText: 0.7em;
+ /* rest of the vars */
+ --backgroundColor: var(--grey-50);
+ --textColor: var(--grey-900);
+ --borderRadius: 0.25rem;
+ --letterSpacing: 1px;
+ --transition: 0.3s ease-in-out all;
+ --max-width: 1120px;
+ --fixed-width: 600px;
+ --fluid-width: 90vw;
+ /* 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),
+ 0 2px 4px -1px rgba(0, 0, 0, 0.06);
+ --shadow-3: 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),
+ 0 10px 10px -5px rgba(0, 0, 0, 0.04);
+}
+
+body {
+ background: var(--backgroundColor);
+ font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen,
+ Ubuntu, Cantarell, 'Open Sans', 'Helvetica Neue', sans-serif;
+ font-weight: 400;
+ line-height: 1.75;
+ color: var(--textColor);
+}
+
+p {
+ margin-bottom: 1.5rem;
+ max-width: 40em;
+}
+
+h1,
+h2,
+h3,
+h4,
+h5 {
+ margin: 0;
+ margin-bottom: 1.38rem;
+ font-family: var(--headingFont);
+ font-weight: 400;
+ line-height: 1.3;
+ text-transform: capitalize;
+ letter-spacing: var(--letterSpacing);
+}
+
+h1 {
+ margin-top: 0;
+ font-size: 3.052rem;
+}
+
+h2 {
+ font-size: 2.441rem;
+}
+
+h3 {
+ font-size: 1.953rem;
+}
+
+h4 {
+ font-size: 1.563rem;
+}
+
+h5 {
+ font-size: 1.25rem;
+}
+
+small,
+.text-small {
+ font-size: var(--smallText);
+}
+
+a {
+ text-decoration: none;
+}
+ul {
+ list-style-type: none;
+ padding: 0;
+}
+
+.img {
+ width: 100%;
+ display: block;
+ object-fit: cover;
+}
+/*
+===============
+Numbers
+===============
+*/
+
+section {
+ padding: 5rem 0;
+ width: 90vw;
+ max-width: 1170px;
+ margin: 0 auto;
+ display: grid;
+ gap: 2rem;
+}
+
+@media screen and (min-width: 768px) {
+ section {
+ grid-template-columns: 1fr 1fr;
+ }
+}
+@media screen and (min-width: 992px) {
+ section {
+ grid-template-columns: 1fr 1fr 1fr;
+ }
+}
+
+article {
+ background: var(--white);
+ padding: 1rem 2rem;
+ border-radius: var(--borderRadius);
+ border-bottom: 4px solid transparent;
+}
+
+article span {
+ font-size: 4rem;
+ font-weight: 700;
+ line-height: 1.25;
+}
+
+article p {
+ margin: 0;
+ text-transform: capitalize;
+ font-size: 1.5rem;
+}
+
+article:nth-of-type(1) {
+ border-bottom-color: #f59e0b;
+}
+article:nth-of-type(2) {
+ border-bottom-color: #14b8a6;
+}
+article:nth-of-type(3) {
+ border-bottom-color: #a855f7;
+}
diff --git a/19-dark-mode/final/README.MD b/19-dark-mode/final/README.MD
new file mode 100644
index 000000000..cbb67a673
--- /dev/null
+++ b/19-dark-mode/final/README.MD
@@ -0,0 +1,41 @@
+## Dark Mode
+
+#### HTML Structure
+
+- main tags
+ - nav
+ - div.nav-center
+ - span 'overreacted'
+ - button.btn 'toggle'
+ - section.articles
+ - article.post
+ - h2 'post title'
+ - div.post-info
+ - span 'date'
+ - span 'read time'
+ - p 'content'
+
+#### Toggle - JS
+
+- select button
+- add click event listener
+
+#### Toggle - CSS
+
+- create .dark-theme class
+
+#### Toggle - JS
+
+- select root element (html), toggle .dark-theme
+
+#### Import Data
+
+#### Display Articles
+
+- select articles
+- iterate over, return article.post for each article
+
+#### Format Date
+
+- import moment.js
+- format date
diff --git a/19-dark-mode/final/app.js b/19-dark-mode/final/app.js
new file mode 100644
index 000000000..461f6b185
--- /dev/null
+++ b/19-dark-mode/final/app.js
@@ -0,0 +1,25 @@
+const toggleBtn = document.querySelector('.btn');
+const articlesContainer = document.querySelector('.articles');
+
+toggleBtn.addEventListener('click', () => {
+ document.documentElement.classList.toggle('dark-theme');
+});
+
+const articlesData = articles
+ .map((article) => {
+ const { title, date, length, snippet } = article;
+ const formatDate = moment(date).format('MMMM Do, YYYY');
+ return `
+ ${title}
+
+ ${formatDate}
+ ${length} min read
+
+
+ ${snippet}
+
+ `;
+ })
+ .join('');
+
+articlesContainer.innerHTML = articlesData;
diff --git a/19-dark-mode/final/data.js b/19-dark-mode/final/data.js
new file mode 100644
index 000000000..90f0582fa
--- /dev/null
+++ b/19-dark-mode/final/data.js
@@ -0,0 +1,30 @@
+const articles = [
+ {
+ id: 1,
+ title: 'the WET Codbase',
+ date: new Date(2020, 9, 4),
+ length: 11,
+ snippet: `I'm baby ramps kombucha gluten-free ennui swag tattooed street art. Marfa biodiesel letterpress blue bottle subway tile, pop-up pok pok of.`,
+ },
+ {
+ id: 2,
+ title: 'goodby, clean code',
+ date: new Date(2019, 10, 22),
+ length: 5,
+ snippet: `Biodiesel artisan seitan plaid sriracha copper mug venmo shabby chic. Kickstarter raclette kombucha, yr post-ironic jianbing try-hard flexitarian vaporware normcore.`,
+ },
+ {
+ id: 3,
+ title: 'my decade in review',
+ date: new Date(2018, 7, 11),
+ length: 5,
+ snippet: `Direct trade shabby chic four dollar toast, tilde actually try-hard bicycle rights aesthetic forage. Meditation keytar asymmetrical tacos artisan truffaut. Pabst jean shorts roof party scenester.`,
+ },
+ {
+ id: 4,
+ title: 'what are the react team principles',
+ date: new Date(2015, 5, 4),
+ length: 5,
+ snippet: `Selvage street art hammock affogato VHS. Mustache shaman literally wayfarers schlitz. Direct trade four loko narwhal VHS pop-up, chartreuse trust fund typewriter street art swag thundercats art party.`,
+ },
+];
diff --git a/19-dark-mode/final/index.html b/19-dark-mode/final/index.html
new file mode 100644
index 000000000..e10831d53
--- /dev/null
+++ b/19-dark-mode/final/index.html
@@ -0,0 +1,36 @@
+
+
+
+
+
+
+ Dark Mode
+
+
+
+
+
+
+ overreacted
+ toggle
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/19-dark-mode/final/styles.css b/19-dark-mode/final/styles.css
new file mode 100644
index 000000000..1ca6cd8da
--- /dev/null
+++ b/19-dark-mode/final/styles.css
@@ -0,0 +1,93 @@
+/*
+===============
+Global Styles
+===============
+*/
+
+:root {
+ --clr-bcg: #fff;
+ --clr-font: #282c35;
+ --clr-primary: #d23669;
+ --clr-grey: #64748b;
+}
+
+.dark-theme {
+ --clr-bcg: #282c35;
+ --clr-font: #fff;
+ --clr-primary: #ffa7c4;
+ --clr-grey: #cbd5e1;
+}
+
+*,
+::after,
+::before {
+ margin: 0;
+ padding: 0;
+ box-sizing: border-box;
+}
+body {
+ font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 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;
+}
+
+/*
+===============
+Navbar
+===============
+*/
+.nav-center {
+ width: 90vw;
+ max-width: 600px;
+ margin: 0 auto;
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ padding: 2rem 0;
+}
+.nav-center span {
+ font-size: 2.5rem;
+ text-transform: capitalize;
+ letter-spacing: 2px;
+}
+.btn {
+ background: var(--clr-primary);
+ color: var(--clr-bcg);
+ padding: 0.25rem 0.5rem;
+ border-radius: 5px;
+ border-color: transparent;
+ text-transform: capitalize;
+ transition: all 0.3s linear;
+ font-weight: bold;
+ letter-spacing: 2px;
+ cursor: pointer;
+}
+
+.articles {
+ padding: 5rem 0;
+ width: 90vw;
+ max-width: 600px;
+ margin: 0 auto;
+}
+.post {
+ margin-bottom: 3rem;
+}
+
+.post h2 {
+ color: var(--clr-primary);
+ text-transform: capitalize;
+ letter-spacing: 2px;
+ font-size: 1.75rem;
+}
+.post-info {
+ margin-bottom: 0.75rem;
+ font-style: italic;
+ color: var(--clr-grey);
+}
+.post-info span {
+ margin-right: 0.5rem;
+}
diff --git a/19-dark-mode/starter/README.MD b/19-dark-mode/starter/README.MD
new file mode 100644
index 000000000..cbb67a673
--- /dev/null
+++ b/19-dark-mode/starter/README.MD
@@ -0,0 +1,41 @@
+## Dark Mode
+
+#### HTML Structure
+
+- main tags
+ - nav
+ - div.nav-center
+ - span 'overreacted'
+ - button.btn 'toggle'
+ - section.articles
+ - article.post
+ - h2 'post title'
+ - div.post-info
+ - span 'date'
+ - span 'read time'
+ - p 'content'
+
+#### Toggle - JS
+
+- select button
+- add click event listener
+
+#### Toggle - CSS
+
+- create .dark-theme class
+
+#### Toggle - JS
+
+- select root element (html), toggle .dark-theme
+
+#### Import Data
+
+#### Display Articles
+
+- select articles
+- iterate over, return article.post for each article
+
+#### Format Date
+
+- import moment.js
+- format date
diff --git a/19-dark-mode/starter/app.js b/19-dark-mode/starter/app.js
new file mode 100644
index 000000000..81cc4c510
--- /dev/null
+++ b/19-dark-mode/starter/app.js
@@ -0,0 +1 @@
+console.log('dark mode');
diff --git a/19-dark-mode/starter/data.js b/19-dark-mode/starter/data.js
new file mode 100644
index 000000000..90f0582fa
--- /dev/null
+++ b/19-dark-mode/starter/data.js
@@ -0,0 +1,30 @@
+const articles = [
+ {
+ id: 1,
+ title: 'the WET Codbase',
+ date: new Date(2020, 9, 4),
+ length: 11,
+ snippet: `I'm baby ramps kombucha gluten-free ennui swag tattooed street art. Marfa biodiesel letterpress blue bottle subway tile, pop-up pok pok of.`,
+ },
+ {
+ id: 2,
+ title: 'goodby, clean code',
+ date: new Date(2019, 10, 22),
+ length: 5,
+ snippet: `Biodiesel artisan seitan plaid sriracha copper mug venmo shabby chic. Kickstarter raclette kombucha, yr post-ironic jianbing try-hard flexitarian vaporware normcore.`,
+ },
+ {
+ id: 3,
+ title: 'my decade in review',
+ date: new Date(2018, 7, 11),
+ length: 5,
+ snippet: `Direct trade shabby chic four dollar toast, tilde actually try-hard bicycle rights aesthetic forage. Meditation keytar asymmetrical tacos artisan truffaut. Pabst jean shorts roof party scenester.`,
+ },
+ {
+ id: 4,
+ title: 'what are the react team principles',
+ date: new Date(2015, 5, 4),
+ length: 5,
+ snippet: `Selvage street art hammock affogato VHS. Mustache shaman literally wayfarers schlitz. Direct trade four loko narwhal VHS pop-up, chartreuse trust fund typewriter street art swag thundercats art party.`,
+ },
+];
diff --git a/19-dark-mode/starter/index.html b/19-dark-mode/starter/index.html
new file mode 100644
index 000000000..d2b421b24
--- /dev/null
+++ b/19-dark-mode/starter/index.html
@@ -0,0 +1,14 @@
+
+
+
+
+
+
+ Dark Mode
+
+
+
+ dark mode starter
+
+
+
diff --git a/19-dark-mode/starter/styles.css b/19-dark-mode/starter/styles.css
new file mode 100644
index 000000000..aed0f806c
--- /dev/null
+++ b/19-dark-mode/starter/styles.css
@@ -0,0 +1,86 @@
+/*
+===============
+Global Styles
+===============
+*/
+
+:root {
+ --clr-bcg: #fff;
+ --clr-font: #282c35;
+ --clr-primary: #d23669;
+ --clr-grey: #64748b;
+}
+
+*,
+::after,
+::before {
+ margin: 0;
+ padding: 0;
+ box-sizing: border-box;
+}
+body {
+ font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 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;
+}
+
+/*
+===============
+Navbar
+===============
+*/
+.nav-center {
+ width: 90vw;
+ max-width: 600px;
+ margin: 0 auto;
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ padding: 2rem 0;
+}
+.nav-center span {
+ font-size: 2.5rem;
+ text-transform: capitalize;
+ letter-spacing: 2px;
+}
+.btn {
+ background: var(--clr-primary);
+ color: var(--clr-bcg);
+ padding: 0.25rem 0.5rem;
+ border-radius: 5px;
+ border-color: transparent;
+ text-transform: capitalize;
+ transition: all 0.3s linear;
+ font-weight: bold;
+ letter-spacing: 2px;
+ cursor: pointer;
+}
+
+.articles {
+ padding: 5rem 0;
+ width: 90vw;
+ max-width: 600px;
+ margin: 0 auto;
+}
+.post {
+ margin-bottom: 3rem;
+}
+
+.post h2 {
+ color: var(--clr-primary);
+ text-transform: capitalize;
+ letter-spacing: 2px;
+ font-size: 1.75rem;
+}
+.post-info {
+ margin-bottom: 0.75rem;
+ font-style: italic;
+ color: var(--clr-grey);
+}
+.post-info span {
+ margin-right: 0.5rem;
+}
diff --git a/20-filters/final/README.md b/20-filters/final/README.md
new file mode 100644
index 000000000..4f6c664d0
--- /dev/null
+++ b/20-filters/final/README.md
@@ -0,0 +1,80 @@
+## Filters Project
+
+#### HTML Structure
+
+- section.products
+
+ - div.filters-container
+ - div.products-container
+
+- .filters-container
+
+ - form.input-form
+ - input.search-input
+ - h5(company)
+ - article.companies
+ - button.company-btn(temp values)
+
+- .products-container
+ - article.product
+ - img.product-img.img (src from products.js)
+ - footer
+ - h5.product-name(name)
+ - span.product-price(price)
+
+#### Display Products
+
+- import products
+- make a copy and assign to new variable (use let keyword)
+- select .products-container
+- setup a function displayProducts, iterate over products, display them
+
+#### Filter Based On Text
+
+- select form, input
+- listen for 'keyup' events on form
+- get input value
+- filter based on the input value
+- call displayProducts
+
+```js
+// Text Filter
+
+const form = document.querySelector('.input-form');
+const searchInput = document.querySelector('.search-input');
+
+form.addEventListener('keyup', () => {
+ // keyup - fired when key released
+ const inputValue = searchInput.value;
+ filteredProducts = products.filter((product) => {
+ return product.title.toLowerCase().includes(inputValue);
+ });
+ displayProducts();
+});
+```
+
+#### Empty Array
+
+- displayProducts()
+- check length of filteredProducts
+- if list.length < 1
+- set productsContainer = some text
+
+#### Display Filter Buttons
+
+- select .companies
+- create function displayButtons
+- get only unique companies (set)
+- iterate over results
+- return button with data-id
+- set .companies innerHTML equal to result
+
+#### Filter Based on Company
+
+- add event listener on .companies
+- look for event.target
+- if contains .company-btn proceed
+ - if 'all' return all products (copy)
+ - else filter based on company value
+- set search value ''
+- call displayProducts
diff --git a/20-filters/final/app.js b/20-filters/final/app.js
new file mode 100644
index 000000000..429935f52
--- /dev/null
+++ b/20-filters/final/app.js
@@ -0,0 +1,82 @@
+let filteredProducts = [...products];
+
+const productsContainer = document.querySelector('.products-container');
+
+const displayProducts = () => {
+ if (filteredProducts.length < 1) {
+ productsContainer.innerHTML = `Sorry, no products matched your search `;
+ return;
+ }
+
+ productsContainer.innerHTML = filteredProducts
+ .map((product) => {
+ const { id, title, image, price } = product;
+ return `
+
+
+ `;
+ })
+ .join('');
+};
+
+displayProducts();
+
+// Text Filter
+
+const form = document.querySelector('.input-form');
+const searchInput = document.querySelector('.search-input');
+
+form.addEventListener('keyup', () => {
+ const inputValue = searchInput.value;
+ filteredProducts = products.filter((product) => {
+ return product.title.toLowerCase().includes(inputValue);
+ });
+ displayProducts();
+});
+
+// console.log(
+// products.filter((product) => {
+// return product.title.toLowerCase().includes('');
+// })
+// );
+
+// Filter Buttons
+
+const companiesDOM = document.querySelector('.companies');
+
+const displayButtons = () => {
+ const buttons = [
+ 'all',
+ ...new Set(products.map((product) => product.company)),
+ ];
+ // console.log(buttons);
+ companiesDOM.innerHTML = buttons
+ .map((company) => {
+ return `${company} `;
+ })
+ .join('');
+};
+
+displayButtons();
+
+companiesDOM.addEventListener('click', (e) => {
+ const el = e.target;
+ if (el.classList.contains('company-btn')) {
+ if (el.dataset.id === 'all') {
+ filteredProducts = [...products];
+ } else {
+ filteredProducts = products.filter((product) => {
+ return product.company === el.dataset.id;
+ });
+ }
+ searchInput.value = '';
+ displayProducts();
+ }
+});
diff --git a/20-filters/final/index.html b/20-filters/final/index.html
new file mode 100644
index 000000000..1eb4c6392
--- /dev/null
+++ b/20-filters/final/index.html
@@ -0,0 +1,46 @@
+
+
+
+
+
+
+ Filters
+
+
+
+
+
+
+
+
+
+
+
Company
+
+ all
+ ikea
+ marcos
+
+
+
+
+
+
+
+
+ high-back bench
+ $9.99
+
+
+
+
+
+
+
+
diff --git a/20-filters/final/products.js b/20-filters/final/products.js
new file mode 100644
index 000000000..dc1f3e4e2
--- /dev/null
+++ b/20-filters/final/products.js
@@ -0,0 +1,88 @@
+const products = [
+ {
+ id: 'rec43w3ipXvP28vog',
+ title: 'high-back bench',
+ company: 'ikea',
+ image: 'https://www.course-api.com/images/store/product-1.jpeg',
+ price: 9.99,
+ },
+ {
+ id: 'rec4f2RIftFCb7aHh',
+ title: 'albany table',
+ company: 'marcos',
+ image: 'https://www.course-api.com/images/store/product-2.jpeg',
+ price: 79.99,
+ },
+ {
+ id: 'rec8kkCmSiMkbkiko',
+ title: 'accent chair',
+ company: 'caressa',
+ image: 'https://www.course-api.com/images/store/product-3.jpeg',
+ price: 25.99,
+ },
+ {
+ id: 'recBohCqQsot4Q4II',
+ title: 'wooden table',
+ company: 'caressa',
+ image: 'https://www.course-api.com/images/store/product-4.jpeg',
+
+ price: 45.99,
+ },
+ {
+ id: 'recDG1JRZnbpRHpoy',
+ title: 'dining table',
+ company: 'caressa',
+ image: 'https://www.course-api.com/images/store/product-5.jpeg',
+
+ price: 6.99,
+ },
+ {
+ id: 'recNWGyP7kjFhSqw3',
+ title: 'sofa set',
+ company: 'liddy',
+ image: 'https://www.course-api.com/images/store/product-6.jpeg',
+ price: 69.99,
+ },
+ {
+ id: 'recZEougL5bbY4AEx',
+ title: 'modern bookshelf',
+ company: 'marcos',
+ image: 'https://www.course-api.com/images/store/product-7.jpeg',
+ price: 8.99,
+ },
+ {
+ id: 'recjMK1jgTb2ld7sv',
+ title: 'emperor bed',
+ company: 'liddy',
+ image: 'https://www.course-api.com/images/store/product-8.jpeg',
+ price: 21.99,
+ },
+ {
+ id: 'recmg2a1ctaEJNZhu',
+ title: 'utopia sofa',
+ company: 'marcos',
+ image: 'https://www.course-api.com/images/store/product-9.jpeg',
+ price: 39.95,
+ },
+ {
+ id: 'recvKMNR3YFw0bEt3',
+ title: 'entertainment center',
+ company: 'liddy',
+ image: 'https://www.course-api.com/images/store/product-10.jpeg',
+ price: 29.98,
+ },
+ {
+ id: 'recxaXFy5IW539sgM',
+ title: 'albany sectional',
+ company: 'ikea',
+ image: 'https://www.course-api.com/images/store/product-11.jpeg',
+ price: 10.99,
+ },
+ {
+ id: 'recyqtRglGNGtO4Q5',
+ title: 'leather sofa',
+ company: 'liddy',
+ image: 'https://www.course-api.com/images/store/product-12.jpeg',
+ price: 9.99,
+ },
+];
diff --git a/20-filters/final/styles.css b/20-filters/final/styles.css
new file mode 100644
index 000000000..488b6a90d
--- /dev/null
+++ b/20-filters/final/styles.css
@@ -0,0 +1,221 @@
+*,
+::after,
+::before {
+ box-sizing: border-box;
+}
+
+:root {
+ --primary-100: hsl(21, 94%, 87%);
+ --primary-200: hsl(21, 80%, 74%);
+ --primary-300: hsl(21, 65%, 59%);
+ --primary-400: hsl(21, 57%, 50%);
+ /* primary/main color */
+ --primary-500: hsl(21, 62%, 45%);
+ --primary-600: hsl(21, 77%, 34%);
+ --primary-700: hsl(21, 81%, 29%);
+ --primary-800: hsl(21, 84%, 25%);
+ --primary-900: hsl(21, 91%, 17%);
+
+ /* grey */
+ --grey-50: #f8fafc;
+ --grey-100: #f1f5f9;
+ --grey-200: #e2e8f0;
+ --grey-300: #cbd5e1;
+ --grey-400: #94a3b8;
+ --grey-500: #64748b;
+ --grey-600: #475569;
+ --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;
+
+ --smallText: 0.7em;
+ /* rest of the vars */
+ --backgroundColor: var(--grey-50);
+ --textColor: var(--grey-900);
+ --borderRadius: 0.25rem;
+ --letterSpacing: 1px;
+ --transition: 0.3s ease-in-out all;
+ --max-width: 1120px;
+ --fixed-width: 600px;
+ --fluid-width: 90vw;
+ /* 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),
+ 0 2px 4px -1px rgba(0, 0, 0, 0.06);
+ --shadow-3: 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),
+ 0 10px 10px -5px rgba(0, 0, 0, 0.04);
+}
+
+body {
+ background: var(--backgroundColor);
+ font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen,
+ Ubuntu, Cantarell, 'Open Sans', 'Helvetica Neue', sans-serif;
+ font-weight: 400;
+ line-height: 1.75;
+ color: var(--textColor);
+}
+
+p {
+ margin-bottom: 1.5rem;
+ max-width: 40em;
+}
+
+h1,
+h2,
+h3,
+h4,
+h5 {
+ margin: 0;
+ margin-bottom: 1.38rem;
+ font-family: var(--headingFont);
+ font-weight: 400;
+ line-height: 1.3;
+ text-transform: capitalize;
+ letter-spacing: var(--letterSpacing);
+}
+
+h1 {
+ margin-top: 0;
+ font-size: 3.052rem;
+}
+
+h2 {
+ font-size: 2.441rem;
+}
+
+h3 {
+ font-size: 1.953rem;
+}
+
+h4 {
+ font-size: 1.563rem;
+}
+
+h5 {
+ font-size: 1.25rem;
+}
+
+small,
+.text-small {
+ font-size: var(--smallText);
+}
+
+a {
+ text-decoration: none;
+}
+ul {
+ list-style-type: none;
+ padding: 0;
+}
+
+.img {
+ width: 100%;
+ display: block;
+ object-fit: cover;
+}
+/*
+===============
+Products
+===============
+*/
+.products {
+ width: var(--fluid-width);
+ display: grid;
+ grid-gap: 1rem;
+ margin: 4rem auto;
+ max-width: var(--max-width);
+}
+
+.filters-container h5 {
+ font-weight: 500;
+ margin: 1.5rem 0 0.5rem;
+ font-size: 0.85rem;
+}
+
+.search-input {
+ padding: 0.5rem;
+ background: var(--grey-200);
+ border-radius: var(--borderRadius);
+ border-color: transparent;
+ letter-spacing: var(--letterSpacing);
+}
+
+.company-btn {
+ display: block;
+ margin: 0.25em 0;
+ padding: 0.25rem;
+ text-transform: capitalize;
+ background: transparent;
+ border-color: transparent;
+ letter-spacing: var(--letterSpacing);
+ color: var(--grey-500);
+ cursor: pointer;
+ transition: var(--transition);
+ font-size: 0.85rem;
+}
+.company-btn:hover {
+ color: var(--grey-700);
+}
+.product {
+ margin-bottom: 1rem;
+}
+.product-img {
+ border-radius: var(--borderRadius);
+ height: 15rem;
+}
+.product footer {
+ padding: 0.75rem 0;
+ text-align: center;
+}
+.product-name {
+ margin-bottom: 0.25rem;
+ text-transform: capitalize;
+ letter-spacing: var(--letterSpacing);
+ color: var(--grey-500);
+}
+.product-price {
+ margin-bottom: 0;
+ color: var(--grey-700);
+ font-weight: 700;
+ font-size: 1rem;
+ letter-spacing: var(--letterSpacing);
+}
+
+@media screen and (min-width: 768px) {
+ .products {
+ grid-template-columns: 200px 1fr;
+ }
+}
+@media screen and (min-width: 992px) {
+ .products-container {
+ display: grid;
+ grid-template-columns: repeat(2, 1fr);
+ gap: 1rem;
+ }
+ .products-container .product-img {
+ height: 10rem;
+ }
+ .products-container .product-name {
+ font-size: 0.85rem;
+ }
+ .products-container .product-price {
+ font-size: 0.85rem;
+ }
+ .product {
+ margin-bottom: 0;
+ }
+}
+@media screen and (min-width: 1170px) {
+ .products-container {
+ grid-template-columns: repeat(3, 1fr);
+ }
+}
diff --git a/20-filters/starter/README.md b/20-filters/starter/README.md
new file mode 100644
index 000000000..4f6c664d0
--- /dev/null
+++ b/20-filters/starter/README.md
@@ -0,0 +1,80 @@
+## Filters Project
+
+#### HTML Structure
+
+- section.products
+
+ - div.filters-container
+ - div.products-container
+
+- .filters-container
+
+ - form.input-form
+ - input.search-input
+ - h5(company)
+ - article.companies
+ - button.company-btn(temp values)
+
+- .products-container
+ - article.product
+ - img.product-img.img (src from products.js)
+ - footer
+ - h5.product-name(name)
+ - span.product-price(price)
+
+#### Display Products
+
+- import products
+- make a copy and assign to new variable (use let keyword)
+- select .products-container
+- setup a function displayProducts, iterate over products, display them
+
+#### Filter Based On Text
+
+- select form, input
+- listen for 'keyup' events on form
+- get input value
+- filter based on the input value
+- call displayProducts
+
+```js
+// Text Filter
+
+const form = document.querySelector('.input-form');
+const searchInput = document.querySelector('.search-input');
+
+form.addEventListener('keyup', () => {
+ // keyup - fired when key released
+ const inputValue = searchInput.value;
+ filteredProducts = products.filter((product) => {
+ return product.title.toLowerCase().includes(inputValue);
+ });
+ displayProducts();
+});
+```
+
+#### Empty Array
+
+- displayProducts()
+- check length of filteredProducts
+- if list.length < 1
+- set productsContainer = some text
+
+#### Display Filter Buttons
+
+- select .companies
+- create function displayButtons
+- get only unique companies (set)
+- iterate over results
+- return button with data-id
+- set .companies innerHTML equal to result
+
+#### Filter Based on Company
+
+- add event listener on .companies
+- look for event.target
+- if contains .company-btn proceed
+ - if 'all' return all products (copy)
+ - else filter based on company value
+- set search value ''
+- call displayProducts
diff --git a/20-filters/starter/app.js b/20-filters/starter/app.js
new file mode 100644
index 000000000..fa29b37d3
--- /dev/null
+++ b/20-filters/starter/app.js
@@ -0,0 +1 @@
+console.log('filters project');
diff --git a/20-filters/starter/index.html b/20-filters/starter/index.html
new file mode 100644
index 000000000..b3d64d1b1
--- /dev/null
+++ b/20-filters/starter/index.html
@@ -0,0 +1,14 @@
+
+
+
+
+
+
+ Filters Starter
+
+
+
+ filters projects starter
+
+
+
diff --git a/20-filters/starter/products.js b/20-filters/starter/products.js
new file mode 100644
index 000000000..dc1f3e4e2
--- /dev/null
+++ b/20-filters/starter/products.js
@@ -0,0 +1,88 @@
+const products = [
+ {
+ id: 'rec43w3ipXvP28vog',
+ title: 'high-back bench',
+ company: 'ikea',
+ image: 'https://www.course-api.com/images/store/product-1.jpeg',
+ price: 9.99,
+ },
+ {
+ id: 'rec4f2RIftFCb7aHh',
+ title: 'albany table',
+ company: 'marcos',
+ image: 'https://www.course-api.com/images/store/product-2.jpeg',
+ price: 79.99,
+ },
+ {
+ id: 'rec8kkCmSiMkbkiko',
+ title: 'accent chair',
+ company: 'caressa',
+ image: 'https://www.course-api.com/images/store/product-3.jpeg',
+ price: 25.99,
+ },
+ {
+ id: 'recBohCqQsot4Q4II',
+ title: 'wooden table',
+ company: 'caressa',
+ image: 'https://www.course-api.com/images/store/product-4.jpeg',
+
+ price: 45.99,
+ },
+ {
+ id: 'recDG1JRZnbpRHpoy',
+ title: 'dining table',
+ company: 'caressa',
+ image: 'https://www.course-api.com/images/store/product-5.jpeg',
+
+ price: 6.99,
+ },
+ {
+ id: 'recNWGyP7kjFhSqw3',
+ title: 'sofa set',
+ company: 'liddy',
+ image: 'https://www.course-api.com/images/store/product-6.jpeg',
+ price: 69.99,
+ },
+ {
+ id: 'recZEougL5bbY4AEx',
+ title: 'modern bookshelf',
+ company: 'marcos',
+ image: 'https://www.course-api.com/images/store/product-7.jpeg',
+ price: 8.99,
+ },
+ {
+ id: 'recjMK1jgTb2ld7sv',
+ title: 'emperor bed',
+ company: 'liddy',
+ image: 'https://www.course-api.com/images/store/product-8.jpeg',
+ price: 21.99,
+ },
+ {
+ id: 'recmg2a1ctaEJNZhu',
+ title: 'utopia sofa',
+ company: 'marcos',
+ image: 'https://www.course-api.com/images/store/product-9.jpeg',
+ price: 39.95,
+ },
+ {
+ id: 'recvKMNR3YFw0bEt3',
+ title: 'entertainment center',
+ company: 'liddy',
+ image: 'https://www.course-api.com/images/store/product-10.jpeg',
+ price: 29.98,
+ },
+ {
+ id: 'recxaXFy5IW539sgM',
+ title: 'albany sectional',
+ company: 'ikea',
+ image: 'https://www.course-api.com/images/store/product-11.jpeg',
+ price: 10.99,
+ },
+ {
+ id: 'recyqtRglGNGtO4Q5',
+ title: 'leather sofa',
+ company: 'liddy',
+ image: 'https://www.course-api.com/images/store/product-12.jpeg',
+ price: 9.99,
+ },
+];
diff --git a/20-filters/starter/styles.css b/20-filters/starter/styles.css
new file mode 100644
index 000000000..488b6a90d
--- /dev/null
+++ b/20-filters/starter/styles.css
@@ -0,0 +1,221 @@
+*,
+::after,
+::before {
+ box-sizing: border-box;
+}
+
+:root {
+ --primary-100: hsl(21, 94%, 87%);
+ --primary-200: hsl(21, 80%, 74%);
+ --primary-300: hsl(21, 65%, 59%);
+ --primary-400: hsl(21, 57%, 50%);
+ /* primary/main color */
+ --primary-500: hsl(21, 62%, 45%);
+ --primary-600: hsl(21, 77%, 34%);
+ --primary-700: hsl(21, 81%, 29%);
+ --primary-800: hsl(21, 84%, 25%);
+ --primary-900: hsl(21, 91%, 17%);
+
+ /* grey */
+ --grey-50: #f8fafc;
+ --grey-100: #f1f5f9;
+ --grey-200: #e2e8f0;
+ --grey-300: #cbd5e1;
+ --grey-400: #94a3b8;
+ --grey-500: #64748b;
+ --grey-600: #475569;
+ --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;
+
+ --smallText: 0.7em;
+ /* rest of the vars */
+ --backgroundColor: var(--grey-50);
+ --textColor: var(--grey-900);
+ --borderRadius: 0.25rem;
+ --letterSpacing: 1px;
+ --transition: 0.3s ease-in-out all;
+ --max-width: 1120px;
+ --fixed-width: 600px;
+ --fluid-width: 90vw;
+ /* 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),
+ 0 2px 4px -1px rgba(0, 0, 0, 0.06);
+ --shadow-3: 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),
+ 0 10px 10px -5px rgba(0, 0, 0, 0.04);
+}
+
+body {
+ background: var(--backgroundColor);
+ font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen,
+ Ubuntu, Cantarell, 'Open Sans', 'Helvetica Neue', sans-serif;
+ font-weight: 400;
+ line-height: 1.75;
+ color: var(--textColor);
+}
+
+p {
+ margin-bottom: 1.5rem;
+ max-width: 40em;
+}
+
+h1,
+h2,
+h3,
+h4,
+h5 {
+ margin: 0;
+ margin-bottom: 1.38rem;
+ font-family: var(--headingFont);
+ font-weight: 400;
+ line-height: 1.3;
+ text-transform: capitalize;
+ letter-spacing: var(--letterSpacing);
+}
+
+h1 {
+ margin-top: 0;
+ font-size: 3.052rem;
+}
+
+h2 {
+ font-size: 2.441rem;
+}
+
+h3 {
+ font-size: 1.953rem;
+}
+
+h4 {
+ font-size: 1.563rem;
+}
+
+h5 {
+ font-size: 1.25rem;
+}
+
+small,
+.text-small {
+ font-size: var(--smallText);
+}
+
+a {
+ text-decoration: none;
+}
+ul {
+ list-style-type: none;
+ padding: 0;
+}
+
+.img {
+ width: 100%;
+ display: block;
+ object-fit: cover;
+}
+/*
+===============
+Products
+===============
+*/
+.products {
+ width: var(--fluid-width);
+ display: grid;
+ grid-gap: 1rem;
+ margin: 4rem auto;
+ max-width: var(--max-width);
+}
+
+.filters-container h5 {
+ font-weight: 500;
+ margin: 1.5rem 0 0.5rem;
+ font-size: 0.85rem;
+}
+
+.search-input {
+ padding: 0.5rem;
+ background: var(--grey-200);
+ border-radius: var(--borderRadius);
+ border-color: transparent;
+ letter-spacing: var(--letterSpacing);
+}
+
+.company-btn {
+ display: block;
+ margin: 0.25em 0;
+ padding: 0.25rem;
+ text-transform: capitalize;
+ background: transparent;
+ border-color: transparent;
+ letter-spacing: var(--letterSpacing);
+ color: var(--grey-500);
+ cursor: pointer;
+ transition: var(--transition);
+ font-size: 0.85rem;
+}
+.company-btn:hover {
+ color: var(--grey-700);
+}
+.product {
+ margin-bottom: 1rem;
+}
+.product-img {
+ border-radius: var(--borderRadius);
+ height: 15rem;
+}
+.product footer {
+ padding: 0.75rem 0;
+ text-align: center;
+}
+.product-name {
+ margin-bottom: 0.25rem;
+ text-transform: capitalize;
+ letter-spacing: var(--letterSpacing);
+ color: var(--grey-500);
+}
+.product-price {
+ margin-bottom: 0;
+ color: var(--grey-700);
+ font-weight: 700;
+ font-size: 1rem;
+ letter-spacing: var(--letterSpacing);
+}
+
+@media screen and (min-width: 768px) {
+ .products {
+ grid-template-columns: 200px 1fr;
+ }
+}
+@media screen and (min-width: 992px) {
+ .products-container {
+ display: grid;
+ grid-template-columns: repeat(2, 1fr);
+ gap: 1rem;
+ }
+ .products-container .product-img {
+ height: 10rem;
+ }
+ .products-container .product-name {
+ font-size: 0.85rem;
+ }
+ .products-container .product-price {
+ font-size: 0.85rem;
+ }
+ .product {
+ margin-bottom: 0;
+ }
+}
+@media screen and (min-width: 1170px) {
+ .products-container {
+ grid-template-columns: repeat(3, 1fr);
+ }
+}
diff --git a/21-dad-jokes/final/README.md b/21-dad-jokes/final/README.md
new file mode 100644
index 000000000..fa83a5d06
--- /dev/null
+++ b/21-dad-jokes/final/README.md
@@ -0,0 +1,79 @@
+## Dad Jokes Project
+
+#### HTML Structure
+
+- div.container
+ - button.btn
+ - p.result(dummy text)
+
+#### External Data
+
+- the main idea the same, just external data
+
+#### What is an API?
+
+[What is an API?](https://www.freecodecamp.org/news/what-is-an-api-in-english-please-b880a3214a82/)
+
+- https://www.course-api.com/javascript-store-products
+- get store products
+
+- https://www.course-api.com/javascript-store-single-product?id=rec43w3ipXvP28vog
+- get single store product
+
+- https://randomuser.me/api/
+- random user
+
+#### Docs
+
+- important
+- search engine
+- test in the browser
+
+#### Dad Jokes Docs
+
+- [Dad Jokes](https://icanhazdadjoke.com/api)
+
+- random joke
+- https://icanhazdadjoke.com/
+
+#### Select Elements
+
+- select btn, result
+- check if both elements selected
+- listen for click events
+
+#### FetchDadJoke Function
+
+- create async function
+- setup fetch
+- set result.textContent = joke
+
+```js
+const fetchDadJoke = async () => {
+ const response = await fetch(url, {
+ headers: {
+ Accept: 'application/json',
+ 'User-Agent': 'learning app',
+ },
+ });
+
+ const data = await response.json();
+ result.textContent = data.joke;
+};
+```
+
+#### Loading
+
+- set result equal to - 'Loading...'
+
+#### Error Handling
+
+- try/catch block
+- set result equal to - 'There was an error..'
+
+#### Check Status
+
+- Fetch - only throws an error if cannot resolve
+- Error response still a response
+- check response.ok property
+- throw new Error('Whoops!')
diff --git a/21-dad-jokes/final/app.js b/21-dad-jokes/final/app.js
new file mode 100644
index 000000000..e35cf2225
--- /dev/null
+++ b/21-dad-jokes/final/app.js
@@ -0,0 +1,31 @@
+const url = 'https://icanhazdadjoke.com/';
+
+const btn = document.querySelector('.btn');
+const result = document.querySelector('.result');
+
+btn.addEventListener('click', () => {
+ fetchDadJoke();
+});
+
+const fetchDadJoke = async () => {
+ result.textContent = 'Loading...';
+ try {
+ const response = await fetch(url, {
+ headers: {
+ Accept: 'application/json',
+ 'User-Agent': 'learning app',
+ },
+ });
+ if (!response.ok) {
+ throw new Error(' error');
+ }
+ const data = await response.json();
+
+ result.textContent = data.joke;
+ } catch (error) {
+ console.log(error.message);
+ result.textContent = 'There was an error...';
+ }
+};
+
+fetchDadJoke();
diff --git a/21-dad-jokes/final/index.html b/21-dad-jokes/final/index.html
new file mode 100644
index 000000000..6fd3ba014
--- /dev/null
+++ b/21-dad-jokes/final/index.html
@@ -0,0 +1,21 @@
+
+
+
+
+
+
+ Dad Jokes
+
+
+
+
+
random dad jokes
+
+ Lorem, ipsum dolor sit amet consectetur adipisicing elit. Exercitationem
+ unde quibusdam quia ex asperiores consectetur quis maxime corporis
+ suscipit expedita.
+
+
+
+
+
diff --git a/21-dad-jokes/final/styles.css b/21-dad-jokes/final/styles.css
new file mode 100644
index 000000000..51f781979
--- /dev/null
+++ b/21-dad-jokes/final/styles.css
@@ -0,0 +1,157 @@
+*,
+::after,
+::before {
+ box-sizing: border-box;
+}
+
+:root {
+ --primary-100: hsl(21, 94%, 87%);
+ --primary-200: hsl(21, 80%, 74%);
+ --primary-300: hsl(21, 65%, 59%);
+ --primary-400: hsl(21, 57%, 50%);
+ /* primary/main color */
+ --primary-500: hsl(21, 62%, 45%);
+ --primary-600: hsl(21, 77%, 34%);
+ --primary-700: hsl(21, 81%, 29%);
+ --primary-800: hsl(21, 84%, 25%);
+ --primary-900: hsl(21, 91%, 17%);
+
+ /* grey */
+ --grey-50: #f8fafc;
+ --grey-100: #f1f5f9;
+ --grey-200: #e2e8f0;
+ --grey-300: #cbd5e1;
+ --grey-400: #94a3b8;
+ --grey-500: #64748b;
+ --grey-600: #475569;
+ --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;
+
+ --smallText: 0.7em;
+ /* rest of the vars */
+ --backgroundColor: var(--grey-50);
+ --textColor: var(--grey-900);
+ --borderRadius: 0.25rem;
+ --letterSpacing: 1px;
+ --transition: 0.3s ease-in-out all;
+ --max-width: 1120px;
+ --fixed-width: 600px;
+ --fluid-width: 90vw;
+ /* 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),
+ 0 2px 4px -1px rgba(0, 0, 0, 0.06);
+ --shadow-3: 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),
+ 0 10px 10px -5px rgba(0, 0, 0, 0.04);
+}
+
+body {
+ background: var(--backgroundColor);
+ font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen,
+ Ubuntu, Cantarell, 'Open Sans', 'Helvetica Neue', sans-serif;
+ font-weight: 400;
+ line-height: 1.75;
+ color: var(--textColor);
+}
+
+p {
+ margin-bottom: 1.5rem;
+ max-width: 40em;
+}
+
+h1,
+h2,
+h3,
+h4,
+h5 {
+ margin: 0;
+ margin-bottom: 1.38rem;
+ font-family: var(--headingFont);
+ font-weight: 400;
+ line-height: 1.3;
+ text-transform: capitalize;
+ letter-spacing: var(--letterSpacing);
+}
+
+h1 {
+ margin-top: 0;
+ font-size: 3.052rem;
+}
+
+h2 {
+ font-size: 2.441rem;
+}
+
+h3 {
+ font-size: 1.953rem;
+}
+
+h4 {
+ font-size: 1.563rem;
+}
+
+h5 {
+ font-size: 1.25rem;
+}
+
+small,
+.text-small {
+ font-size: var(--smallText);
+}
+
+a {
+ text-decoration: none;
+}
+ul {
+ list-style-type: none;
+ padding: 0;
+}
+
+.img {
+ width: 100%;
+ display: block;
+ object-fit: cover;
+}
+.btn {
+ cursor: pointer;
+ color: var(--white);
+ background: var(--primary-500);
+ border: transparent;
+ border-radius: var(--borderRadius);
+ letter-spacing: var(--letterSpacing);
+ padding: 0.375rem 0.75rem;
+ box-shadow: var(--shadow-1);
+ transition: var(--transition);
+ text-transform: capitalize;
+ display: inline-block;
+}
+.btn:hover {
+ background: var(--primary-700);
+ box-shadow: var(--shadow-3);
+}
+/*
+===============
+Jokes
+===============
+*/
+
+.container {
+ width: 90vw;
+ max-width: 700px;
+ margin: 0 auto;
+ padding-top: 5rem;
+}
+
+.result {
+ font-size: 1.5rem;
+}
diff --git a/21-dad-jokes/starter/README.md b/21-dad-jokes/starter/README.md
new file mode 100644
index 000000000..fa83a5d06
--- /dev/null
+++ b/21-dad-jokes/starter/README.md
@@ -0,0 +1,79 @@
+## Dad Jokes Project
+
+#### HTML Structure
+
+- div.container
+ - button.btn
+ - p.result(dummy text)
+
+#### External Data
+
+- the main idea the same, just external data
+
+#### What is an API?
+
+[What is an API?](https://www.freecodecamp.org/news/what-is-an-api-in-english-please-b880a3214a82/)
+
+- https://www.course-api.com/javascript-store-products
+- get store products
+
+- https://www.course-api.com/javascript-store-single-product?id=rec43w3ipXvP28vog
+- get single store product
+
+- https://randomuser.me/api/
+- random user
+
+#### Docs
+
+- important
+- search engine
+- test in the browser
+
+#### Dad Jokes Docs
+
+- [Dad Jokes](https://icanhazdadjoke.com/api)
+
+- random joke
+- https://icanhazdadjoke.com/
+
+#### Select Elements
+
+- select btn, result
+- check if both elements selected
+- listen for click events
+
+#### FetchDadJoke Function
+
+- create async function
+- setup fetch
+- set result.textContent = joke
+
+```js
+const fetchDadJoke = async () => {
+ const response = await fetch(url, {
+ headers: {
+ Accept: 'application/json',
+ 'User-Agent': 'learning app',
+ },
+ });
+
+ const data = await response.json();
+ result.textContent = data.joke;
+};
+```
+
+#### Loading
+
+- set result equal to - 'Loading...'
+
+#### Error Handling
+
+- try/catch block
+- set result equal to - 'There was an error..'
+
+#### Check Status
+
+- Fetch - only throws an error if cannot resolve
+- Error response still a response
+- check response.ok property
+- throw new Error('Whoops!')
diff --git a/21-dad-jokes/starter/app.js b/21-dad-jokes/starter/app.js
new file mode 100644
index 000000000..14681936e
--- /dev/null
+++ b/21-dad-jokes/starter/app.js
@@ -0,0 +1 @@
+console.log('Dad Jokes Starter');
diff --git a/21-dad-jokes/starter/index.html b/21-dad-jokes/starter/index.html
new file mode 100644
index 000000000..f220903da
--- /dev/null
+++ b/21-dad-jokes/starter/index.html
@@ -0,0 +1,14 @@
+
+
+
+
+
+
+ Dad Jokes Starter
+
+
+
+ dad jokes starter
+
+
+
diff --git a/21-dad-jokes/starter/styles.css b/21-dad-jokes/starter/styles.css
new file mode 100644
index 000000000..51f781979
--- /dev/null
+++ b/21-dad-jokes/starter/styles.css
@@ -0,0 +1,157 @@
+*,
+::after,
+::before {
+ box-sizing: border-box;
+}
+
+:root {
+ --primary-100: hsl(21, 94%, 87%);
+ --primary-200: hsl(21, 80%, 74%);
+ --primary-300: hsl(21, 65%, 59%);
+ --primary-400: hsl(21, 57%, 50%);
+ /* primary/main color */
+ --primary-500: hsl(21, 62%, 45%);
+ --primary-600: hsl(21, 77%, 34%);
+ --primary-700: hsl(21, 81%, 29%);
+ --primary-800: hsl(21, 84%, 25%);
+ --primary-900: hsl(21, 91%, 17%);
+
+ /* grey */
+ --grey-50: #f8fafc;
+ --grey-100: #f1f5f9;
+ --grey-200: #e2e8f0;
+ --grey-300: #cbd5e1;
+ --grey-400: #94a3b8;
+ --grey-500: #64748b;
+ --grey-600: #475569;
+ --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;
+
+ --smallText: 0.7em;
+ /* rest of the vars */
+ --backgroundColor: var(--grey-50);
+ --textColor: var(--grey-900);
+ --borderRadius: 0.25rem;
+ --letterSpacing: 1px;
+ --transition: 0.3s ease-in-out all;
+ --max-width: 1120px;
+ --fixed-width: 600px;
+ --fluid-width: 90vw;
+ /* 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),
+ 0 2px 4px -1px rgba(0, 0, 0, 0.06);
+ --shadow-3: 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),
+ 0 10px 10px -5px rgba(0, 0, 0, 0.04);
+}
+
+body {
+ background: var(--backgroundColor);
+ font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen,
+ Ubuntu, Cantarell, 'Open Sans', 'Helvetica Neue', sans-serif;
+ font-weight: 400;
+ line-height: 1.75;
+ color: var(--textColor);
+}
+
+p {
+ margin-bottom: 1.5rem;
+ max-width: 40em;
+}
+
+h1,
+h2,
+h3,
+h4,
+h5 {
+ margin: 0;
+ margin-bottom: 1.38rem;
+ font-family: var(--headingFont);
+ font-weight: 400;
+ line-height: 1.3;
+ text-transform: capitalize;
+ letter-spacing: var(--letterSpacing);
+}
+
+h1 {
+ margin-top: 0;
+ font-size: 3.052rem;
+}
+
+h2 {
+ font-size: 2.441rem;
+}
+
+h3 {
+ font-size: 1.953rem;
+}
+
+h4 {
+ font-size: 1.563rem;
+}
+
+h5 {
+ font-size: 1.25rem;
+}
+
+small,
+.text-small {
+ font-size: var(--smallText);
+}
+
+a {
+ text-decoration: none;
+}
+ul {
+ list-style-type: none;
+ padding: 0;
+}
+
+.img {
+ width: 100%;
+ display: block;
+ object-fit: cover;
+}
+.btn {
+ cursor: pointer;
+ color: var(--white);
+ background: var(--primary-500);
+ border: transparent;
+ border-radius: var(--borderRadius);
+ letter-spacing: var(--letterSpacing);
+ padding: 0.375rem 0.75rem;
+ box-shadow: var(--shadow-1);
+ transition: var(--transition);
+ text-transform: capitalize;
+ display: inline-block;
+}
+.btn:hover {
+ background: var(--primary-700);
+ box-shadow: var(--shadow-3);
+}
+/*
+===============
+Jokes
+===============
+*/
+
+.container {
+ width: 90vw;
+ max-width: 700px;
+ margin: 0 auto;
+ padding-top: 5rem;
+}
+
+.result {
+ font-size: 1.5rem;
+}
diff --git a/22-products/final/README.md b/22-products/final/README.md
new file mode 100644
index 000000000..6fc0056d3
--- /dev/null
+++ b/22-products/final/README.md
@@ -0,0 +1,100 @@
+## Products Project
+
+#### Structure (HTML)
+
+- section.products
+
+ - div.title
+ - h2(products)
+ - div.title-underline
+ - div.products-center
+ - div.products-container
+ - a.single-product href="product.html"
+ - img.single-product-img.img
+ - footer
+ - h5.name (product title)
+ - span.price($9.99)
+
+- create product.html
+- basic structure
+
+#### Loading and Error
+
+- (CSS Loading Spinner)[https://youtu.be/DqqZEpctZ8w]
+- in .products-center
+- div.loading
+- p.error
+
+#### API Docs
+
+- (Course API)[https://www.course-api.com/]
+
+- (Products)[https://www.course-api.com/javascript-store-products]
+
+- (Single Product)[https://www.course-api.com/javascript-store-single-product?id=rec43w3ipXvP28vog]
+
+#### Fetch Products
+
+- select .products-center
+- fetch products
+- log result
+- try/catch
+
+#### Loading and Error
+
+- add loading while fetching
+- add error in catch
+
+#### Display Products - Setup
+
+- return data from fetchProducts
+- create displayProducts(list)
+- create start()
+- invoke fetchProducts and displayProducts in start
+- invoke start
+
+#### Display Products - Complete
+
+- iterate over list
+- pull out all the values
+- set productsDOM equal to result
+
+#### Single Product
+
+- link styles.css
+- a.btn.home-link(back home)
+- section.product
+- div.product-wrapper
+ - img.img
+ - div.product-info
+ - h3 (title)
+ - h5 (company)
+ - span (price)
+ - div.colors
+ - span.product-color
+ - p (lorem text)
+ - button.btn(add to cart)
+
+#### product.js setup
+
+- create product.js
+- link product.html
+- select .product
+- get single product url
+- setup fetchProduct(),displayProduct(),start()
+
+#### Loading, Error, Fetch Single Product
+
+- fetch single product
+- setup loading and error
+- make id dynamic
+- new URLSearchParams
+- window.location.search
+- get(keyName)
+
+#### Display Single Product
+
+#### Display Colors
+
+- iterate over colors array
+- return span with dynamic color value
diff --git a/22-products/final/app.js b/22-products/final/app.js
new file mode 100644
index 000000000..48c271a71
--- /dev/null
+++ b/22-products/final/app.js
@@ -0,0 +1,44 @@
+const url = 'https://www.course-api.com/javascript-store-products';
+
+const productsDOM = document.querySelector('.products-center');
+
+const fetchProducts = async () => {
+ productsDOM.innerHTML = '
';
+ try {
+ const resp = await fetch(url);
+ const data = await resp.json();
+ return data;
+ } catch (error) {
+ productsDOM.innerHTML = 'there was an error
';
+ }
+};
+
+const displayProducts = (list) => {
+ 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}
+ $${formatPrice}
+
+ `;
+ })
+ .join('');
+ productsDOM.innerHTML = `
+ ${productList}
+
+
`;
+};
+
+const start = async () => {
+ const data = await fetchProducts();
+ displayProducts(data);
+};
+
+start();
diff --git a/22-products/final/couch.jpg b/22-products/final/couch.jpg
new file mode 100644
index 000000000..ce4d87cf5
Binary files /dev/null and b/22-products/final/couch.jpg differ
diff --git a/22-products/final/index.html b/22-products/final/index.html
new file mode 100644
index 000000000..af8d9d34d
--- /dev/null
+++ b/22-products/final/index.html
@@ -0,0 +1,22 @@
+
+
+
+
+
+
+ Products Complete
+
+
+
+
+
+
+
diff --git a/22-products/final/product.html b/22-products/final/product.html
new file mode 100644
index 000000000..0b5d186b2
--- /dev/null
+++ b/22-products/final/product.html
@@ -0,0 +1,15 @@
+
+
+
+
+
+
+ Single Product
+
+
+
+ back home
+
+
+
+
diff --git a/22-products/final/product.js b/22-products/final/product.js
new file mode 100644
index 000000000..0708ebd2e
--- /dev/null
+++ b/22-products/final/product.js
@@ -0,0 +1,63 @@
+const productDOM = document.querySelector('.product');
+const url = 'https://www.course-api.com/javascript-store-single-product';
+
+const fetchProduct = async () => {
+ try {
+ productDOM.innerHTML = 'Loading... ';
+ // console.log(window.location.search);
+ const params = new URLSearchParams(window.location.search);
+ const id = params.get('id');
+
+ const response = await fetch(`${url}?id=${id}`);
+ const data = await response.json();
+ return data;
+ } catch (error) {
+ productDOM.innerHTML =
+ 'There was a problem loading the product. Please try again later
';
+ }
+};
+
+const displayProduct = (product) => {
+ // company, colors, description, name:title, price, image(url:img)
+ const {
+ company,
+ colors,
+ description,
+ name: title,
+ price,
+ image,
+ } = product.fields;
+ const { url: img } = image[0];
+ document.title = title.toUpperCase();
+
+ // colors
+ const colorsList = colors
+ .map((color) => {
+ return ` `;
+ })
+ .join('');
+
+ productDOM.innerHTML = `
+
+
+
${title}
+
${company}
+
${price / 100}
+
+ ${colorsList}
+
+
+
+ ${description}
+
+
add to cart
+
+
`;
+};
+
+const start = async () => {
+ const data = await fetchProduct();
+ displayProduct(data);
+};
+
+start();
diff --git a/22-products/final/styles.css b/22-products/final/styles.css
new file mode 100644
index 000000000..cfe361180
--- /dev/null
+++ b/22-products/final/styles.css
@@ -0,0 +1,315 @@
+*,
+::after,
+::before {
+ box-sizing: border-box;
+}
+
+html {
+ font-size: 100%;
+} /*16px*/
+
+: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 */
+ --grey-50: #f8fafc;
+ --grey-100: #f1f5f9;
+ --grey-200: #e2e8f0;
+ --grey-300: #cbd5e1;
+ --grey-400: #94a3b8;
+ --grey-500: #64748b;
+ --grey-600: #475569;
+ --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);
+ --textColor: var(--grey-900);
+ --borderRadius: 0.25rem;
+ --letterSpacing: 1px;
+ --transition: 0.3s ease-in-out all;
+ --max-width: 1120px;
+ --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),
+ 0 2px 4px -1px rgba(0, 0, 0, 0.06);
+ --shadow-3: 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),
+ 0 10px 10px -5px rgba(0, 0, 0, 0.04);
+}
+
+body {
+ background: var(--backgroundColor);
+ font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen,
+ Ubuntu, Cantarell, 'Open Sans', 'Helvetica Neue', sans-serif;
+ font-weight: 400;
+ line-height: 1.75;
+ color: var(--textColor);
+}
+
+p {
+ margin-bottom: 1.5rem;
+ max-width: 40em;
+}
+
+h1,
+h2,
+h3,
+h4,
+h5 {
+ margin: 0;
+ margin-bottom: 1.38rem;
+ font-weight: 400;
+ line-height: 1.3;
+ text-transform: capitalize;
+ letter-spacing: var(--letterSpacing);
+}
+
+h1 {
+ margin-top: 0;
+ font-size: 3.052rem;
+}
+
+h2 {
+ font-size: 2.441rem;
+}
+
+h3 {
+ font-size: 1.953rem;
+}
+
+h4 {
+ font-size: 1.563rem;
+}
+
+h5 {
+ font-size: 1.25rem;
+}
+
+small,
+.text-small {
+ font-size: var(--small-text);
+}
+
+a {
+ text-decoration: none;
+}
+ul {
+ list-style-type: none;
+ padding: 0;
+}
+
+.img {
+ width: 100%;
+ display: block;
+ object-fit: cover;
+}
+/* buttons */
+
+.btn {
+ cursor: pointer;
+ color: var(--white);
+ background: var(--primary-500);
+ border: transparent;
+ border-radius: var(--borderRadius);
+ letter-spacing: var(--letterSpacing);
+ padding: 0.375rem 0.75rem;
+ box-shadow: var(--shadow-1);
+ transition: var(--transition);
+ text-transform: capitalize;
+ display: inline-block;
+}
+.btn:hover {
+ background: var(--primary-700);
+ box-shadow: var(--shadow-3);
+}
+
+@keyframes spinner {
+ to {
+ transform: rotate(360deg);
+ }
+}
+
+.loading {
+ width: 6rem;
+ height: 6rem;
+ border: 5px solid var(--grey-400);
+ border-radius: 50%;
+ border-top-color: var(--primary-500);
+ animation: spinner 0.6s linear infinite;
+}
+.loading {
+ margin: 0 auto;
+ margin-top: 4rem;
+}
+/* title */
+
+.title {
+ text-align: center;
+ margin-bottom: 3rem;
+}
+
+.title-underline {
+ background: var(--primary-500);
+ width: 7rem;
+ height: 0.25rem;
+ margin: 0 auto;
+ margin-top: -1rem;
+}
+/*
+===============
+Products
+===============
+*/
+.products {
+ padding: 5rem 0;
+}
+.products-center {
+ width: 90vw;
+ max-width: 1170px;
+ margin: 0 auto;
+}
+.products-container {
+ display: grid;
+ gap: 2rem;
+}
+.single-product-img {
+ border-radius: var(--borderRadius);
+ height: 15rem;
+}
+.single-product footer {
+ padding: 0.75rem 0;
+ text-align: center;
+}
+.single-product .name {
+ margin-bottom: 0.25rem;
+ text-transform: capitalize;
+ letter-spacing: var(--letterSpacing);
+ color: var(--grey-500);
+}
+.single-product .price {
+ margin-bottom: 0;
+ color: var(--grey-700);
+ font-weight: 700;
+ font-size: 1rem;
+ letter-spacing: var(--letterSpacing);
+}
+
+@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;
+ }
+}
+@media screen and (min-width: 1000px) {
+ .products-container {
+ grid-template-columns: repeat(3, 1fr);
+ }
+}
+
+.error {
+ text-align: center;
+ font-size: 2rem;
+}
+
+/*
+===============
+Single Product
+===============
+*/
+.home-link {
+ display: block;
+ width: 150px;
+ margin: 2rem auto;
+ text-align: center;
+}
+.product {
+ padding: 5rem 0;
+ width: 90vw;
+ max-width: 1170px;
+ margin: 0 auto;
+}
+.product-loading {
+ text-align: center;
+}
+
+.product-wrapper img {
+ height: 15rem;
+ border-radius: var(--borderRadius);
+}
+
+.product-color {
+ display: inline-block;
+ width: 1rem;
+ height: 1rem;
+ border-radius: 50%;
+ background: #222;
+ margin: 0.5rem 0.5rem 0.25rem 0;
+}
+
+.product-info {
+ padding-top: 1rem;
+}
+.product-info h3 {
+ margin: 0;
+ margin-bottom: 0.25rem;
+}
+.product-info h5 {
+ color: var(--grey-500);
+ margin: 0;
+ margin-bottom: 0.25rem;
+}
+.product span {
+ color: var(--grey-800);
+}
+
+.product p {
+ margin: 0;
+ margin-bottom: 1.25rem;
+}
+
+@media screen and (min-width: 768px) {
+ .product-wrapper {
+ display: grid;
+ grid-template-columns: 1fr 1fr;
+ column-gap: 2rem;
+ }
+ .product-wrapper img {
+ height: 100%;
+ max-height: 500px;
+ }
+}
diff --git a/22-products/starter/README.md b/22-products/starter/README.md
new file mode 100644
index 000000000..6fc0056d3
--- /dev/null
+++ b/22-products/starter/README.md
@@ -0,0 +1,100 @@
+## Products Project
+
+#### Structure (HTML)
+
+- section.products
+
+ - div.title
+ - h2(products)
+ - div.title-underline
+ - div.products-center
+ - div.products-container
+ - a.single-product href="product.html"
+ - img.single-product-img.img
+ - footer
+ - h5.name (product title)
+ - span.price($9.99)
+
+- create product.html
+- basic structure
+
+#### Loading and Error
+
+- (CSS Loading Spinner)[https://youtu.be/DqqZEpctZ8w]
+- in .products-center
+- div.loading
+- p.error
+
+#### API Docs
+
+- (Course API)[https://www.course-api.com/]
+
+- (Products)[https://www.course-api.com/javascript-store-products]
+
+- (Single Product)[https://www.course-api.com/javascript-store-single-product?id=rec43w3ipXvP28vog]
+
+#### Fetch Products
+
+- select .products-center
+- fetch products
+- log result
+- try/catch
+
+#### Loading and Error
+
+- add loading while fetching
+- add error in catch
+
+#### Display Products - Setup
+
+- return data from fetchProducts
+- create displayProducts(list)
+- create start()
+- invoke fetchProducts and displayProducts in start
+- invoke start
+
+#### Display Products - Complete
+
+- iterate over list
+- pull out all the values
+- set productsDOM equal to result
+
+#### Single Product
+
+- link styles.css
+- a.btn.home-link(back home)
+- section.product
+- div.product-wrapper
+ - img.img
+ - div.product-info
+ - h3 (title)
+ - h5 (company)
+ - span (price)
+ - div.colors
+ - span.product-color
+ - p (lorem text)
+ - button.btn(add to cart)
+
+#### product.js setup
+
+- create product.js
+- link product.html
+- select .product
+- get single product url
+- setup fetchProduct(),displayProduct(),start()
+
+#### Loading, Error, Fetch Single Product
+
+- fetch single product
+- setup loading and error
+- make id dynamic
+- new URLSearchParams
+- window.location.search
+- get(keyName)
+
+#### Display Single Product
+
+#### Display Colors
+
+- iterate over colors array
+- return span with dynamic color value
diff --git a/22-products/starter/app.js b/22-products/starter/app.js
new file mode 100644
index 000000000..db779ad9b
--- /dev/null
+++ b/22-products/starter/app.js
@@ -0,0 +1 @@
+console.log('products starter');
diff --git a/22-products/starter/couch.jpg b/22-products/starter/couch.jpg
new file mode 100644
index 000000000..ce4d87cf5
Binary files /dev/null and b/22-products/starter/couch.jpg differ
diff --git a/22-products/starter/index.html b/22-products/starter/index.html
new file mode 100644
index 000000000..57d1adcd1
--- /dev/null
+++ b/22-products/starter/index.html
@@ -0,0 +1,22 @@
+
+
+
+
+
+
+ Products Starter
+
+
+
+
+
+
+
diff --git a/22-products/starter/styles.css b/22-products/starter/styles.css
new file mode 100644
index 000000000..cfe361180
--- /dev/null
+++ b/22-products/starter/styles.css
@@ -0,0 +1,315 @@
+*,
+::after,
+::before {
+ box-sizing: border-box;
+}
+
+html {
+ font-size: 100%;
+} /*16px*/
+
+: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 */
+ --grey-50: #f8fafc;
+ --grey-100: #f1f5f9;
+ --grey-200: #e2e8f0;
+ --grey-300: #cbd5e1;
+ --grey-400: #94a3b8;
+ --grey-500: #64748b;
+ --grey-600: #475569;
+ --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);
+ --textColor: var(--grey-900);
+ --borderRadius: 0.25rem;
+ --letterSpacing: 1px;
+ --transition: 0.3s ease-in-out all;
+ --max-width: 1120px;
+ --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),
+ 0 2px 4px -1px rgba(0, 0, 0, 0.06);
+ --shadow-3: 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),
+ 0 10px 10px -5px rgba(0, 0, 0, 0.04);
+}
+
+body {
+ background: var(--backgroundColor);
+ font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen,
+ Ubuntu, Cantarell, 'Open Sans', 'Helvetica Neue', sans-serif;
+ font-weight: 400;
+ line-height: 1.75;
+ color: var(--textColor);
+}
+
+p {
+ margin-bottom: 1.5rem;
+ max-width: 40em;
+}
+
+h1,
+h2,
+h3,
+h4,
+h5 {
+ margin: 0;
+ margin-bottom: 1.38rem;
+ font-weight: 400;
+ line-height: 1.3;
+ text-transform: capitalize;
+ letter-spacing: var(--letterSpacing);
+}
+
+h1 {
+ margin-top: 0;
+ font-size: 3.052rem;
+}
+
+h2 {
+ font-size: 2.441rem;
+}
+
+h3 {
+ font-size: 1.953rem;
+}
+
+h4 {
+ font-size: 1.563rem;
+}
+
+h5 {
+ font-size: 1.25rem;
+}
+
+small,
+.text-small {
+ font-size: var(--small-text);
+}
+
+a {
+ text-decoration: none;
+}
+ul {
+ list-style-type: none;
+ padding: 0;
+}
+
+.img {
+ width: 100%;
+ display: block;
+ object-fit: cover;
+}
+/* buttons */
+
+.btn {
+ cursor: pointer;
+ color: var(--white);
+ background: var(--primary-500);
+ border: transparent;
+ border-radius: var(--borderRadius);
+ letter-spacing: var(--letterSpacing);
+ padding: 0.375rem 0.75rem;
+ box-shadow: var(--shadow-1);
+ transition: var(--transition);
+ text-transform: capitalize;
+ display: inline-block;
+}
+.btn:hover {
+ background: var(--primary-700);
+ box-shadow: var(--shadow-3);
+}
+
+@keyframes spinner {
+ to {
+ transform: rotate(360deg);
+ }
+}
+
+.loading {
+ width: 6rem;
+ height: 6rem;
+ border: 5px solid var(--grey-400);
+ border-radius: 50%;
+ border-top-color: var(--primary-500);
+ animation: spinner 0.6s linear infinite;
+}
+.loading {
+ margin: 0 auto;
+ margin-top: 4rem;
+}
+/* title */
+
+.title {
+ text-align: center;
+ margin-bottom: 3rem;
+}
+
+.title-underline {
+ background: var(--primary-500);
+ width: 7rem;
+ height: 0.25rem;
+ margin: 0 auto;
+ margin-top: -1rem;
+}
+/*
+===============
+Products
+===============
+*/
+.products {
+ padding: 5rem 0;
+}
+.products-center {
+ width: 90vw;
+ max-width: 1170px;
+ margin: 0 auto;
+}
+.products-container {
+ display: grid;
+ gap: 2rem;
+}
+.single-product-img {
+ border-radius: var(--borderRadius);
+ height: 15rem;
+}
+.single-product footer {
+ padding: 0.75rem 0;
+ text-align: center;
+}
+.single-product .name {
+ margin-bottom: 0.25rem;
+ text-transform: capitalize;
+ letter-spacing: var(--letterSpacing);
+ color: var(--grey-500);
+}
+.single-product .price {
+ margin-bottom: 0;
+ color: var(--grey-700);
+ font-weight: 700;
+ font-size: 1rem;
+ letter-spacing: var(--letterSpacing);
+}
+
+@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;
+ }
+}
+@media screen and (min-width: 1000px) {
+ .products-container {
+ grid-template-columns: repeat(3, 1fr);
+ }
+}
+
+.error {
+ text-align: center;
+ font-size: 2rem;
+}
+
+/*
+===============
+Single Product
+===============
+*/
+.home-link {
+ display: block;
+ width: 150px;
+ margin: 2rem auto;
+ text-align: center;
+}
+.product {
+ padding: 5rem 0;
+ width: 90vw;
+ max-width: 1170px;
+ margin: 0 auto;
+}
+.product-loading {
+ text-align: center;
+}
+
+.product-wrapper img {
+ height: 15rem;
+ border-radius: var(--borderRadius);
+}
+
+.product-color {
+ display: inline-block;
+ width: 1rem;
+ height: 1rem;
+ border-radius: 50%;
+ background: #222;
+ margin: 0.5rem 0.5rem 0.25rem 0;
+}
+
+.product-info {
+ padding-top: 1rem;
+}
+.product-info h3 {
+ margin: 0;
+ margin-bottom: 0.25rem;
+}
+.product-info h5 {
+ color: var(--grey-500);
+ margin: 0;
+ margin-bottom: 0.25rem;
+}
+.product span {
+ color: var(--grey-800);
+}
+
+.product p {
+ margin: 0;
+ margin-bottom: 1.25rem;
+}
+
+@media screen and (min-width: 768px) {
+ .product-wrapper {
+ display: grid;
+ grid-template-columns: 1fr 1fr;
+ column-gap: 2rem;
+ }
+ .product-wrapper img {
+ height: 100%;
+ max-height: 500px;
+ }
+}
diff --git a/23-random-user/final/app.js b/23-random-user/final/app.js
new file mode 100644
index 000000000..1d86ff9a7
--- /dev/null
+++ b/23-random-user/final/app.js
@@ -0,0 +1,16 @@
+import get from './utils/getElement.js';
+import getUser from './utils/fetchUser.js';
+import displayUser from './utils/displayUser.js';
+
+const btn = get('.btn');
+
+const showUser = async () => {
+ // get user from api
+ const person = await getUser();
+ displayUser(person);
+
+ // display user
+};
+
+window.addEventListener('DOMContentLoaded', showUser);
+btn.addEventListener('click', showUser);
diff --git a/23-random-user/final/index.html b/23-random-user/final/index.html
new file mode 100644
index 000000000..b671c0ab2
--- /dev/null
+++ b/23-random-user/final/index.html
@@ -0,0 +1,64 @@
+
+
+
+
+
+ Random User
+
+
+
+
+
+
+
+
+
+
+
+
My name is
+
John Smith
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
random user
+
+
+
+
+
diff --git a/23-random-user/final/styles.css b/23-random-user/final/styles.css
new file mode 100644
index 000000000..b708c1823
--- /dev/null
+++ b/23-random-user/final/styles.css
@@ -0,0 +1,253 @@
+/*
+===============
+Fonts
+===============
+*/
+@import url("https://fonts.googleapis.com/css?family=Open+Sans|Roboto:400,700&display=swap");
+
+/*
+===============
+Variables
+===============
+*/
+
+:root {
+ /* dark shades of primary color*/
+ --clr-primary-1: hsl(205, 86%, 17%);
+ --clr-primary-2: hsl(205, 77%, 27%);
+ --clr-primary-3: hsl(205, 72%, 37%);
+ --clr-primary-4: hsl(205, 63%, 48%);
+ /* primary/main color */
+ --clr-primary-5: #49a6e9;
+ /* lighter shades of primary color */
+ --clr-primary-6: hsl(205, 89%, 70%);
+ --clr-primary-7: hsl(205, 90%, 76%);
+ --clr-primary-8: hsl(205, 86%, 81%);
+ --clr-primary-9: hsl(205, 90%, 88%);
+ --clr-primary-10: hsl(205, 100%, 96%);
+ /* darkest grey - used for headings */
+ --clr-grey-1: hsl(209, 61%, 16%);
+ --clr-grey-2: hsl(211, 39%, 23%);
+ --clr-grey-3: hsl(209, 34%, 30%);
+ --clr-grey-4: hsl(209, 28%, 39%);
+ /* grey used for paragraphs */
+ --clr-grey-5: hsl(210, 22%, 49%);
+ --clr-grey-6: hsl(209, 23%, 60%);
+ --clr-grey-7: hsl(211, 27%, 70%);
+ --clr-grey-8: hsl(210, 31%, 80%);
+ --clr-grey-9: hsl(212, 33%, 89%);
+ --clr-grey-10: hsl(210, 36%, 96%);
+ --clr-white: #fff;
+ --clr-red-dark: hsl(360, 67%, 44%);
+ --clr-red-light: hsl(360, 71%, 66%);
+ --clr-green-dark: hsl(125, 67%, 44%);
+ --clr-green-light: hsl(125, 71%, 66%);
+ --clr-black: #222;
+ --ff-primary: "Roboto", sans-serif;
+ --ff-secondary: "Open Sans", sans-serif;
+ --transition: all 0.3s linear;
+ --spacing: 0.25rem;
+ --radius: 0.5rem;
+ --light-shadow: 0 5px 15px rgba(0, 0, 0, 0.1);
+ --dark-shadow: 0 5px 15px rgba(0, 0, 0, 0.2);
+ --max-width: 1170px;
+ --fixed-width: 620px;
+}
+/*
+===============
+Global Styles
+===============
+*/
+
+*,
+::after,
+::before {
+ margin: 0;
+ padding: 0;
+ box-sizing: border-box;
+}
+body {
+ font-family: var(--ff-secondary);
+ background: var(--clr-grey-10);
+ color: var(--clr-grey-1);
+ line-height: 1.5;
+ font-size: 0.875rem;
+}
+ul {
+ list-style-type: none;
+}
+a {
+ text-decoration: none;
+}
+
+h1,
+h2,
+h3,
+h4 {
+ letter-spacing: var(--spacing);
+ text-transform: capitalize;
+ line-height: 1.25;
+ margin-bottom: 0.75rem;
+ font-family: var(--ff-primary);
+}
+h1 {
+ font-size: 3rem;
+}
+h2 {
+ font-size: 2rem;
+}
+h3 {
+ font-size: 1.25rem;
+}
+h4 {
+ font-size: 0.875rem;
+}
+p {
+ margin-bottom: 1.25rem;
+ color: var(--clr-grey-5);
+}
+@media screen and (min-width: 800px) {
+ h1 {
+ font-size: 4rem;
+ }
+ h2 {
+ font-size: 2.5rem;
+ }
+ h3 {
+ font-size: 1.75rem;
+ }
+ h4 {
+ font-size: 1rem;
+ }
+ body {
+ font-size: 1rem;
+ }
+ h1,
+ h2,
+ h3,
+ h4 {
+ line-height: 1;
+ }
+}
+/* global classes */
+
+.btn {
+ text-transform: uppercase;
+ background: var(--clr-primary-5);
+ color: var(--clr-white);
+ padding: 0.375rem 0.75rem;
+ letter-spacing: var(--spacing);
+ display: block;
+ transition: var(--transition);
+ font-size: 0.875rem;
+ border: 2px solid var(--clr-primary-5);
+ cursor: pointer;
+ box-shadow: 0 1px 3px rgba(0, 0, 0, 0.2);
+ border-radius: var(--radius);
+}
+.btn:hover {
+ color: var(--clr-primary-5);
+ background: var(--clr-primary-1);
+ border-color: var(--clr-primary-1);
+}
+/* section */
+.section {
+ padding: 5rem 0;
+}
+
+.section-center {
+ width: 90vw;
+ margin: 0 auto;
+ max-width: 1170px;
+}
+@media screen and (min-width: 992px) {
+ .section-center {
+ width: 95vw;
+ }
+}
+
+/* container */
+.block {
+ min-height: 50vh;
+}
+.bcg-black {
+ background: #2c2e31;
+}
+
+.container {
+ width: 90vw;
+ max-width: 730px;
+ background: var(--clr-white);
+ margin: 0 auto;
+ margin-top: -200px;
+ border-radius: var(--radius);
+ position: relative;
+ text-align: center;
+ padding: 1.5rem;
+ box-shadow: var(--dark-shadow);
+}
+
+.container::before {
+ content: "";
+ position: absolute;
+ top: 0;
+ left: 0;
+ width: 100%;
+ height: 130px;
+ background: var(--clr-grey-10);
+ border-top-left-radius: var(--radius);
+ border-top-right-radius: var(--radius);
+ border-bottom: 1px solid rgba(0, 0, 0, 0.25);
+}
+.container img {
+ width: 150px;
+ height: 150px;
+ position: relative;
+ border-radius: 50%;
+ padding: 5px;
+ background: var(--clr-white);
+ border: 1px solid rgba(0, 0, 0, 0.25);
+ margin-bottom: 2rem;
+ box-shadow: var(--dark-shadow);
+}
+
+.user-title {
+ font-size: 18px;
+ margin-bottom: 0;
+}
+.user-value {
+ color: var(--clr-grey-1);
+ font-size: 28px;
+}
+.values-list {
+ display: grid;
+ grid-template-columns: repeat(6, 1fr);
+ justify-content: center;
+}
+.icon {
+ background: transparent;
+ border: transparent;
+ font-size: 1.5rem;
+ cursor: pointer;
+ color: var(--clr-grey-5);
+ transition: var(--transition);
+}
+.icon:hover {
+ color: var(--clr-primary-5);
+}
+.active {
+ color: var(--clr-primary-5);
+}
+.btn {
+ margin: 0 auto;
+ margin-top: 1.5rem;
+}
+
+@media screen and (min-width: 800px) {
+ .user-value {
+ font-size: 38px;
+ }
+ .icon {
+ font-size: 2rem;
+ }
+}
diff --git a/23-random-user/final/utils/displayUser.js b/23-random-user/final/utils/displayUser.js
new file mode 100644
index 000000000..ed236ce5e
--- /dev/null
+++ b/23-random-user/final/utils/displayUser.js
@@ -0,0 +1,25 @@
+import get from './getElement.js';
+import removeActive from './removeActive.js';
+
+const img = get('.user-img');
+const title = get('.user-title');
+const value = get('.user-value');
+const btns = [...document.querySelectorAll('.icon')];
+const displayUser = (person) => {
+ img.src = person.image;
+ value.textContent = person.name;
+ title.textContent = `My name is`;
+ removeActive(btns);
+ btns[0].classList.remove('active');
+ btns.forEach((btn) => {
+ const label = btn.dataset.label;
+ btn.addEventListener('click', () => {
+ title.textContent = `My ${label} is`;
+ value.textContent = person[label];
+ removeActive(btns);
+ btn.classList.add('active');
+ });
+ });
+};
+
+export default displayUser;
diff --git a/23-random-user/final/utils/fetchUser.js b/23-random-user/final/utils/fetchUser.js
new file mode 100644
index 000000000..c3944194e
--- /dev/null
+++ b/23-random-user/final/utils/fetchUser.js
@@ -0,0 +1,29 @@
+const URL = 'https://randomuser.me/api/';
+
+const getUser = async () => {
+ const response = await fetch(URL);
+ const data = await response.json();
+ // destructure
+ const person = data.results[0];
+ const { phone, email } = person;
+ const { large: image } = person.picture;
+ const { password } = person.login;
+ const { first, last } = person.name;
+ const {
+ dob: { age },
+ } = person;
+ const {
+ street: { number, name },
+ } = person.location;
+ return {
+ image,
+ phone,
+ email,
+ password,
+ age,
+ street: `${number} ${name}`,
+ name: `${first} ${last}`,
+ };
+};
+
+export default getUser;
diff --git a/23-random-user/final/utils/getElement.js b/23-random-user/final/utils/getElement.js
new file mode 100644
index 000000000..730a547a0
--- /dev/null
+++ b/23-random-user/final/utils/getElement.js
@@ -0,0 +1,7 @@
+const getElement = (selection) => {
+ const element = document.querySelector(selection);
+ if (element) return element;
+ throw new Error('no element selected');
+};
+
+export default getElement;
diff --git a/23-random-user/final/utils/removeActive.js b/23-random-user/final/utils/removeActive.js
new file mode 100644
index 000000000..e6bccbcb6
--- /dev/null
+++ b/23-random-user/final/utils/removeActive.js
@@ -0,0 +1,3 @@
+export default function removeActive(items) {
+ items.forEach((btn) => btn.classList.remove('active'));
+}
diff --git a/5-sidebar/setup/app.js b/23-random-user/setup/app.js
similarity index 100%
rename from 5-sidebar/setup/app.js
rename to 23-random-user/setup/app.js
diff --git a/23-random-user/setup/index.html b/23-random-user/setup/index.html
new file mode 100644
index 000000000..9b61f608b
--- /dev/null
+++ b/23-random-user/setup/index.html
@@ -0,0 +1,20 @@
+
+
+
+
+
+ Random Person
+
+
+
+
+
+
+
+ random person
+
+
+
diff --git a/23-random-user/setup/styles.css b/23-random-user/setup/styles.css
new file mode 100644
index 000000000..b708c1823
--- /dev/null
+++ b/23-random-user/setup/styles.css
@@ -0,0 +1,253 @@
+/*
+===============
+Fonts
+===============
+*/
+@import url("https://fonts.googleapis.com/css?family=Open+Sans|Roboto:400,700&display=swap");
+
+/*
+===============
+Variables
+===============
+*/
+
+:root {
+ /* dark shades of primary color*/
+ --clr-primary-1: hsl(205, 86%, 17%);
+ --clr-primary-2: hsl(205, 77%, 27%);
+ --clr-primary-3: hsl(205, 72%, 37%);
+ --clr-primary-4: hsl(205, 63%, 48%);
+ /* primary/main color */
+ --clr-primary-5: #49a6e9;
+ /* lighter shades of primary color */
+ --clr-primary-6: hsl(205, 89%, 70%);
+ --clr-primary-7: hsl(205, 90%, 76%);
+ --clr-primary-8: hsl(205, 86%, 81%);
+ --clr-primary-9: hsl(205, 90%, 88%);
+ --clr-primary-10: hsl(205, 100%, 96%);
+ /* darkest grey - used for headings */
+ --clr-grey-1: hsl(209, 61%, 16%);
+ --clr-grey-2: hsl(211, 39%, 23%);
+ --clr-grey-3: hsl(209, 34%, 30%);
+ --clr-grey-4: hsl(209, 28%, 39%);
+ /* grey used for paragraphs */
+ --clr-grey-5: hsl(210, 22%, 49%);
+ --clr-grey-6: hsl(209, 23%, 60%);
+ --clr-grey-7: hsl(211, 27%, 70%);
+ --clr-grey-8: hsl(210, 31%, 80%);
+ --clr-grey-9: hsl(212, 33%, 89%);
+ --clr-grey-10: hsl(210, 36%, 96%);
+ --clr-white: #fff;
+ --clr-red-dark: hsl(360, 67%, 44%);
+ --clr-red-light: hsl(360, 71%, 66%);
+ --clr-green-dark: hsl(125, 67%, 44%);
+ --clr-green-light: hsl(125, 71%, 66%);
+ --clr-black: #222;
+ --ff-primary: "Roboto", sans-serif;
+ --ff-secondary: "Open Sans", sans-serif;
+ --transition: all 0.3s linear;
+ --spacing: 0.25rem;
+ --radius: 0.5rem;
+ --light-shadow: 0 5px 15px rgba(0, 0, 0, 0.1);
+ --dark-shadow: 0 5px 15px rgba(0, 0, 0, 0.2);
+ --max-width: 1170px;
+ --fixed-width: 620px;
+}
+/*
+===============
+Global Styles
+===============
+*/
+
+*,
+::after,
+::before {
+ margin: 0;
+ padding: 0;
+ box-sizing: border-box;
+}
+body {
+ font-family: var(--ff-secondary);
+ background: var(--clr-grey-10);
+ color: var(--clr-grey-1);
+ line-height: 1.5;
+ font-size: 0.875rem;
+}
+ul {
+ list-style-type: none;
+}
+a {
+ text-decoration: none;
+}
+
+h1,
+h2,
+h3,
+h4 {
+ letter-spacing: var(--spacing);
+ text-transform: capitalize;
+ line-height: 1.25;
+ margin-bottom: 0.75rem;
+ font-family: var(--ff-primary);
+}
+h1 {
+ font-size: 3rem;
+}
+h2 {
+ font-size: 2rem;
+}
+h3 {
+ font-size: 1.25rem;
+}
+h4 {
+ font-size: 0.875rem;
+}
+p {
+ margin-bottom: 1.25rem;
+ color: var(--clr-grey-5);
+}
+@media screen and (min-width: 800px) {
+ h1 {
+ font-size: 4rem;
+ }
+ h2 {
+ font-size: 2.5rem;
+ }
+ h3 {
+ font-size: 1.75rem;
+ }
+ h4 {
+ font-size: 1rem;
+ }
+ body {
+ font-size: 1rem;
+ }
+ h1,
+ h2,
+ h3,
+ h4 {
+ line-height: 1;
+ }
+}
+/* global classes */
+
+.btn {
+ text-transform: uppercase;
+ background: var(--clr-primary-5);
+ color: var(--clr-white);
+ padding: 0.375rem 0.75rem;
+ letter-spacing: var(--spacing);
+ display: block;
+ transition: var(--transition);
+ font-size: 0.875rem;
+ border: 2px solid var(--clr-primary-5);
+ cursor: pointer;
+ box-shadow: 0 1px 3px rgba(0, 0, 0, 0.2);
+ border-radius: var(--radius);
+}
+.btn:hover {
+ color: var(--clr-primary-5);
+ background: var(--clr-primary-1);
+ border-color: var(--clr-primary-1);
+}
+/* section */
+.section {
+ padding: 5rem 0;
+}
+
+.section-center {
+ width: 90vw;
+ margin: 0 auto;
+ max-width: 1170px;
+}
+@media screen and (min-width: 992px) {
+ .section-center {
+ width: 95vw;
+ }
+}
+
+/* container */
+.block {
+ min-height: 50vh;
+}
+.bcg-black {
+ background: #2c2e31;
+}
+
+.container {
+ width: 90vw;
+ max-width: 730px;
+ background: var(--clr-white);
+ margin: 0 auto;
+ margin-top: -200px;
+ border-radius: var(--radius);
+ position: relative;
+ text-align: center;
+ padding: 1.5rem;
+ box-shadow: var(--dark-shadow);
+}
+
+.container::before {
+ content: "";
+ position: absolute;
+ top: 0;
+ left: 0;
+ width: 100%;
+ height: 130px;
+ background: var(--clr-grey-10);
+ border-top-left-radius: var(--radius);
+ border-top-right-radius: var(--radius);
+ border-bottom: 1px solid rgba(0, 0, 0, 0.25);
+}
+.container img {
+ width: 150px;
+ height: 150px;
+ position: relative;
+ border-radius: 50%;
+ padding: 5px;
+ background: var(--clr-white);
+ border: 1px solid rgba(0, 0, 0, 0.25);
+ margin-bottom: 2rem;
+ box-shadow: var(--dark-shadow);
+}
+
+.user-title {
+ font-size: 18px;
+ margin-bottom: 0;
+}
+.user-value {
+ color: var(--clr-grey-1);
+ font-size: 28px;
+}
+.values-list {
+ display: grid;
+ grid-template-columns: repeat(6, 1fr);
+ justify-content: center;
+}
+.icon {
+ background: transparent;
+ border: transparent;
+ font-size: 1.5rem;
+ cursor: pointer;
+ color: var(--clr-grey-5);
+ transition: var(--transition);
+}
+.icon:hover {
+ color: var(--clr-primary-5);
+}
+.active {
+ color: var(--clr-primary-5);
+}
+.btn {
+ margin: 0 auto;
+ margin-top: 1.5rem;
+}
+
+@media screen and (min-width: 800px) {
+ .user-value {
+ font-size: 38px;
+ }
+ .icon {
+ font-size: 2rem;
+ }
+}
diff --git a/24-cocktails/final/app.js b/24-cocktails/final/app.js
new file mode 100644
index 000000000..800526b4d
--- /dev/null
+++ b/24-cocktails/final/app.js
@@ -0,0 +1,7 @@
+import presentDrinks from './src/presentDrinks.js';
+import './src/searchForm.js';
+const URL = 'https://www.thecocktaildb.com/api/json/v1/1/search.php?s=a';
+
+window.addEventListener('DOMContentLoaded', () => {
+ presentDrinks(URL);
+});
diff --git a/24-cocktails/final/cocktail.jpg b/24-cocktails/final/cocktail.jpg
new file mode 100644
index 000000000..4e9061348
Binary files /dev/null and b/24-cocktails/final/cocktail.jpg differ
diff --git a/24-cocktails/final/drink.html b/24-cocktails/final/drink.html
new file mode 100644
index 000000000..5c16164f4
--- /dev/null
+++ b/24-cocktails/final/drink.html
@@ -0,0 +1,30 @@
+
+
+
+
+
+ Single Cocktail
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/24-cocktails/final/index.html b/24-cocktails/final/index.html
new file mode 100644
index 000000000..4f941f75b
--- /dev/null
+++ b/24-cocktails/final/index.html
@@ -0,0 +1,34 @@
+
+
+
+
+
+ Cocktails
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/24-cocktails/final/loading.gif b/24-cocktails/final/loading.gif
new file mode 100644
index 000000000..e1e580cf9
Binary files /dev/null and b/24-cocktails/final/loading.gif differ
diff --git a/24-cocktails/final/singleDrink.js b/24-cocktails/final/singleDrink.js
new file mode 100644
index 000000000..9d1758269
--- /dev/null
+++ b/24-cocktails/final/singleDrink.js
@@ -0,0 +1,16 @@
+import fetchDrinks from './src/fetchDrinks.js';
+import displayDrink from './src/displaySingleDrink.js';
+
+const presentDrink = async () => {
+ const id = localStorage.getItem('drink');
+ if (!id) {
+ window.location.replace('index.html');
+ } else {
+ const drink = await fetchDrinks(
+ `https://www.thecocktaildb.com/api/json/v1/1/lookup.php?i=${id}`
+ );
+ displayDrink(drink);
+ }
+};
+
+window.addEventListener('DOMContentLoaded', presentDrink);
diff --git a/24-cocktails/final/src/displayDrinks.js b/24-cocktails/final/src/displayDrinks.js
new file mode 100644
index 000000000..b79e838b3
--- /dev/null
+++ b/24-cocktails/final/src/displayDrinks.js
@@ -0,0 +1,31 @@
+import get from './getElement.js';
+import { hideLoading } from './toggleLoading.js';
+const displayDrinks = ({ drinks }) => {
+ const section = get('.section-center');
+ const title = get('.title');
+ if (!drinks) {
+ // hide loading
+ hideLoading();
+ title.textContent = 'sorry, no drinks matched your search';
+ section.innerHTML = null;
+ return;
+ }
+ const newDrinks = drinks
+ .map((drink) => {
+ const { idDrink: id, strDrink: name, strDrinkThumb: image } = drink;
+
+ return `
+
+
+ ${name}
+
+ `;
+ })
+ .join('');
+ hideLoading();
+ title.textContent = '';
+ section.innerHTML = newDrinks;
+ return section;
+};
+
+export default displayDrinks;
diff --git a/24-cocktails/final/src/displaySingleDrink.js b/24-cocktails/final/src/displaySingleDrink.js
new file mode 100644
index 000000000..76a4df57b
--- /dev/null
+++ b/24-cocktails/final/src/displaySingleDrink.js
@@ -0,0 +1,31 @@
+import { hideLoading } from './toggleLoading.js';
+import get from './getElement.js';
+
+const displayDrink = (data) => {
+ hideLoading();
+
+ const drink = data.drinks[0];
+ const { strDrinkThumb: image, strDrink: name, strInstructions: desc } = drink;
+ const list = [
+ drink.strIngredient1,
+ drink.strIngredient2,
+ drink.strIngredient3,
+ drink.strIngredient4,
+ drink.strIngredient5,
+ ];
+ const img = get('.drink-img');
+ const drinkName = get('.drink-name');
+ const description = get('.drink-desc');
+ const ingredients = get('.drink-ingredients');
+ img.src = image;
+ document.title = name;
+ drinkName.textContent = name;
+ description.textContent = desc;
+ ingredients.innerHTML = list
+ .map((item) => {
+ if (!item) return;
+ return ` ${item} `;
+ })
+ .join('');
+};
+export default displayDrink;
diff --git a/24-cocktails/final/src/fetchDrinks.js b/24-cocktails/final/src/fetchDrinks.js
new file mode 100644
index 000000000..76764fcab
--- /dev/null
+++ b/24-cocktails/final/src/fetchDrinks.js
@@ -0,0 +1,13 @@
+import { showLoading } from './toggleLoading.js';
+const fetchDrinks = async (url) => {
+ showLoading();
+ try {
+ const response = await fetch(url);
+ const data = await response.json();
+ return data;
+ } catch (error) {
+ console.log(error);
+ }
+};
+
+export default fetchDrinks;
diff --git a/24-cocktails/final/src/getElement.js b/24-cocktails/final/src/getElement.js
new file mode 100644
index 000000000..1bfca0879
--- /dev/null
+++ b/24-cocktails/final/src/getElement.js
@@ -0,0 +1,7 @@
+const getElement = (selection) => {
+ const element = document.querySelector(selection);
+ if (element) return element;
+ throw new Error("no element selected");
+};
+
+export default getElement;
diff --git a/24-cocktails/final/src/presentDrinks.js b/24-cocktails/final/src/presentDrinks.js
new file mode 100644
index 000000000..3142fac72
--- /dev/null
+++ b/24-cocktails/final/src/presentDrinks.js
@@ -0,0 +1,15 @@
+import fetchDrinks from './fetchDrinks.js';
+import displayDrinks from './displayDrinks.js';
+import setDrink from './setDrink.js';
+const showDrinks = async (url) => {
+ // fetch drinks
+ const data = await fetchDrinks(url);
+
+ // display drinks
+ const section = await displayDrinks(data);
+ if (section) {
+ setDrink(section);
+ }
+};
+
+export default showDrinks;
diff --git a/24-cocktails/final/src/searchForm.js b/24-cocktails/final/src/searchForm.js
new file mode 100644
index 000000000..26e4522a5
--- /dev/null
+++ b/24-cocktails/final/src/searchForm.js
@@ -0,0 +1,14 @@
+import get from './getElement.js';
+import presentDrinks from './presentDrinks.js';
+
+const baseURL = 'https://www.thecocktaildb.com/api/json/v1/1/search.php?s=';
+
+const form = get('.search-form');
+const input = get('[name="drink"]');
+
+form.addEventListener('keyup', function (e) {
+ e.preventDefault();
+ const value = input.value;
+ if (!value) return;
+ presentDrinks(`${baseURL}${value}`);
+});
diff --git a/24-cocktails/final/src/setDrink.js b/24-cocktails/final/src/setDrink.js
new file mode 100644
index 000000000..bef153ec4
--- /dev/null
+++ b/24-cocktails/final/src/setDrink.js
@@ -0,0 +1,10 @@
+const setDrink = (section) => {
+ section.addEventListener('click', function (e) {
+ // e.preventDefault();
+ const id = e.target.parentElement.dataset.id;
+ // JSON.stringify JSON.parse
+ localStorage.setItem('drink', id);
+ });
+};
+
+export default setDrink;
diff --git a/24-cocktails/final/src/toggleLoading.js b/24-cocktails/final/src/toggleLoading.js
new file mode 100644
index 000000000..e1e828639
--- /dev/null
+++ b/24-cocktails/final/src/toggleLoading.js
@@ -0,0 +1,10 @@
+import get from './getElement.js';
+
+const loading = get('.loading');
+
+export const showLoading = () => {
+ loading.classList.remove('hide-loading');
+};
+export const hideLoading = () => {
+ loading.classList.add('hide-loading');
+};
diff --git a/24-cocktails/final/styles.css b/24-cocktails/final/styles.css
new file mode 100644
index 000000000..7634a90a3
--- /dev/null
+++ b/24-cocktails/final/styles.css
@@ -0,0 +1,299 @@
+/*
+===============
+Fonts
+===============
+*/
+@import url('https://fonts.googleapis.com/css?family=Open+Sans|Roboto:400,700&display=swap');
+
+/*
+===============
+Variables
+===============
+*/
+
+:root {
+ /* dark shades of primary color*/
+ --clr-primary-1: hsl(205, 86%, 17%);
+ --clr-primary-2: hsl(205, 77%, 27%);
+ --clr-primary-3: hsl(205, 72%, 37%);
+ --clr-primary-4: hsl(205, 63%, 48%);
+ /* primary/main color */
+ --clr-primary-5: #49a6e9;
+ /* lighter shades of primary color */
+ --clr-primary-6: hsl(205, 89%, 70%);
+ --clr-primary-7: hsl(205, 90%, 76%);
+ --clr-primary-8: hsl(205, 86%, 81%);
+ --clr-primary-9: hsl(205, 90%, 88%);
+ --clr-primary-10: hsl(205, 100%, 96%);
+ /* darkest grey - used for headings */
+ --clr-grey-1: hsl(209, 61%, 16%);
+ --clr-grey-2: hsl(211, 39%, 23%);
+ --clr-grey-3: hsl(209, 34%, 30%);
+ --clr-grey-4: hsl(209, 28%, 39%);
+ /* grey used for paragraphs */
+ --clr-grey-5: hsl(210, 22%, 49%);
+ --clr-grey-6: hsl(209, 23%, 60%);
+ --clr-grey-7: hsl(211, 27%, 70%);
+ --clr-grey-8: hsl(210, 31%, 80%);
+ --clr-grey-9: hsl(212, 33%, 89%);
+ --clr-grey-10: hsl(210, 36%, 96%);
+ --clr-white: #fff;
+ --clr-red-dark: hsl(360, 67%, 44%);
+ --clr-red-light: hsl(360, 71%, 66%);
+ --clr-green-dark: hsl(125, 67%, 44%);
+ --clr-green-light: hsl(125, 71%, 66%);
+ --clr-black: #222;
+ --ff-primary: 'Roboto', sans-serif;
+ --ff-secondary: 'Open Sans', sans-serif;
+ --transition: all 0.3s linear;
+ --spacing: 0.25rem;
+ --radius: 0.5rem;
+ --light-shadow: 0 5px 15px rgba(0, 0, 0, 0.1);
+ --dark-shadow: 0 5px 15px rgba(0, 0, 0, 0.2);
+ --max-width: 1170px;
+ --fixed-width: 620px;
+}
+/*
+===============
+Global Styles
+===============
+*/
+
+*,
+::after,
+::before {
+ margin: 0;
+ padding: 0;
+ box-sizing: border-box;
+}
+body {
+ font-family: var(--ff-secondary);
+ background: var(--clr-grey-10);
+ color: var(--clr-grey-1);
+ line-height: 1.5;
+ font-size: 0.875rem;
+}
+ul {
+ list-style-type: none;
+}
+a {
+ text-decoration: none;
+}
+
+h1,
+h2,
+h3,
+h4 {
+ letter-spacing: var(--spacing);
+ text-transform: capitalize;
+ line-height: 1.25;
+ margin-bottom: 0.75rem;
+ font-family: var(--ff-primary);
+}
+h1 {
+ font-size: 3rem;
+}
+h2 {
+ font-size: 2rem;
+}
+h3 {
+ font-size: 1.25rem;
+}
+h4 {
+ font-size: 0.875rem;
+}
+p {
+ margin-bottom: 1.25rem;
+ color: var(--clr-grey-5);
+}
+@media screen and (min-width: 800px) {
+ h1 {
+ font-size: 4rem;
+ }
+ h2 {
+ font-size: 2.5rem;
+ }
+ h3 {
+ font-size: 1.75rem;
+ }
+ h4 {
+ font-size: 1rem;
+ }
+ body {
+ font-size: 1rem;
+ }
+ h1,
+ h2,
+ h3,
+ h4 {
+ line-height: 1;
+ }
+}
+/* global classes */
+
+.btn {
+ text-transform: uppercase;
+ background: var(--clr-primary-5);
+ color: var(--clr-white);
+ padding: 0.375rem 0.75rem;
+ letter-spacing: var(--spacing);
+ display: block;
+ transition: var(--transition);
+ font-size: 0.875rem;
+ border: 2px solid var(--clr-primary-5);
+ cursor: pointer;
+ box-shadow: 0 1px 3px rgba(0, 0, 0, 0.2);
+ border-radius: var(--radius);
+}
+.btn:hover {
+ color: var(--clr-primary-5);
+ background: var(--clr-primary-1);
+ border-color: var(--clr-primary-1);
+}
+/* section */
+.section {
+ padding: 5rem 0;
+}
+
+.section-center {
+ width: 85vw;
+ margin: 0 auto;
+ max-width: 1170px;
+}
+@media screen and (min-width: 576px) {
+ .section-center {
+ display: grid;
+ grid-template-columns: repeat(auto-fill, minmax(300px, 1fr));
+ column-gap: 4rem;
+ }
+}
+@media screen and (min-width: 992px) {
+ .section-center {
+ width: 95vw;
+ }
+}
+/* loading */
+.loading {
+ min-height: 100vh;
+ display: grid;
+ justify-content: center;
+ position: absolute;
+ top: 0;
+ left: 0;
+ width: 100%;
+ height: 100%;
+ background: var(--clr-grey-10);
+ padding-top: 5rem;
+ z-index: 999;
+}
+
+.hide-loading {
+ display: none;
+}
+
+/* search form */
+.search-form {
+ text-align: center;
+ padding-top: 5rem;
+ width: 85vw;
+ max-width: 500px;
+ margin: 0 auto;
+}
+
+input {
+ width: 100%;
+ font-size: 1rem;
+ border-color: transparent;
+ margin-top: 1rem;
+ padding: 0.5rem 1rem;
+}
+input {
+ border-radius: var(--radius);
+}
+input::placeholder {
+ text-transform: capitalize;
+ color: var(--clr-grey-3);
+}
+
+/* cocktails */
+.cocktails {
+ position: relative;
+}
+.title {
+ font-size: 2rem;
+ text-align: center;
+}
+
+.cocktail {
+ background: var(--clr-white);
+ border-radius: var(--radius);
+ box-shadow: var(--light-shadow);
+ transition: var(--transition);
+ margin-bottom: 3rem;
+ position: relative;
+}
+.cocktail:hover {
+ box-shadow: var(--dark-shadow);
+ transform: scale(1.005);
+}
+.cocktail img {
+ width: 100%;
+ display: block;
+ border-radius: var(--radius);
+ border: 10px solid var(--clr-white);
+ transition: var(--transition);
+ height: 268px;
+ object-fit: cover;
+}
+.cocktail:hover img {
+ filter: blur(2px) grayscale(50%);
+}
+.cocktail h3 {
+ position: absolute;
+ bottom: -1rem;
+ right: -1rem;
+ background: var(--clr-black);
+ color: var(--clr-white);
+ padding: 1rem 1.5rem;
+ text-align: center;
+ margin-bottom: 0;
+ text-transform: uppercase;
+ font-family: monospace;
+ font-size: 2rem;
+ border-radius: var(--radius);
+}
+
+/* single cocktail */
+.single-drink {
+ width: 85vw;
+ max-width: var(--max-width);
+ margin: 0 auto;
+ margin-top: 4rem;
+}
+.drink-img {
+ width: 100%;
+ height: 400px;
+ object-fit: cover;
+ display: block;
+ border: 10px solid var(--clr-white);
+ border-radius: var(--radius);
+}
+@media screen and (min-width: 992px) {
+ .single-drink {
+ display: grid;
+ grid-template-columns: 1fr 1fr;
+ column-gap: 3rem;
+ }
+}
+.drink-info {
+ padding-top: 2rem;
+}
+
+.drink-ingredients .far {
+ margin-right: 1rem;
+}
+
+.single-drink .btn {
+ display: inline-block;
+ margin-top: 2rem;
+}
diff --git a/24-cocktails/setup/app.js b/24-cocktails/setup/app.js
new file mode 100644
index 000000000..e69de29bb
diff --git a/24-cocktails/setup/cocktail.jpg b/24-cocktails/setup/cocktail.jpg
new file mode 100644
index 000000000..4e9061348
Binary files /dev/null and b/24-cocktails/setup/cocktail.jpg differ
diff --git a/24-cocktails/setup/drink.html b/24-cocktails/setup/drink.html
new file mode 100644
index 000000000..0a966e014
--- /dev/null
+++ b/24-cocktails/setup/drink.html
@@ -0,0 +1,19 @@
+
+
+
+
+
+ Single Cocktail
+
+
+
+
+
+
+ single drink
+
+
+
diff --git a/24-cocktails/setup/index.html b/24-cocktails/setup/index.html
new file mode 100644
index 000000000..c78d52b62
--- /dev/null
+++ b/24-cocktails/setup/index.html
@@ -0,0 +1,20 @@
+
+
+
+
+
+ Cocktails
+
+
+
+
+
+
+
+ drinks page
+
+
+
diff --git a/24-cocktails/setup/loading.gif b/24-cocktails/setup/loading.gif
new file mode 100644
index 000000000..e1e580cf9
Binary files /dev/null and b/24-cocktails/setup/loading.gif differ
diff --git a/24-cocktails/setup/singleDrink.js b/24-cocktails/setup/singleDrink.js
new file mode 100644
index 000000000..e69de29bb
diff --git a/24-cocktails/setup/src/displayDrinks.js b/24-cocktails/setup/src/displayDrinks.js
new file mode 100644
index 000000000..e69de29bb
diff --git a/24-cocktails/setup/src/displaySingleDrink.js b/24-cocktails/setup/src/displaySingleDrink.js
new file mode 100644
index 000000000..e69de29bb
diff --git a/24-cocktails/setup/src/fetchDrinks.js b/24-cocktails/setup/src/fetchDrinks.js
new file mode 100644
index 000000000..e69de29bb
diff --git a/24-cocktails/setup/src/getElement.js b/24-cocktails/setup/src/getElement.js
new file mode 100644
index 000000000..1bfca0879
--- /dev/null
+++ b/24-cocktails/setup/src/getElement.js
@@ -0,0 +1,7 @@
+const getElement = (selection) => {
+ const element = document.querySelector(selection);
+ if (element) return element;
+ throw new Error("no element selected");
+};
+
+export default getElement;
diff --git a/24-cocktails/setup/src/presentDrinks.js b/24-cocktails/setup/src/presentDrinks.js
new file mode 100644
index 000000000..e69de29bb
diff --git a/24-cocktails/setup/src/searchForm.js b/24-cocktails/setup/src/searchForm.js
new file mode 100644
index 000000000..e69de29bb
diff --git a/24-cocktails/setup/src/setDrink.js b/24-cocktails/setup/src/setDrink.js
new file mode 100644
index 000000000..e69de29bb
diff --git a/24-cocktails/setup/src/toggleLoading.js b/24-cocktails/setup/src/toggleLoading.js
new file mode 100644
index 000000000..e69de29bb
diff --git a/24-cocktails/setup/styles.css b/24-cocktails/setup/styles.css
new file mode 100644
index 000000000..7634a90a3
--- /dev/null
+++ b/24-cocktails/setup/styles.css
@@ -0,0 +1,299 @@
+/*
+===============
+Fonts
+===============
+*/
+@import url('https://fonts.googleapis.com/css?family=Open+Sans|Roboto:400,700&display=swap');
+
+/*
+===============
+Variables
+===============
+*/
+
+:root {
+ /* dark shades of primary color*/
+ --clr-primary-1: hsl(205, 86%, 17%);
+ --clr-primary-2: hsl(205, 77%, 27%);
+ --clr-primary-3: hsl(205, 72%, 37%);
+ --clr-primary-4: hsl(205, 63%, 48%);
+ /* primary/main color */
+ --clr-primary-5: #49a6e9;
+ /* lighter shades of primary color */
+ --clr-primary-6: hsl(205, 89%, 70%);
+ --clr-primary-7: hsl(205, 90%, 76%);
+ --clr-primary-8: hsl(205, 86%, 81%);
+ --clr-primary-9: hsl(205, 90%, 88%);
+ --clr-primary-10: hsl(205, 100%, 96%);
+ /* darkest grey - used for headings */
+ --clr-grey-1: hsl(209, 61%, 16%);
+ --clr-grey-2: hsl(211, 39%, 23%);
+ --clr-grey-3: hsl(209, 34%, 30%);
+ --clr-grey-4: hsl(209, 28%, 39%);
+ /* grey used for paragraphs */
+ --clr-grey-5: hsl(210, 22%, 49%);
+ --clr-grey-6: hsl(209, 23%, 60%);
+ --clr-grey-7: hsl(211, 27%, 70%);
+ --clr-grey-8: hsl(210, 31%, 80%);
+ --clr-grey-9: hsl(212, 33%, 89%);
+ --clr-grey-10: hsl(210, 36%, 96%);
+ --clr-white: #fff;
+ --clr-red-dark: hsl(360, 67%, 44%);
+ --clr-red-light: hsl(360, 71%, 66%);
+ --clr-green-dark: hsl(125, 67%, 44%);
+ --clr-green-light: hsl(125, 71%, 66%);
+ --clr-black: #222;
+ --ff-primary: 'Roboto', sans-serif;
+ --ff-secondary: 'Open Sans', sans-serif;
+ --transition: all 0.3s linear;
+ --spacing: 0.25rem;
+ --radius: 0.5rem;
+ --light-shadow: 0 5px 15px rgba(0, 0, 0, 0.1);
+ --dark-shadow: 0 5px 15px rgba(0, 0, 0, 0.2);
+ --max-width: 1170px;
+ --fixed-width: 620px;
+}
+/*
+===============
+Global Styles
+===============
+*/
+
+*,
+::after,
+::before {
+ margin: 0;
+ padding: 0;
+ box-sizing: border-box;
+}
+body {
+ font-family: var(--ff-secondary);
+ background: var(--clr-grey-10);
+ color: var(--clr-grey-1);
+ line-height: 1.5;
+ font-size: 0.875rem;
+}
+ul {
+ list-style-type: none;
+}
+a {
+ text-decoration: none;
+}
+
+h1,
+h2,
+h3,
+h4 {
+ letter-spacing: var(--spacing);
+ text-transform: capitalize;
+ line-height: 1.25;
+ margin-bottom: 0.75rem;
+ font-family: var(--ff-primary);
+}
+h1 {
+ font-size: 3rem;
+}
+h2 {
+ font-size: 2rem;
+}
+h3 {
+ font-size: 1.25rem;
+}
+h4 {
+ font-size: 0.875rem;
+}
+p {
+ margin-bottom: 1.25rem;
+ color: var(--clr-grey-5);
+}
+@media screen and (min-width: 800px) {
+ h1 {
+ font-size: 4rem;
+ }
+ h2 {
+ font-size: 2.5rem;
+ }
+ h3 {
+ font-size: 1.75rem;
+ }
+ h4 {
+ font-size: 1rem;
+ }
+ body {
+ font-size: 1rem;
+ }
+ h1,
+ h2,
+ h3,
+ h4 {
+ line-height: 1;
+ }
+}
+/* global classes */
+
+.btn {
+ text-transform: uppercase;
+ background: var(--clr-primary-5);
+ color: var(--clr-white);
+ padding: 0.375rem 0.75rem;
+ letter-spacing: var(--spacing);
+ display: block;
+ transition: var(--transition);
+ font-size: 0.875rem;
+ border: 2px solid var(--clr-primary-5);
+ cursor: pointer;
+ box-shadow: 0 1px 3px rgba(0, 0, 0, 0.2);
+ border-radius: var(--radius);
+}
+.btn:hover {
+ color: var(--clr-primary-5);
+ background: var(--clr-primary-1);
+ border-color: var(--clr-primary-1);
+}
+/* section */
+.section {
+ padding: 5rem 0;
+}
+
+.section-center {
+ width: 85vw;
+ margin: 0 auto;
+ max-width: 1170px;
+}
+@media screen and (min-width: 576px) {
+ .section-center {
+ display: grid;
+ grid-template-columns: repeat(auto-fill, minmax(300px, 1fr));
+ column-gap: 4rem;
+ }
+}
+@media screen and (min-width: 992px) {
+ .section-center {
+ width: 95vw;
+ }
+}
+/* loading */
+.loading {
+ min-height: 100vh;
+ display: grid;
+ justify-content: center;
+ position: absolute;
+ top: 0;
+ left: 0;
+ width: 100%;
+ height: 100%;
+ background: var(--clr-grey-10);
+ padding-top: 5rem;
+ z-index: 999;
+}
+
+.hide-loading {
+ display: none;
+}
+
+/* search form */
+.search-form {
+ text-align: center;
+ padding-top: 5rem;
+ width: 85vw;
+ max-width: 500px;
+ margin: 0 auto;
+}
+
+input {
+ width: 100%;
+ font-size: 1rem;
+ border-color: transparent;
+ margin-top: 1rem;
+ padding: 0.5rem 1rem;
+}
+input {
+ border-radius: var(--radius);
+}
+input::placeholder {
+ text-transform: capitalize;
+ color: var(--clr-grey-3);
+}
+
+/* cocktails */
+.cocktails {
+ position: relative;
+}
+.title {
+ font-size: 2rem;
+ text-align: center;
+}
+
+.cocktail {
+ background: var(--clr-white);
+ border-radius: var(--radius);
+ box-shadow: var(--light-shadow);
+ transition: var(--transition);
+ margin-bottom: 3rem;
+ position: relative;
+}
+.cocktail:hover {
+ box-shadow: var(--dark-shadow);
+ transform: scale(1.005);
+}
+.cocktail img {
+ width: 100%;
+ display: block;
+ border-radius: var(--radius);
+ border: 10px solid var(--clr-white);
+ transition: var(--transition);
+ height: 268px;
+ object-fit: cover;
+}
+.cocktail:hover img {
+ filter: blur(2px) grayscale(50%);
+}
+.cocktail h3 {
+ position: absolute;
+ bottom: -1rem;
+ right: -1rem;
+ background: var(--clr-black);
+ color: var(--clr-white);
+ padding: 1rem 1.5rem;
+ text-align: center;
+ margin-bottom: 0;
+ text-transform: uppercase;
+ font-family: monospace;
+ font-size: 2rem;
+ border-radius: var(--radius);
+}
+
+/* single cocktail */
+.single-drink {
+ width: 85vw;
+ max-width: var(--max-width);
+ margin: 0 auto;
+ margin-top: 4rem;
+}
+.drink-img {
+ width: 100%;
+ height: 400px;
+ object-fit: cover;
+ display: block;
+ border: 10px solid var(--clr-white);
+ border-radius: var(--radius);
+}
+@media screen and (min-width: 992px) {
+ .single-drink {
+ display: grid;
+ grid-template-columns: 1fr 1fr;
+ column-gap: 3rem;
+ }
+}
+.drink-info {
+ padding-top: 2rem;
+}
+
+.drink-ingredients .far {
+ margin-right: 1rem;
+}
+
+.single-drink .btn {
+ display: inline-block;
+ margin-top: 2rem;
+}
diff --git a/25-slider/final/app.js b/25-slider/final/app.js
new file mode 100644
index 000000000..5263ceb67
--- /dev/null
+++ b/25-slider/final/app.js
@@ -0,0 +1,74 @@
+import data from './data.js'
+const container = document.querySelector('.slide-container')
+const nextBtn = document.querySelector('.next-btn')
+const prevBtn = document.querySelector('.prev-btn')
+// if length is 1 hide buttons
+if (data.length === 1) {
+ nextBtn.style.display = 'none'
+ prevBtn.style.display = 'none'
+}
+// if length is 2, add copies of slides
+let people = [...data]
+if (data.length === 2) {
+ people = [...data, ...data]
+}
+container.innerHTML = people
+ .map((person, slideIndex) => {
+ const { img, name, job, text } = person
+ let position = 'next'
+ if (slideIndex === 0) {
+ position = 'active'
+ }
+ if (slideIndex === people.length - 1) {
+ position = 'last'
+ }
+ if (data.length <= 1) {
+ position = 'active'
+ }
+ return `
+
+ ${name}
+ ${job}
+
+ ${text}
+
+
+
+
+ `
+ })
+ .join('')
+
+const startSlider = (type) => {
+ // get all three slides active,last next
+ const active = document.querySelector('.active')
+ const last = document.querySelector('.last')
+ let next = active.nextElementSibling
+ if (!next) {
+ next = container.firstElementChild
+ }
+ active.classList.remove('active')
+ last.classList.remove('last')
+ next.classList.remove('next')
+
+ if (type === 'prev') {
+ active.classList.add('next')
+ last.classList.add('active')
+ next = last.previousElementSibling
+ if (!next) {
+ next = container.lastElementChild
+ }
+ next.classList.remove('next')
+ next.classList.add('last')
+ return
+ }
+ active.classList.add('last')
+ last.classList.add('next')
+ next.classList.add('active')
+}
+nextBtn.addEventListener('click', () => {
+ startSlider()
+})
+prevBtn.addEventListener('click', () => {
+ startSlider('prev')
+})
diff --git a/16-ES6-slider/final/data.js b/25-slider/final/data.js
similarity index 52%
rename from 16-ES6-slider/final/data.js
rename to 25-slider/final/data.js
index 679d6b121..78ff8598f 100644
--- a/16-ES6-slider/final/data.js
+++ b/25-slider/final/data.js
@@ -1,23 +1,20 @@
const people = [
{
- img:
- "https://res.cloudinary.com/diqqf3eq2/image/upload/c_scale,w_200/v1595959121/person-1_aufeoq.jpg",
- name: "peter doe",
- job: "product manager",
+ img: 'https://www.course-api.com/images/people/person-3.jpeg',
+ name: 'peter doe',
+ job: 'product manager',
text: `Lorem ipsum dolor sit amet consectetur adipisicing elit. Quidem quoeius recusandae officia voluptas sint deserunt dicta nihil nam omnis? `,
},
{
- img:
- "https://res.cloudinary.com/diqqf3eq2/image/upload/c_scale,w_200/v1595959131/person-2_ipcjws.jpg",
- name: "susan doe",
- job: "developer",
+ img: 'https://www.course-api.com/images/people/person-1.jpeg',
+ name: 'susan doe',
+ job: 'developer',
text: `Lorem ipsum dolor sit amet consectetur adipisicing elit. Quidem quoeius recusandae officia voluptas sint deserunt dicta nihil nam omnis?`,
},
{
- img:
- "https://res.cloudinary.com/diqqf3eq2/image/upload/c_scale,w_200/v1595959131/person-3_rxtqvi.jpg",
- name: "emma doe",
- job: "designer",
+ img: 'https://www.course-api.com/images/people/person-2.jpeg',
+ name: 'emma doe',
+ job: 'designer',
text: `Lorem ipsum dolor sit amet consectetur adipisicing elit. Quidem quoeius recusandae officia voluptas sint deserunt dicta nihil nam omnis?`,
},
];
diff --git a/16-ES6-slider/final/index.html b/25-slider/final/index.html
similarity index 100%
rename from 16-ES6-slider/final/index.html
rename to 25-slider/final/index.html
diff --git a/16-ES6-slider/final/styles.css b/25-slider/final/styles.css
similarity index 100%
rename from 16-ES6-slider/final/styles.css
rename to 25-slider/final/styles.css
diff --git a/25-slider/setup/app.js b/25-slider/setup/app.js
new file mode 100644
index 000000000..e69de29bb
diff --git a/16-ES6-slider/setup/data.js b/25-slider/setup/data.js
similarity index 50%
rename from 16-ES6-slider/setup/data.js
rename to 25-slider/setup/data.js
index 247442d73..78ff8598f 100644
--- a/16-ES6-slider/setup/data.js
+++ b/25-slider/setup/data.js
@@ -1,23 +1,22 @@
const people = [
{
- img:
- "https://res.cloudinary.com/diqqf3eq2/image/upload/c_scale,w_200/v1595959121/person-1_aufeoq.jpg",
- name: "peter doe",
- job: "product manager",
+ img: 'https://www.course-api.com/images/people/person-3.jpeg',
+ name: 'peter doe',
+ job: 'product manager',
text: `Lorem ipsum dolor sit amet consectetur adipisicing elit. Quidem quoeius recusandae officia voluptas sint deserunt dicta nihil nam omnis? `,
},
{
- img:
- "https://res.cloudinary.com/diqqf3eq2/image/upload/c_scale,w_200/v1595959131/person-2_ipcjws.jpg",
- name: "susan doe",
- job: "developer",
+ img: 'https://www.course-api.com/images/people/person-1.jpeg',
+ name: 'susan doe',
+ job: 'developer',
text: `Lorem ipsum dolor sit amet consectetur adipisicing elit. Quidem quoeius recusandae officia voluptas sint deserunt dicta nihil nam omnis?`,
},
{
- img:
- "https://res.cloudinary.com/diqqf3eq2/image/upload/c_scale,w_200/v1595959131/person-3_rxtqvi.jpg",
- name: "emma doe",
- job: "designer",
+ img: 'https://www.course-api.com/images/people/person-2.jpeg',
+ name: 'emma doe',
+ job: 'designer',
text: `Lorem ipsum dolor sit amet consectetur adipisicing elit. Quidem quoeius recusandae officia voluptas sint deserunt dicta nihil nam omnis?`,
},
];
+
+export default people;
diff --git a/16-ES6-slider/setup/index.html b/25-slider/setup/index.html
similarity index 100%
rename from 16-ES6-slider/setup/index.html
rename to 25-slider/setup/index.html
diff --git a/16-ES6-slider/setup/styles.css b/25-slider/setup/styles.css
similarity index 100%
rename from 16-ES6-slider/setup/styles.css
rename to 25-slider/setup/styles.css
diff --git a/17-stripe-submenus/final/app.js b/26-stripe-submenus/final/app.js
similarity index 100%
rename from 17-stripe-submenus/final/app.js
rename to 26-stripe-submenus/final/app.js
diff --git a/17-stripe-submenus/final/data.js b/26-stripe-submenus/final/data.js
similarity index 100%
rename from 17-stripe-submenus/final/data.js
rename to 26-stripe-submenus/final/data.js
diff --git a/17-stripe-submenus/final/images/hero.svg b/26-stripe-submenus/final/images/hero.svg
similarity index 100%
rename from 17-stripe-submenus/final/images/hero.svg
rename to 26-stripe-submenus/final/images/hero.svg
diff --git a/17-stripe-submenus/final/images/logo.svg b/26-stripe-submenus/final/images/logo.svg
similarity index 100%
rename from 17-stripe-submenus/final/images/logo.svg
rename to 26-stripe-submenus/final/images/logo.svg
diff --git a/17-stripe-submenus/final/images/phone.svg b/26-stripe-submenus/final/images/phone.svg
similarity index 100%
rename from 17-stripe-submenus/final/images/phone.svg
rename to 26-stripe-submenus/final/images/phone.svg
diff --git a/17-stripe-submenus/final/index.html b/26-stripe-submenus/final/index.html
similarity index 100%
rename from 17-stripe-submenus/final/index.html
rename to 26-stripe-submenus/final/index.html
diff --git a/17-stripe-submenus/final/styles.css b/26-stripe-submenus/final/styles.css
similarity index 100%
rename from 17-stripe-submenus/final/styles.css
rename to 26-stripe-submenus/final/styles.css
diff --git a/26-stripe-submenus/setup/app.js b/26-stripe-submenus/setup/app.js
new file mode 100644
index 000000000..e69de29bb
diff --git a/17-stripe-submenus/setup/data.js b/26-stripe-submenus/setup/data.js
similarity index 100%
rename from 17-stripe-submenus/setup/data.js
rename to 26-stripe-submenus/setup/data.js
diff --git a/17-stripe-submenus/setup/images/hero.svg b/26-stripe-submenus/setup/images/hero.svg
similarity index 100%
rename from 17-stripe-submenus/setup/images/hero.svg
rename to 26-stripe-submenus/setup/images/hero.svg
diff --git a/17-stripe-submenus/setup/images/logo.svg b/26-stripe-submenus/setup/images/logo.svg
similarity index 100%
rename from 17-stripe-submenus/setup/images/logo.svg
rename to 26-stripe-submenus/setup/images/logo.svg
diff --git a/17-stripe-submenus/setup/images/phone.svg b/26-stripe-submenus/setup/images/phone.svg
similarity index 100%
rename from 17-stripe-submenus/setup/images/phone.svg
rename to 26-stripe-submenus/setup/images/phone.svg
diff --git a/17-stripe-submenus/setup/index.html b/26-stripe-submenus/setup/index.html
similarity index 100%
rename from 17-stripe-submenus/setup/index.html
rename to 26-stripe-submenus/setup/index.html
diff --git a/17-stripe-submenus/setup/styles.css b/26-stripe-submenus/setup/styles.css
similarity index 100%
rename from 17-stripe-submenus/setup/styles.css
rename to 26-stripe-submenus/setup/styles.css
diff --git a/18-pagination/final/app.js b/27-pagination/final/app.js
similarity index 100%
rename from 18-pagination/final/app.js
rename to 27-pagination/final/app.js
diff --git a/18-pagination/final/displayButtons.js b/27-pagination/final/displayButtons.js
similarity index 100%
rename from 18-pagination/final/displayButtons.js
rename to 27-pagination/final/displayButtons.js
diff --git a/18-pagination/final/displayFollowers.js b/27-pagination/final/displayFollowers.js
similarity index 100%
rename from 18-pagination/final/displayFollowers.js
rename to 27-pagination/final/displayFollowers.js
diff --git a/18-pagination/final/fetchFollowers.js b/27-pagination/final/fetchFollowers.js
similarity index 100%
rename from 18-pagination/final/fetchFollowers.js
rename to 27-pagination/final/fetchFollowers.js
diff --git a/18-pagination/final/index.html b/27-pagination/final/index.html
similarity index 100%
rename from 18-pagination/final/index.html
rename to 27-pagination/final/index.html
diff --git a/18-pagination/final/paginate.js b/27-pagination/final/paginate.js
similarity index 100%
rename from 18-pagination/final/paginate.js
rename to 27-pagination/final/paginate.js
diff --git a/18-pagination/final/styles.css b/27-pagination/final/styles.css
similarity index 100%
rename from 18-pagination/final/styles.css
rename to 27-pagination/final/styles.css
diff --git a/18-pagination/setup/app.js b/27-pagination/setup/app.js
similarity index 100%
rename from 18-pagination/setup/app.js
rename to 27-pagination/setup/app.js
diff --git a/18-pagination/setup/displayButtons.js b/27-pagination/setup/displayButtons.js
similarity index 100%
rename from 18-pagination/setup/displayButtons.js
rename to 27-pagination/setup/displayButtons.js
diff --git a/18-pagination/setup/displayFollowers.js b/27-pagination/setup/displayFollowers.js
similarity index 100%
rename from 18-pagination/setup/displayFollowers.js
rename to 27-pagination/setup/displayFollowers.js
diff --git a/18-pagination/setup/fetchFollowers.js b/27-pagination/setup/fetchFollowers.js
similarity index 100%
rename from 18-pagination/setup/fetchFollowers.js
rename to 27-pagination/setup/fetchFollowers.js
diff --git a/18-pagination/setup/index.html b/27-pagination/setup/index.html
similarity index 100%
rename from 18-pagination/setup/index.html
rename to 27-pagination/setup/index.html
diff --git a/18-pagination/setup/paginate.js b/27-pagination/setup/paginate.js
similarity index 100%
rename from 18-pagination/setup/paginate.js
rename to 27-pagination/setup/paginate.js
diff --git a/18-pagination/setup/styles.css b/27-pagination/setup/styles.css
similarity index 100%
rename from 18-pagination/setup/styles.css
rename to 27-pagination/setup/styles.css
diff --git a/28-wikipedia/final/README.md b/28-wikipedia/final/README.md
new file mode 100644
index 000000000..03d68ba59
--- /dev/null
+++ b/28-wikipedia/final/README.md
@@ -0,0 +1,44 @@
+#### HTML Structure
+
+- section.wiki
+ - div.container
+ - img
+ - h3(text)
+ - form.form
+ - input.form-input type='text'
+ - button.submit-btn (search) type='submit'
+ - div.results
+ - div.articles
+ - a
+ - h4
+ - p (lorem20)
+
+#### API DOCS
+
+- [wiki docs](https://www.mediawiki.org/wiki/API:Main_page)
+
+- ready to go url's
+
+#### Initial Setup
+
+- select form, input, results
+- listen for submit events
+- if empty value, display error
+- create fetchPages()
+- pass valid input value into the fetchPages()
+
+#### Fetch Pages
+
+- display loading while fetching
+- construct dynamic url
+- display if error
+- display error no items
+- create renderResults()
+- pass valid results into renderResults()
+
+#### Render Results
+
+- iterate over the list
+- pull out title, snippet, pageid
+- setup a card
+- set results with div.articles and list inside
diff --git a/28-wikipedia/final/app.js b/28-wikipedia/final/app.js
new file mode 100644
index 000000000..b2e189ae5
--- /dev/null
+++ b/28-wikipedia/final/app.js
@@ -0,0 +1,51 @@
+const url =
+ 'https://en.wikipedia.org/w/api.php?action=query&list=search&srlimit=20&format=json&origin=*&srsearch=';
+
+const formDOM = document.querySelector('.form');
+const inputDOM = document.querySelector('.form-input');
+const resultsDOM = document.querySelector('.results');
+
+formDOM.addEventListener('submit', (e) => {
+ e.preventDefault();
+ const value = inputDOM.value;
+ if (!value) {
+ resultsDOM.innerHTML =
+ ' please enter valid search term
';
+ return;
+ }
+ fetchPages(value);
+});
+
+const fetchPages = async (searchValue) => {
+ resultsDOM.innerHTML = '
';
+ try {
+ const response = await fetch(`${url}${searchValue}`);
+ const data = await response.json();
+ const results = data.query.search;
+ if (results.length < 1) {
+ resultsDOM.innerHTML =
+ 'no matching results. Please try again
';
+ return;
+ }
+ renderResults(results);
+ } catch (error) {
+ resultsDOM.innerHTML = ' there was an error...
';
+ }
+};
+
+const renderResults = (list) => {
+ const cardsList = list
+ .map((item) => {
+ const { title, snippet, pageid } = item;
+ return `
+ ${title}
+
+ ${snippet}
+
+ `;
+ })
+ .join('');
+ resultsDOM.innerHTML = `
+ ${cardsList}
+
`;
+};
diff --git a/28-wikipedia/final/index.html b/28-wikipedia/final/index.html
new file mode 100644
index 000000000..bf08fce7a
--- /dev/null
+++ b/28-wikipedia/final/index.html
@@ -0,0 +1,25 @@
+
+
+
+
+
+
+ Wikipedia
+
+
+
+
+
+
+
Search Wikipedia
+
+
+
+
+
+
+
+
diff --git a/28-wikipedia/final/styles.css b/28-wikipedia/final/styles.css
new file mode 100644
index 000000000..0e57ad877
--- /dev/null
+++ b/28-wikipedia/final/styles.css
@@ -0,0 +1,284 @@
+*,
+::after,
+::before {
+ box-sizing: border-box;
+}
+
+html {
+ font-size: 100%;
+} /*16px*/
+
+: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 */
+ --grey-50: #f8fafc;
+ --grey-100: #f1f5f9;
+ --grey-200: #e2e8f0;
+ --grey-300: #cbd5e1;
+ --grey-400: #94a3b8;
+ --grey-500: #64748b;
+ --grey-600: #475569;
+ --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);
+ --textColor: var(--grey-900);
+ --borderRadius: 0.25rem;
+ --letterSpacing: 1px;
+ --transition: 0.3s ease-in-out all;
+ --max-width: 1120px;
+ --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),
+ 0 2px 4px -1px rgba(0, 0, 0, 0.06);
+ --shadow-3: 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),
+ 0 10px 10px -5px rgba(0, 0, 0, 0.04);
+}
+
+body {
+ background: var(--backgroundColor);
+ font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen,
+ Ubuntu, Cantarell, 'Open Sans', 'Helvetica Neue', sans-serif;
+ font-weight: 400;
+ line-height: 1.75;
+ color: var(--textColor);
+}
+
+p {
+ margin-bottom: 1.5rem;
+ max-width: 40em;
+}
+
+h1,
+h2,
+h3,
+h4,
+h5 {
+ margin: 0;
+ margin-bottom: 1.38rem;
+ font-weight: 400;
+ line-height: 1.3;
+ text-transform: capitalize;
+ letter-spacing: var(--letterSpacing);
+}
+
+h1 {
+ margin-top: 0;
+ font-size: 3.052rem;
+}
+
+h2 {
+ font-size: 2.441rem;
+}
+
+h3 {
+ font-size: 1.953rem;
+}
+
+h4 {
+ font-size: 1.563rem;
+}
+
+h5 {
+ font-size: 1.25rem;
+}
+
+small,
+.text-small {
+ font-size: var(--small-text);
+}
+
+a {
+ text-decoration: none;
+}
+ul {
+ list-style-type: none;
+ padding: 0;
+}
+
+.img {
+ width: 100%;
+ display: block;
+ object-fit: cover;
+}
+/* buttons */
+
+.btn {
+ cursor: pointer;
+ color: var(--white);
+ background: var(--primary-500);
+ border: transparent;
+ border-radius: var(--borderRadius);
+ letter-spacing: var(--letterSpacing);
+ padding: 0.375rem 0.75rem;
+ box-shadow: var(--shadow-1);
+ transition: var(--transition);
+ text-transform: capitalize;
+ display: inline-block;
+}
+.btn:hover {
+ background: var(--primary-700);
+ box-shadow: var(--shadow-3);
+}
+
+@keyframes spinner {
+ to {
+ transform: rotate(360deg);
+ }
+}
+
+.loading {
+ width: 6rem;
+ height: 6rem;
+ border: 5px solid var(--grey-400);
+ border-radius: 50%;
+ border-top-color: var(--primary-500);
+ animation: spinner 0.6s linear infinite;
+}
+.loading {
+ margin: 0 auto;
+ margin-top: 4rem;
+}
+/* title */
+
+.title {
+ text-align: center;
+ margin-bottom: 3rem;
+}
+
+.title-underline {
+ background: var(--primary-500);
+ width: 7rem;
+ height: 0.25rem;
+ margin: 0 auto;
+ margin-top: -1rem;
+}
+
+/*
+===============
+Wikipedia
+===============
+*/
+.wiki {
+ padding: 5rem 0;
+ width: 90vw;
+ max-width: 1170px;
+ margin: 0 auto;
+}
+.container {
+ text-align: center;
+}
+.container img {
+ width: 200px;
+}
+.container h3 {
+ margin-bottom: 2rem;
+}
+
+.form {
+ background: #fff;
+ width: 100%;
+ margin: 0 auto;
+ padding: 2.5rem;
+ border-radius: var(--borderRadius);
+ display: grid;
+ grid-template-columns: auto 100px;
+}
+.form-input,
+.submit-btn {
+ padding: 0.375rem 0.75rem;
+ background: var(--backgroundColor);
+ border: 1px solid var(--grey-200);
+}
+.form-input {
+ border-right: transparent;
+ border-top-left-radius: var(--borderRadius);
+ border-bottom-left-radius: var(--borderRadius);
+}
+.submit-btn {
+ border: 1px solid var(--primary-500);
+ border-left: transparent;
+ border-top-right-radius: var(--borderRadius);
+ border-bottom-right-radius: var(--borderRadius);
+ text-transform: capitalize;
+ background: var(--primary-500);
+ color: var(--white);
+ transition: var(--transition);
+ cursor: pointer;
+}
+.submit-btn:hover {
+ color: var(--primary-900);
+}
+
+.results {
+ padding: 2rem 0;
+}
+
+.error {
+ text-align: center;
+ text-transform: capitalize;
+ color: var(--red-dark);
+}
+
+.articles {
+ display: grid;
+ gap: 1rem;
+}
+
+.articles a {
+ display: block;
+ background: var(--white);
+ color: var(--textColor);
+ padding: 1.5rem 2rem;
+ border-radius: var(--borderRadius);
+ transition: var(--transition);
+}
+.articles p {
+ color: var(--grey-500);
+ transition: var(--transition);
+}
+
+.articles a:hover {
+ background: var(--black);
+ color: var(--white);
+}
+
+@media screen and (min-width: 768px) {
+ .articles {
+ grid-template-columns: 1fr 1fr;
+ }
+ .form {
+ max-width: 600px;
+ }
+}
+@media screen and (min-width: 992px) {
+ .articles {
+ grid-template-columns: 1fr 1fr 1fr;
+ }
+}
diff --git a/28-wikipedia/final/wiki-logo.png b/28-wikipedia/final/wiki-logo.png
new file mode 100644
index 000000000..1bb8adee3
Binary files /dev/null and b/28-wikipedia/final/wiki-logo.png differ
diff --git a/28-wikipedia/final/wiki-urls.js b/28-wikipedia/final/wiki-urls.js
new file mode 100644
index 000000000..433d81c2e
--- /dev/null
+++ b/28-wikipedia/final/wiki-urls.js
@@ -0,0 +1,10 @@
+const url =
+ 'https://en.wikipedia.org/w/api.php?action=query&list=search&srlimit=20&format=json&origin=*&srsearch=searchValue';
+
+// list=search - perform a full text search
+// srsearch="inputValue" - search for page titles or content matching this value.
+// srlimit=20 How many total pages to return.
+// format=json json response
+// "origin=*" fix cors errors
+
+const page_url = 'href=http://en.wikipedia.org/?curid=${pageid}';
diff --git a/28-wikipedia/starter/README.md b/28-wikipedia/starter/README.md
new file mode 100644
index 000000000..03d68ba59
--- /dev/null
+++ b/28-wikipedia/starter/README.md
@@ -0,0 +1,44 @@
+#### HTML Structure
+
+- section.wiki
+ - div.container
+ - img
+ - h3(text)
+ - form.form
+ - input.form-input type='text'
+ - button.submit-btn (search) type='submit'
+ - div.results
+ - div.articles
+ - a
+ - h4
+ - p (lorem20)
+
+#### API DOCS
+
+- [wiki docs](https://www.mediawiki.org/wiki/API:Main_page)
+
+- ready to go url's
+
+#### Initial Setup
+
+- select form, input, results
+- listen for submit events
+- if empty value, display error
+- create fetchPages()
+- pass valid input value into the fetchPages()
+
+#### Fetch Pages
+
+- display loading while fetching
+- construct dynamic url
+- display if error
+- display error no items
+- create renderResults()
+- pass valid results into renderResults()
+
+#### Render Results
+
+- iterate over the list
+- pull out title, snippet, pageid
+- setup a card
+- set results with div.articles and list inside
diff --git a/28-wikipedia/starter/app.js b/28-wikipedia/starter/app.js
new file mode 100644
index 000000000..67301cd32
--- /dev/null
+++ b/28-wikipedia/starter/app.js
@@ -0,0 +1 @@
+console.log('wiki starter');
diff --git a/28-wikipedia/starter/index.html b/28-wikipedia/starter/index.html
new file mode 100644
index 000000000..85ebab7df
--- /dev/null
+++ b/28-wikipedia/starter/index.html
@@ -0,0 +1,14 @@
+
+
+
+
+
+
+ Wikipedia Starter
+
+
+
+ wiki starter
+
+
+
diff --git a/28-wikipedia/starter/styles.css b/28-wikipedia/starter/styles.css
new file mode 100644
index 000000000..0e57ad877
--- /dev/null
+++ b/28-wikipedia/starter/styles.css
@@ -0,0 +1,284 @@
+*,
+::after,
+::before {
+ box-sizing: border-box;
+}
+
+html {
+ font-size: 100%;
+} /*16px*/
+
+: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 */
+ --grey-50: #f8fafc;
+ --grey-100: #f1f5f9;
+ --grey-200: #e2e8f0;
+ --grey-300: #cbd5e1;
+ --grey-400: #94a3b8;
+ --grey-500: #64748b;
+ --grey-600: #475569;
+ --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);
+ --textColor: var(--grey-900);
+ --borderRadius: 0.25rem;
+ --letterSpacing: 1px;
+ --transition: 0.3s ease-in-out all;
+ --max-width: 1120px;
+ --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),
+ 0 2px 4px -1px rgba(0, 0, 0, 0.06);
+ --shadow-3: 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),
+ 0 10px 10px -5px rgba(0, 0, 0, 0.04);
+}
+
+body {
+ background: var(--backgroundColor);
+ font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen,
+ Ubuntu, Cantarell, 'Open Sans', 'Helvetica Neue', sans-serif;
+ font-weight: 400;
+ line-height: 1.75;
+ color: var(--textColor);
+}
+
+p {
+ margin-bottom: 1.5rem;
+ max-width: 40em;
+}
+
+h1,
+h2,
+h3,
+h4,
+h5 {
+ margin: 0;
+ margin-bottom: 1.38rem;
+ font-weight: 400;
+ line-height: 1.3;
+ text-transform: capitalize;
+ letter-spacing: var(--letterSpacing);
+}
+
+h1 {
+ margin-top: 0;
+ font-size: 3.052rem;
+}
+
+h2 {
+ font-size: 2.441rem;
+}
+
+h3 {
+ font-size: 1.953rem;
+}
+
+h4 {
+ font-size: 1.563rem;
+}
+
+h5 {
+ font-size: 1.25rem;
+}
+
+small,
+.text-small {
+ font-size: var(--small-text);
+}
+
+a {
+ text-decoration: none;
+}
+ul {
+ list-style-type: none;
+ padding: 0;
+}
+
+.img {
+ width: 100%;
+ display: block;
+ object-fit: cover;
+}
+/* buttons */
+
+.btn {
+ cursor: pointer;
+ color: var(--white);
+ background: var(--primary-500);
+ border: transparent;
+ border-radius: var(--borderRadius);
+ letter-spacing: var(--letterSpacing);
+ padding: 0.375rem 0.75rem;
+ box-shadow: var(--shadow-1);
+ transition: var(--transition);
+ text-transform: capitalize;
+ display: inline-block;
+}
+.btn:hover {
+ background: var(--primary-700);
+ box-shadow: var(--shadow-3);
+}
+
+@keyframes spinner {
+ to {
+ transform: rotate(360deg);
+ }
+}
+
+.loading {
+ width: 6rem;
+ height: 6rem;
+ border: 5px solid var(--grey-400);
+ border-radius: 50%;
+ border-top-color: var(--primary-500);
+ animation: spinner 0.6s linear infinite;
+}
+.loading {
+ margin: 0 auto;
+ margin-top: 4rem;
+}
+/* title */
+
+.title {
+ text-align: center;
+ margin-bottom: 3rem;
+}
+
+.title-underline {
+ background: var(--primary-500);
+ width: 7rem;
+ height: 0.25rem;
+ margin: 0 auto;
+ margin-top: -1rem;
+}
+
+/*
+===============
+Wikipedia
+===============
+*/
+.wiki {
+ padding: 5rem 0;
+ width: 90vw;
+ max-width: 1170px;
+ margin: 0 auto;
+}
+.container {
+ text-align: center;
+}
+.container img {
+ width: 200px;
+}
+.container h3 {
+ margin-bottom: 2rem;
+}
+
+.form {
+ background: #fff;
+ width: 100%;
+ margin: 0 auto;
+ padding: 2.5rem;
+ border-radius: var(--borderRadius);
+ display: grid;
+ grid-template-columns: auto 100px;
+}
+.form-input,
+.submit-btn {
+ padding: 0.375rem 0.75rem;
+ background: var(--backgroundColor);
+ border: 1px solid var(--grey-200);
+}
+.form-input {
+ border-right: transparent;
+ border-top-left-radius: var(--borderRadius);
+ border-bottom-left-radius: var(--borderRadius);
+}
+.submit-btn {
+ border: 1px solid var(--primary-500);
+ border-left: transparent;
+ border-top-right-radius: var(--borderRadius);
+ border-bottom-right-radius: var(--borderRadius);
+ text-transform: capitalize;
+ background: var(--primary-500);
+ color: var(--white);
+ transition: var(--transition);
+ cursor: pointer;
+}
+.submit-btn:hover {
+ color: var(--primary-900);
+}
+
+.results {
+ padding: 2rem 0;
+}
+
+.error {
+ text-align: center;
+ text-transform: capitalize;
+ color: var(--red-dark);
+}
+
+.articles {
+ display: grid;
+ gap: 1rem;
+}
+
+.articles a {
+ display: block;
+ background: var(--white);
+ color: var(--textColor);
+ padding: 1.5rem 2rem;
+ border-radius: var(--borderRadius);
+ transition: var(--transition);
+}
+.articles p {
+ color: var(--grey-500);
+ transition: var(--transition);
+}
+
+.articles a:hover {
+ background: var(--black);
+ color: var(--white);
+}
+
+@media screen and (min-width: 768px) {
+ .articles {
+ grid-template-columns: 1fr 1fr;
+ }
+ .form {
+ max-width: 600px;
+ }
+}
+@media screen and (min-width: 992px) {
+ .articles {
+ grid-template-columns: 1fr 1fr 1fr;
+ }
+}
diff --git a/28-wikipedia/starter/wiki-logo.png b/28-wikipedia/starter/wiki-logo.png
new file mode 100644
index 000000000..1bb8adee3
Binary files /dev/null and b/28-wikipedia/starter/wiki-logo.png differ
diff --git a/28-wikipedia/starter/wiki-urls.js b/28-wikipedia/starter/wiki-urls.js
new file mode 100644
index 000000000..433d81c2e
--- /dev/null
+++ b/28-wikipedia/starter/wiki-urls.js
@@ -0,0 +1,10 @@
+const url =
+ 'https://en.wikipedia.org/w/api.php?action=query&list=search&srlimit=20&format=json&origin=*&srsearch=searchValue';
+
+// list=search - perform a full text search
+// srsearch="inputValue" - search for page titles or content matching this value.
+// srlimit=20 How many total pages to return.
+// format=json json response
+// "origin=*" fix cors errors
+
+const page_url = 'href=http://en.wikipedia.org/?curid=${pageid}';
diff --git a/29-comfy-store/.vscode/settings.json b/29-comfy-store/.vscode/settings.json
new file mode 100644
index 000000000..6b665aaa0
--- /dev/null
+++ b/29-comfy-store/.vscode/settings.json
@@ -0,0 +1,3 @@
+{
+ "liveServer.settings.port": 5501
+}
diff --git a/29-comfy-store/final/about.html b/29-comfy-store/final/about.html
new file mode 100644
index 000000000..780dcdfe5
--- /dev/null
+++ b/29-comfy-store/final/about.html
@@ -0,0 +1,130 @@
+
+
+
+
+
+ About | Comfy
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 1
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ total : $12.99
+
+ checkout
+
+
+
+
+
+
+
/ our history
+
+
+ Lorem ipsum, dolor sit amet consectetur adipisicing elit. Fugiat
+ accusantium sapiente tempora sed dolore esse deserunt eaque excepturi,
+ delectus error accusamus vel eligendi, omnis beatae. Quisquam, dicta.
+ Eos quod quisquam esse recusandae vitae neque dolore, obcaecati incidunt
+ sequi blanditiis est exercitationem molestiae delectus saepe odio
+ eligendi modi porro eaque in libero minus unde sapiente consectetur
+ architecto. Ullam rerum, nemo iste ex, eaque perspiciatis nisi, eum
+ totam velit saepe sed quos similique amet. Ex, voluptate accusamus
+ nesciunt totam vitae esse iste.
+
+
+
+
+
diff --git a/29-comfy-store/final/images/logo-black.svg b/29-comfy-store/final/images/logo-black.svg
new file mode 100644
index 000000000..f12a1801a
--- /dev/null
+++ b/29-comfy-store/final/images/logo-black.svg
@@ -0,0 +1,16 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/29-comfy-store/final/images/logo-white.svg b/29-comfy-store/final/images/logo-white.svg
new file mode 100644
index 000000000..fa15b575a
--- /dev/null
+++ b/29-comfy-store/final/images/logo-white.svg
@@ -0,0 +1,16 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/29-comfy-store/final/images/main-bcg.jpeg b/29-comfy-store/final/images/main-bcg.jpeg
new file mode 100644
index 000000000..2dd3e455e
Binary files /dev/null and b/29-comfy-store/final/images/main-bcg.jpeg differ
diff --git a/29-comfy-store/final/index.html b/29-comfy-store/final/index.html
new file mode 100644
index 000000000..6ee4ca023
--- /dev/null
+++ b/29-comfy-store/final/index.html
@@ -0,0 +1,152 @@
+
+
+
+
+
+
+
+
+
+
+ Home | Comfy
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 1
+
+
+
+
+
+
+
+ rest, relax, unwind
+
+
Embrace your choices - we do
+
+ show now
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ total : $12.99
+
+ checkout
+
+
+
+
+
+
+
+
diff --git a/29-comfy-store/final/index.js b/29-comfy-store/final/index.js
new file mode 100644
index 000000000..0d604d567
--- /dev/null
+++ b/29-comfy-store/final/index.js
@@ -0,0 +1,21 @@
+// global imports
+import './src/toggleSidebar.js';
+import './src/cart/toggleCart.js';
+import './src/cart/setupCart.js';
+// specific imports
+import fetchProducts from './src/fetchProducts.js';
+import { setupStore, store } from './src/store.js';
+import display from './src/displayProducts.js';
+import { getElement } from './src/utils.js';
+
+const init = async () => {
+ const products = await fetchProducts();
+ if (products) {
+ // add products to the store
+ setupStore(products);
+ const featured = store.filter((product) => product.featured === true);
+ display(featured, getElement('.featured-center'));
+ }
+};
+
+window.addEventListener('DOMContentLoaded', init);
diff --git a/29-comfy-store/final/product.html b/29-comfy-store/final/product.html
new file mode 100644
index 000000000..3da03a7c7
--- /dev/null
+++ b/29-comfy-store/final/product.html
@@ -0,0 +1,143 @@
+
+
+
+
+
+ Single Product
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 1
+
+
+
+
+
+
+
Home / Single Product
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ total : $12.99
+
+ checkout
+
+
+
+
+
+
+
+
+
+
couch
+
+ by marcos
+
+
$30.00
+
+
+ Lorem ipsum dolor sit amet, consectetur adipisicing elit. Id,
+ modi? Minima libero doloremque necessitatibus! Praesentium
+ recusandae quod nesciunt animi voluptatem!
+
+
+ add to cart
+
+
+
+
+
+
+
loading...
+
+
+
+
diff --git a/29-comfy-store/final/products.html b/29-comfy-store/final/products.html
new file mode 100644
index 000000000..897fbce6b
--- /dev/null
+++ b/29-comfy-store/final/products.html
@@ -0,0 +1,148 @@
+
+
+
+
+
+ Products | Comfy
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 1
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ total : $12.99
+
+ checkout
+
+
+
+
+
+
+
+
+
+
+
+
Company
+
+ all
+ ikea
+
+
+
Price
+
+
+
+
+
+
+
+
+
+
Loading...
+
+
+
+
diff --git a/29-comfy-store/final/src/cart/addToCartDOM.js b/29-comfy-store/final/src/cart/addToCartDOM.js
new file mode 100644
index 000000000..c7414043b
--- /dev/null
+++ b/29-comfy-store/final/src/cart/addToCartDOM.js
@@ -0,0 +1,31 @@
+import { formatPrice, getElement } from '../utils.js';
+const cartItemsDOM = getElement('.cart-items');
+const addToCartDOM = ({ id, name, price, image, amount }) => {
+ const article = document.createElement('article');
+ article.classList.add('cart-item');
+ article.setAttribute('data-id', id);
+ article.innerHTML = `
+
+
+
${name}
+
${formatPrice(price)}
+
remove
+
+
+
+
+
+
+
${amount}
+
+
+
+
+ `;
+ cartItemsDOM.appendChild(article);
+};
+
+export default addToCartDOM;
diff --git a/29-comfy-store/final/src/cart/setupCart.js b/29-comfy-store/final/src/cart/setupCart.js
new file mode 100644
index 000000000..79899fdbe
--- /dev/null
+++ b/29-comfy-store/final/src/cart/setupCart.js
@@ -0,0 +1,131 @@
+// import
+import {
+ getStorageItem,
+ setStorageItem,
+ formatPrice,
+ getElement,
+} from '../utils.js';
+import { openCart } from './toggleCart.js';
+import { findProduct } from '../store.js';
+import addToCartDOM from './addToCartDOM.js';
+// set items
+
+const cartItemCountDOM = getElement('.cart-item-count');
+const cartItemsDOM = getElement('.cart-items');
+const cartTotalDOM = getElement('.cart-total');
+
+let cart = getStorageItem('cart');
+
+export const addToCart = (id) => {
+ let item = cart.find((cartItem) => cartItem.id === id);
+
+ if (!item) {
+ let product = findProduct(id);
+ // add item to the the
+ product = { ...product, amount: 1 };
+ cart = [...cart, product];
+ // add item to the DOM;
+ addToCartDOM(product);
+ } else {
+ // update values
+ const amount = increaseAmount(id);
+ const items = [...cartItemsDOM.querySelectorAll('.cart-item-amount')];
+ const newAmount = items.find((value) => value.dataset.id === id);
+ newAmount.textContent = amount;
+ }
+ // add one to the item count
+ displayCartItemCount();
+ // display cart totals
+ displayCartTotal();
+ // set cart in local storage
+
+ setStorageItem('cart', cart);
+ //more stuff coming up
+ openCart();
+};
+function displayCartItemCount() {
+ const amount = cart.reduce((total, cartItem) => {
+ return (total += cartItem.amount);
+ }, 0);
+ cartItemCountDOM.textContent = amount;
+}
+function displayCartTotal() {
+ let total = cart.reduce((total, cartItem) => {
+ return (total += cartItem.price * cartItem.amount);
+ }, 0);
+ cartTotalDOM.textContent = `Total : ${formatPrice(total)} `;
+}
+function displayCartItemsDOM() {
+ cart.forEach((cartItem) => {
+ addToCartDOM(cartItem);
+ });
+}
+function removeItem(id) {
+ cart = cart.filter((cartItem) => cartItem.id !== id);
+}
+function increaseAmount(id) {
+ let newAmount;
+ cart = cart.map((cartItem) => {
+ if (cartItem.id === id) {
+ newAmount = cartItem.amount + 1;
+ cartItem = { ...cartItem, amount: newAmount };
+ }
+ return cartItem;
+ });
+ return newAmount;
+}
+function decreaseAmount(id) {
+ let newAmount;
+ cart = cart.map((cartItem) => {
+ if (cartItem.id === id) {
+ newAmount = cartItem.amount - 1;
+ cartItem = { ...cartItem, amount: newAmount };
+ }
+ return cartItem;
+ });
+ return newAmount;
+}
+
+function setupCartFunctionality() {
+ cartItemsDOM.addEventListener('click', function (e) {
+ const element = e.target;
+ const parent = e.target.parentElement;
+ const id = e.target.dataset.id;
+ const parentID = e.target.parentElement.dataset.id;
+ // remove
+ if (element.classList.contains('cart-item-remove-btn')) {
+ removeItem(id);
+ // parent.parentElement.remove();
+ element.parentElement.parentElement.remove();
+ }
+ // increase
+ if (parent.classList.contains('cart-item-increase-btn')) {
+ const newAmount = increaseAmount(parentID);
+ parent.nextElementSibling.textContent = newAmount;
+ }
+ // decrease
+ if (parent.classList.contains('cart-item-decrease-btn')) {
+ const newAmount = decreaseAmount(parentID);
+ if (newAmount === 0) {
+ removeItem(parentID);
+ parent.parentElement.parentElement.remove();
+ } else {
+ parent.previousElementSibling.textContent = newAmount;
+ }
+ }
+ displayCartItemCount();
+ displayCartTotal();
+ setStorageItem('cart', cart);
+ });
+}
+const init = () => {
+ // display amount of cart items
+ displayCartItemCount();
+ // display total
+ displayCartTotal();
+ // add all cart items to the dom
+ displayCartItemsDOM();
+ // setup cart functionality
+ setupCartFunctionality();
+};
+init();
diff --git a/29-comfy-store/final/src/cart/toggleCart.js b/29-comfy-store/final/src/cart/toggleCart.js
new file mode 100644
index 000000000..bc0814359
--- /dev/null
+++ b/29-comfy-store/final/src/cart/toggleCart.js
@@ -0,0 +1,16 @@
+import { getElement } from '../utils.js';
+
+const cartOverlay = getElement('.cart-overlay');
+const closeCartBtn = getElement('.cart-close');
+const toggleCartBtn = getElement('.toggle-cart');
+
+toggleCartBtn.addEventListener('click', () => {
+ cartOverlay.classList.add('show');
+});
+closeCartBtn.addEventListener('click', () => {
+ cartOverlay.classList.remove('show');
+});
+
+export const openCart = () => {
+ cartOverlay.classList.add('show');
+};
diff --git a/29-comfy-store/final/src/displayProducts.js b/29-comfy-store/final/src/displayProducts.js
new file mode 100644
index 000000000..6bf1c4af8
--- /dev/null
+++ b/29-comfy-store/final/src/displayProducts.js
@@ -0,0 +1,39 @@
+import { formatPrice } from './utils.js';
+import { addToCart } from './cart/setupCart.js';
+const display = (products, element, filters) => {
+ // display products
+ element.innerHTML = products
+ .map((product) => {
+ const { id, name, image, price } = product;
+ return `
+
+
+
+
+
+
+ ${name}
+ ${formatPrice(price)}
+
+ `;
+ })
+ .join('');
+
+ if (filters) return;
+
+ element.addEventListener('click', function (e) {
+ const parent = e.target.parentElement;
+ if (parent.classList.contains('product-cart-btn')) {
+ addToCart(parent.dataset.id);
+ }
+ });
+};
+
+export default display;
diff --git a/29-comfy-store/final/src/fetchProducts.js b/29-comfy-store/final/src/fetchProducts.js
new file mode 100644
index 000000000..a8b2ff12f
--- /dev/null
+++ b/29-comfy-store/final/src/fetchProducts.js
@@ -0,0 +1,11 @@
+import { allProductsUrl } from './utils.js';
+
+const fetchProducts = async () => {
+ const response = await fetch(allProductsUrl).catch((err) => console.log(err));
+ if (response) {
+ return response.json();
+ }
+ return response;
+};
+
+export default fetchProducts;
diff --git a/29-comfy-store/final/src/filters/companies.js b/29-comfy-store/final/src/filters/companies.js
new file mode 100644
index 000000000..5011ae529
--- /dev/null
+++ b/29-comfy-store/final/src/filters/companies.js
@@ -0,0 +1,29 @@
+import { getElement } from '../utils.js';
+import display from '../displayProducts.js';
+
+const setupCompanies = (store) => {
+ let companies = ['all', ...new Set(store.map((product) => product.company))];
+ const companiesDOM = getElement('.companies');
+ companiesDOM.innerHTML = companies
+ .map((company) => {
+ return ` ${company} `;
+ })
+ .join('');
+ companiesDOM.addEventListener('click', function (e) {
+ const element = e.target;
+ if (element.classList.contains('company-btn')) {
+ let newStore = [];
+ if (element.textContent === 'all') {
+ newStore = [...store];
+ } else {
+ newStore = store.filter(
+ (product) => product.company === e.target.textContent
+ );
+ }
+
+ display(newStore, getElement('.products-container'), true);
+ }
+ });
+};
+
+export default setupCompanies;
diff --git a/29-comfy-store/final/src/filters/price.js b/29-comfy-store/final/src/filters/price.js
new file mode 100644
index 000000000..0a334ed3e
--- /dev/null
+++ b/29-comfy-store/final/src/filters/price.js
@@ -0,0 +1,29 @@
+import { getElement } from '../utils.js';
+import display from '../displayProducts.js';
+
+const setupPrice = (store) => {
+ const priceInput = getElement('.price-filter');
+ const priceValue = getElement('.price-value');
+
+ // setup filter
+ let maxPrice = store.map((product) => product.price);
+ maxPrice = Math.max(...maxPrice);
+ maxPrice = Math.ceil(maxPrice / 100);
+ priceInput.value = maxPrice;
+ priceInput.max = maxPrice;
+ priceInput.min = 0;
+ priceValue.textContent = `Value : $${maxPrice}`;
+
+ priceInput.addEventListener('input', function () {
+ const value = parseInt(priceInput.value);
+ priceValue.textContent = `Value : $${value}`;
+ let newStore = store.filter((product) => product.price / 100 <= value);
+ display(newStore, getElement('.products-container'), true);
+ if (newStore.length < 1) {
+ const products = getElement('.products-container');
+ products.innerHTML = `sorry, no products matched your search `;
+ }
+ });
+};
+
+export default setupPrice;
diff --git a/29-comfy-store/final/src/filters/search.js b/29-comfy-store/final/src/filters/search.js
new file mode 100644
index 000000000..10d8ac9ec
--- /dev/null
+++ b/29-comfy-store/final/src/filters/search.js
@@ -0,0 +1,29 @@
+import { getElement } from '../utils.js';
+import display from '../displayProducts.js';
+const setupSearch = (store) => {
+ const form = getElement('.input-form');
+ const nameInput = getElement('.search-input');
+ form.addEventListener('keyup', function () {
+ const value = nameInput.value;
+ if (value) {
+ const newStore = store.filter((product) => {
+ let { name } = product;
+ name = name.toLowerCase();
+ if (name.startsWith(value)) {
+ return product;
+ }
+ });
+ display(newStore, getElement('.products-container'), true);
+ if (newStore.length < 1) {
+ const products = getElement('.products-container');
+ products.innerHTML = `
+ sorry, no products matched your search
+ `;
+ }
+ } else {
+ display(store, getElement('.products-container'), true);
+ }
+ });
+};
+
+export default setupSearch;
diff --git a/29-comfy-store/final/src/pages/about.js b/29-comfy-store/final/src/pages/about.js
new file mode 100644
index 000000000..e4bf4a496
--- /dev/null
+++ b/29-comfy-store/final/src/pages/about.js
@@ -0,0 +1,4 @@
+// global imports
+import '../toggleSidebar.js';
+import '../cart/toggleCart.js';
+import '../cart/setupCart.js';
diff --git a/29-comfy-store/final/src/pages/product.js b/29-comfy-store/final/src/pages/product.js
new file mode 100644
index 000000000..3c4a76e1b
--- /dev/null
+++ b/29-comfy-store/final/src/pages/product.js
@@ -0,0 +1,71 @@
+// global imports
+import '../toggleSidebar.js';
+import '../cart/toggleCart.js';
+import '../cart/setupCart.js';
+// specific
+import { addToCart } from '../cart/setupCart.js';
+import { singleProductUrl, getElement, formatPrice } from '../utils.js';
+
+// selections
+const loading = getElement('.page-loading');
+const centerDOM = getElement('.single-product-center');
+const pageTitleDOM = getElement('.page-hero-title');
+const imgDOM = getElement('.single-product-img');
+const titleDOM = getElement('.single-product-title');
+const companyDOM = getElement('.single-product-company');
+const priceDOM = getElement('.single-product-price');
+const colorsDOM = getElement('.single-product-colors');
+const descDOM = getElement('.single-product-desc');
+const cartBtn = getElement('.addToCartBtn');
+
+// cart product
+let productID;
+
+// show product when page loads
+window.addEventListener('DOMContentLoaded', async function () {
+ const urlID = window.location.search;
+
+ try {
+ const response = await fetch(`${singleProductUrl}${urlID}`);
+ if (response.status >= 200 && response.status <= 299) {
+ const product = await response.json();
+ // grab data
+ const { id, fields } = product;
+ productID = id;
+
+ const { name, company, price, colors, description } = fields;
+ const image = fields.image[0].thumbnails.large.url;
+ // set values
+
+ document.title = `${name.toUpperCase()} | Comfy`;
+ pageTitleDOM.textContent = `Home / ${name}`;
+ imgDOM.src = image;
+ titleDOM.textContent = name;
+ companyDOM.textContent = `by ${company}`;
+ priceDOM.textContent = formatPrice(price);
+ descDOM.textContent = description;
+ colors.forEach((color) => {
+ const span = document.createElement('span');
+ span.classList.add('product-color');
+ span.style.backgroundColor = `${color}`;
+ colorsDOM.appendChild(span);
+ });
+ } else {
+ console.log(response.status, response.statusText);
+ centerDOM.innerHTML = `
+
+ `;
+ }
+ } catch (error) {
+ console.log(error);
+ }
+
+ loading.style.display = 'none';
+});
+
+cartBtn.addEventListener('click', function () {
+ addToCart(productID);
+});
diff --git a/29-comfy-store/final/src/pages/products.js b/29-comfy-store/final/src/pages/products.js
new file mode 100644
index 000000000..436bdab2d
--- /dev/null
+++ b/29-comfy-store/final/src/pages/products.js
@@ -0,0 +1,32 @@
+// global imports
+import '../toggleSidebar.js';
+import '../cart/toggleCart.js';
+import '../cart/setupCart.js';
+
+// filter imports
+import setupSearch from '../filters/search.js';
+import setupCompanies from '../filters/companies.js';
+import setupPrice from '../filters/price.js';
+
+// specific imports
+import { store, setupStore } from '../store.js';
+import display from '../displayProducts.js';
+import { getElement } from '../utils.js';
+// import fetch products
+import fetchProducts from '../fetchProducts.js';
+
+const init = async () => {
+ const loading = getElement('.page-loading');
+ if (store.length < 1) {
+ const products = await fetchProducts();
+ setupStore(products);
+ }
+ display(store, getElement('.products-container'));
+
+ setupSearch(store);
+ setupCompanies(store);
+ setupPrice(store);
+ loading.style.display = 'none';
+};
+
+init();
diff --git a/29-comfy-store/final/src/store.js b/29-comfy-store/final/src/store.js
new file mode 100644
index 000000000..7a4cf8266
--- /dev/null
+++ b/29-comfy-store/final/src/store.js
@@ -0,0 +1,20 @@
+import { getStorageItem, setStorageItem } from './utils.js';
+let store = getStorageItem('store');
+const setupStore = (products) => {
+ store = products.map((product) => {
+ const {
+ id,
+ fields: { featured, name, price, company, colors, image: img },
+ } = product;
+ const image = img[0].thumbnails.large.url;
+ return { id, featured, name, price, company, colors, image };
+ });
+ setStorageItem('store', store);
+};
+
+const findProduct = (id) => {
+ let product = store.find((product) => product.id === id);
+ return product;
+};
+
+export { store, setupStore, findProduct };
diff --git a/29-comfy-store/final/src/toggleSidebar.js b/29-comfy-store/final/src/toggleSidebar.js
new file mode 100644
index 000000000..0c502561f
--- /dev/null
+++ b/29-comfy-store/final/src/toggleSidebar.js
@@ -0,0 +1,12 @@
+import { getElement } from './utils.js';
+
+const toggleNav = getElement('.toggle-nav');
+const sidebarOverlay = getElement('.sidebar-overlay');
+const closeBtn = getElement('.sidebar-close');
+
+toggleNav.addEventListener('click', () => {
+ sidebarOverlay.classList.add('show');
+});
+closeBtn.addEventListener('click', () => {
+ sidebarOverlay.classList.remove('show');
+});
diff --git a/29-comfy-store/final/src/utils.js b/29-comfy-store/final/src/utils.js
new file mode 100644
index 000000000..eeb4cb9bb
--- /dev/null
+++ b/29-comfy-store/final/src/utils.js
@@ -0,0 +1,49 @@
+// ATTENTION!!!!!!!!!!!
+// I SWITCHED TO PERMANENT DOMAIN
+// DATA IS THE SAME JUST A DIFFERENT URL,
+// DOES NOT AFFECT PROJECT FUNCTIONALITY
+
+const allProductsUrl = 'https://www.course-api.com/javascript-store-products';
+// temporary single product
+// 'https://www.course-api.com/javascript-store-single-product?id=rec43w3ipXvP28vog'
+const singleProductUrl =
+ 'https://www.course-api.com/javascript-store-single-product';
+
+const getElement = (selection) => {
+ const element = document.querySelector(selection);
+ if (element) return element;
+ throw new Error(
+ `Please check "${selection}" selector, no such element exist`
+ );
+};
+
+const formatPrice = (price) => {
+ let formattedPrice = new Intl.NumberFormat('en-US', {
+ style: 'currency',
+ currency: 'USD',
+ }).format((price / 100).toFixed(2));
+ return formattedPrice;
+};
+
+const getStorageItem = (item) => {
+ let storageItem = localStorage.getItem(item);
+ if (storageItem) {
+ storageItem = JSON.parse(localStorage.getItem(item));
+ } else {
+ storageItem = [];
+ }
+ return storageItem;
+};
+
+const setStorageItem = (name, item) => {
+ localStorage.setItem(name, JSON.stringify(item));
+};
+
+export {
+ allProductsUrl,
+ singleProductUrl,
+ getElement,
+ formatPrice,
+ getStorageItem,
+ setStorageItem,
+};
diff --git a/29-comfy-store/final/styles.css b/29-comfy-store/final/styles.css
new file mode 100644
index 000000000..e671ccd12
--- /dev/null
+++ b/29-comfy-store/final/styles.css
@@ -0,0 +1,777 @@
+@import url('https://fonts.googleapis.com/css2?family=Kaushan+Script&display=swap');
+/*
+===============
+Variables
+===============
+*/
+
+:root {
+ /* dark shades of primary color*/
+ --clr-primary-1: hsl(21, 91%, 17%);
+ --clr-primary-2: hsl(21, 84%, 25%);
+ --clr-primary-3: hsl(21, 81%, 29%);
+ --clr-primary-4: hsl(21, 77%, 34%);
+ /* primary/main color */
+ --clr-primary-5: hsl(21, 62%, 45%);
+ /* lighter shades of primary color */
+ --clr-primary-6: hsl(21, 57%, 50%);
+ --clr-primary-7: hsl(21, 65%, 59%);
+ --clr-primary-8: hsl(21, 80%, 74%);
+ --clr-primary-9: hsl(21, 94%, 87%);
+ --clr-primary-10: hsl(21, 100%, 94%);
+ /* darkest grey - used for headings */
+ --clr-grey-1: #102a42;
+ --clr-grey-2: hsl(211, 39%, 23%);
+ --clr-grey-3: hsl(209, 34%, 30%);
+ --clr-grey-4: hsl(209, 28%, 39%);
+ /* grey used for paragraphs */
+ --clr-grey-5: hsl(210, 22%, 49%);
+ --clr-grey-6: hsl(209, 23%, 60%);
+ --clr-grey-7: hsl(211, 27%, 70%);
+ --clr-grey-8: hsl(210, 31%, 80%);
+ --clr-grey-9: hsl(212, 33%, 89%);
+ --clr-grey-10: hsl(210, 36%, 96%);
+ --clr-white: #fff;
+ --clr-red-dark: hsl(360, 67%, 44%);
+ --clr-red-light: hsl(360, 71%, 66%);
+ --clr-green-dark: hsl(125, 67%, 44%);
+ --clr-green-light: hsl(125, 71%, 66%);
+ --clr-black: #222;
+
+ --transition: all 0.3s linear;
+ --spacing: 0.1rem;
+ --radius: 0.25rem;
+ --light-shadow: 0 5px 15px rgba(0, 0, 0, 0.1);
+ --dark-shadow: 0 5px 15px rgba(0, 0, 0, 0.2);
+ --max-width: 1170px;
+ --fixed-width: 620px;
+}
+/*
+===============
+Global Styles
+===============
+*/
+
+*,
+::after,
+::before {
+ margin: 0;
+ padding: 0;
+ box-sizing: border-box;
+}
+body {
+ font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen,
+ Ubuntu, Cantarell, 'Open Sans', 'Helvetica Neue', sans-serif;
+ background: var(--clr-white);
+ color: var(--clr-grey-1);
+ line-height: 1.5;
+ font-size: 0.875rem;
+}
+ul {
+ list-style-type: none;
+}
+a {
+ text-decoration: none;
+}
+h1,
+h2,
+h3,
+h4 {
+ letter-spacing: var(--spacing);
+ text-transform: capitalize;
+ line-height: 1.25;
+ margin-bottom: 0.75rem;
+ font-weight: 400;
+}
+h1 {
+ font-size: 3rem;
+}
+h2 {
+ font-size: 2rem;
+}
+h3 {
+ font-size: 1.25rem;
+}
+h4 {
+ font-size: 0.875rem;
+}
+p {
+ margin-bottom: 1.25rem;
+ color: var(--clr-grey-5);
+}
+@media screen and (min-width: 800px) {
+ h1 {
+ font-size: 4rem;
+ }
+ h2 {
+ font-size: 2.5rem;
+ }
+ h3 {
+ font-size: 1.75rem;
+ }
+ h4 {
+ font-size: 1rem;
+ }
+ body {
+ font-size: 1rem;
+ }
+ h1,
+ h2,
+ h3,
+ h4 {
+ line-height: 1;
+ }
+}
+.img {
+ width: 100%;
+ display: block;
+}
+.text-slanted {
+ font-family: 'Kaushan Script', cursive;
+}
+.section-center {
+ width: 90vw;
+ max-width: var(--max-width);
+ margin: 0 auto;
+}
+.section {
+ padding: 5rem 0;
+}
+.btn {
+ background: var(--clr-primary-5);
+ color: var(--clr-white);
+ border-radius: var(--radius);
+ padding: 0.375rem 0.75rem;
+ text-transform: uppercase;
+ letter-spacing: var(--spacing);
+ display: inline-block;
+ transition: var(--transition);
+ border-color: transparent;
+ cursor: pointer;
+}
+.btn:hover {
+ background: var(--clr-primary-7);
+ color: var(--clr-black);
+}
+.section-loading {
+ text-align: center;
+ position: absolute;
+ top: 2rem;
+ left: 50%;
+ transform: translate(-50%, -50%);
+ text-align: center;
+}
+.page-loading {
+ position: fixed;
+ top: 0;
+ left: 0;
+ width: 100%;
+ height: 100%;
+ background: var(--clr-grey-10);
+ display: flex;
+ align-items: center;
+ justify-content: center;
+}
+/*
+===============
+Navbar
+===============
+*/
+
+.navbar {
+ height: 6rem;
+ background: transparent;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+}
+
+.nav-center {
+ width: 90vw;
+ max-width: var(--max-width);
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+}
+.nav-links {
+ display: none;
+}
+.toggle-nav {
+ background: var(--clr-primary-5);
+ border-color: transparent;
+ color: var(--clr-white);
+ width: 3.75rem;
+ height: 2.25rem;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ font-size: 1.5rem;
+ border-radius: 2rem;
+ cursor: pointer;
+ transition: var(--transition);
+}
+.toggle-nav:hover {
+ background: var(--clr-primary-3);
+}
+.toggle-container {
+ position: relative;
+ margin-top: 0.75rem;
+}
+.toggle-cart {
+ background: transparent;
+ border-color: transparent;
+ font-size: 1.6rem;
+ color: var(--clr-white);
+ cursor: pointer;
+}
+.cart-item-count {
+ position: absolute;
+ top: -0.85rem;
+ right: -0.85rem;
+ background: var(--clr-primary-5);
+ width: 1.75rem;
+ height: 1.75rem;
+ display: grid;
+ place-items: center;
+ border-radius: 50%;
+ color: var(--clr-white);
+ font-weight: bold;
+ font-size: 1rem;
+}
+@media screen and (min-width: 800px) {
+ .nav-center {
+ position: relative;
+ }
+ .nav-logo {
+ position: absolute;
+ top: 50%;
+ left: 65%;
+ transform: translate(-50%, -50%);
+ }
+ .toggle-nav {
+ display: none;
+ }
+ .nav-links {
+ display: flex;
+ font-size: 1.5rem;
+ text-transform: capitalize;
+ }
+ .nav-link {
+ color: var(--clr-white);
+ margin-right: 3rem;
+ letter-spacing: var(--spacing);
+ transition: var(--transition);
+ font-size: 1.25rem;
+ }
+ .nav-link:hover {
+ color: var(--clr-primary-5);
+ }
+}
+@media screen and (min-width: 992px) {
+ .nav-logo {
+ left: 50%;
+ }
+}
+/* page navbar */
+.page .nav-link {
+ color: var(--clr-grey-1);
+}
+.page .nav-link:hover {
+ color: var(--clr-primary-5);
+}
+.page .toggle-cart {
+ color: var(--clr-grey-1);
+}
+/*
+===============
+Hero
+===============
+*/
+.hero {
+ min-height: 100vh;
+ margin-top: -6rem;
+ background: linear-gradient(rgba(0, 0, 0, 0.5), rgba(0, 0, 0, 0.5)),
+ url(./images/main-bcg.jpeg) center/cover;
+ display: grid;
+ place-items: center;
+ color: var(--clr-white);
+}
+.hero-container {
+ width: 90vw;
+ max-width: var(--max-width);
+}
+.hero h1 {
+ font-weight: 700;
+}
+.hero h3 {
+ text-transform: none;
+ font-size: 1.5rem;
+}
+.hero-btn {
+ color: var(--clr-white);
+ background: transparent;
+ border: 1px solid var(--clr-white);
+ padding: 0.5rem 0.75rem;
+ display: inline-block;
+ margin-top: 0.75rem;
+ text-transform: uppercase;
+ letter-spacing: var(--spacing);
+ border-radius: var(--radius);
+ transition: var(--transition);
+}
+.hero-btn:hover {
+ background: var(--clr-white);
+ color: var(--clr-primary-5);
+}
+
+@media screen and (min-width: 800px) {
+ .hero h3 {
+ font-size: 1.5rem;
+ margin: 1rem 0;
+ }
+}
+
+@media screen and (min-width: 992px) {
+ .hero h1 {
+ font-size: 5.25rem;
+ letter-spacing: 5px;
+ }
+ .hero h3 {
+ font-size: 2.75rem;
+ margin: 1.5rem 0;
+ }
+}
+
+/*
+===============
+Sidebar
+===============
+*/
+.sidebar-overlay {
+ position: fixed;
+ top: 0;
+ left: 0;
+ width: 100%;
+ height: 100%;
+ display: grid;
+ place-items: center;
+ z-index: -1;
+ transition: var(--transition);
+ opacity: 0;
+ background: rgba(0, 0, 0, 0.5);
+}
+.sidebar-overlay.show {
+ opacity: 1;
+ z-index: 100;
+}
+.sidebar {
+ width: 90vw;
+ height: 95vh;
+ max-width: var(--fixed-width);
+ background: var(--clr-white);
+ border-radius: var(--radius);
+ box-shadow: var(--dark-shadow);
+ position: relative;
+ padding: 4rem;
+ transform: scale(0);
+}
+.show .sidebar {
+ transform: scale(1);
+}
+.sidebar-close {
+ font-size: 2rem;
+ background: transparent;
+ border-color: transparent;
+ color: var(--clr-grey-5);
+ position: absolute;
+ top: 1rem;
+ right: 1rem;
+ cursor: pointer;
+}
+.sidebar-link {
+ font-size: 1.5rem;
+ text-transform: capitalize;
+ color: var(--clr-grey-1);
+}
+.sidebar-link i {
+ color: var(--clr-grey-5);
+ margin-right: 1rem;
+ margin-bottom: 1rem;
+}
+@media screen and (min-width: 800px) {
+ .sidebar-overlay {
+ display: none;
+ }
+}
+/*
+===============
+Cart
+===============
+*/
+.cart-overlay {
+ position: fixed;
+ top: 0;
+ left: 0;
+ width: 100%;
+ height: 100%;
+ background: rgba(0, 0, 0, 0.5);
+ transition: var(--transition);
+ opacity: 0;
+ z-index: -1;
+}
+.cart-overlay.show {
+ opacity: 1;
+ z-index: 100;
+}
+.cart {
+ position: fixed;
+ top: 0;
+ right: 0;
+ width: 100%;
+ height: 100%;
+ max-width: 400px;
+ background: var(--clr-grey-10);
+ padding: 3rem 1rem 0 1rem;
+ display: grid;
+ grid-template-rows: auto 1fr auto;
+ transition: var(--transition);
+ transform: translateX(100%);
+ overflow: scroll;
+}
+.show .cart {
+ transform: translateX(0);
+}
+.cart-close {
+ font-size: 2rem;
+ background: transparent;
+ border-color: transparent;
+ color: var(--clr-grey-5);
+ position: absolute;
+ top: 0.5rem;
+ left: 1rem;
+ cursor: pointer;
+}
+.cart header {
+ text-align: center;
+}
+.cart header h3 {
+ font-weight: 500;
+}
+.cart-total {
+ text-align: center;
+ margin-bottom: 2rem;
+ font-weight: 500;
+}
+.cart-checkout {
+ display: block;
+ width: 75%;
+ margin: 0 auto;
+ margin-bottom: 3rem;
+}
+/* cart item */
+.cart-item {
+ margin: 1rem 0;
+ display: grid;
+ grid-template-columns: auto 1fr auto;
+
+ column-gap: 1.5rem;
+ align-items: center;
+}
+.cart-item-img {
+ width: 75px;
+ height: 50px;
+ object-fit: cover;
+ border-radius: var(--radius);
+}
+.cart-item-name {
+ margin-bottom: 0.15rem;
+}
+.cart-item-price {
+ margin-bottom: 0;
+ font-size: 0.75rem;
+ color: var(--clr-grey-3);
+}
+.cart-item-remove-btn {
+ background: transparent;
+ border-color: transparent;
+ color: var(--clr-grey-5);
+ letter-spacing: var(--spacing);
+ cursor: pointer;
+}
+.cart-item-amount {
+ margin-bottom: 0;
+ text-align: center;
+ color: var(--clr-grey-3);
+ line-height: 1;
+}
+.cart-item-increase-btn,
+.cart-item-decrease-btn {
+ background: transparent;
+ border-color: transparent;
+ color: var(--clr-primary-5);
+ cursor: pointer;
+ font-size: 0.85rem;
+ padding: 0.25rem;
+}
+/*
+===============
+Title
+===============
+*/
+
+.title h2 {
+ display: flex;
+ justify-content: center;
+ align-items: center;
+ font-weight: 500;
+}
+.title span {
+ color: var(--clr-primary-5);
+ font-size: 0.85em;
+ margin-right: 1rem;
+}
+/*
+===============
+product
+===============
+*/
+.product-img {
+ height: 15rem;
+ object-fit: cover;
+ border-radius: var(--radius);
+}
+.product-container {
+ position: relative;
+}
+.product-icons {
+ position: absolute;
+ top: 50%;
+ left: 50%;
+ transform: translate(-50%, -50%);
+ opacity: 0;
+ display: flex;
+ transition: var(--transition);
+}
+.product-icon {
+ width: 2.25rem;
+ height: 2.25rem;
+ background: var(--clr-primary-5);
+ color: var(--clr-white);
+ display: grid;
+ place-items: center;
+ border-radius: 50%;
+ transition: var(--transition);
+ cursor: pointer;
+ font-size: 1rem;
+ border-color: transparent;
+ margin: 0 0.5rem;
+}
+.product-icon:hover {
+ background: var(--clr-primary-7);
+}
+.product-container:hover .product-icons {
+ opacity: 1;
+}
+.product footer {
+ padding: 0.75rem 0;
+ text-align: center;
+}
+.product-name {
+ margin-bottom: 0.25rem;
+ text-transform: capitalize;
+ letter-spacing: var(--spacing);
+}
+.product-price {
+ margin-bottom: 0;
+ color: var(--clr-grey-3);
+ font-weight: 700;
+}
+
+.featured-center {
+ margin: 3rem auto 2rem auto;
+ display: grid;
+ gap: 1rem;
+ min-height: 6rem;
+ position: relative;
+}
+.featured .btn {
+ display: block;
+ width: 11rem;
+ margin: 0 auto;
+ text-align: center;
+}
+@media screen and (min-width: 992px) {
+ .featured-center {
+ display: grid;
+ grid-template-columns: 1fr 1fr;
+ }
+}
+@media screen and (min-width: 1200px) {
+ .featured-center {
+ display: grid;
+ grid-template-columns: 1fr 1fr 1fr;
+ }
+ .product .img {
+ height: 13rem;
+ }
+}
+/*
+===============
+About Page
+===============
+*/
+.about-text {
+ line-height: 2;
+ max-width: 45em;
+ margin: 0 auto;
+ margin-top: 2rem;
+}
+
+/*
+===============
+Products Page
+===============
+*/
+.products {
+ width: 90vw;
+ display: grid;
+ grid-gap: 1rem;
+ margin: 4rem auto;
+ max-width: var(--max-width);
+ position: relative;
+}
+.filters-container {
+ position: sticky;
+ top: 1rem;
+}
+.filters h4 {
+ font-weight: 500;
+ margin: 1.5rem 0 0.5rem;
+}
+
+.search-input {
+ padding: 0.5rem;
+ background: var(--clr-grey-10);
+ border-radius: var(--radius);
+ border-color: transparent;
+ letter-spacing: var(--spacing);
+}
+.search-input::placeholder {
+ text-transform: capitalize;
+}
+.company-btn {
+ display: block;
+ margin: 0.25em 0;
+ padding: 0.25rem;
+ text-transform: capitalize;
+ background: transparent;
+ border-color: transparent;
+ letter-spacing: var(--spacing);
+ color: var(--clr-grey-5);
+ cursor: pointer;
+ transition: var(--transition);
+}
+.company-btn:hover {
+ color: var(--clr-grey-3);
+}
+.price-filter {
+ background: var(--clr-grey-5) !important;
+ color: var(--clr-grey-5);
+}
+@media screen and (min-width: 768px) {
+ .products {
+ grid-template-columns: 200px 1fr;
+ }
+ .categories {
+ position: sticky;
+ top: 1rem;
+ }
+}
+@media screen and (min-width: 992px) {
+ .products-container {
+ display: grid;
+ grid-template-columns: repeat(2, 1fr);
+ gap: 1rem;
+ }
+ .products-container .product-img {
+ height: 10rem;
+ }
+ .products-container .product-name {
+ font-size: 0.85rem;
+ }
+ .products-container .product-price {
+ font-size: 0.85rem;
+ }
+}
+@media screen and (min-width: 1170px) {
+ .products-container {
+ grid-template-columns: repeat(3, 1fr);
+ }
+}
+.filter-error {
+ position: absolute;
+ top: 5rem;
+ left: 0;
+ width: 100%;
+ text-align: center;
+ margin-top: 4rem;
+}
+/*
+===============
+Single Product Page
+===============
+*/
+.page-hero {
+ min-height: 20vh;
+ display: grid;
+ place-items: center;
+ background: var(--clr-grey-10);
+ color: var(--clr-grey-5);
+}
+.page-hero-title {
+ font-weight: 500;
+}
+.single-product {
+ padding: 2rem 0;
+}
+.single-product-center {
+ margin: 2rem auto;
+ display: grid;
+ gap: 1rem 2rem;
+}
+.single-product-img {
+ height: 25rem;
+ border-radius: var(--radius);
+ object-fit: cover;
+}
+.single-product-company {
+ font-size: 1.2rem;
+ color: var(--clr-grey-8);
+ text-transform: uppercase;
+ letter-spacing: var(--spacing);
+ margin-bottom: 1.25rem;
+}
+.single-product-price {
+ color: var(--clr-grey-3);
+ font-size: 1.25rem;
+ font-weight: 500;
+}
+
+.product-color {
+ display: inline-block;
+ width: 1rem;
+ height: 1rem;
+ border-radius: 50%;
+ background: #222;
+ margin: 0.5rem 0.5rem 1.5rem 0;
+}
+
+.single-product-desc {
+ max-width: 25em;
+ line-height: 1.8;
+}
+@media screen and (min-width: 992px) {
+ .single-product-center {
+ grid-template-columns: 1fr 1fr;
+ }
+}
diff --git a/29-comfy-store/starter/about.html b/29-comfy-store/starter/about.html
new file mode 100644
index 000000000..6c79eed51
--- /dev/null
+++ b/29-comfy-store/starter/about.html
@@ -0,0 +1,21 @@
+
+
+
+
+
+ About | Comfy
+
+
+
+
+
+
+
+
+ about page
+
+
+
diff --git a/29-comfy-store/starter/images/logo-black.svg b/29-comfy-store/starter/images/logo-black.svg
new file mode 100644
index 000000000..f12a1801a
--- /dev/null
+++ b/29-comfy-store/starter/images/logo-black.svg
@@ -0,0 +1,16 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/29-comfy-store/starter/images/logo-white.svg b/29-comfy-store/starter/images/logo-white.svg
new file mode 100644
index 000000000..fa15b575a
--- /dev/null
+++ b/29-comfy-store/starter/images/logo-white.svg
@@ -0,0 +1,16 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/29-comfy-store/starter/images/main-bcg.jpeg b/29-comfy-store/starter/images/main-bcg.jpeg
new file mode 100644
index 000000000..2dd3e455e
Binary files /dev/null and b/29-comfy-store/starter/images/main-bcg.jpeg differ
diff --git a/29-comfy-store/starter/index.html b/29-comfy-store/starter/index.html
new file mode 100644
index 000000000..266e65945
--- /dev/null
+++ b/29-comfy-store/starter/index.html
@@ -0,0 +1,20 @@
+
+
+
+
+
+
+
+
+
+
+ Home | Comfy
+
+
+ home page
+
+
+
diff --git a/29-comfy-store/starter/index.js b/29-comfy-store/starter/index.js
new file mode 100644
index 000000000..6204a24d6
--- /dev/null
+++ b/29-comfy-store/starter/index.js
@@ -0,0 +1,9 @@
+// global imports
+import './src/toggleSidebar.js';
+import './src/cart/toggleCart.js';
+import './src/cart/setupCart.js';
+// specific imports
+import fetchProducts from './src/fetchProducts.js';
+import { setupStore, store } from './src/store.js';
+import display from './src/displayProducts.js';
+import { getElement } from './src/utils.js';
diff --git a/29-comfy-store/starter/product.html b/29-comfy-store/starter/product.html
new file mode 100644
index 000000000..66cb456a2
--- /dev/null
+++ b/29-comfy-store/starter/product.html
@@ -0,0 +1,20 @@
+
+
+
+
+
+ Single Product
+
+
+
+
+
+
+
+ single product
+
+
+
diff --git a/29-comfy-store/starter/products.html b/29-comfy-store/starter/products.html
new file mode 100644
index 000000000..1d3bf7720
--- /dev/null
+++ b/29-comfy-store/starter/products.html
@@ -0,0 +1,20 @@
+
+
+
+
+
+ Products | Comfy
+
+
+
+
+
+
+
+ products page
+
+
+
diff --git a/29-comfy-store/starter/src/cart/addToCartDOM.js b/29-comfy-store/starter/src/cart/addToCartDOM.js
new file mode 100644
index 000000000..e375ab6a6
--- /dev/null
+++ b/29-comfy-store/starter/src/cart/addToCartDOM.js
@@ -0,0 +1,5 @@
+import { formatPrice, getElement } from '../utils.js';
+
+const addToCartDOM = () => {};
+
+export default addToCartDOM;
diff --git a/29-comfy-store/starter/src/cart/setupCart.js b/29-comfy-store/starter/src/cart/setupCart.js
new file mode 100644
index 000000000..897c23393
--- /dev/null
+++ b/29-comfy-store/starter/src/cart/setupCart.js
@@ -0,0 +1,13 @@
+// import
+import {
+ getStorageItem,
+ setStorageItem,
+ formatPrice,
+ getElement,
+} from '../utils.js';
+import { openCart } from './toggleCart.js';
+import { findProduct } from '../store.js';
+import addToCartDOM from './addToCartDOM.js';
+// set items
+
+export const addToCart = () => {};
diff --git a/29-comfy-store/starter/src/cart/toggleCart.js b/29-comfy-store/starter/src/cart/toggleCart.js
new file mode 100644
index 000000000..185dda856
--- /dev/null
+++ b/29-comfy-store/starter/src/cart/toggleCart.js
@@ -0,0 +1,3 @@
+import { getElement } from '../utils.js';
+
+export const openCart = () => {};
diff --git a/29-comfy-store/starter/src/displayProducts.js b/29-comfy-store/starter/src/displayProducts.js
new file mode 100644
index 000000000..b2bbbe7f2
--- /dev/null
+++ b/29-comfy-store/starter/src/displayProducts.js
@@ -0,0 +1,5 @@
+import { formatPrice } from './utils.js';
+import { addToCart } from './cart/setupCart.js';
+const display = () => {};
+
+export default display;
diff --git a/29-comfy-store/starter/src/fetchProducts.js b/29-comfy-store/starter/src/fetchProducts.js
new file mode 100644
index 000000000..694b81253
--- /dev/null
+++ b/29-comfy-store/starter/src/fetchProducts.js
@@ -0,0 +1,5 @@
+import { allProductsUrl } from './utils.js';
+
+const fetchProducts = async () => {};
+
+export default fetchProducts;
diff --git a/29-comfy-store/starter/src/filters/companies.js b/29-comfy-store/starter/src/filters/companies.js
new file mode 100644
index 000000000..f7a053b8f
--- /dev/null
+++ b/29-comfy-store/starter/src/filters/companies.js
@@ -0,0 +1,6 @@
+import { getElement } from '../utils.js';
+import display from '../displayProducts.js';
+
+const setupCompanies = () => {};
+
+export default setupCompanies;
diff --git a/29-comfy-store/starter/src/filters/price.js b/29-comfy-store/starter/src/filters/price.js
new file mode 100644
index 000000000..f47d67f49
--- /dev/null
+++ b/29-comfy-store/starter/src/filters/price.js
@@ -0,0 +1,6 @@
+import { getElement } from '../utils.js';
+import display from '../displayProducts.js';
+
+const setupPrice = () => {};
+
+export default setupPrice;
diff --git a/29-comfy-store/starter/src/filters/search.js b/29-comfy-store/starter/src/filters/search.js
new file mode 100644
index 000000000..6bd3dd7e0
--- /dev/null
+++ b/29-comfy-store/starter/src/filters/search.js
@@ -0,0 +1,5 @@
+import { getElement } from '../utils.js';
+import display from '../displayProducts.js';
+const setupSearch = () => {};
+
+export default setupSearch;
diff --git a/29-comfy-store/starter/src/pages/about.js b/29-comfy-store/starter/src/pages/about.js
new file mode 100644
index 000000000..e4bf4a496
--- /dev/null
+++ b/29-comfy-store/starter/src/pages/about.js
@@ -0,0 +1,4 @@
+// global imports
+import '../toggleSidebar.js';
+import '../cart/toggleCart.js';
+import '../cart/setupCart.js';
diff --git a/29-comfy-store/starter/src/pages/product.js b/29-comfy-store/starter/src/pages/product.js
new file mode 100644
index 000000000..ce6f8b9d8
--- /dev/null
+++ b/29-comfy-store/starter/src/pages/product.js
@@ -0,0 +1,24 @@
+// global imports
+import '../toggleSidebar.js';
+import '../cart/toggleCart.js';
+import '../cart/setupCart.js';
+// specific
+import { addToCart } from '../cart/setupCart.js';
+import { singleProductUrl, getElement, formatPrice } from '../utils.js';
+
+// selections
+// const loading = getElement('.page-loading');
+// const centerDOM = getElement('.single-product-center');
+// const pageTitleDOM = getElement('.page-hero-title');
+// const imgDOM = getElement('.single-product-img');
+// const titleDOM = getElement('.single-product-title');
+// const companyDOM = getElement('.single-product-company');
+// const priceDOM = getElement('.single-product-price');
+// const colorsDOM = getElement('.single-product-colors');
+// const descDOM = getElement('.single-product-desc');
+// const cartBtn = getElement('.addToCartBtn');
+
+// cart product
+// let productID;
+
+// show product when page loads
diff --git a/29-comfy-store/starter/src/pages/products.js b/29-comfy-store/starter/src/pages/products.js
new file mode 100644
index 000000000..5e67c061c
--- /dev/null
+++ b/29-comfy-store/starter/src/pages/products.js
@@ -0,0 +1,14 @@
+// global imports
+import '../toggleSidebar.js';
+import '../cart/toggleCart.js';
+import '../cart/setupCart.js';
+
+// filter imports
+import setupSearch from '../filters/search.js';
+import setupCompanies from '../filters/companies.js';
+import setupPrice from '../filters/price.js';
+
+// specific imports
+import { store } from '../store.js';
+import display from '../displayProducts.js';
+import { getElement } from '../utils.js';
diff --git a/29-comfy-store/starter/src/store.js b/29-comfy-store/starter/src/store.js
new file mode 100644
index 000000000..e0ea2af52
--- /dev/null
+++ b/29-comfy-store/starter/src/store.js
@@ -0,0 +1,5 @@
+import { getStorageItem, setStorageItem } from './utils.js';
+let store = [];
+const setupStore = () => {};
+const findProduct = () => {};
+export { store, setupStore, findProduct };
diff --git a/29-comfy-store/starter/src/toggleSidebar.js b/29-comfy-store/starter/src/toggleSidebar.js
new file mode 100644
index 000000000..46470512b
--- /dev/null
+++ b/29-comfy-store/starter/src/toggleSidebar.js
@@ -0,0 +1 @@
+import { getElement } from './utils.js';
diff --git a/29-comfy-store/starter/src/utils.js b/29-comfy-store/starter/src/utils.js
new file mode 100644
index 000000000..2f36ef175
--- /dev/null
+++ b/29-comfy-store/starter/src/utils.js
@@ -0,0 +1,32 @@
+// ATTENTION!!!!!!!!!!!
+// I SWITCHED TO PERMANENT DOMAIN
+// DATA IS THE SAME JUST A DIFFERENT URL,
+// DOES NOT AFFECT PROJECT FUNCTIONALITY
+
+const allProductsUrl = 'https://www.course-api.com/javascript-store-products';
+// temporary single product
+// 'https://www.course-api.com/javascript-store-single-product?id=rec43w3ipXvP28vog'
+const singleProductUrl =
+ 'https://www.course-api.com/javascript-store-single-product';
+
+const getElement = (selection) => {
+ const element = document.querySelector(selection);
+ if (element) return element;
+ throw new Error(
+ `Please check "${selection}" selector, no such element exist`
+ );
+};
+
+const formatPrice = () => {};
+
+const getStorageItem = () => {};
+const setStorageItem = () => {};
+
+export {
+ allProductsUrl,
+ singleProductUrl,
+ getElement,
+ formatPrice,
+ getStorageItem,
+ setStorageItem,
+};
diff --git a/29-comfy-store/starter/styles.css b/29-comfy-store/starter/styles.css
new file mode 100644
index 000000000..e671ccd12
--- /dev/null
+++ b/29-comfy-store/starter/styles.css
@@ -0,0 +1,777 @@
+@import url('https://fonts.googleapis.com/css2?family=Kaushan+Script&display=swap');
+/*
+===============
+Variables
+===============
+*/
+
+:root {
+ /* dark shades of primary color*/
+ --clr-primary-1: hsl(21, 91%, 17%);
+ --clr-primary-2: hsl(21, 84%, 25%);
+ --clr-primary-3: hsl(21, 81%, 29%);
+ --clr-primary-4: hsl(21, 77%, 34%);
+ /* primary/main color */
+ --clr-primary-5: hsl(21, 62%, 45%);
+ /* lighter shades of primary color */
+ --clr-primary-6: hsl(21, 57%, 50%);
+ --clr-primary-7: hsl(21, 65%, 59%);
+ --clr-primary-8: hsl(21, 80%, 74%);
+ --clr-primary-9: hsl(21, 94%, 87%);
+ --clr-primary-10: hsl(21, 100%, 94%);
+ /* darkest grey - used for headings */
+ --clr-grey-1: #102a42;
+ --clr-grey-2: hsl(211, 39%, 23%);
+ --clr-grey-3: hsl(209, 34%, 30%);
+ --clr-grey-4: hsl(209, 28%, 39%);
+ /* grey used for paragraphs */
+ --clr-grey-5: hsl(210, 22%, 49%);
+ --clr-grey-6: hsl(209, 23%, 60%);
+ --clr-grey-7: hsl(211, 27%, 70%);
+ --clr-grey-8: hsl(210, 31%, 80%);
+ --clr-grey-9: hsl(212, 33%, 89%);
+ --clr-grey-10: hsl(210, 36%, 96%);
+ --clr-white: #fff;
+ --clr-red-dark: hsl(360, 67%, 44%);
+ --clr-red-light: hsl(360, 71%, 66%);
+ --clr-green-dark: hsl(125, 67%, 44%);
+ --clr-green-light: hsl(125, 71%, 66%);
+ --clr-black: #222;
+
+ --transition: all 0.3s linear;
+ --spacing: 0.1rem;
+ --radius: 0.25rem;
+ --light-shadow: 0 5px 15px rgba(0, 0, 0, 0.1);
+ --dark-shadow: 0 5px 15px rgba(0, 0, 0, 0.2);
+ --max-width: 1170px;
+ --fixed-width: 620px;
+}
+/*
+===============
+Global Styles
+===============
+*/
+
+*,
+::after,
+::before {
+ margin: 0;
+ padding: 0;
+ box-sizing: border-box;
+}
+body {
+ font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen,
+ Ubuntu, Cantarell, 'Open Sans', 'Helvetica Neue', sans-serif;
+ background: var(--clr-white);
+ color: var(--clr-grey-1);
+ line-height: 1.5;
+ font-size: 0.875rem;
+}
+ul {
+ list-style-type: none;
+}
+a {
+ text-decoration: none;
+}
+h1,
+h2,
+h3,
+h4 {
+ letter-spacing: var(--spacing);
+ text-transform: capitalize;
+ line-height: 1.25;
+ margin-bottom: 0.75rem;
+ font-weight: 400;
+}
+h1 {
+ font-size: 3rem;
+}
+h2 {
+ font-size: 2rem;
+}
+h3 {
+ font-size: 1.25rem;
+}
+h4 {
+ font-size: 0.875rem;
+}
+p {
+ margin-bottom: 1.25rem;
+ color: var(--clr-grey-5);
+}
+@media screen and (min-width: 800px) {
+ h1 {
+ font-size: 4rem;
+ }
+ h2 {
+ font-size: 2.5rem;
+ }
+ h3 {
+ font-size: 1.75rem;
+ }
+ h4 {
+ font-size: 1rem;
+ }
+ body {
+ font-size: 1rem;
+ }
+ h1,
+ h2,
+ h3,
+ h4 {
+ line-height: 1;
+ }
+}
+.img {
+ width: 100%;
+ display: block;
+}
+.text-slanted {
+ font-family: 'Kaushan Script', cursive;
+}
+.section-center {
+ width: 90vw;
+ max-width: var(--max-width);
+ margin: 0 auto;
+}
+.section {
+ padding: 5rem 0;
+}
+.btn {
+ background: var(--clr-primary-5);
+ color: var(--clr-white);
+ border-radius: var(--radius);
+ padding: 0.375rem 0.75rem;
+ text-transform: uppercase;
+ letter-spacing: var(--spacing);
+ display: inline-block;
+ transition: var(--transition);
+ border-color: transparent;
+ cursor: pointer;
+}
+.btn:hover {
+ background: var(--clr-primary-7);
+ color: var(--clr-black);
+}
+.section-loading {
+ text-align: center;
+ position: absolute;
+ top: 2rem;
+ left: 50%;
+ transform: translate(-50%, -50%);
+ text-align: center;
+}
+.page-loading {
+ position: fixed;
+ top: 0;
+ left: 0;
+ width: 100%;
+ height: 100%;
+ background: var(--clr-grey-10);
+ display: flex;
+ align-items: center;
+ justify-content: center;
+}
+/*
+===============
+Navbar
+===============
+*/
+
+.navbar {
+ height: 6rem;
+ background: transparent;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+}
+
+.nav-center {
+ width: 90vw;
+ max-width: var(--max-width);
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+}
+.nav-links {
+ display: none;
+}
+.toggle-nav {
+ background: var(--clr-primary-5);
+ border-color: transparent;
+ color: var(--clr-white);
+ width: 3.75rem;
+ height: 2.25rem;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ font-size: 1.5rem;
+ border-radius: 2rem;
+ cursor: pointer;
+ transition: var(--transition);
+}
+.toggle-nav:hover {
+ background: var(--clr-primary-3);
+}
+.toggle-container {
+ position: relative;
+ margin-top: 0.75rem;
+}
+.toggle-cart {
+ background: transparent;
+ border-color: transparent;
+ font-size: 1.6rem;
+ color: var(--clr-white);
+ cursor: pointer;
+}
+.cart-item-count {
+ position: absolute;
+ top: -0.85rem;
+ right: -0.85rem;
+ background: var(--clr-primary-5);
+ width: 1.75rem;
+ height: 1.75rem;
+ display: grid;
+ place-items: center;
+ border-radius: 50%;
+ color: var(--clr-white);
+ font-weight: bold;
+ font-size: 1rem;
+}
+@media screen and (min-width: 800px) {
+ .nav-center {
+ position: relative;
+ }
+ .nav-logo {
+ position: absolute;
+ top: 50%;
+ left: 65%;
+ transform: translate(-50%, -50%);
+ }
+ .toggle-nav {
+ display: none;
+ }
+ .nav-links {
+ display: flex;
+ font-size: 1.5rem;
+ text-transform: capitalize;
+ }
+ .nav-link {
+ color: var(--clr-white);
+ margin-right: 3rem;
+ letter-spacing: var(--spacing);
+ transition: var(--transition);
+ font-size: 1.25rem;
+ }
+ .nav-link:hover {
+ color: var(--clr-primary-5);
+ }
+}
+@media screen and (min-width: 992px) {
+ .nav-logo {
+ left: 50%;
+ }
+}
+/* page navbar */
+.page .nav-link {
+ color: var(--clr-grey-1);
+}
+.page .nav-link:hover {
+ color: var(--clr-primary-5);
+}
+.page .toggle-cart {
+ color: var(--clr-grey-1);
+}
+/*
+===============
+Hero
+===============
+*/
+.hero {
+ min-height: 100vh;
+ margin-top: -6rem;
+ background: linear-gradient(rgba(0, 0, 0, 0.5), rgba(0, 0, 0, 0.5)),
+ url(./images/main-bcg.jpeg) center/cover;
+ display: grid;
+ place-items: center;
+ color: var(--clr-white);
+}
+.hero-container {
+ width: 90vw;
+ max-width: var(--max-width);
+}
+.hero h1 {
+ font-weight: 700;
+}
+.hero h3 {
+ text-transform: none;
+ font-size: 1.5rem;
+}
+.hero-btn {
+ color: var(--clr-white);
+ background: transparent;
+ border: 1px solid var(--clr-white);
+ padding: 0.5rem 0.75rem;
+ display: inline-block;
+ margin-top: 0.75rem;
+ text-transform: uppercase;
+ letter-spacing: var(--spacing);
+ border-radius: var(--radius);
+ transition: var(--transition);
+}
+.hero-btn:hover {
+ background: var(--clr-white);
+ color: var(--clr-primary-5);
+}
+
+@media screen and (min-width: 800px) {
+ .hero h3 {
+ font-size: 1.5rem;
+ margin: 1rem 0;
+ }
+}
+
+@media screen and (min-width: 992px) {
+ .hero h1 {
+ font-size: 5.25rem;
+ letter-spacing: 5px;
+ }
+ .hero h3 {
+ font-size: 2.75rem;
+ margin: 1.5rem 0;
+ }
+}
+
+/*
+===============
+Sidebar
+===============
+*/
+.sidebar-overlay {
+ position: fixed;
+ top: 0;
+ left: 0;
+ width: 100%;
+ height: 100%;
+ display: grid;
+ place-items: center;
+ z-index: -1;
+ transition: var(--transition);
+ opacity: 0;
+ background: rgba(0, 0, 0, 0.5);
+}
+.sidebar-overlay.show {
+ opacity: 1;
+ z-index: 100;
+}
+.sidebar {
+ width: 90vw;
+ height: 95vh;
+ max-width: var(--fixed-width);
+ background: var(--clr-white);
+ border-radius: var(--radius);
+ box-shadow: var(--dark-shadow);
+ position: relative;
+ padding: 4rem;
+ transform: scale(0);
+}
+.show .sidebar {
+ transform: scale(1);
+}
+.sidebar-close {
+ font-size: 2rem;
+ background: transparent;
+ border-color: transparent;
+ color: var(--clr-grey-5);
+ position: absolute;
+ top: 1rem;
+ right: 1rem;
+ cursor: pointer;
+}
+.sidebar-link {
+ font-size: 1.5rem;
+ text-transform: capitalize;
+ color: var(--clr-grey-1);
+}
+.sidebar-link i {
+ color: var(--clr-grey-5);
+ margin-right: 1rem;
+ margin-bottom: 1rem;
+}
+@media screen and (min-width: 800px) {
+ .sidebar-overlay {
+ display: none;
+ }
+}
+/*
+===============
+Cart
+===============
+*/
+.cart-overlay {
+ position: fixed;
+ top: 0;
+ left: 0;
+ width: 100%;
+ height: 100%;
+ background: rgba(0, 0, 0, 0.5);
+ transition: var(--transition);
+ opacity: 0;
+ z-index: -1;
+}
+.cart-overlay.show {
+ opacity: 1;
+ z-index: 100;
+}
+.cart {
+ position: fixed;
+ top: 0;
+ right: 0;
+ width: 100%;
+ height: 100%;
+ max-width: 400px;
+ background: var(--clr-grey-10);
+ padding: 3rem 1rem 0 1rem;
+ display: grid;
+ grid-template-rows: auto 1fr auto;
+ transition: var(--transition);
+ transform: translateX(100%);
+ overflow: scroll;
+}
+.show .cart {
+ transform: translateX(0);
+}
+.cart-close {
+ font-size: 2rem;
+ background: transparent;
+ border-color: transparent;
+ color: var(--clr-grey-5);
+ position: absolute;
+ top: 0.5rem;
+ left: 1rem;
+ cursor: pointer;
+}
+.cart header {
+ text-align: center;
+}
+.cart header h3 {
+ font-weight: 500;
+}
+.cart-total {
+ text-align: center;
+ margin-bottom: 2rem;
+ font-weight: 500;
+}
+.cart-checkout {
+ display: block;
+ width: 75%;
+ margin: 0 auto;
+ margin-bottom: 3rem;
+}
+/* cart item */
+.cart-item {
+ margin: 1rem 0;
+ display: grid;
+ grid-template-columns: auto 1fr auto;
+
+ column-gap: 1.5rem;
+ align-items: center;
+}
+.cart-item-img {
+ width: 75px;
+ height: 50px;
+ object-fit: cover;
+ border-radius: var(--radius);
+}
+.cart-item-name {
+ margin-bottom: 0.15rem;
+}
+.cart-item-price {
+ margin-bottom: 0;
+ font-size: 0.75rem;
+ color: var(--clr-grey-3);
+}
+.cart-item-remove-btn {
+ background: transparent;
+ border-color: transparent;
+ color: var(--clr-grey-5);
+ letter-spacing: var(--spacing);
+ cursor: pointer;
+}
+.cart-item-amount {
+ margin-bottom: 0;
+ text-align: center;
+ color: var(--clr-grey-3);
+ line-height: 1;
+}
+.cart-item-increase-btn,
+.cart-item-decrease-btn {
+ background: transparent;
+ border-color: transparent;
+ color: var(--clr-primary-5);
+ cursor: pointer;
+ font-size: 0.85rem;
+ padding: 0.25rem;
+}
+/*
+===============
+Title
+===============
+*/
+
+.title h2 {
+ display: flex;
+ justify-content: center;
+ align-items: center;
+ font-weight: 500;
+}
+.title span {
+ color: var(--clr-primary-5);
+ font-size: 0.85em;
+ margin-right: 1rem;
+}
+/*
+===============
+product
+===============
+*/
+.product-img {
+ height: 15rem;
+ object-fit: cover;
+ border-radius: var(--radius);
+}
+.product-container {
+ position: relative;
+}
+.product-icons {
+ position: absolute;
+ top: 50%;
+ left: 50%;
+ transform: translate(-50%, -50%);
+ opacity: 0;
+ display: flex;
+ transition: var(--transition);
+}
+.product-icon {
+ width: 2.25rem;
+ height: 2.25rem;
+ background: var(--clr-primary-5);
+ color: var(--clr-white);
+ display: grid;
+ place-items: center;
+ border-radius: 50%;
+ transition: var(--transition);
+ cursor: pointer;
+ font-size: 1rem;
+ border-color: transparent;
+ margin: 0 0.5rem;
+}
+.product-icon:hover {
+ background: var(--clr-primary-7);
+}
+.product-container:hover .product-icons {
+ opacity: 1;
+}
+.product footer {
+ padding: 0.75rem 0;
+ text-align: center;
+}
+.product-name {
+ margin-bottom: 0.25rem;
+ text-transform: capitalize;
+ letter-spacing: var(--spacing);
+}
+.product-price {
+ margin-bottom: 0;
+ color: var(--clr-grey-3);
+ font-weight: 700;
+}
+
+.featured-center {
+ margin: 3rem auto 2rem auto;
+ display: grid;
+ gap: 1rem;
+ min-height: 6rem;
+ position: relative;
+}
+.featured .btn {
+ display: block;
+ width: 11rem;
+ margin: 0 auto;
+ text-align: center;
+}
+@media screen and (min-width: 992px) {
+ .featured-center {
+ display: grid;
+ grid-template-columns: 1fr 1fr;
+ }
+}
+@media screen and (min-width: 1200px) {
+ .featured-center {
+ display: grid;
+ grid-template-columns: 1fr 1fr 1fr;
+ }
+ .product .img {
+ height: 13rem;
+ }
+}
+/*
+===============
+About Page
+===============
+*/
+.about-text {
+ line-height: 2;
+ max-width: 45em;
+ margin: 0 auto;
+ margin-top: 2rem;
+}
+
+/*
+===============
+Products Page
+===============
+*/
+.products {
+ width: 90vw;
+ display: grid;
+ grid-gap: 1rem;
+ margin: 4rem auto;
+ max-width: var(--max-width);
+ position: relative;
+}
+.filters-container {
+ position: sticky;
+ top: 1rem;
+}
+.filters h4 {
+ font-weight: 500;
+ margin: 1.5rem 0 0.5rem;
+}
+
+.search-input {
+ padding: 0.5rem;
+ background: var(--clr-grey-10);
+ border-radius: var(--radius);
+ border-color: transparent;
+ letter-spacing: var(--spacing);
+}
+.search-input::placeholder {
+ text-transform: capitalize;
+}
+.company-btn {
+ display: block;
+ margin: 0.25em 0;
+ padding: 0.25rem;
+ text-transform: capitalize;
+ background: transparent;
+ border-color: transparent;
+ letter-spacing: var(--spacing);
+ color: var(--clr-grey-5);
+ cursor: pointer;
+ transition: var(--transition);
+}
+.company-btn:hover {
+ color: var(--clr-grey-3);
+}
+.price-filter {
+ background: var(--clr-grey-5) !important;
+ color: var(--clr-grey-5);
+}
+@media screen and (min-width: 768px) {
+ .products {
+ grid-template-columns: 200px 1fr;
+ }
+ .categories {
+ position: sticky;
+ top: 1rem;
+ }
+}
+@media screen and (min-width: 992px) {
+ .products-container {
+ display: grid;
+ grid-template-columns: repeat(2, 1fr);
+ gap: 1rem;
+ }
+ .products-container .product-img {
+ height: 10rem;
+ }
+ .products-container .product-name {
+ font-size: 0.85rem;
+ }
+ .products-container .product-price {
+ font-size: 0.85rem;
+ }
+}
+@media screen and (min-width: 1170px) {
+ .products-container {
+ grid-template-columns: repeat(3, 1fr);
+ }
+}
+.filter-error {
+ position: absolute;
+ top: 5rem;
+ left: 0;
+ width: 100%;
+ text-align: center;
+ margin-top: 4rem;
+}
+/*
+===============
+Single Product Page
+===============
+*/
+.page-hero {
+ min-height: 20vh;
+ display: grid;
+ place-items: center;
+ background: var(--clr-grey-10);
+ color: var(--clr-grey-5);
+}
+.page-hero-title {
+ font-weight: 500;
+}
+.single-product {
+ padding: 2rem 0;
+}
+.single-product-center {
+ margin: 2rem auto;
+ display: grid;
+ gap: 1rem 2rem;
+}
+.single-product-img {
+ height: 25rem;
+ border-radius: var(--radius);
+ object-fit: cover;
+}
+.single-product-company {
+ font-size: 1.2rem;
+ color: var(--clr-grey-8);
+ text-transform: uppercase;
+ letter-spacing: var(--spacing);
+ margin-bottom: 1.25rem;
+}
+.single-product-price {
+ color: var(--clr-grey-3);
+ font-size: 1.25rem;
+ font-weight: 500;
+}
+
+.product-color {
+ display: inline-block;
+ width: 1rem;
+ height: 1rem;
+ border-radius: 50%;
+ background: #222;
+ margin: 0.5rem 0.5rem 1.5rem 0;
+}
+
+.single-product-desc {
+ max-width: 25em;
+ line-height: 1.8;
+}
+@media screen and (min-width: 992px) {
+ .single-product-center {
+ grid-template-columns: 1fr 1fr;
+ }
+}
diff --git a/3-reviews/final/app.js b/3-reviews/final/app.js
deleted file mode 100644
index f47c854b0..000000000
--- a/3-reviews/final/app.js
+++ /dev/null
@@ -1,92 +0,0 @@
-// local reviews data
-const reviews = [
- {
- id: 1,
- name: "susan smith",
- job: "web developer",
- img:
- "https://res.cloudinary.com/diqqf3eq2/image/upload/v1586883334/person-1_rfzshl.jpg",
- text:
- "I'm baby meggings twee health goth +1. Bicycle rights tumeric chartreuse before they sold out chambray pop-up. Shaman humblebrag pickled coloring book salvia hoodie, cold-pressed four dollar toast everyday carry",
- },
- {
- id: 2,
- name: "anna johnson",
- job: "web designer",
- img:
- "https://res.cloudinary.com/diqqf3eq2/image/upload/v1586883409/person-2_np9x5l.jpg",
- text:
- "Helvetica artisan kinfolk thundercats lumbersexual blue bottle. Disrupt glossier gastropub deep v vice franzen hell of brooklyn twee enamel pin fashion axe.photo booth jean shorts artisan narwhal.",
- },
- {
- id: 3,
- name: "peter jones",
- job: "intern",
- img:
- "https://res.cloudinary.com/diqqf3eq2/image/upload/v1586883417/person-3_ipa0mj.jpg",
- text:
- "Sriracha literally flexitarian irony, vape marfa unicorn. Glossier tattooed 8-bit, fixie waistcoat offal activated charcoal slow-carb marfa hell of pabst raclette post-ironic jianbing swag.",
- },
- {
- id: 4,
- name: "bill anderson",
- job: "the boss",
- img:
- "https://res.cloudinary.com/diqqf3eq2/image/upload/v1586883423/person-4_t9nxjt.jpg",
- text:
- "Edison bulb put a bird on it humblebrag, marfa pok pok heirloom fashion axe cray stumptown venmo actually seitan. VHS farm-to-table schlitz, edison bulb pop-up 3 wolf moon tote bag street art shabby chic. ",
- },
-];
-// select items
-const img = document.getElementById("person-img");
-const author = document.getElementById("author");
-const job = document.getElementById("job");
-const info = document.getElementById("info");
-
-const prevBtn = document.querySelector(".prev-btn");
-const nextBtn = document.querySelector(".next-btn");
-const randomBtn = document.querySelector(".random-btn");
-
-// set starting item
-let currentItem = 0;
-
-// load initial item
-window.addEventListener("DOMContentLoaded", function () {
- const item = reviews[currentItem];
- img.src = item.img;
- author.textContent = item.name;
- job.textContent = item.job;
- info.textContent = item.text;
-});
-
-// show person based on item
-function showPerson(person) {
- const item = reviews[person];
- img.src = item.img;
- author.textContent = item.name;
- job.textContent = item.job;
- info.textContent = item.text;
-}
-// show next person
-nextBtn.addEventListener("click", function () {
- currentItem++;
- if (currentItem > reviews.length - 1) {
- currentItem = 0;
- }
- showPerson(currentItem);
-});
-// show prev person
-prevBtn.addEventListener("click", function () {
- currentItem--;
- if (currentItem < 0) {
- currentItem = reviews.length - 1;
- }
- showPerson(currentItem);
-});
-// show random person
-randomBtn.addEventListener("click", function () {
- console.log("hello");
-
- currentItem = Math.floor(Math.random() * reviews.length);
- showPerson(currentItem);
-});
diff --git a/3-reviews/setup/app.js b/3-reviews/setup/app.js
deleted file mode 100644
index 94d488f60..000000000
--- a/3-reviews/setup/app.js
+++ /dev/null
@@ -1,39 +0,0 @@
-// local reviews data
-const reviews = [
- {
- id: 1,
- name: "susan smith",
- job: "web developer",
- img:
- "https://res.cloudinary.com/diqqf3eq2/image/upload/v1586883334/person-1_rfzshl.jpg",
- text:
- "I'm baby meggings twee health goth +1. Bicycle rights tumeric chartreuse before they sold out chambray pop-up. Shaman humblebrag pickled coloring book salvia hoodie, cold-pressed four dollar toast everyday carry",
- },
- {
- id: 2,
- name: "anna johnson",
- job: "web designer",
- img:
- "https://res.cloudinary.com/diqqf3eq2/image/upload/v1586883409/person-2_np9x5l.jpg",
- text:
- "Helvetica artisan kinfolk thundercats lumbersexual blue bottle. Disrupt glossier gastropub deep v vice franzen hell of brooklyn twee enamel pin fashion axe.photo booth jean shorts artisan narwhal.",
- },
- {
- id: 3,
- name: "peter jones",
- job: "intern",
- img:
- "https://res.cloudinary.com/diqqf3eq2/image/upload/v1586883417/person-3_ipa0mj.jpg",
- text:
- "Sriracha literally flexitarian irony, vape marfa unicorn. Glossier tattooed 8-bit, fixie waistcoat offal activated charcoal slow-carb marfa hell of pabst raclette post-ironic jianbing swag.",
- },
- {
- id: 4,
- name: "bill anderson",
- job: "the boss",
- img:
- "https://res.cloudinary.com/diqqf3eq2/image/upload/v1586883423/person-4_t9nxjt.jpg",
- text:
- "Edison bulb put a bird on it humblebrag, marfa pok pok heirloom fashion axe cray stumptown venmo actually seitan. VHS farm-to-table schlitz, edison bulb pop-up 3 wolf moon tote bag street art shabby chic. ",
- },
-];
diff --git a/README.md b/README.md
index 040d4ca81..61f6c7d29 100644
--- a/README.md
+++ b/README.md
@@ -1,3 +1,46 @@
+# Vanilla JS Projects
+
+#### Enroll In The Course
+
+[My Javascript Course](https://www.udemy.com/course/javascript-tutorial-for-beginners-w/?referralCode=DD9FA6C0D976918D3E1C)
+
+#### Support
+
+Find the Content Useful? [You can always buy me a coffee](https://www.buymeacoffee.com/johnsmilga)
+
## You can see all projects in action here
[Projects](https://www.vanillajavascriptprojects.com/)
+
+1. Color Flipper
+2. Counter
+3. Reviews
+4. Navbar
+5. Sidebar
+6. Modal
+7. Questions
+8. Menu
+9. Video
+10. Scroll
+11. Tabs
+12. Countdown Timer
+13. Lorem Ipsum
+14. Grocery Bud
+15. Slider
+
+#### Course Exclusive
+
+16. Counters (OOP)
+17. Gallery (OOP)
+18. Numbers
+19. Dark Mode
+20. Filters
+21. Dad Jokes
+22. Products
+23. Random User
+24. Cocktails
+25. Slider
+26. Stripe Submenus
+27. Pagination
+28. Wikipedia
+29. Comfy Sloth