feat(curriculum): adding photography lab (#61270)

Co-authored-by: Dario-DC <105294544+Dario-DC@users.noreply.github.com>
This commit is contained in:
Jessica Wilkins
2025-07-29 23:25:13 -07:00
committed by GitHub
parent c2bfdef948
commit 59e7f4e8c7
5 changed files with 434 additions and 0 deletions
+6
View File
@@ -4097,6 +4097,12 @@
"You will practice working with Tailwind CSS utility classes for flexbox layouts, colors, breakpoints and more."
]
},
"lab-photography-exhibit": {
"title": "Design a Photography Exhibit",
"intro": [
"In this lab, you will practice working with Tailwind CSS by designing a photography exhibit webpage."
]
},
"review-css-libraries-and-frameworks": {
"title": "CSS Libraries and Frameworks Review",
"intro": [
@@ -0,0 +1,9 @@
---
title: Introduction to the Design a Photography Exhibit
block: lab-photography-exhibit
superBlock: full-stack-developer
---
## Introduction to the Design a Photography Exhibit
In this lab, you will practice working with Tailwind CSS by designing a photography exhibit webpage.
@@ -0,0 +1,11 @@
{
"name": "Design a Photography Exhibit",
"blockType": "lab",
"blockLayout": "link",
"isUpcomingChange": true,
"dashedName": "lab-photography-exhibit",
"superBlock": "full-stack-developer",
"helpCategory": "HTML-CSS",
"challengeOrder": [{ "id": "686e64be3cd9db25282a956d", "title": "Design a Photography Exhibit" }],
"usesMultifileEditor": true
}
@@ -0,0 +1,405 @@
---
id: 686e64be3cd9db25282a956d
title: Design a Photography Exhibit
challengeType: 25
dashedName: lab-photography-exhibit
demoType: onClick
---
# --description--
In this lab, you'll create a photography exhibit layout using Tailwind CSS utility classes.
For the images, you can use the following URLs if you like:
- https://cdn.freecodecamp.org/curriculum/labs/colosseo.jpg
- https://cdn.freecodecamp.org/curriculum/labs/alps.jpg
- https://cdn.freecodecamp.org/curriculum/labs/sea.jpg
Fulfill the user stories below and get all the tests to pass to complete the lab.
**User Stories:**
1. You should have an element with a class called `main-container`.
2. Your `.main-container` element should also have the following Tailwind CSS utility classes:
- the correct utility class for creating a CSS Grid container.
- a utility class for defining the number of columns within the grid. You should use the utility class that utilizes the fixed number of columns. Ex. `grid-cols-<number>`.
- a utility class for setting the row and column gap for the grid items.
3. Inside your `.main-container` element, there should be at least three elements each with a class called `card`.
4. Each of your `.card` elements should also have the following Tailwind CSS utility classes:
- a utility class for setting a rounded border of your choice.
- a utility class for setting padding of your choice. You should use this format `p-<number>`.
5. Inside each of your `.card` elements, you should have the following elements:
- an image element with `src` and `alt` attributes.
- an element with the class called `subheading`.
- an element with the class called `description`.
6. Each of your image elements should have a utility class for setting a rounded border of your choice.
7. Each of your `.subheading` elements should use utility classes to set the font weight and size of your choosing.
8. Each of your `.description` elements should use a utility class to set the font size of your choosing.
# --hints--
You should have an element with a class called `main-container`.
```js
assert.exists(document.querySelector(".main-container"));
```
Your `.main-container` element should use the correct Tailwind CSS utility class for creating a CSS Grid container.
```js
const mainContainer = document.querySelector(".main-container");
assert.isTrue(mainContainer.classList.contains("grid"));
```
Your `.main-container` element should use a utility class for defining the number of columns within the grid.
```js
const mainContainer = document.querySelector(".main-container");
// Grid classes can vary, so this check is for the presence of any grid-cols class
const hasGridCols = [...mainContainer.classList].some(className => className.startsWith("grid-cols-"));
assert.isTrue(hasGridCols);
```
Your `.main-container` element should use a utility class for setting the row and column gap for the grid items.
```js
const mainContainer = document.querySelector(".main-container");
// Check for any gap class including gap-x and gap-y
const hasGap = [...mainContainer.classList].some(c => c.startsWith("gap-")) ||
(classList.some(c => c.startsWith("gap-x-")) &&
classList.some(c => c.startsWith("gap-y-")));
assert.isTrue(hasGap);
```
Your `.main-container` element should contain at least three elements each with a class called `card`.
```js
const cards = document.querySelectorAll(".main-container .card");
assert.isAtLeast(cards.length, 3);
```
Each of your `.card` elements should have a utility class for setting a rounded border.
```js
const cards = document.querySelectorAll(".main-container .card");
assert.isNotEmpty(cards);
const predefinedRoundedCorners = [
"rounded-none",
"rounded-sm",
"rounded",
"rounded-md",
"rounded-lg",
"rounded-xl",
"rounded-2xl",
"rounded-3xl",
"rounded-full"
];
cards.forEach(card => {
const hasRoundedCorners = predefinedRoundedCorners.some(corner => card.classList.contains(corner));
assert.isTrue(hasRoundedCorners);
})
```
Each of your `.card` elements should have a utility class for setting padding.
```js
const cards = document.querySelectorAll(".main-container .card");
assert.isNotEmpty(cards);
const hasPadding = [...cards].every(card => {
return [...card.classList].some(c => c.startsWith("p-"));
});
assert.isTrue(hasPadding);
```
Each of your `.card` elements should contain an image element.
```js
const images = document.querySelectorAll(".main-container .card img");
assert.isAtLeast(images.length, 3);
```
Each of your `img` elements should have a `src` attribute.
```js
const images = document.querySelectorAll(".main-container .card img");
assert.isAtLeast(images.length, 3);
images.forEach(img => assert.isTrue(img.hasAttribute("src")));
images.forEach(img => assert.isTrue(img.getAttribute("src").length > 0));
```
Each of your `img` elements should have an `alt` attribute.
```js
const images = document.querySelectorAll(".main-container .card img");
assert.isAtLeast(images.length, 3);
images.forEach(img => assert.isTrue(img.hasAttribute("alt")));
images.forEach(img => assert.isTrue(img.getAttribute("alt").length > 0));
```
Each of your `.card` elements should contain an element with the class called `subheading`.
```js
const subheadings = document.querySelectorAll(".main-container .card .subheading");
assert.isAtLeast(subheadings.length, 3);
```
Each of your `.subheading` elements should have some text content.
```js
const subheadings = document.querySelectorAll(".main-container .card .subheading");
// Ensure we are not dealing with an empty NodeList
assert.isNotEmpty(subheadings);
subheadings.forEach(subheading => {
assert.isNotEmpty(subheading.textContent.trim());
});
```
Each of your `.card` elements should contain an element with the class called `description`.
```js
const descriptions = document.querySelectorAll(".main-container .card .description");
assert.isAtLeast(descriptions.length, 3);
```
Each of your `.description` elements should have some text content.
```js
const descriptions = document.querySelectorAll(".main-container .card .description");
// Ensure we are not dealing with an empty NodeList
assert.isNotEmpty(descriptions);
descriptions.forEach(description => {
assert.isNotEmpty(description.textContent.trim());
});
```
Each of your `img` elements should have a utility class for setting a rounded border.
```js
const images = document.querySelectorAll(".main-container .card img");
assert.isNotEmpty(images);
const predefinedRoundedCorners = [
"rounded-none",
"rounded-sm",
"rounded",
"rounded-md",
"rounded-lg",
"rounded-xl",
"rounded-2xl",
"rounded-3xl",
"rounded-full"
];
images.forEach(img => {
const hasRoundedCorners = predefinedRoundedCorners.some(corner => img.classList.contains(corner));
assert.isTrue(hasRoundedCorners);
});
```
Each of your `.subheading` elements should use utility classes to set the font weight.
```js
const subheadings = document.querySelectorAll(".main-container .card .subheading");
assert.isNotEmpty(subheadings);
const predefinedFontWeights = [
"font-thin",
"font-extralight",
"font-light",
"font-normal",
"font-medium",
"font-semibold",
"font-bold",
"font-extrabold",
"font-black"
];
subheadings.forEach(subheading => {
const hasFontWeight = predefinedFontWeights.some(weight => subheading.classList.contains(weight));
assert.isTrue(hasFontWeight);
});
```
Each of your `.subheading` elements should use utility classes to set the font size.
```js
const subheadings = document.querySelectorAll(".main-container .card .subheading");
assert.isNotEmpty(subheadings);
const predefinedSizes = [
"text-xs",
"text-sm",
"text-base",
"text-lg",
"text-xl",
"text-2xl",
"text-3xl",
"text-4xl",
"text-5xl",
"text-6xl",
"text-7xl",
"text-8xl",
"text-9xl"
];
subheadings.forEach(subheading => {
const hasFontSize = predefinedSizes.some(size => subheading.classList.contains(size));
assert.isTrue(hasFontSize);
});
```
Each of your `.description` elements should use a utility class to set the font size.
```js
const descriptions = document.querySelectorAll(".description");
assert.isNotEmpty(descriptions);
const predefinedSizes = [
"text-xs",
"text-sm",
"text-base",
"text-lg",
"text-xl",
"text-2xl",
"text-3xl",
"text-4xl",
"text-5xl",
"text-6xl",
"text-7xl",
"text-8xl",
"text-9xl"
];
descriptions.forEach(description => {
const hasFontSize = predefinedSizes.some(size => description.classList.contains(size));
assert.isTrue(hasFontSize);
});
```
# --seed--
## --seed-contents--
```html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Photography Exhibit</title>
<script src="https://cdn.tailwindcss.com"></script>
</head>
<body>
</body>
</html>
```
# --solutions--
```html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Photography Exhibit</title>
<script src="https://cdn.tailwindcss.com"></script>
</head>
<body class="bg-purple-800 text-yellow-400 min-h-screen">
<h1 class="text-center text-4xl font-bold my-12">Photography Exhibit</h1>
<main class="main-container grid grid-cols-1 md:grid-cols-3 gap-8 px-4">
<figure
class="card bg-purple-900 border border-yellow-400 rounded-lg p-4"
>
<img
src="https://cdn.freecodecamp.org/curriculum/labs/colosseo.jpg"
alt="Ancient Colosseum"
class="h-48 w-full object-cover rounded-md mb-4"
/>
<figcaption>
<h2 class="subheading text-lg font-semibold mb-2">Colosseum, Rome</h2>
<p class="description text-sm mb-4">
There is so much history and beauty. The light hits it just right.
</p>
<div class="grid grid-cols-2 gap-4">
<button
class="bg-purple-800 hover:bg-purple-700 border border-yellow-400 py-1 px-2 rounded-md"
>
Like
</button>
<a
href="#"
class="bg-yellow-400 hover:bg-yellow-300 text-purple-900 font-semibold py-1 px-2 rounded-md text-center"
>
View Details
</a>
</div>
</figcaption>
</figure>
<figure
class="card bg-purple-900 border border-yellow-400 rounded-lg p-4"
>
<img
src="https://cdn.freecodecamp.org/curriculum/labs/alps.jpg"
alt="Alpine Mountains"
class="h-48 w-full object-cover rounded-md mb-4"
/>
<figcaption>
<h2 class="subheading text-lg font-semibold mb-2">The Alps</h2>
<p class="description text-sm mb-4">
The mountains go on forever and the view is breathtaking.
</p>
<div class="grid grid-cols-2 gap-4">
<button
class="bg-purple-800 hover:bg-purple-700 border border-yellow-400 py-1 px-2 rounded-md"
>
Like
</button>
<a
href="#"
class="bg-yellow-400 hover:bg-yellow-300 text-purple-900 font-semibold py-1 px-2 rounded-md text-center"
>
View Details
</a>
</div>
</figcaption>
</figure>
<figure
class="card bg-purple-900 border border-yellow-400 rounded-lg p-4"
>
<img
src="https://cdn.freecodecamp.org/curriculum/labs/sea.jpg"
alt="Calm Sea"
class="h-48 w-full object-cover rounded-md mb-4"
/>
<figcaption>
<h2 class="subheading text-lg font-semibold mb-2">Seascape</h2>
<p class="description text-sm mb-4">
The water was still, the air was cool and the feeling is pure peace.
</p>
<div class="grid grid-cols-2 gap-4">
<button
class="bg-purple-800 hover:bg-purple-700 border border-yellow-400 py-1 px-2 rounded-md"
>
Like
</button>
<a
href="#"
class="bg-yellow-400 hover:bg-yellow-300 text-purple-900 font-semibold py-1 px-2 rounded-md text-center"
>
View Details
</a>
</div>
</figcaption>
</figure>
</main>
</body>
</html>
```
@@ -1228,6 +1228,9 @@
{
"dashedName": "lab-music-shopping-cart-page"
},
{
"dashedName": "lab-photography-exhibit"
},
{
"dashedName": "review-css-libraries-and-frameworks"
},