100% Free Forever
AI-Powered Learning
Industry Expert Content
Certificates & Badges
Learn At Your Own Pace
HomeBlogLearn CSS Through Photography: Build a Portfolio Gallery
Learn Through Hobbies

Learn CSS Through Photography: Build a Portfolio Gallery

SV

SkillVeris Team

Content Team

Jun 8, 2026 10 min read
Share:
Learn CSS Through Photography: Build a Portfolio Gallery
Key Takeaway

CSS Grid and Flexbox are the two layout systems every web developer must know, and photography makes them tangible.

In this guide, you'll learn:

  • A photo gallery gives instant visual feedback: change a grid column and you watch the layout update.
  • The repeat(auto-fill, minmax(280px, 1fr)) pattern builds responsive grids with zero media queries.
  • Combining aspect-ratio with object-fit: cover keeps image cards uniform without distorting photos.
  • Animate only transform and opacity so effects stay GPU-accelerated and smooth on every device.

1Why Photography Is Perfect for Learning CSS

CSS is a visual language, and learning it with visual content is dramatically more effective than memorising properties in isolation. Every CSS rule you write instantly changes how photos are arranged, sized, and revealed.

When you add a gap and the grid breathes, or you write a transform: scale(1.05) and an image lifts on hover, the connection between code and output is immediate and satisfying.

The gallery project covers CSS Grid, Flexbox, transitions, custom properties, media queries, and a touch of JavaScript — the complete modern CSS toolkit.

2Project Structure

The project keeps a small, flat structure: one HTML file for markup, one stylesheet, a minimal JavaScript file for the lightbox and filter logic, and a folder of images.

Use your own photos, or download royalty-free images from Unsplash (unsplash.com) or Pexels (pexels.com). Aim for a mix of landscape and portrait orientations — it makes the grid layout more interesting.

Folder Layout

code
photo-gallery/
index.html # gallery markup
style.css # all CSS
app.js # lightbox + filter logic (minimal JS)
photos/ # your images
city-1.jpg
nature-1.jpg
...

4CSS Grid: The Foundation Layout

The gallery is laid out as a CSS Grid against a dark background, with consistent spacing and a centred maximum width. Four gallery features — masonry grid, hover overlay, lightbox, and filter bar — each teach a different CSS technique.

repeat(auto-fill, minmax(280px, 1fr)) is one of the most powerful CSS Grid patterns: it creates as many columns as fit, each at least 280px wide, expanding to fill available space. On a wide screen that's 4 columns, on a tablet 2, and on mobile 1 — with zero media queries needed for this behaviour.

style.css

code
/* style.css */
*, *::before, *::after { box-sizing: border-box; }
body {
margin: 0;
background: #0f172a;
color: #e5e7eb;
font-family: 'Helvetica Neue', Arial, sans-serif;
}
.gallery {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(280px, 1fr));
gap: 16px;
padding: 24px;
max-width: 1400px;
margin: 0 auto;
}

5Aspect Ratio and Object Fit

Each photo card has rounded corners, hidden overflow, and relative positioning so the caption can be layered on top. The image itself fills the card and lifts slightly on hover with a smooth transform transition.

aspect-ratio: 4/3 keeps every card the same height regardless of the photo's original dimensions. object-fit: cover crops the image to fill the space without distortion — the combination eliminates the broken grid you get when images have different sizes.

aspect-ratio plus object-fit: cover keeps every card uniform without distorting photos.
aspect-ratio plus object-fit: cover keeps every card uniform without distorting photos.

Card and Image Styles

code
.photo-card {
margin: 0;
border-radius: 12px;
overflow: hidden;
position: relative;
cursor: pointer;
}
.photo-card img {
width: 100%;
aspect-ratio: 4 / 3; /* consistent card height */
object-fit: cover; /* crop, never distort */
display: block;
transition: transform 0.4s ease;
}
.photo-card:hover img {
transform: scale(1.06);
}

6Hover Overlays with Transitions

The caption is absolutely positioned along the bottom of the card with a gradient background, hidden below the card by default and slid into view on hover.

This pattern — hide with transform: translateY(100%) and reveal with translateY(0) on hover — is far smoother than toggling display: none because transforms are GPU-accelerated. The gradient overlay ensures caption text is always readable regardless of the photo's colours.

💡Pro Tip

Always use transform and opacity for CSS animations — not width, height, top, or margin. Only transform and opacity trigger GPU compositing; everything else causes layout recalculation and janky animation on less powerful devices.

Caption Reveal

code
.photo-card figcaption {
position: absolute;
bottom: 0; left: 0; right: 0;
padding: 32px 16px 16px;
background: linear-gradient(transparent, rgba(0,0,0,0.8));
color: #fff;
font-size: 0.9rem;
letter-spacing: 0.5px;
transform: translateY(100%); /* hidden below the card */
transition: transform 0.3s ease;
}
.photo-card:hover figcaption {
transform: translateY(0%); /* slides up on hover */
}

7Responsive Grid with Media Queries

The four CSS concepts this project teaches in a visual context are CSS Grid, Flexbox, custom properties, and media queries. The auto-fill/minmax grid handles most responsiveness automatically — you only need explicit breakpoints for the header and filter bar.

