Building A Profile Card with HTML and CSS Grid

Caston Boyd
12 min readMar 11, 2024

--

About

In this article, I’m excited to guide you through coding the profile card shown above. While there are numerous approaches one could take, I’ve chosen to leverage CSS Grid for its powerful layout capabilities. Along the way, I’ll share insights into my thought process regarding placement and styling, complemented by visuals to aid your understanding. Let’s get started.

Designing a Semantic HTML Structure

Some individuals might prefer to dive straight into coding, but I find it helpful to sketch boxes around the components of the image I’m replicating with HTML/CSS. This approach gives me a clearer understanding of how different elements will interact and positions me to start contemplating the styling aspects early on. For the image above, I’ve outlined the following structure:

Right off the bat, I’m recognizing that my container will encapsulate all elements, serving as the parent class. This realization prompts me to consider its positioning/display relative to its overarching container (html, body) and how its child elements will be similarly arranged. However, before we delve into those details, let’s lay out the HTML structure. Starting with the container, I’ll name it “card-container,” which will reside within the body, making the body its parent:

<body>
<!-- card starts here -->
<article class="card-container">

</article>
<!-- card ends here -->
</body>

Next, I’m turning my attention to the images that appear prominently at the top of this container box. To incorporate them, I plan to set both a background image and a profile image. Each image will be housed in its own dedicated container, as shown below:

    <!-- card starts here -->
<article class="card-container">
<figure class="bg-img">
<img src="bg.png" alt="background" class="bg-image">
</figure>
<figure class="profile-img">
<img src="profile-picture.jpg" alt="profile picture" class="profile-pic">
</figure>
</article>
<!-- card ends here -->

Following the images, I notice a section dedicated to the content of the profile card. I’ll craft a container for this content, incorporating a <p> tag for the description and a header tag for the name.

    <article class="card-container">
<figure class="bg-img">
<img src="bg.png" alt="background" class="bg-image">
</figure>
<figure class="profile-img">
<img src="profile-picture.jpg" alt="profile picture" class="profile-pic">
</figure>
<section class="profile-info">
<h2 class="profile-name"> Jessica Randall</h2>
<p class="profile-bio"> Lorem ipsum dolor sit, amet consectetur adipisicing elit. Quae natus sed dolor nihil quam voluptas suscipit sequi, iusto quia quidem voluptates necessitatibus magnam et saepe tenetur earum sunt fugit impedit?</p>
</section>
</article>

From your live server, you should see the following:

To conclude, I’ll add the social links section. If you’re interested in using the same icons featured in the image, visit FontAwesome. You’ll find a search box similar to the one described below:

Copy that link and add it to the head of your html file like so

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Profile Card</title>
<link rel="stylesheet" href="/styles/styles.css">
<script src="https://kit.fontawesome.com/8695b27503.js" crossorigin="anonymous"></script>
</head>

From that point, you’ll have access to a wide range of free icons. Next, you’ll need to embed them within an <a> tag, as these icons will serve as links to social media accounts. Considering there are approximately four to five accounts, it's advisable to group them in their own container. For semantic clarity, placing them within a <nav> element, with individual links for each social account, is the best approach:

        <nav class="social-links">
<a class="fa-brands fa-facebook"> </a>
<a class="fa-brands fa-instagram"> </a>
<a class="fa-brands fa-whatsapp"> </a>
<a class="fa-brands fa-linkedin"> </a>
</nav>

This completes the html section of our work. Your live server should render the following:

Positioning and Styling Profile Card

I emphasize positioning and styling because it’s important to consider not only how elements appear but also their placement and relationships with other elements. My approach involves evaluating the current state of my HTML and envisioning the desired outcome. This entails methodically mapping out each element and determining the necessary steps to achieve the final output. You can see my mapping below:

Setting a Universal Base: Aligning All Elements to the Origin

I prefer to begin with a clean slate by setting the margins and padding to 0 and applying a border-box model to every element. I also just wanted to add the font so that it applies to all elements with text. This will not always the approach but for this project, it will be. This approach ensures that elements behave as expected when I adjust their size or make other modifications. If you’re curious about the border-box model and its benefits, I recommend reading this article to deepen your understanding.

