Stephanie Eckles
Posted on March 21, 2020
In this post, we will cover:
- consolidating global styles
- the
:first-child
pseudo class* display: inline-block
- introduction to CSS grid
- how to define a dynamically resized image
This is the fourteenth post and video in a series on learning web development. Learn more about the series and see the post schedule >
You may want to refer back to the first lesson that created our initial card HTML, episode 6:
You may watch the following video or follow along with the expanded transcript that follows.
To begin this lesson, open the starter project you began in episode 1, or review the source files from episode 13.
To begin this lesson, open our project in VSCode.
In the Terminal, type the start command npm run start
to run the project.
Open card-layout.html
in VSCode, and organize your screen to be split with the browser. Then update the browser url to include /card-layout.html
.
Similar to the last lesson, the first thing we need to do is create a dedicated stylesheet for this page's styles since we do not wish to inherit styles from style.css
which is what is giving us the text colors.
Create card-layout.css
, and then in our HTML file, update the stylesheet link
in the <head>
from style.css
to card-layout.css
and save.
<link rel="stylesheet" href="card-layout.css" />
We're nearly back to default browser styles, except we cheated a bit in the HTML lesson and added a handful of styles directly in the HTML. That technique is called "inline styles".
Because inline styles are closest to the element, they are calculated as having a higher specificity in the CSS cascade than element or class selectors. There are ways to override this, but it's an additional rule of the cascade to be aware of.
Refer back to episode 11 if needed to learn more about the CSS cascade.
Intro to the CSS Cascade - The "C" in CSS!
Stephanie Eckles ・ Mar 3 '20
Let's work on moving the inline styles into our stylesheet.
Looking at the layout we created, it seems reasonable to create two classes: .card-row
and .card
. We'll move the styles on the <section>
element into .card-row
and the styles from the <article>
elements to .card
.
.card-row {
display: flex;
justify-content: space-between;
}
.card {
width: 320px;
padding: 20px;
border: 1px solid #c9c9c9;
}
Then we'll replace the style
attribute with the class
attribute and the appropriate class:
<section class="card-row">
<article class="card">
Be sure to save both the CSS and HTML files and you can see our appearance is unchanged from what we created in the HTML lesson.
For now, let's remove the second and third card so we can focus on the typography and .card
styles.
Starting with typography, it would be nice to use what we already defined for the blog page, if we pretend that the blog and product layouts are pages of the same site.
So let's create one more stylesheet - global.css
. Then open blog-layout.css
and let's move from the body
rule through the p
rule to our new stylesheet. Then also get from the .container
rule through to the body > footer
rule.
You can view the final global.css stylesheet in the project repo.
Now, in both blog-layout.html
and card-layout.html
, we need to add a new stylesheet link, and it needs to be placed above the existing stylesheet link for the cascade to work optimally.
<link rel="stylesheet" href="global.css" />
<!-- existing page stylesheet link -->
Following that, we need to update the markup of our <header>
and <footer>
to match what we did for the blog page, as you can see the spacing is not correct since they are missing the div
with the container
class.
<header>
<div class="container">
<!-- header content -->
<!-- main -->
<footer>
<div class="container">
<!-- footer content -->
Then, we'll add the container
class to the main
element and our layout is looking much better already.
<main class="container">
We can now turn our attention to the .card
and first, we'll update the card styles with some other properties we've already learned about:
.card {
/* previously added styles */
border-radius: 7px;
box-shadow: 2px 2px 4px rgba(0, 0, 0, 0.15);
}
Save, and the card feels more on-trend.
Next, we'll create styles for the image. If we assume that every time a .card
is in use it has one img
placed at the top, then we can use the selector .card img
to define that the rule will apply to images only within .card
.
However, to ensure it only ever applies to the first image, we'll use the direct child selector >
and what's called a "pseudo class" to make our full selector .card > img:first-child
. The full meaning of this selector is now "apply these properties only to images that are a direct child of the card and are the first-child element within the card."
We'll start with one definition for border-radius
which will round the top two corners but not the bottom two.
.card > img:first-child {
border-radius: 7px 7px 0 0;
}
Then in the HTML let's move the image below the h3
and save.
You'll see that when the image no longer meets the :first-child
pseudo class condition, it does not have rounded corners.
Move the image back to the top, and we'll add one more rule for margin-bottom: 20px
.
Let's jazz up the card typography, and again use the .card
qualifier prior to the typography element tag name:
.card h3 {
color: rebeccapurple;
}
.card p {
color: #757575; /* Lightest grey with 4.5:1 contrast over white */
line-height: 1.5; /* Space between lines of the paragraph */
}
Finally, we've arrived at the all-important "Add to Cart" link. It's common on e-commerce sites for this to have a more "button" appearance to help it stand out. We may want to have a button appearance on more than just card links, so let's create a rule for .button
.button {
/* Reset text link default */
text-decoration: none;
background-color: rebeccapurple;
color: #fff; /* white */
padding: 0.5em 1em; /* vertical | horizontal */
border-radius: 4px;
}
Those are all properties we're familiar with, so let's save. But before we see the results, we need to add the button
class to the <a>
tag in the HTML, then save.
It looks pretty good, except it seems to be overlapping the bottom spacing of the card. Let's inspect and see what's happening.
If we inspect the .card
, we see that the padding seems to go into the button and our button text is resting there. This is because links are inline elements, and only their horizontal padding is added into the box model algorithm in terms of changing their position in relation to other elements.
Using inspector, we can change the display
property to block
which we learned is the complement to inline
.
But that's not quite right - we don't want the button to take up the fullwidth, although it did remove the overlapping behavior.
Instead, a third available value for display
is inline-block
which allows the element width to behave using the inline
algorithm, but also allows it to have both it's vertical margin and padding used in the display algorithm as well.
Toggling this on and off, you can see it also increases the space above the button due to it now being pushed down by the paragraphs bottom margin.
Let's add the display: inline-block
to our rule, and save.
We're almost done, but it would be nice for the price to stand out a bit more, so we'll create a rule with the selector .card h3 em
, and let's place it following our .card h3
rule for the sake of organization.
.card h3 em {
padding: 0.25em;
/* pale purple used for blog blockquote */
background-color: #eddbff;
border-radius: 4px;
}
Great, it stands out more, but what happens if we have a longer product title, say "Whizzbang Widget SuperDeluxe"? Let's update the HTML and find out.
Oops! The price get's knocked down to the next line, and since it's an inline element, it's overlapping the text.
A newer display
value is grid
and this will let us define virtual "columns" and "rows" for our content to live in. If we define display: grid
on the .card h3
then a grid cell is created for the text and one for the em
tag.
Save, and you'll see the price is still below the title but it's now appearing as a block due to the default grid
algorithm.
If we inspect the h3
the inspector has added dashed lines to visualize each grid cell.
What we want are two columns where the price takes the width it needs but always stays to the far right of the title. We can accomplish this by adding grid-template-columns: 1fr auto
. Each width designation with this property defines a separate column, so with two definitions, we've created two columns.
The fr
keyword means "fraction" and is unique to grid
and using it requests the browser to compute the available "fraction" of space that is left to distribute to that column or row. In this case, it will take up the remaining space not needed for the price to fit. Save, and that's almost what we want.
The final property we will add to the h3
rule is align-items: start
which defines how the grid cell content should horizontally align in relation to each other. Save, and you'll see the em
is now aligned to the top relative to the product title.
This has consequently also fixed its height by not allowing it to stretch the full height of the virtual row.
Now we just need to add back in a couple more cards, so let's copy our card HTML twice to have three cards again, and save.
The first apparent thing is that the cards are overflowing the viewable browser area! The reason is the absolute width we used on the .card
of 320px
. Again, that was for demonstration purposes of our original HTML lesson.
We will remove the width
on .card
and then change .card-row
to use grid
for display. Then we'll setup grid-template-columns: 32% 32% 32%
. The existing justify-content: space-between
rule will distribute any leftover space between each card.
But we still have overflow of the card area... why is that? The reason will not be apparent even from inspecting but is the type of thing to file away in your memory. What is happening is that the browser is able to infer the image display size, and so the image width is keeping the cards wide.
The essential rule to memorize for this scenario is: max-width: 100%; height: auto;
which means the image should take up no more than 100% of the available space within its container, and that its height should proportionately resize. We'll add this definition to our rule for card images.
Save, and we now have 3 equal-width, nicely styled cards!
We'll learn more tricks for card layouts in our capstone project.
Here's the final result on CodePen:
Note: the demo is best on desktop since we haven't yet covered responsive design - but that's the topic for Episode 16!
Next up in Episode 15: Browser Compatibility
Posted on March 21, 2020
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.