Mobile-first principle: write CSS for mobile screens first, then use min-width media queries to adapt for larger screens. It produces cleaner, lighter stylesheets than the reverse.

Header Breakpoints

code
header {
display: flex;
justify-content: space-between;
align-items: center;
flex-wrap: wrap;
gap: 16px;
padding: 24px;
border-bottom: 1px solid #1e293b;
}
@media (max-width: 600px) {
header {
flex-direction: column;
text-align: center;
}
.filter-bar {
justify-content: center;
}
}

8CSS Custom Properties

Define your design tokens once in :root — accent colour, backgrounds, text colours, radius, gap, and transition — then reference them throughout the stylesheet with var().

CSS custom properties (variables) let you change the entire colour scheme by updating five lines at the top of the file. It's also how design systems and dark/light mode switching work — the same technique used at scale in every major design system.

Design Tokens

code
:root {
--accent: #f59e0b;
--bg: #0f172a;
--surface: #1e293b;
--text: #e5e7eb;
--muted: #9aa3af;
--radius: 12px;
--gap: 16px;
--transition: 0.3s ease;
}
/* Use them throughout */
.gallery { gap: var(--gap); }
.photo-card { border-radius: var(--radius); }
.filter-bar button.active { border-color: var(--accent); color: var(--accent); }

10Category Filter Bar

The filter bar toggles which photos are visible by reading a data attribute on each button and comparing it against each card's category.

HTML data-* attributes are the correct way to store custom data on elements. They're readable from JavaScript via element.dataset.category and don't affect styling or semantics. This pattern appears in every filter UI, tab panel, and drag-and-drop interface.

Filter Logic

code
// Filter photos by category using data attributes
document.querySelectorAll('.filter-bar button').forEach(btn => {
btn.addEventListener('click', () => {
document.querySelector('.filter-bar .active').classList.remove('active');
btn.classList.add('active');
const filter = btn.dataset.filter;
document.querySelectorAll('.photo-card').forEach(card => {
const show = filter === 'all' || card.dataset.category === filter;
card.style.display = show ? '' : 'none';
});
});
});

11Finishing Touches

A few extra refinements turn the gallery from functional into polished — a staggered entrance animation, keyboard accessibility, image optimisation, and a free deployment.

  • Loading animation: add animation: fadeIn 0.4s ease to .photo-card for a staggered entrance effect.
  • Keyboard accessibility: close the lightbox on the Escape key with a document.addEventListener('keydown', ...) handler.
  • Image optimisation: compress images with Squoosh (squoosh.app) before deploying — uncompressed photos are the single biggest performance issue on gallery sites.
  • Deploy: push to GitHub and enable GitHub Pages in repository settings for a free live URL.

12Key Takeaways

These principles carry over to almost every layout you'll build with CSS, far beyond a photo gallery.

  • repeat(auto-fill, minmax(280px, 1fr)) is the one CSS Grid pattern that makes most responsive grids work without media queries.
  • Use aspect-ratio + object-fit: cover to keep image cards uniform without distorting photos.
  • Animate with transform and opacity only — they're GPU-accelerated and stay smooth on all devices.
  • CSS custom properties make theming, dark mode, and design consistency straightforward.

13What to Learn Next

Once the gallery works, keep growing your CSS skills with a few natural next steps.

  • Add smooth filter animations: use opacity and transform: scale transitions instead of toggling display:none.
  • React Hooks Explained — rebuild the gallery as a React component with state-driven filters.
  • How to Build a Developer Portfolio — this gallery can be your portfolio itself.

14Frequently Asked Questions

Q: What is the difference between CSS Grid and Flexbox?

A: Flexbox is one-dimensional: it lays out items in a single row or column. CSS Grid is two-dimensional: it arranges items in rows and columns simultaneously. Use Flexbox for navigation bars, button groups, and card content, and use Grid for overall page layouts and photo galleries. They complement each other perfectly.

Q: How do I make a true Pinterest-style masonry layout in CSS?

A: The CSS columns property creates a column-based masonry layout: .gallery { columns: 3; gap: 16px; } with .photo-card { break-inside: avoid; }. True masonry, where items fill gaps optimally, requires JavaScript or the experimental CSS grid-template-rows: masonry (not yet in all browsers as of 2026).

Q: Should I use CSS Grid or a framework like Bootstrap?

A: Learn CSS Grid first — it gives you a deep understanding of layout that makes any framework easier to use. Bootstrap's grid is built on Flexbox and CSS Grid underneath. Once you understand the fundamentals, Bootstrap accelerates production work; without them, you're fighting magic class names.

Q: How do I optimise images for the web?

A: Resize images to the largest size they'll be displayed, no larger. Convert to WebP format for 30–50% smaller files, use loading="lazy" on images below the fold, and use srcset to serve different sizes for different screen densities. Squoosh (squoosh.app) handles all of this for free in the browser.

📄

Get The Print Version

Download a PDF of this article for offline reading.

About the Publisher

SV

SkillVeris Team

Content Team

We believe the best way to learn tech is through what you already love — sports, music, photography, and more.

View all posts

Never miss an update

Get the latest tutorials and guides delivered to your inbox.

No spam. Unsubscribe anytime.