Skip to main content
All projects
Web project

Responsive photo gallery

Galleries are a classic use of CSS Grid: one line of CSS can decide how many columns fit. The starter uses coloured tiles as stand-ins for photos, so it works anywhere. Swap in your own pictures whenever you like.

Skills you'll practise

  • Semantic HTML
  • CSS Grid
  • Responsive design
  • aspect-ratio

Steps

  1. Step 1: Mark up the photos

    Each photo is a <figure> with the picture and a <figcaption>. Add at least six.

    Show a hint

    Using a real image? Write <img src="..." alt="what the picture shows"> in place of the coloured <div>.

  2. Step 2: Make a grid

    Turn the gallery into a grid with a gap between the tiles.

    Show a hint

    display: grid; gap: 16px; on the .gallery element.

  3. Step 3: Let the columns decide themselves

    Use repeat(auto-fill, minmax(180px, 1fr)) so the grid fits as many columns as it can, with no media queries.

  4. Step 4: Keep every tile the same shape

    Give each photo aspect-ratio: 4 / 3 so tiles stay even however wide they get.

    Show a hint

    For real images, add object-fit: cover so they fill the tile without stretching.

  5. Step 5: Add a gentle hover

    Lift a tile slightly on hover, and turn the effect off for people who prefer reduced motion.

    Show a hint

    Wrap the transition in @media (prefers-reduced-motion: no-preference) { ... }.

Starter code

It already shows something on the page. The TODO comments mark where to start.

Open in playground
index.html
<main>
  <h1>My gallery</h1>
  <section class="gallery">
    <figure>
      <div class="photo sunrise"></div>
      <figcaption>Sunrise</figcaption>
    </figure>
    <figure>
      <div class="photo forest"></div>
      <figcaption>Forest</figcaption>
    </figure>
    <!-- TODO: add at least four more figures -->
  </section>
</main>
style.css
body {
  font-family: system-ui, sans-serif;
  background: #fcf4e8;
  color: #15120c;
}

.gallery {
  /* TODO: make this a responsive grid */
}

.photo {
  height: 120px;
}

.sunrise { background: linear-gradient(135deg, #ffd59e, #f28c6b); }
.forest { background: linear-gradient(135deg, #a8d5a2, #3e7f5c); }

Example solution

One way to finish it. Have a go first; yours doesn't need to match.

Reveal the solution
index.html
<main>
  <h1>My gallery</h1>
  <section class="gallery" aria-label="Photos">
    <figure>
      <div class="photo sunrise" role="img" aria-label="Orange sunrise"></div>
      <figcaption>Sunrise</figcaption>
    </figure>
    <figure>
      <div class="photo forest" role="img" aria-label="Green forest"></div>
      <figcaption>Forest</figcaption>
    </figure>
    <figure>
      <div class="photo ocean" role="img" aria-label="Blue ocean"></div>
      <figcaption>Ocean</figcaption>
    </figure>
    <figure>
      <div class="photo meadow" role="img" aria-label="Pale green meadow"></div>
      <figcaption>Meadow</figcaption>
    </figure>
    <figure>
      <div class="photo dusk" role="img" aria-label="Purple dusk sky"></div>
      <figcaption>Dusk</figcaption>
    </figure>
    <figure>
      <div class="photo desert" role="img" aria-label="Sandy desert"></div>
      <figcaption>Desert</figcaption>
    </figure>
  </section>
</main>
style.css
* {
  box-sizing: border-box;
}

body {
  margin: 0;
  font-family: system-ui, sans-serif;
  background: #fcf4e8;
  color: #15120c;
}

main {
  max-width: 960px;
  margin: 0 auto;
  padding: 32px 16px;
}

h1 {
  margin: 0 0 20px;
}

.gallery {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(180px, 1fr));
  gap: 16px;
}

figure {
  margin: 0;
  overflow: hidden;
  background: #ffffff;
  border-radius: 12px;
  box-shadow: 0 4px 12px rgba(46, 38, 24, 0.08);
}

.photo {
  width: 100%;
  aspect-ratio: 4 / 3;
}

figcaption {
  padding: 10px 12px;
  font-size: 14px;
  color: #4f483d;
}

.sunrise { background: linear-gradient(135deg, #ffd59e, #f28c6b); }
.forest { background: linear-gradient(135deg, #a8d5a2, #3e7f5c); }
.ocean { background: linear-gradient(135deg, #a7d8f0, #2f6f9f); }
.meadow { background: linear-gradient(135deg, #e6f4d7, #9cc98a); }
.dusk { background: linear-gradient(135deg, #c9b6e4, #5b4b8a); }
.desert { background: linear-gradient(135deg, #f5e0b7, #c9935a); }

@media (prefers-reduced-motion: no-preference) {
  figure {
    transition: transform 0.2s ease, box-shadow 0.2s ease;
  }

  figure:hover {
    transform: translateY(-3px);
    box-shadow: 0 10px 20px rgba(46, 38, 24, 0.12);
  }
}

Stretch goal

Make one photo stand out by letting it span two columns with grid-column: span 2, and check the layout still works on a phone-sized screen.

Chapters that help