* {
margin: 0;
padding: 0;
box-sizing: border-box;
font-family: 'Poppins', 'sans-serif';
}

Setting these adjustments will position your elements in the top left corner, as they fall under the parent class. With margins and padding set to zero for all elements, including the parent html/body that also lacks margin or padding, their initial position defaults to the coordinates 0,0, aligning them at the top left. Your rendering should look like the below:

Centralizing with HTML/Body Elements

The HTML and Body elements serve as the core container for my card, enabling me to centralize the card and effectively manage its child components via the container class. This approach affords me control over the card’s positioning and alignment throughout the webpage. To provide a clearer distinction, I will apply a background color to the body and a white background color for the card container:

html, body {
background-color: #282828;
}
.card-container {
background-color: white;
}

This visual aid helps differentiate between the body — as the overarching container — and the card container, which houses all elements of the card, including the background picture, profile picture, information, and social links. The card container stretches the width of the screen and covers the content height wise which is the default for a block element. My job soon will be to make it smaller so that it looks like our exemplar.

Set html/body to the viewport (browser screen that you see)

Next, I usually define the width and height of the html/body container to ensure it spans the entire browser window, achieving the expansive coverage you observe.

html, body {
width: 100vw;
height: 100vh;
background-color: #282828;
}

Using width: 100vw and height: 100vh for the html and body elements ensures they fill the entire viewport, providing a uniform full-screen layout. This method, akin to setting width and height to 100%, guarantees that the webpage makes use of all available screen space. Consequently, the elements inside operate within a parent container that has been allocated 100% of the viewport space, creating a scalable foundation for designing responsive layouts. This means, any child element can be sized and positioned relative to this full viewport canvas, allowing for precise control over the page layout and ensuring content is displayed consistently across different screen sizes.

Since the html/body serve as parent containers, I intend to use a grid layout for the card, the child element. Grid enables easy centering and systematic arrangement of components. If you want to learn more about grid, click here.

html, body {
width: 100vw;
height: 100vh;
display: grid;
background-color: #282828;
}

Since there is only one element as a direct child of the html/body container, I need to consider just this single element in my layout template, particularly focusing on its column configuration.

html, body {
width: 100vw;
height: 100vh;
display: grid;
grid-template-columns: 1fr;
background-color: #282828;
}

What you’ll observe is the container filling the entire screen, indicating that the 1fr setting is effectively allocating the full column grid space to the single element. Below is an illustration to demonstrate this concept:

Next we align-content:

html, body {
width: 100vw;
height: 100vh;
display: grid;
grid-template-columns: 1fr;
align-content: center;
background-color: #282828;
}

the align-content: center; will simply ensure that this content is centered vertically, which might give the appearance of the content "fitting" because it occupies less vertical space, centered within a larger container.

Profile Container

Now we’re moving on to the profile container itself. Currently, it takes up 100% of the width of its parent the HTML/Body container. I want it to be a lot less than that so I’m going to give it a quarter of that:

I also need to center it so I have to go back to the HTML/Body element and justify items to the center:

html, body {
width: 100%;
height: 100%;
display: grid;
grid-template-columns: 1fr;
align-content: center;
justify-items: center;
background-color: #282828;
}

Now, let’s recall that we’re working within the profile container, which houses all the visible content as child elements. This setup allows us to implement a grid layout, organizing these elements into a structured grid as follows:

.card-container {
background-color: white;
width: 25%;
display: grid;
grid-template-rows: repeat(4,auto);
}

By setting display: grid and grid-template-rows: repeat(4, auto), we've created a distinct cell for each element, with each occupying its own row. Since the rows are set to auto, they automatically adjust to fit the size of their content. For instance, the picture of the woman occupies its own row, which resizes to match the dimensions of the picture.

A few other things to apply to the container to make it closer to the model design would be to give it a less sharp border, an more accurate background color rather than the white, and give it a background shadow. You will have to play with these styling points but what I have below gets us close:

.container {
width: 25%;
display: grid;
grid-template-rows: repeat(4,auto);
border-radius: 1.5%;
box-shadow: 0 4px 8px rgba(255, 255, 255, 0.1);
background-color: rgb(238, 238, 238);
}

