Tour Package Cards

Develop the "card" and "button" components, and learn about background image properties.

Next video loading... 5
Get Code

Transcript

In this lesson we're going to style the "Tour Packages" cards.

Looking at our final design, we see that the cards have a few distinguishing characteristics.

First, they're each contained in a rounded-off container. And each include an image, a stylized price next to the headline, and a link has been styled as a button.

Our first change is to update the HTML semantics surrounding each card. Right now we only have them marked up using semantic typography tags, but we need to wrap them in containing elements to create the card appearance.

In this case, the most semantic tag is a list, so we will open up a list element and add three list items.

<ul>
<li></li>
<li></li>
<li></li>
</ul>

THe shortcut I just used is called Emmett and can rapidly create HTML elements with CSS selector type syntax.

# Emmet shortcut
ul>li*3

We need to move the contents into each list item.

On save, the primary visual change is that each item now has the standard list bullet attached to it. So our first style change is to create a utility class called .list-unstyled to remove default list styling.

However, the method we'll use also removes the semantics of it being a list when the page is consumed by assistive technology. To address that, we also need to explicitly add the role attribute with the value of list.

<ul role="list" class="list-unstyled"></ul>

In our styles, we will add the .list-unstyled rule as a /* Type Utility */ and the essential styles here are:

.list-unstyled {
margin: 0;
padding: 0;
list-style: none;
}

On save, we see that we have removed all of the spacing that the initial list had added, as well as the bullets. Now we can continue to style our card appearance.

Our first step is to add the class of card to each li element.

Referring back to our final design, it's now time to add a placeholder for the card image. As in the previous lesson for the footer logo, we will use a service call placehold.it.

However, we're not going to enter this as a standard img tag. Instead, we are going to create a div with the class of .card__img and give it an inline style with the value of background-image using the placehold.it service. We will copy this div as the first element within each card.

<div class="card__img" style="background-image: url(https://placehold.it/300x200);"></div>

On save, there's no changes on the page because we need to explicitly define dimensions for this div since it has no actual content. So we will begin a new section in our stylesheet for /* Component: Card */.

For the card itself, we will add a border-radius of 8px, and a slight box-shadow.

.card {
border-radius: 8px;
box-shadow: 0px 3px 5px rgba(0, 0, 0, 0.18);
}

Then we'll begin the rule for the card image.

First let's apply some height. We'll use the value min-height: 20vh. vh is relative to the viewport and vh specifically stands for "viewheight". So as the viewport is resized, such as down to a mobile device, the image will only ever be as tall as essentially 20% of the height.

.card__img {
min-height: 20vh;
}

You can see that once we've done that, this background image has repeated itself to fill the space. We want to remove that behavior, which we can do with background-repeat: no-repeat.

.card__img {
/* ..existing styles */
background-repeat: no-repeat;
}

We want to direct the background image to [cover] any available space, and for that we will use the property background-size with the keyword cover, and as you can see it has in fact filled the space.

.card__img {
/* ..existing styles */
background-size: cover;
}

Before we move on to the main content let's address the overall layout of the card section.

Returning to our list HTML, the first class we will use is our pre-existing flexbox class. And upon save, our card items are now aligned in a row which is close to what we want for the final appearance. However, we need to resolve their width and we would like to have a bit of space between each card.

We will be creating a utility class for .flexbox--3col to represent three columns, so let's create those styles.

Our original flexbox class was located in our /* Utility */ section. For our .flexbox--3col class, we actually want to attach the rules we provide to the flex children. And because those children may not always be list items, we will use the child combinator (>) selector along with the universal selector (*) where the universal selector means that this will apply to any child elements of the flexbox container.

.flexbox--3col > * {
}

Then we will use the flex shorthand to define that the elements are not able to grow with the value of 0, but they are able to shrink the value of 1, and that their default width, which corresponds to the property flex-basis, should be 30%.

