js/components/card.js — the card by hand
Now — the card. In the code column, js/components/card.js: createCard(pic) takes one picture's data and returns a ready <article> element.
Read it as a recipe: create an article, put a header with an img inside it, then a body with a title and date. Every node is createElement, configured, and appendChild. The function returns the card but never places it — where it goes is the caller's decision (in lesson 5, the gallery grid).
Three details worth noticing:
img.alt = pic.title— a real alt text. Screen readers read it; this is accessibility, not decoration.img.loading = 'lazy'— the browser only downloads the image when it's near the viewport. A free performance win (more in lesson 5).title.textContent = pic.title— the title comes from the API, sotextContent, notinnerHTML.
Verify. Temporarily, in main.js, create one card with fake data and put it on the page:
import { createCard } from './components/card.js';
const demo = {
url: 'https://apod.nasa.gov/apod/image/2312/… .jpg',
title: 'The Andromeda Galaxy',
date: '2026-07-11',
};
document.getElementById('app').appendChild(createCard(demo));
Serve it (python -m http.server) and open it — you should see a card with the image, title and date.
Gotcha (XSS). Imagine setting the title through
innerHTML:title.innerHTML = pic.title; // ❌ dangerousIf some title (from an API or a user) were
<img src=x onerror="alert(document.cookie)">, the browser would execute it — someone else's code on your page. WithtextContent, that same text is just shown as characters. Always put dynamic text in viatextContent. ReserveinnerHTMLfor safe HTML you wrote yourself.