Album Component Tutorial - HTML/CSS

Album Component Tutorial - HTML/CSS

Posted on 2025 February 23 · Tutorials

Component when not hovered Component when hovered

This tutorial will teach you how to make an HTML album component, as shown above, for the gallery or artwork pages for your websites. This component is inspired by early smartphone and skeuomorphic design, such as Samsung's Gallery app on their old phones. Much like the inspiration, this component will have images that are stylized to have photo borders, are visibly stacked on each other, and complete with animations. Ready to replace your boring square image* link?

*I actually do this tool in mobile mode. Still keep accessibility in mind!

Before we begin, I'm going to assume that you know how to setup a new HTML file and that is what you will be working with in this tutorial. I'll also assume that you are using your own images for this tutorial. The ones I'll use are below:

First image I will use Second image I will use Third image I will use

HTML Breakdown

This component is intended to be a link. As such, all the elements that we create from now on will be a child of a single a element.

We'll then add a span element to the anchor, which will contain our images. We'll also give this span the class name imgs-holder. We then add three <img> tags as a child to this span. Of course, set the src attribute to the thumbnails you'd like to use. The alt attributes of the image elements can just be left empty.

The next child of the anchor will be another span element, which has the content of our name. As such, give this element the class name album-name.

<a href="#">
 <span class="imgs-holder">
   <img src="1.png" alt="" />
   <img src="2.png" alt="" />
   <img src="3.png" alt="" />
 </span>

 <span class="album-name">
   Album Name
 </span>
</a>

Without any styling, the page should look like the following below.

Raw HTML with no styling

Clearly, styling is needed.

CSS Breakdown

Image Sizing and Stacking

Let's start killing the first bird by limiting the image sizes first.

img {
  max-height: 12rem;
  max-width: 12rem;
}

Moving on to the second bird, there's various ways to stack elements using CSS, but the method I'll use will be using CSS grid. Let's use a grid layout on imgs-holder. It should only have one column and row, and while we're at it, center the images too.

span.imgs-holder {
  display: grid;
  grid-template-columns: 1fr;
  grid-template-rows: 1fr;
  align-items: center;
  justify-items: center;
}

We also need to tell the images in which column and row they belong, otherwise they will be placed in newly created rows, which is what we don't want.

img {
  grid-column: 1;
  grid-row: 1;
  ...
}

Now, the images should be stacked... But don't blame yourself if you can't tell.

Images with no decoration and stacked in the wrong order

Image Order and Frames

After setting up image stacking, you may have noticed that the images are stacked in the wrong order. As such, we'll have to tell each image what order they should be in.

img:first-child {
  order: 3;
}

img:nth-child(2) {
  order: 2;
}

img:last-child {
  order: 1;
}

In addition to making each image visible and differentiable from one another, let's also add some image borders. From my personal experience, I found that HTML elements that use the border property have weird effects when using transitions and transformations. Instead of using borders on the images, let's instead give the images some padding with a background. Let's also top it off with a light drop-shadow filter.

img {
  ...
  padding: 0.25rem;
  background: linear-gradient(rgb(240, 240, 240), rgb(220, 220, 220));
  filter: drop-shadow(0 0 2px rgba(96, 96, 96, 0.75));
}

Now, the images looks nice, but we still got to adjust the rest of the component.

Images with white borders, shadows, and properly stacked

Size, Alignment, and Text

Currently, the component consumes the entire width of the page. Let's add a width and height for the component to use.

a {
  width: 16rem;
  height: 16rem;
}

Once the size has been set, let's then make the elements in the align to the component's center. We can do this with flexbox. We need to also specify that the direction the component is going in a column. Let's also add some spacing between the label and images.

a {
  display: flex;
  flex-direction: column;
  justify-content: center;
  align-items: center;
  gap: 0.5rem;
  ...
}

Finally, let's modify the text to be black, set the font, and remove the underline (at least when the component isn't focused or hovered).

a {
  ...
  color: black;
  font-family: Arial, sans-serif;
  text-decoration: none;
}

Now, it's time for the component to react to our hovers and focuses.

Component with alignmnet, size, and text adjustments

Transforms and Animation

The component will do four things when hovered/focused:

  • Have all images grow in size.
  • Have the two images behind the first slightly move outward.
  • Have the label move slightly downward to get out of the images' way.
  • Animate all the transformations above.

Each image will scale with the same factor. So let's create a scale variable so we don't have to type in the same value each time and in case we would like to easily adjust the scaling factor.

:root {
  --scale: scale(1.075);
}

We can easily animate the transformations of img and span.album-name with the transition property.

img,
span.album-name {
  transition: transform 0.1s;
}

Since this component is a link, we should probably indicate that by underlining the text on hover/focus.

a:focus,
a:hover {
  text-decoration: underline;
}

And now, time it's time to add all the transformations we would like to apply to our images and label.

a:focus img:first-child,
a:hover img:first-child {
  transform: var(--scale);
}

a:focus img:nth-child(2),
a:hover img:nth-child(2) {
  transform: var(--scale) translate(0.4rem, 0.25rem) rotate(3deg);
}

a:focus img:last-child,
a:hover img:last-child {
  transform: var(--scale) translate(-0.3rem, -0.345rem) rotate(-2deg);
}

a:focus span.album-name,
a:hover span.album-name {
  transform: translateY(0.5rem)
}

And... Voilà!

Animated component recording

Full Code

The code below is also available on Codeberg.

<!DOCTYPE html>
<html lang=" en">

<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <title>Album Component</title>
</head>

<body>
  <a href="#">
    <span class="imgs-holder">
      <img src="1.png" alt="" />
      <img src="2.png" alt="" />
      <img src="3.png" alt="" />
    </span>

    <span class="album-name">
      Album Name
    </span>
  </a>
</body>

<style>
  /* This was for screenshots. */
  body {
    background: #f5f5f5;
  }

  :root {
    --scale: scale(1.075);
  }

  a {
    display: flex;
    flex-direction: column;
    justify-content: center;
    align-items: center;
    gap: 0.5rem;

    width: 16rem;
    height: 16rem;

    color: black;
    font-family: Arial, sans-serif;
    text-decoration: none;
  }

  span.imgs-holder {
    display: grid;
    grid-template-columns: 1fr;
    grid-template-rows: 1fr;
    align-items: center;
    justify-items: center;
  }

  img {
    grid-column: 1;
    grid-row: 1;

    max-height: 12rem;
    max-width: 12rem;
    padding: 0.25rem;
    background: linear-gradient(rgb(240, 240, 240), rgb(220, 220, 220));
    filter: drop-shadow(0 0 2px rgba(96, 96, 96, 0.75));
  }

  img:first-child {
    order: 3;
  }

  img:nth-child(2) {
    order: 2;
  }

  img:last-child {
    order: 1;
  }

  img,
  span.album-name {
    transition: transform 0.1s;
  }

  a:focus,
  a:hover {
    text-decoration: underline;
  }

  a:focus img:first-child,
  a:hover img:first-child {
    transform: var(--scale);
  }

  a:focus img:nth-child(2),
  a:hover img:nth-child(2) {
    transform: var(--scale) translate(0.4rem, 0.25rem) rotate(3deg);
  }

  a:focus img:last-child,
  a:hover img:last-child {
    transform: var(--scale) translate(-0.3rem, -0.345rem) rotate(-2deg);
  }

  a:focus span.album-name,
  a:hover span.album-name {
    transform: translateY(0.5rem)
  }
</style>

</html>