.flexbox--3col > * {
flex: 0 1 30%;
}

And on save, we have three nicely equal columns.

Two things are making the space possible between card elements. The first is that 3 * 30 is not quite 100%, which allows there to be extra space within this overall width. Additionally our initial flexbox rule includes justify-content: space-between which we also used in the footer, and if you recall, assigns that extra space between elements.

Okay, back to our cards. For the .card content, we would like there to be some space around the edges. So, you might think that we want to add something like padding, and we'll say 24px.

But on save, you can see that's not quite the result we want if we compare it to our final design. Whereas in the final design design, this padding is only around the content area and not around the image.

To resolve that, let's return to our HTML and wrap the typography elements in a new div called .card__content and we'll do this for each cards' content.

Now, instead of placing the padding on the main card class, we will apply instead to the card content only.

.card__content {
padding: 24px;
}

And on save, the padding issue is resolved.

If you look closely you'll notice that the image has squared-off corners when it should have rounded corners. We applied border-radius to all four sides of the card, but the image sits on a virtual layer above that card. So we also need to apply a border-radius rule, and for this we'll use the long-hand method. Which means we apply 8px for the top-left, 8px for the top-right, and 0 for the bottom-right and bottom-left.

.card__img {
/* ..existing styles */
border-radius: 8px 8px 0 0;
}

And on save, you can see the corners now match what we expect for the card base.

The remaining content elements are the price, and the button appearance.

We see that each price is wrapped in a span tag inside of the h3. We can use that information to create a style rule where we specifically look for the span inside of an h3 inside of the card.

.card h3 span {
}

And the styles we'll apply here are a new font-family, a slightly reduced font size, bit of padding, a variation of our pink brand color, and again some border-radius.

.card h3 span {
font-family: monospace;
font-size: 0.8em;
padding: 0.05em 0.25em;
background-color: rgba(255, 124, 166, 0.4);
border-radius: 4px;
}

Since we used selector styles instead of classes, on save you can see that this has been correctly applied.

It is acceptable to use elements selectors when you have something like a card that's a repeatable, expected component, or you can depend on the elements used and the order they're used in.

Finally, we need to create a button style for the call to action button in each card.

Let's create a new section for /* Component: Button */ and then create the class .button.

We'll then apply it to each link so we can see how our styles affect it.

The first thing we need to do to make it look less like a link is to remove the text decoration style, which is how the underline is being applied.

.button {
text-decoration: none;
}

then we'll use some padding, border-radius, and our brand's blue as the background-color. Which leads us to the need to resolve the text color itself, and for that will use our brand pink. We'll also adjust the font weight to bold.

.button {
/* ...existing styles */
padding: 0.5em 1em;
border-radius: 4px;
background-color: #150f64;
color: #ff7ca6;
font-weight: bold;
}

And let's see our changes so far. That gets us nearly there, but let's resize.

As we resize, we see the button breaking. one way to resolve that is to apply display: inline-block which causes the link to be treated as its own box container.

.button {
/* ...existing styles */
display: inline-block;
}

Then you can also see we need to resolve the text alignment once the button breaks to two lines, and that is of course text-align: center.

.button {
/* ...existing styles */
text-align: center;
}

We can also tighten up the space between the lines once it breaks to two lines with the property line-height and the value 1.1.

.button {
/* ...existing styles */
line-height: 1.1;
}

And with that, we have our button styled.

You may have noticed one other issue as we resize down, and that is that the clipping of the background image could be improved

To finish this lesson, we'll add one more property to the .card__img, an additional background property called background-position with the value center.

.card__img {
/* ..existing styles */
background-position: center;
}

And on save, you'll see that the image content is centered within the .card__img container.

In summary, in this lesson we updated the semantics of the "Tour Packages" section to create each of these cards as a list item. And for that, we had to remove the list default styles, including the bullets. And then we moved on to create a .card__img container, and a .card__content container as well as create a style for buttons.