No Time Dad

A blog about web development written by a busy dad

A Straightforward CSS Card Example

Intro

There are a lot of convoluted and confusing examples of building a card out there. You can basically pick any CSS framework and they will most likely have some version of the humble card. The most famous of which is probably bootstrap and Material. These libraries have fantastic cards that should absolutely be used if you are already using those libraries. But if you aren’t already using those libraries and still want something half-presentable and quick to make then please read on.

The example below is inspired by the excellent examples at TailwindUI.

Card Structure

In this example we are going to be building a card that contains information for a job posting. We’ll start with a base and add more features as we go. The first step will be to frame out the card and add a title. Below is what our initial CSS and HTML for the card will look like, as well as an example of the card so far.

<div class="card">
  <div class="card__header">
    <h3 class="card__header_title">Job Title</h3>
    <span class="card__header_subtitle">HTML and CSS Specialist</span>
  </div>
</div>
.card {
  overflow: hidden;
  border-radius: 0.5rem;
  box-shadow: 0 4px 8px 0 rgba(0,0,0,0.1);
}

.card__header {
  padding: 1rem;
}

.card__header_title {
  padding: 0;
  margin: 0;
}

.card__header_subtitle {
  opacity: 0.6;
}
Card Structure Result

Front-End Developer

HTML and CSS Specialist
Card Structure Deep Dive
.card {
  overflow: hidden;
  border-radius: 0.5rem;
  box-shadow: 0 4px 8px 0 rgba(0,0,0,0.1);
}

The overflow property lets us set how the content will look if it is larger than the parent container. Here we are using hidden, which means that any content that is larger than the parent will not be visible.

Next we have the border-radius property which determines the roundness of the corners of an element. Generally with cards we want the corners to have a bit of roundness to them, so we use 0.5rem here.

The star of the show for the card, in my opinion, is the box-shadow property. This is basically the dark (or any color) area shown on the edges of the card, giving it a sort of 3-D pop. The box-shadow property is extremely customizable, and can be a little hard to understand as a result. You might even consider using a box-shadow generator to help with this property.


.card__header {
  padding: 1rem;
}

The padding property determines the space around an element. Here, padding: 1rem; is short-hand for having to write out padding-top: 1rem;, padding-bottom: 1rem;, padding-right: 1rem;, and padding-left: 1rem;.


.card__header_title {
  padding: 0;
  margin: 0;
}

This block of CSS is really just a personal preference thing which removes the padding and margin to h3 tags. I find it easier to space things out later if I remove the default spacing from h1,2,3,4... tags.


.card__header_subtitle {
  opacity: 0.6;
}

Finally, we add a subititle with a little bit of opactiy so it stands out less than the title. This is a common design on many cards that you’ll see.


Card Content

The main feature of any card is the content. Here we will add our job description and other related information. We more or less want this information to be displayed cleanly in columns like a table, so using CSS Grid is probably our best candidate for that.

The CSS displayed below will be added to our existing CSS from the previous section. The HTML shown below includes the HTML shown from the previous section.

... .card__content {
  border-top: 1px solid lightgray;
}

.card__content_row {
  display: grid;
  grid-template-columns: 1fr 1fr 1fr;
  padding: 1rem;
}

.card__content_row_desc {
  font-weight: 600;
}

.card__content_row_data {
  grid-column: span 2 / span 2;
}
<div class="card">
  <div class="card__header">
    <h3 class="card__header_title"">Front-End Developer</h3>
    <span class="card__header_subtitle">HTML and CSS Specialist</span>
  </div>
  <div class="card__content">
    <dl>
      <div class="card__content_row">
        <dt class="card__content_row_desc">Job Req ID</dt>
        <dd class="card__content_row_data">#456wrt</dd>
      </div>
    </dl>
    <dl>
      <div class="card__content_row">
        <dt class="card__content_row_desc">Start Date</dt>
        <dd class="card__content_row_data">Jan. 24th, 2021</dd>
      </div>
    </dl>
    <dl>
      <div class="card__content_row">
        <dt class="card__content_row_desc">Department</dt>
        <dd class="card__content_row_data">Marketing</dd>
      </div>
    </dl>
    <dl>
      <div class="card__content_row">
        <dt class="card__content_row_desc">Description</dt>
        <dd class="card__content_row_data">
          Lorem ipsum dolor sit amet, consectetur adipiscing elit...
        </dd>
      </div>
    </dl>
  </div>