Background Image

.card-image img {
width: 100%;
height: 100%;
border-top-left-radius: 5px;
border-top-right-radius: 5px;
object-fit: cover;
}
  • width: 100%; ensures that the image spans the full width of its container, allowing it to stretch across the entire grid cell it occupies.
  • height: 100%; similarly, this property makes the image stretch to fill the entire height of its container. In a grid context, where this image takes up a single row, setting the height to 100% ensures the image covers the whole vertical space of that row.
  • object-fit: cover; is really important for background images. It makes sure the image stretches to fill the space without looking stretched or squished. If the image shape doesn't exactly fit the space it's in, this setting will trim off some edges of the image so that it fills the whole area without leaving gaps

Profile image

In our model design, the profile image appears above the background. To ensure this layering, we position the image so it always sits on top, with the background remaining underneath. This creates a visually distinct separation where the profile photo is prominently displayed against the backdrop:

We achieve this by doing the following in CSS:

.profile-img img {
position: relative;
z-index: 1;
}

In this setup, position: relative; allows us to adjust the profile image's position from where it would normally be (we still have to move it with top, left, bottom, right) , and z-index: 1; makes sure this image appears on top of other elements, like the background.

I used bottom: 100%; with a margin-bottomof -20% to position it slightly below the center of the background and to remove the deep spacing between the image and the bio.

.profile-img img {
position: relative;
z-index: 1;
bottom: 100%;
margin-bottom: -20%;
}

Next I centered the profile image:

.profile-img img {
position: relative;
z-index: 1;
bottom: 110%;
margin-bottom: -20%;
display: block;
margin-left: auto;
margin-right: auto;
}
  • display: block; makes the element a block-level element, allowing it to occupy the full available width of its parent container, unlike inline elements which only take up as much width as necessary. This is necessary for horizontal centering because margin-left: auto; and margin-right: auto; only work on block-level elements.
  • margin-left: auto; and margin-right: auto; automatically calculate equal margins on both the left and right sides of the element, centering it within its parent container. This technique is widely used for horizontally aligning elements like images, divs, or sections in the middle of a page or container.

Obviously, next step would be to apply a border-radius and a white border.

.profile-img img {
position: relative;
z-index: 1;
bottom: 110%;
margin-bottom: -20%;
display: block;
margin-left: auto;
margin-right: auto;
border-radius: 50%;
border: #fff 10px solid;
}

Header on card content

Within the third row of our grid layout, acting as a distinct container, we have the flexibility to use Flexbox or Grid for more detailed layouts. However, for our purposes, we’ll keep it straightforward. By setting the position to relative for our text element, we adjust its vertical placement to bring it closer to the profile picture directly above. The application of margin-top: -15%; reduces the gap between the profile image and the text, while text-align: center; ensures the text is aligned in the middle, creating a balanced and focused area for the profile information.

.profile-info h2 {
position: relative;
font-size: 20px;
text-align: center;
margin-top: -15%;
}

To ensure the biography content is neatly centered within its enclosing space and to create a comfortable buffer between the text and its border, we’ll apply center alignment and introduce padding.

.profile-info p {
text-align: center;
padding: 2%;
}

Social Links

Same approach, center and sizing first:

.social-links {
padding: 20px;
text-align: center;
}

Recalling the border-box model, where the content area is highlighted in blue and the padding is indicated in green, we’ve implemented a 20px padding. This adjustment ensures the links are positioned away from the screen edges.

.social-links a {
padding: 0% 5%;
font-size: 2rem;
color: #ED1ABE;
}

We set the padding on each anchor tag in the social links container to ensure appropriate spacing between them, focusing specifically on applying padding to the right side. This deliberate choice avoids unintended spacing on the left, top, or bottom, achieving a consistent and clean layout. Your final rendering should appear as follows:

Bonus: Hovering Links

To implement a hover effect, we simply apply the :hover pseudo-class to each anchor tag. Any styling defined within this state will alter the element's default appearance. For our purpose, this involves changing the font color and applying a transformation to slightly enlarge the element upon hovering.

.social-links a:hover {
color: #31006B;
transform: scale(1.5);
}

--

--