feat(curriculum): add Build a Bookstore Page Workshop to FSD cert (#62723)

Co-authored-by: Jessica Wilkins <67210629+jdwilkin4@users.noreply.github.com>
Co-authored-by: Dario <105294544+Dario-DC@users.noreply.github.com>
This commit is contained in:
Alaa Yahia
2025-11-06 17:31:11 +02:00
committed by GitHub
parent 930b09a328
commit af13a79f4a
20 changed files with 918 additions and 0 deletions
+6
View File
@@ -2087,6 +2087,12 @@
"In these lessons, you will learn about HTML fundamentals like the <code>div</code> element, the <code>id</code> and <code>class</code> attributes, the HTML boilerplate, HTML entities, and more."
]
},
"workshop-bookstore-page": {
"title": "Build a Bookstore Page",
"intro": [
"In this workshop, you will practice working with classes, ids and the <code>div</code> element by building a bookstore page."
]
},
"lecture-understanding-how-html-affects-seo": {
"title": "Understanding How HTML Affects SEO",
"intro": [
@@ -0,0 +1,9 @@
---
title: Introduction to the Build a Bookstore Page
block: workshop-bookstore-page
superBlock: full-stack-developer
---
## Introduction to the Build a Bookstore Page
In this workshop, you will practice working with classes, ids and the <code>div</code> element by building a bookstore page.
@@ -0,0 +1,37 @@
---
id: 68e97fe79367ad7b5dd6c9cd
title: Step 1
challengeType: 0
dashedName: step-1
demoType: onLoad
---
# --description--
In this workshop, you will build a bookstore page by creating book cards that display information about different books. You'll practice organizing content using `div` elements, classes, and IDs.
Start by adding an `h1` element with the text `XYZ Bookstore`.
# --hints--
You should have an `h1` element.
```js
assert.exists(document.querySelector('h1'));
```
Your `h1` element's text should be `XYZ Bookstore`. Double check for spelling or capitalization errors.
```js
assert.equal(document.querySelector('h1')?.innerText.trim(), 'XYZ Bookstore');
```
# --seed--
## --seed-contents--
```html
--fcc-editable-region--
--fcc-editable-region--
```
@@ -0,0 +1,41 @@
---
id: 68ebdbacdd3fa474132cc975
title: Step 2
challengeType: 0
dashedName: step-2
---
# --description--
Below the `h1` element, add a `p` element with this text: `Browse our collection of amazing books!`.
# --hints--
You should have a `p` element.
```js
assert.exists(document.querySelector('p'));
```
Your `p` element's text should be: `Browse our collection of amazing books!`.
```js
assert.equal(document.querySelector('p')?.innerText.trim(), 'Browse our collection of amazing books!');
```
Your `p` element should be below your `h1` element.
```js
assert.exists(document.querySelector('h1 + p'));
```
# --seed--
## --seed-contents--
```html
--fcc-editable-region--
<h1>XYZ Bookstore</h1>
--fcc-editable-region--
```
@@ -0,0 +1,38 @@
---
id: 68ec6cd5b7e8f5a8f7319e32
title: Step 3
challengeType: 0
dashedName: step-3
---
# --description--
The `div` element is used as a container to group other HTML elements. You will mainly use the `div` element when you want to group HTML elements that will share a set of CSS styles.
Below the `p` element, add a `div` element. This `div` will be a container for your book cards.
# --hints--
You should have a `div` element.
```js
assert.exists(document.querySelector('div'));
```
Your `div` element should be below your `p` element.
```js
assert.exists(document.querySelector('p + div'));
```
# --seed--
## --seed-contents--
```html
--fcc-editable-region--
<h1>XYZ Bookstore</h1>
<p>Browse our collection of amazing books!</p>
--fcc-editable-region--
```
@@ -0,0 +1,46 @@
---
id: 68ec6d9a315221aa31e54816
title: Step 4
challengeType: 0
dashedName: step-4
---
# --description--
The `class` attribute is used to identify one or more elements for styling. Unlike the `id` attribute, class names do not need to be unique: multiple elements can share the same class.
Here is an example:
```html
<p class="example">example paragraph</p>
```
Add a `class` attribute to your `div` element and set its value to `card-container`.
# --hints--
Your `div` element should have a `class` attribute.
```js
assert.isTrue(document.querySelector('div')?.hasAttribute('class'));
```
Your `div` element should have a `class` attribute with the value of `card-container`.
```js
assert.equal(document.querySelector('div')?.className, 'card-container');
```
# --seed--
## --seed-contents--
```html
--fcc-editable-region--
<h1>XYZ Bookstore</h1>
<p>Browse our collection of amazing books!</p>
<div>
</div>
--fcc-editable-region--
```
@@ -0,0 +1,46 @@
---
id: 68ec6e8d0caee3afaaf142ef
title: Step 5
challengeType: 0
dashedName: step-5
---
# --description--
Inside the element having a `class` of `card-container`, create another `div` element. This `div` will represent the first book card.
Add a `class` attribute to this new `div` element and set the value of the `class` attribute to `card`.
# --hints--
You should have a `div` element nested inside the element with a class of `card-container`.
```js
assert.exists(document.querySelector('.card-container div'));
```
Your new `div` element should have a `class` attribute.
```js
assert.isTrue(document.querySelector('.card-container div')?.hasAttribute('class'));
```
Your new `div` element should have a `class` having the value of `card`.
```js
assert.exists(document.querySelector('.card-container div.card'));
```
# --seed--
## --seed-contents--
```html
--fcc-editable-region--
<h1>XYZ Bookstore</h1>
<p>Browse our collection of amazing books!</p>
<div class="card-container">
</div>
--fcc-editable-region--
```
@@ -0,0 +1,50 @@
---
id: 68ec9332a9b5b2b32487bd00
title: Step 6
challengeType: 0
dashedName: step-6
---
# --description--
The `id` attribute adds a unique identifier to an HTML element. Each `id` should be unique within a page and should only be used once.
`id` values cannot contain spaces and should only contain letters, digits, underscores, and dashes.
Here is an example:
```html
<p id="para">example paragraph</p>
```
Add an `id` attribute to your element having a class of `card` and set its value to `sally-adventure-book`.
# --hints--
Your element with a class of `card` should have an `id` attribute.
```js
assert.isTrue(document.querySelector('.card')?.hasAttribute('id'));
```
Your element having a class of `card` should have an `id` having the value of `sally-adventure-book`.
```js
assert.equal(document.querySelector('.card')?.id, 'sally-adventure-book');
```
# --seed--
## --seed-contents--
```html
<h1>XYZ Bookstore</h1>
<p>Browse our collection of amazing books!</p>
<div class="card-container">
--fcc-editable-region--
<div class="card">
</div>
--fcc-editable-region--
</div>
```
@@ -0,0 +1,40 @@
---
id: 68ec98b38d83a3c28dd30efe
title: Step 7
challengeType: 0
dashedName: step-7
---
# --description--
Inside the first element having a class of `card`, add an `h2` element with the text `Sally's SciFi Adventure`.
# --hints--
You should have an `h2` element nested inside the element having a class of `card`.
```js
assert.exists(document.querySelector('.card h2'));
```
Your `h2` element's text should be `Sally's SciFi Adventure`.
```js
assert.equal(document.querySelector('.card h2')?.innerText.trim(), "Sally's SciFi Adventure");
```
# --seed--
## --seed-contents--
```html
<h1>XYZ Bookstore</h1>
<p>Browse our collection of amazing books!</p>
<div class="card-container">
--fcc-editable-region--
<div class="card" id="sally-adventure-book">
</div>
--fcc-editable-region--
</div>
```
@@ -0,0 +1,51 @@
---
id: 68ec99e478211dc578699944
title: Step 8
challengeType: 0
dashedName: step-8
---
# --description--
Below the `h2` element in the first element having a class of `card`, add a `p` element with the following text:
```md
This is an epic story of Sally and her dog Rex as they navigate through other worlds.
```
# --hints--
You should have a `p` element nested inside the element having a class of `card`.
```js
assert.exists(document.querySelector('.card p'));
```
Your `p` element's text should be `This is an epic story of Sally and her dog Rex as they navigate through other worlds.`.
```js
assert.equal(document.querySelector('.card p')?.innerText.trim(), "This is an epic story of Sally and her dog Rex as they navigate through other worlds.");
```
Your `p` element should be below your `h2` element.
```js
assert.exists(document.querySelector('.card h2 + p'));
```
# --seed--
## --seed-contents--
```html
<h1>XYZ Bookstore</h1>
<p>Browse our collection of amazing books!</p>
<div class="card-container">
--fcc-editable-region--
<div class="card" id="sally-adventure-book">
<h2>Sally's SciFi Adventure</h2>
</div>
--fcc-editable-region--
</div>
```
@@ -0,0 +1,56 @@
---
id: 68eca11e99e3c5c894ca9d69
title: Step 9
challengeType: 0
dashedName: step-9
---
# --description--
The `button` element is used to create clickable buttons on a webpage. Buttons are interactive elements that users can click to perform actions.
You can add multiple elements inside a `div` element to group related content. Add a `button` element inside the element that has a `class` of `card`, give the button a `class` attribute set to `btn`, and the text `Buy Now`.
# --hints--
You should have a `button` element nested inside the element that has a class of `card`.
```js
assert.exists(document.querySelector('.card button'));
```
Your `button` element should have a `class` attribute.
```js
assert.isTrue(document.querySelector('.card button')?.hasAttribute('class'));
```
Your `button` element should have a `class` that has a value of `btn`.
```js
assert.equal(document.querySelector('.card button')?.className, 'btn');
```
Your `button` element's text should be `Buy Now`.
```js
assert.equal(document.querySelector('.card button')?.innerText.trim(), 'Buy Now');
```
# --seed--
## --seed-contents--
```html
<h1>XYZ Bookstore</h1>
<p>Browse our collection of amazing books!</p>
<div class="card-container">
--fcc-editable-region--
<div class="card" id="sally-adventure-book">
<h2>Sally's SciFi Adventure</h2>
<p>This is an epic story of Sally and her dog Rex as they navigate through other worlds.</p>
</div>
--fcc-editable-region--
</div>
```
@@ -0,0 +1,49 @@
---
id: 68eca2a795b333ca5fee30a8
title: Step 10
challengeType: 0
dashedName: step-10
---
# --description--
Now create a second book card. Add another `div` element with the `class` attribute set to `card`. Notice how you can reuse the same class name for multiple elements to apply consistent styling.
# --hints--
You should have two `div` elements with a `class` attribute having a value of `card`.
```js
assert.lengthOf(document.querySelectorAll('.card'), 2);
```
The second element with a class of `card` should be inside the element having a class of `card-container`.
```js
assert.lengthOf(document.querySelectorAll('.card-container .card'), 2);
```
The two elements with a class of `card` should be siblings.
```js
assert.exists(document.querySelector('.card + .card'));
```
# --seed--
## --seed-contents--
```html
<h1>XYZ Bookstore</h1>
<p>Browse our collection of amazing books!</p>
<div class="card-container">
--fcc-editable-region--
<div class="card" id="sally-adventure-book">
<h2>Sally's SciFi Adventure</h2>
<p>This is an epic story of Sally and her dog Rex as they navigate through other worlds.</p>
<button class="btn">Buy Now</button>
</div>
--fcc-editable-region--
</div>
```
@@ -0,0 +1,47 @@
---
id: 68eca3cfeebef2cd8cc5f814
title: Step 11
challengeType: 0
dashedName: step-11
---
# --description--
Add an `id` attribute to your second element having a class of `card` and set its value to `dave-cooking-book`. Remember that each `id` must be unique.
# --hints--
Your second element having a class of `card` should have an `id` attribute.
```js
const cards = document.querySelectorAll('.card');
assert.isTrue(cards[1]?.hasAttribute('id'));
```
Your second element having a class of`card` should have an `id` with value of `dave-cooking-book`.
```js
const cards = document.querySelectorAll('.card');
assert.equal(cards[1]?.id, 'dave-cooking-book');
```
# --seed--
## --seed-contents--
```html
<h1>XYZ Bookstore</h1>
<p>Browse our collection of amazing books!</p>
<div class="card-container">
<div class="card" id="sally-adventure-book">
<h2>Sally's SciFi Adventure</h2>
<p>This is an epic story of Sally and her dog Rex as they navigate through other worlds.</p>
<button class="btn">Buy Now</button>
</div>
--fcc-editable-region--
<div class="card">
</div>
--fcc-editable-region--
</div>
```
@@ -0,0 +1,47 @@
---
id: 68eca4c4b0f952cf09fabe09
title: Step 12
challengeType: 0
dashedName: step-12
---
# --description--
Inside the second element having a class of `card`, add an `h2` element with the text `Dave's Cooking Adventure`.
# --hints--
You should have an `h2` element nested inside the second element that has a class of `card`.
```js
const cards = document.querySelectorAll('.card');
assert.exists(cards[1]?.querySelector('h2'));
```
The `h2` element inside the second element having a class of `card` should have a text of `Dave's Cooking Adventure`.
```js
const cards = document.querySelectorAll('.card');
assert.equal(cards[1]?.querySelector('h2')?.innerText.trim(), "Dave's Cooking Adventure");
```
# --seed--
## --seed-contents--
```html
<h1>XYZ Bookstore</h1>
<p>Browse our collection of amazing books!</p>
<div class="card-container">
<div class="card" id="sally-adventure-book">
<h2>Sally's SciFi Adventure</h2>
<p>This is an epic story of Sally and her dog Rex as they navigate through other worlds.</p>
<button class="btn">Buy Now</button>
</div>
--fcc-editable-region--
<div class="card" id="dave-cooking-book">
</div>
--fcc-editable-region--
</div>
```
@@ -0,0 +1,59 @@
---
id: 68eca5412b5dd9d06b3d6404
title: Step 13
challengeType: 0
dashedName: step-13
---
# --description--
Below the `h2` element in the second card, add a `p` element with this text:
```md
This is the story of Dave as he learns to cook everything from pancakes to pasta, one recipe at a time.
```
# --hints--
You should have a `p` element nested inside the second element that has a class of `card`.
```js
const cards = document.querySelectorAll('.card');
assert.exists(cards[1]?.querySelector('p'));
```
Your second card's `p` element's text should be `This is the story of Dave as he learns to cook everything from pancakes to pasta, one recipe at a time.`.
```js
const cards = document.querySelectorAll('.card');
assert.equal(cards[1]?.querySelector('p')?.innerText.trim(), "This is the story of Dave as he learns to cook everything from pancakes to pasta, one recipe at a time.");
```
Your second card's `p` element should be below your `h2` element.
```js
const cards = document.querySelectorAll('.card');
assert.exists(cards[1]?.querySelector('h2 + p'));
```
# --seed--
## --seed-contents--
```html
<h1>XYZ Bookstore</h1>
<p>Browse our collection of amazing books!</p>
<div class="card-container">
<div class="card" id="sally-adventure-book">
<h2>Sally's SciFi Adventure</h2>
<p>This is an epic story of Sally and her dog Rex as they navigate through other worlds.</p>
<button class="btn">Buy Now</button>
</div>
--fcc-editable-region--
<div class="card" id="dave-cooking-book">
<h2>Dave's Cooking Adventure</h2>
</div>
--fcc-editable-region--
</div>
```
@@ -0,0 +1,64 @@
---
id: 68eca5c0065c82d256d29ca3
title: Step 14
challengeType: 0
dashedName: step-14
---
# --description--
Inside the second card, add a `button` element with the `class` attribute set to `btn` and the text `Buy Now`.
Both `button` elements now share the same `class`, which means they can be styled consistently together.
# --hints--
You should have a `button` element nested inside the second element that has a class of `card`.
```js
const cards = document.querySelectorAll('.card');
assert.exists(cards[1]?.querySelector('button'));
```
Your second card's `button` element should have a `class` attribute.
```js
const cards = document.querySelectorAll('.card');
assert.isTrue(cards[1]?.querySelector('button')?.hasAttribute('class'));
```
Your second card's `button` element should have a `class` with the value of `btn`.
```js
const cards = document.querySelectorAll('.card');
assert.equal(cards[1]?.querySelector('button')?.className, 'btn');
```
Your second card's `button` element's text should be `Buy Now`.
```js
const cards = document.querySelectorAll('.card');
assert.equal(cards[1]?.querySelector('button')?.innerText.trim(), 'Buy Now');
```
# --seed--
## --seed-contents--
```html
<h1>XYZ Bookstore</h1>
<p>Browse our collection of amazing books!</p>
<div class="card-container">
<div class="card" id="sally-adventure-book">
<h2>Sally's SciFi Adventure</h2>
<p>This is an epic story of Sally and her dog Rex as they navigate through other worlds.</p>
<button class="btn">Buy Now</button>
</div>
--fcc-editable-region--
<div class="card" id="dave-cooking-book">
<h2>Dave's Cooking Adventure</h2>
<p>This is the story of Dave as he learns to cook everything from pancakes to pasta, one recipe at a time.</p>
</div>
--fcc-editable-region--
</div>
```
@@ -0,0 +1,67 @@
---
id: 68eca6364c5616d393389a30
title: Step 15
challengeType: 0
dashedName: step-15
---
# --description--
Below the element with the class `card-container`, add a new `p` element with this text:
```md
Review your selections and continue to checkout.
```
Below the `p` element, create a `div` element with the `class` attribute set to `btn-container`. This container will group your navigation button elements.
# --hints--
The newly added `p` element should be below the element with a class of `card-container`.
```js
assert.exists(document.querySelector('.card-container + p'));
```
Your new `p` element's text should be `Review your selections and continue to checkout.`.
```js
const cardContainer = document.querySelector('.card-container');
assert.equal(cardContainer?.nextElementSibling?.innerText.trim(), 'Review your selections and continue to checkout.');
```
You should have a `div` element with a `class` of `btn-container`.
```js
assert.exists(document.querySelector('div.btn-container'));
```
Your `div` element having a `class` of `btn-container` should come after the newly added `p` element.
```js
assert.exists(document.querySelector('p + .btn-container'));
```
# --seed--
## --seed-contents--
```html
--fcc-editable-region--
<h1>XYZ Bookstore</h1>
<p>Browse our collection of amazing books!</p>
<div class="card-container">
<div class="card" id="sally-adventure-book">
<h2>Sally's SciFi Adventure</h2>
<p>This is an epic story of Sally and her dog Rex as they navigate through other worlds.</p>
<button class="btn">Buy Now</button>
</div>
<div class="card" id="dave-cooking-book">
<h2>Dave's Cooking Adventure</h2>
<p>This is the story of Dave as he learns to cook everything from pancakes to pasta, one recipe at a time.</p>
<button class="btn">Buy Now</button>
</div>
</div>
--fcc-editable-region--
```
@@ -0,0 +1,136 @@
---
id: 68eca6e5a6759ed4ea0034dc
title: Step 16
challengeType: 0
dashedName: step-16
---
# --description--
Inside the element with a class of `btn-container`, add two `button` elements:
First button:
- Id: `view-cart-btn`
- Class: `btn`
- Text: `View Cart`
Second button:
- Id: `checkout-btn`
- Class: `btn`
- Text: `Checkout`
Congratulations! You have successfully built the structure of a bookstore page using divs, classes, and ids to organize your content. You used classes to group elements that share styling (the card divs and buttons), and you used unique ids to identify specific elements (individual book cards and action buttons).
# --hints--
You should have two `button` elements nested inside the `.btn-container` element.
```js
assert.lengthOf(document.querySelectorAll('.btn-container button'), 2);
```
Your first button inside element with the class of `btn-container` should have an `id` attribute with the value of `view-cart-btn`.
```js
const buttons = document.querySelectorAll('.btn-container button');
assert.equal(buttons[0]?.id, 'view-cart-btn');
```
Your first button inside the element having a class of `btn-container` should have a `class` attribute with the value of `btn`.
```js
const buttons = document.querySelectorAll('.btn-container button');
assert.equal(buttons[0]?.className, 'btn');
```
Your first button's text should be `View Cart`.
```js
const buttons = document.querySelectorAll('.btn-container button');
assert.equal(buttons[0]?.innerText.trim(), 'View Cart');
```
Your second button inside the element with a class of `btn-container` should have an `id` attribute with the value of `checkout-btn`.
```js
const buttons = document.querySelectorAll('.btn-container button');
assert.equal(buttons[1]?.id, 'checkout-btn');
```
Your second button inside the element with `class="btn-container"` should have a `class` attribute with the value of `btn`.
```js
const buttons = document.querySelectorAll('.btn-container button');
assert.equal(buttons[1]?.className, 'btn');
```
Your second button's text should be `Checkout`.
```js
const buttons = document.querySelectorAll('.btn-container button');
assert.equal(buttons[1]?.innerText.trim(), 'Checkout');
```
# --seed--
## --seed-contents--
```html
<h1>XYZ Bookstore</h1>
<p>Browse our collection of amazing books!</p>
<div class="card-container">
<div class="card" id="sally-adventure-book">
<h2>Sally's SciFi Adventure</h2>
<p>This is an epic story of Sally and her dog Rex as they navigate through other worlds.</p>
<button class="btn">Buy Now</button>
</div>
<div class="card" id="dave-cooking-book">
<h2>Dave's Cooking Adventure</h2>
<p>This is the story of Dave as he learns to cook everything from pancakes to pasta, one recipe at a time.</p>
<button class="btn">Buy Now</button>
</div>
</div>
--fcc-editable-region--
<p>Review your selections and continue to checkout.</p>
<div class="btn-container">
</div>
--fcc-editable-region--
```
# --solutions--
```html
<h1>XYZ Bookstore</h1>
<p>Browse our collection of amazing books!</p>
<div class="card-container">
<div class="card" id="sally-adventure-book">
<h2>Sally's SciFi Adventure</h2>
<p>
This is an epic story of Sally and her dog Rex as they navigate
through other worlds.
</p>
<button class="btn">Buy Now</button>
</div>
<div class="card" id="dave-cooking-book">
<h2>Dave's Cooking Adventure</h2>
<p>
This is the story of Dave as he learns to cook everything from
pancakes to pasta, one recipe at a time.
</p>
<button class="btn">Buy Now</button>
</div>
</div>
<p>Review your selections and continue to checkout.</p>
<div class="btn-container">
<button id="view-cart-btn" class="btn">View Cart</button>
<button id="checkout-btn" class="btn">Checkout</button>
</div>
```
@@ -0,0 +1,28 @@
{
"name": "Build a Bookstore Page",
"isUpcomingChange": false,
"dashedName": "workshop-bookstore-page",
"helpCategory": "HTML-CSS",
"blockLayout": "challenge-grid",
"challengeOrder": [
{ "id": "68e97fe79367ad7b5dd6c9cd", "title": "Step 1" },
{ "id": "68ebdbacdd3fa474132cc975", "title": "Step 2" },
{ "id": "68ec6cd5b7e8f5a8f7319e32", "title": "Step 3" },
{ "id": "68ec6d9a315221aa31e54816", "title": "Step 4" },
{ "id": "68ec6e8d0caee3afaaf142ef", "title": "Step 5" },
{ "id": "68ec9332a9b5b2b32487bd00", "title": "Step 6" },
{ "id": "68ec98b38d83a3c28dd30efe", "title": "Step 7" },
{ "id": "68ec99e478211dc578699944", "title": "Step 8" },
{ "id": "68eca11e99e3c5c894ca9d69", "title": "Step 9" },
{ "id": "68eca2a795b333ca5fee30a8", "title": "Step 10" },
{ "id": "68eca3cfeebef2cd8cc5f814", "title": "Step 11" },
{ "id": "68eca4c4b0f952cf09fabe09", "title": "Step 12" },
{ "id": "68eca5412b5dd9d06b3d6404", "title": "Step 13" },
{ "id": "68eca5c0065c82d256d29ca3", "title": "Step 14" },
{ "id": "68eca6364c5616d393389a30", "title": "Step 15" },
{ "id": "68eca6e5a6759ed4ea0034dc", "title": "Step 16" }
],
"blockLabel": "workshop",
"usesMultifileEditor": true,
"hasEditableBoundaries": true
}
@@ -14,6 +14,7 @@
"workshop-cat-photo-app",
"lab-recipe-page",
"lecture-html-fundamentals",
"workshop-bookstore-page",
"lecture-understanding-how-html-affects-seo",
"lab-travel-agency-page",
"lecture-working-with-audio-and-video-elements",