</div>
Card Content Result

Front-End Developer

HTML and CSS Specialist
Job Req ID
#456wrt
Start Date
Jan. 24th, 2021
Department
Marketing
Description
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.
Card Content Deep Dive
.card__content {
  border-top: 1px solid lightgray;
}

The first thing we’ll do is add a line to cleanly separate the header from the content. You could use an <hr> here, but a border-top on the card__content class seemed faster in the moment.

The border-top property has some shorthands that we’re taking advantage of here, which are thickness, style, and color respectively. You can individually set these properties if you’d like.


.card__content_row {
  display: grid;
  grid-template-columns: 1fr 1fr 1fr;
  padding: 1rem;
}

So, things get interesting here. We are telling that browser that we want to use grid as our layout for this element, and that we are going to have three columns. We use the new-ish fr (fraction) unit to determine the size of each column. This basically says that each column should try to take up 33.3333% or as close as possible to that given the space available. You can read more about the fr unit here.

Lastly, we’ll add some padding around each row. The left and right padding will match the padding used in the header, so everything will line up nicely.


.card__content_row_desc {
  font-weight: 600;
}

We want the description to standout a little bit, so we’ll add a slight “boldness” aka font-weight property to the dt element.


.card__content_row_data {
  grid-column: span 2 / span 2;
}

If you were paying attention to where we defined the grid-template-columns, you’ll notice there are three 1fr, so technically we have three columns in our grid even though visually you will only see three. The reason for this is to allow the columns to have some extra room, especially for longer text blocks.

So, when using grid-column: span 2 / span 2; we are saying that we want to allow the second column to span 2 columns.


Wrapping Up

I think this is a pretty decent card that didn’t take much work. It could certainly be fancier, but I think it looks good and serves its purpose well. It also didn’t require any libraries or special tooling.

As I mentioned at the start, if you’re already using some libraries that have card components built-in I’d urge you to just use those. This was really just a fun example to see what we could create from scratch and how easy it would be.

Final CSS:

.card {
  overflow: hidden;
  border-radius: 0.5rem;
  box-shadow: 0 4px 8px 0 rgba(0, 0, 0, 0.1);
}

.card__header {
  padding: 1rem;
}

.card__header_title {
  padding: 0;
  margin: 0;
}

.card__header_subtitle {
  opacity: 0.6;
}

.card__content {
  border-top: 1px solid lightgray;
}

.card__content_row {
  display: grid;
  grid-template-columns: 1fr 1fr 1fr;
  padding: 1rem;
}

.card__content_row_desc {
  font-weight: 600;
}

.card__content_row_data {
  grid-column: span 2 / span 2;
}

Final HTML:

<div class="card">
  <div class="card__header">
    <h3 class="card__header_title"">Front-End Developer</h3>
    <span class="card__header_subtitle">HTML and CSS Specialist</span>
  </div>
  <div class="card__content">
    <dl>
      <div class="card__content_row">
        <dt class="card__content_row_desc">Job Req ID</dt>
        <dd class="card__content_row_data">#456wrt</dd>
      </div>
    </dl>
    <dl>
      <div class="card__content_row">
        <dt class="card__content_row_desc">Start Date</dt>
        <dd class="card__content_row_data">Jan. 24th, 2021</dd>
      </div>
    </dl>
    <dl>
      <div class="card__content_row">
        <dt class="card__content_row_desc">Department</dt>
        <dd class="card__content_row_data">Marketing</dd>
      </div>
    </dl>
    <dl>
      <div class="card__content_row">
        <dt class="card__content_row_desc">Description</dt>
        <dd class="card__content_row_data">
          Lorem ipsum dolor sit amet, consectetur adipiscing elit...
        </dd>
      </div>
    </dl>
  </div>
</div>
Final Result

Front-End Developer

HTML and CSS Specialist
Job Req ID
#456wrt
Start Date
Jan. 24th, 2021
Department
Marketing
Description
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.