The following exercise will walk you through creating a web page that uses CSS to create a thumbnail grid of products. When the exercise is complete, you will have a web page that looks something like what is shown in steps/final.html.
Through this exercise you will gain experience in the following techniques:
Start the exercise using the html file called start-here.html
. The css you'll use is already linked at the top of the page.
If you get lost or need to restart the exercise at a specific point, you can refer to the HTML files in the steps folder. Each of these is named for the specific step in the exercise and linked to for reference.
The starting html contains a list of thumbnails and links. Each list item looks similar to this:
<li>
<img src="images/clock_thumb.jpg" alt="alarm clock" />
<p class="description">Analog Travel Alarm Clock Braun
<span class="price">$40.00</span>
</p>
</li>
We can make this markup more meaningful by using the figure
and figcaption
elements. Review proper usage here: http://html5doctor.com/the-figure-figcaption-elements/
Modify the first couple of list items to include figure
and figcaption
elements. You should not need to remove any markup.
When finished, each thumbnail block should be marked up like the following:
<li>
<figure>
<img src="images/clock_thumb.jpg" alt="alarm clock" />
<figcaption>
<p class="description">Analog Travel Alarm Clock Braun <span class="price">$40.00</span> </p>
</figcaption>
</figure>
</li>
The thumbnails should also be links to their respective product pages.
For each thumbnail, wrap the entire figure
in a link. As of HTML5, it’s ok to wrap a block level elements with a link.
<li>
<a href="#" title="View product details">
<figure> <img src="images/clock_thumb.jpg" alt="alarm clock" /> <figcaption> <p class="description">Analog Travel Alarm Clock Braun <span class="price">$40.00</span> </p> </figcaption> </figure>
</a>
</li>
Let’s also center our page and set a max-width
of 1400px. We can use the container
class on the outermost section
for that.
<body>
<div class="container">
<header> <h1>Thumbnail Grid Layout Demo</h1> </header>
Set the margin
on the body
to be 1em on the top and 3% on the left and right.
We're going to align our 8 thumbnails into a grid to make them display better. We'll start with aligning 4 thumbnails in each row.
This will create a 4x2 grid:
We'll use the CSS Flexbox model to arrange our thumbnails into rows. Flexbox can arrange items into columns as well. CSS Tricks has an excellent reference page on flexbox:
Whenever we want elements to use flexbox as their layout module, the first step is to set the container of the elements to use flexbox. We do this by setting the display
property to the value of flex
.
In this case, we want our thumbnails to use flexbox. Each thumbnail is in an li
element. This means the container will be the ul
.
Create a CSS selector that targets the ul
that is the descendent of the thumbnail-grid
class.
Using the thumbnail-grid class as part of our CSS selector will ensure that the styles we add below will only affect our thumbnails and not the rest of the site.
Add the following property to the selector you just created:
display: flex;
If done correctly, you should see your thumbnails arranged in a line, stretching off the page.
Now that the ul has been set to display: flex;
, it is referred to as the flex container. Each of the li
s are the flex items.
Flexbox allows you to arrange items in rows or columns, and also order items from left-to-right, right-to-left, top-to-bottom, or bottom-to-top. This stems from the desires to natively support languages that are written vertically or from right-to-left.
Flexbox defaults to arranging items in a row and from a left-to-right order, much as you would expect in latin-based language.
Use the Developer Tools or Web Inspector to modify the following propties to view their effects:
Add the following property to the flex container:
flex-direction: column;
You should see the items stack in a column.
Change flex-direction to row and add the following property to the flex container:
flex-wrap: wrap-reverse;
The flex items should now be arranged in a grid, with the last item first.
In your CSS file, add flex properties to the flex container so that the flex items display in a row and wrap.
.thumbnail-grid ul {
display: flex;
flex-direction: row;
flex-wrap: wrap;
}
Our thumbnails are too large at their natural size to fit four across.
To fit 4 thumbnails across the page, each thumbnail should have a width of 25% of its container: (100% รท 4).
Create a new selector in your CSS for the flex items, which in this case are each li
element. Make sure to use the thumbnail-grid
class in your CSS to only target our li
's in our thumbnail grid.
.thumbnail-grid li {
}
Set the width of each thumbnail li
to 25%.
Our images are now too large to fit in their containers and are overflowing out of their thumbnails. This of course is also breaking our layout.
Images are one of the few content elements that will not reflow to fit inside their containers to fit automatically.
Fix this by setting the CSS max-width
property on your images to 100%. This will cause the images to never exceed 100% of their containing box.
This is a very important step to ensure our designs are responsive.
Our thumbnail blocks should now fit into a 4x2 grid on our page.
Looking closely we can see that our images are unusually small and have a lot of space around them.
Use the web inspector to look at one of the figure
elements. Notice that it has a lot of margin
applied to it.
This is a browser default that is not getting reset by the reset.css
(because figure
is an html5 element).
Let’s reset it ourselves. Create a CSS rule for figure
elements that sets both the margin
and padding
to 0.
Once we’ve done this, our images will be larger, but there will no longer be a gutter between them.
Add a gutter using the margin
property on each of the li
items. The margin should only be on the right and be 1.33%.
This will cause our rows to only have three items, bumping the fourth one down because there isn't enough space in the row.
We can fix our layout grid by adjusting the width and margins of our thumbnails to fit. The elements in each row must add up to less than 100% to fit. If they are over 100%, the last element will fall to the next row.
Change the width of each li
to 24%
Our elements are still only lining up three across. This is because our width
and margin
together equals more than 100% of the container width, causing the last item to bump down a line.
( 24% * 4 ) + ( 1.33% * 4 ) = 101.32%
Because we added a margin to the right of each item, we actually added an unnecessary margin to the right of the last item in each row.
Removing just this last margin will make our rows fit across the page.
( 24% * 4 ) + ( 1.33% * 3 ) = 99.99%
The right margin on the last item in each row needs to be removed. Since our grid will have four items in each row, we can say we need to remove the right margin from every 4th item.
We can accomplish this using the CSS :nth-child
pseudo-selector.
:nth-child is best understood by example. See this CSS Tricks page:
Going back to our grid, the following CSS rule will select every fourth li
in our thumbnail grid and set the right margin to 0.
.thumbnail-grid li:nth-child(4n) {
margin-right: 0;
}
Add the rule above to your code.
Read even more about the nth-child
pseudo-selector in How nth-child Works by CSS Tricks.
Flexbox actually allows us to do all of the things in the last step, but with less math!
Using the Developer Tools or Web Inspector, add the justify-content property to the flex container. Experiment with each of the following values to see the effect:
Finish by setting the following properties in your CSS to allow for the best visual fit:
width
property of your flex itemsjustify-content
property of your flex containerWhile our thumbnails are now lining up nicely, our captions still leave something to be desired.
We want to style the captions and price so that the price is on the right side of the thumbnail. We’ll achieve this by careful use of CSS positioning.
description
class for this. Check your HTML to understand why we would use this class.Next we'll use the CSS position
property to adjust where the prices appear.
Using the price
class, set the position
property to absolute
. This will cause our prices to be "absolutely positioned" on the screen.
.thumbnail-grid .price {
position: absolute;
}
Set the top
and right
properties to 0
.
.thumbnail-grid .price {
position: absolute;
right: 0;
top: 0;
}
Save your files and refresh your browser.
That made all of the prices go to the top right of the browser window!
This is because we positioned them absolutely, relative to the browser window.
That is the default behavior with absolute positioning.
In this case, we want to position our prices relative to their container rather than the browser window. Review the HTML to discover what the containing element (or parent) of our prices are. Does that element have a class or id?
description
class.
Add a background color to the description class to better illustrate what’s happening. Any color is fine.
Our goal is to make the prices fall into the top right corner of the background we just set.
In order to absolutely position the prices relative to their containers we must first create a positioning context. To do this, set the position property on the element's container.
The container is .description
.
To create a positioning context, change the position
property of the container to relative
.
This by itself does not have any visual effect on the page, but when combined with the absolute positioning on the child element, it sets the positioning context.
Add the following rule to your CSS:
.thumbnail-grid .description {
position: relative;
}
At smaller browser widths you might have noticed that our prices now overlap our descriptions. Obviously we'll want to avoid this. We can use padding
on the description to ensure the description text doesn't reach to the right side.
Add a right padding
of 25% to the description
class.
This helps with the layout quite a bit, but pay attention to the Black Porcelain Bowl. This item has a price of "From $24.00". At sizes below 1100px the text still overlaps. We can set the width of our price elements to cause this text to fall into two lines at smaller widths.
Set the width
of the price
elements to 25%.
Save your files and refresh your browser.
Right align the price text to make it look better when it falls onto two lines.
Remove the background color you added to the description
element.
These captions will work well on all but the smallest of screen sizes. For those sizes we would use an @media rule (or media query) to target the smaller sizes and style them differently. For now, we can be content with this.
To make our page a little bit more interesting, we want to add some interaction when we hover over our thumbnails with the mouse.
We're going to create styles that will display a colored overlay with some text when we hover over a thumbnail. Look at final.html
for an example.
In order to accomplish this, we need to add some markup that will contain our overlay text.
We’ll use a span
element with a class of overlay
for this.
The text inside the span should say “View”. (You would probably use better text for a real website)
Add the HTML immediately before the first image on the first thumbnail.
<a href="#" title="View product details"> <figure>
<span class="overlay">View</span>
<img src="images/clock_thumb.jpg" alt="alarm clock" /> <figcaption> <p class="description">Analog Travel Alarm Clock Braun <span class="price">$40.00</span> </p> </figcaption> </figure> </a>
Create a new selector in your CSS using the overlay class.
Add a rule to create a partially transparent background color. Pick any color you like and make it transparent by using using rgba
colors.
We want to style the overlay so that it is exactly the same size as the image and then overlaps it.
In your CSS, set the width
and height
of the overlay
to 100%.
Set the top
and left
properties on the overlay
to 0
.
Set the position
of the overlay to absolute
. What happens? Can you figure out why?
Our overlay covered the entire screen. This again, is relative to the browser window by default.
Setting the position to absolute caused the overlay to display according the positioning context. Since we haven't specified otherwise, it is the browser window.
Setting the left and top properties to 0 caused the overlay to start at:
We need to create a position context by setting the container of our overlay to use position relative.
Relatively position the containing element of the overlay (That’s the one that surrounds the overlay element).
What happens?
The overlay should now cover the entire figure element, including the caption.
For purely aesthetic reasons, we only want the overlay to cover the photo, not the caption.
To do this, we need to create a new container for the overlay which only contains the element with the photo.
We’ll use a div
since there is no semantic context for the container.
Create a new div
element in the first thumbnail li
that contains both the overlay and the photo.
<a href="#" title="View product details"> <figure>
<div>
<span class="overlay">View</span> <img src="images/clock_thumb.jpg" alt="alarm clock" />
</div>
<figcaption> <p class="description">Analog Travel Alarm Clock Braun <span class="price">$40.00</span> </p> </figcaption> </figure> </a>
Add a class of thumbnail
to the div
.
<a href="#" title="View product details"> <figure> <div
class="thumbnail"
> <span class="overlay">View</span> <img src="images/clock_thumb.jpg" alt="alarm clock" /> </div>
Relatively position the new container. What happens?
Style the overlay text. You can use some variation of these styles:
color: white;
font-weight: bold;
font-size: 200%;
padding-top: 40%;
text-align: center;
You may have noticed there is a tiny gap below the images where the overlay extends past the images.
This happens because they are inline by default and a small space is reserved below the element.
We can set our thumbnails to display as block to fix this. Adding a little margin below each image adds needed space between the photos and description text.
Add the following styles to get rid of the tiny gap below the images:
.thumbnail-grid img {
display: block; /* Get rid of tiny space below images */
margin-bottom: .3em;
}
While our overlay is now covering our photos just like we want, we really only want it to show when we’re hovering over the thumbnail.
Hide the overlay by setting the opacity
property to 0.
Use the hover
pseudo-class selector on the overlay
class to set the opacity
of the overlay back to 1 when we hover over it.
Add the following CSS:
.overlay:hover {
opacity: 1;
}
This effect can be enhanced even more by using CSS transitions to smooth the state change when we hover. This will have the effect of fading in our overlay background.
To learn more about CSS transitions see:
To create a simple 1 second long transition of all the properties that change on the overlay, add the following CSS:
.thumbnail-grid .overlay {
transition: 1s all;
}
Experiment with transitioning another property on your overlay.
Try changing the background color on the hover state of the overlay.
Read the CSS Tricks article to learn how to transition multiple states with different timing.
Note: If you're using Chrome, you might have noticed that you overlay text likes to "drop in" when you reload the page. This is due to a weird bug in Chrome that causes transitions to fire when the page loads. See this Stack Overflow page for details if you're curious.
Adding an empty script element to the top of your HTML page will fix this for this demo.
<link rel="stylesheet" href="css/reset.css" /> <link rel="stylesheet" href="css/styles.css" />
<script> </script>
</head>