CSSIntermediate

Real Flexbox Layouts: Navbar, Cards, Search Bar and Centering

Build five common interface patterns with Flexbox: a navbar, a centered hero, equal-height cards, a comment row and a search bar, with a map of every class.

All CSS lessons

What you will learn

Knowing the Flexbox properties is one thing. Knowing which ones to reach for when you meet a real design is what matters. In this lesson you will build five patterns that appear on almost every website: a navbar, a centered hero section, a row of equal-height cards, a comment with an avatar beside the text, and a search bar. For each one you will see the HTML, which element is the flex container, which are the flex items, and why each property is there.

The HTML we will style

<!-- Pattern 1: navbar -->
<header class="navbar">
  <a href="#home" class="navbar-logo">CodeNest</a>
  <nav class="navbar-links">
    <a href="#html" class="navbar-link">HTML</a>
    <a href="#css" class="navbar-link">CSS</a>
    <a href="#js" class="navbar-link">JavaScript</a>
  </nav>
  <a href="#signup" class="navbar-cta">Sign up</a>
</header>

<!-- Pattern 2: centering inside a hero -->
<section class="hero">
  <div class="hero-content">
    <h1 class="hero-title">Learn web development</h1>
    <p class="hero-text">Free lessons, simple examples.</p>
  </div>
</section>

<!-- Pattern 3: equal-height cards -->
<section class="cards">
  <article class="card">
    <h3 class="card-title">HTML</h3>
    <p class="card-text">The structure of every web page.</p>
    <a href="#html" class="card-link">Start HTML</a>
  </article>
  <!-- two more cards: CSS and JavaScript -->
</section>

<!-- Pattern 4: avatar next to text -->
<div class="comment">
  <div class="comment-avatar">A</div>
  <div class="comment-body">
    <h4 class="comment-name">Aisha</h4>
    <p class="comment-text">The flexbox lessons finally made layout click...</p>
  </div>
</div>

<!-- Pattern 5: search bar -->
<form class="search">
  <input class="search-input" type="search" placeholder="Search lessons">
  <button class="search-button" type="submit">Search</button>
</form>

This table shows, for each pattern, who is the container and who are the items. Keep it in mind as you read the CSS:

Pattern Flex container Flex items Main idea
1. Navbar .navbar .navbar-logo, .navbar-links, .navbar-cta align-items: center and margin-left: auto
1b. Links inside it .navbar-links (a nested container) the three .navbar-links a second flex row inside the first
2. Hero .hero .hero-content justify-content and align-items: center
3. Cards .cards the three .cards flex: 1 1 220px and flex-wrap
3b. Inside each card .card (a column container) .card-title, .card-text, .card-link margin-top: auto pins the link to the bottom
4. Comment .comment .comment-avatar, .comment-body a fixed avatar and a fluid text column
5. Search bar .search .search-input, .search-button flex: 1 makes the input fill the row

Notice that one element can be both an item and a container: .navbar-links is an item of .navbar, and a container for the links. This is called nesting and it is the key to building complex layouts from simple pieces.

Pattern 1: the navbar

A navbar has a logo on the left, links in the middle or on the right, and sometimes a button at the far end. All of the children are in one row, centred vertically.

/* the <header class="navbar">: the flex container */
.navbar {
  display: flex;
  align-items: center;        /* vertically center logo, links and button */
  gap: 24px;
  padding: 12px 20px;
  background-color: #1e293b;
}

/* the <a class="navbar-logo"> */
.navbar-logo {
  color: white;
  font-weight: bold;
  text-decoration: none;
}

/* the <nav class="navbar-links">: an item of .navbar AND a container itself */
.navbar-links {
  display: flex;
  gap: 16px;
  margin-left: auto;          /* eat all the free space on the left */
}

/* each <a class="navbar-link"> */
.navbar-link {
  color: #cbd5e1;
  text-decoration: none;
}

/* the <a class="navbar-cta"> button */
.navbar-cta {
  padding: 8px 16px;
  background-color: royalblue;
  color: white;
  text-decoration: none;
  border-radius: 6px;
}

The trick is margin-left: auto on .navbar-links. As you learned, an auto margin absorbs all the free space, so everything from the links onwards is pushed to the right while the logo stays on the left. Compare this with justify-content: space-between, which would spread the logo, links and button evenly, putting the links in the middle. Both are valid. Which looks better depends on your design.

Why is .navbar-links a flex container too? Because its links should sit in a row with a gap. Without display: flex they would flow as inline text. Making the <nav> a second flex container is the easy way to space them evenly.

A navbar that wraps on small screens

On narrow screens, the row can get too crowded. The simplest fix is to allow wrapping:

.navbar {
  flex-wrap: wrap;
}

Items that do not fit move onto a second line instead of squashing. Later, in the responsive lessons, you will learn to use a media query to restructure it properly.

Pattern 2: centering inside a hero

A hero is a big banner with text in the middle. You learned the three-line recipe. Here it is in a real place:

/* the <section class="hero"> */
.hero {
  display: flex;
  justify-content: center;    /* horizontal centre */
  align-items: center;        /* vertical centre */
  min-height: 240px;
  background-color: #eef2ff;
  text-align: center;
}

/* the <div class="hero-content"> */
.hero-content {
  max-width: 480px;
  padding: 20px;
}

.hero-title {
  margin: 0 0 8px;
}

.hero-text {
  margin: 0;
  color: #475569;
}

min-height (not height) means the hero can grow if the text gets longer. .hero-content has a max-width so that long text does not stretch across the whole banner. Note that text-align: center is still needed to centre the text lines inside the box. Flexbox centres the box, not the words inside it.

Pattern 3: equal-height cards

Cards in a row should line up, with the same height, even when one has more text. Flexbox does this almost for free, because align-items defaults to stretch.

/* the <section class="cards">: the flex container */
.cards {
  display: flex;
  flex-wrap: wrap;
  gap: 16px;
  padding: 20px;
}

/* each <article class="card"> */
.card {
  flex: 1 1 220px;            /* grow and shrink, but ideally 220px wide */
  display: flex;              /* the card is ALSO a container: its content stacks */
  flex-direction: column;
  padding: 20px;
  border: 1px solid #dddddd;
  border-radius: 12px;
  background-color: white;
}

Two flex ideas work together:

  1. flex: 1 1 220px on .card means each card wants to be 220px wide, but they can grow to share the row, and shrink if needed. With flex-wrap: wrap on .cards, if three cards do not fit, the last one wraps to a new line. This gives you a responsive card grid without any media query. All cards in a line are the same height because of stretch.
  2. display: flex; flex-direction: column on .card turns each card into a vertical container. That lets us push the link to the bottom of the card:
/* the <a class="card-link"> */
.card-link {
  margin-top: auto;           /* absorbs the free space ABOVE the link */
  padding-top: 16px;
  color: royalblue;
  text-decoration: none;
  font-weight: bold;
}

.card-title {
  margin: 0 0 8px;
}

.card-text {
  margin: 0;
  color: #475569;
}

Because the card is a column container, margin-top: auto on the link eats the leftover vertical space, so the link sticks to the bottom edge. In the middle card, the text is longer, but all three links still line up on the same baseline at the bottom, which looks tidy.

Pattern 4: avatar beside text (the media object)

A fixed-size image or avatar on one side, and flexible text on the other, is called the media object. It is used in comments, news lists and chat messages.

/* the <div class="comment">: the flex container */
.comment {
  display: flex;
  align-items: flex-start;    /* avatar stays at the top, not stretched */
  gap: 12px;
  margin: 24px 20px;
}

/* the <div class="comment-avatar">: a fixed-size circle */
.comment-avatar {
  flex: 0 0 48px;             /* never grow or shrink: always 48px */
  height: 48px;
  border-radius: 50%;
  background-color: royalblue;
  color: white;
  font-weight: bold;
  text-align: center;
  line-height: 48px;
}

/* the <div class="comment-body">: takes the rest of the width */
.comment-body {
  flex: 1;
  min-width: 0;               /* lets long words or code wrap instead of overflowing */
}

.comment-name {
  margin: 0 0 4px;
}

.comment-text {
  margin: 0;
}

The recipe is always the same: the small thing gets flex: 0 0 <size> and the text column gets flex: 1. Without flex: 0 0 48px, a long comment could squash the avatar into an oval. Without align-items: flex-start, the avatar would stretch to the full height of the text.

An input and a button side by side, with the input filling all the free space:

/* the <form class="search">: the flex container */
.search {
  display: flex;
  gap: 8px;
  margin: 20px;
  max-width: 480px;
}

/* the <input class="search-input"> */
.search-input {
  flex: 1;                    /* fill the row */
  padding: 10px 12px;
  border: 2px solid #cccccc;
  border-radius: 6px;
  font: inherit;
}

/* the <button class="search-button"> */
.search-button {
  padding: 10px 18px;
  border: none;
  border-radius: 6px;
  background-color: royalblue;
  color: white;
  font: inherit;
  cursor: pointer;
}

The button keeps its natural size, and flex: 1 makes the input take everything that remains. No widths, no percentages, no calculations. When the container gets narrower, the input shrinks and the button stays put.

Which Flexbox tool for which job?

You want to… Use
Put things in a row display: flex on the parent
Centre something both ways justify-content: center; align-items: center;
Push one item to the far end margin-left: auto on that item
Make a column fill the rest flex: 1
Keep an item a fixed size flex: 0 0 <size>
Let items flow onto new lines flex-wrap: wrap with flex: 1 1 <size>
Pin something to the bottom of a card flex-direction: column on the card, margin-top: auto on the item
Space items evenly justify-content: space-between (or gap)

Putting it together

*, *::before, *::after { box-sizing: border-box; }
body { margin: 0; font-family: Arial, Helvetica, sans-serif; }

/* 1. navbar */
.navbar { display: flex; align-items: center; gap: 24px; padding: 12px 20px; background-color: #1e293b; }
.navbar-logo { color: white; font-weight: bold; text-decoration: none; }
.navbar-links { display: flex; gap: 16px; margin-left: auto; }
.navbar-link { color: #cbd5e1; text-decoration: none; }
.navbar-cta { padding: 8px 16px; background-color: royalblue; color: white; text-decoration: none; border-radius: 6px; }

/* 2. hero */
.hero { display: flex; justify-content: center; align-items: center; min-height: 240px; background-color: #eef2ff; text-align: center; }
.hero-content { max-width: 480px; padding: 20px; }

/* 3. cards */
.cards { display: flex; flex-wrap: wrap; gap: 16px; padding: 20px; }
.card { flex: 1 1 220px; display: flex; flex-direction: column; padding: 20px; border: 1px solid #dddddd; border-radius: 12px; }
.card-link { margin-top: auto; padding-top: 16px; color: royalblue; text-decoration: none; font-weight: bold; }

/* 4. comment */
.comment { display: flex; align-items: flex-start; gap: 12px; margin: 24px 20px; }
.comment-avatar { flex: 0 0 48px; height: 48px; border-radius: 50%; background-color: royalblue; color: white; text-align: center; line-height: 48px; }
.comment-body { flex: 1; min-width: 0; }

/* 5. search */
.search { display: flex; gap: 8px; margin: 20px; max-width: 480px; }
.search-input { flex: 1; padding: 10px 12px; border: 2px solid #cccccc; border-radius: 6px; font: inherit; }
.search-button { padding: 10px 18px; border: none; border-radius: 6px; background-color: royalblue; color: white; font: inherit; cursor: pointer; }

Common mistakes

  • Forgetting that .navbar-links needs its own display: flex. The <nav> is an item of the navbar, but its links are not items of anything until you make it a container too.
  • Using height for a hero, so long text overflows. Use min-height.
  • Expecting justify-content: center to centre the text lines. It centres the box. Use text-align for text.
  • Letting the avatar shrink because flex: 0 0 48px was left out.
  • Forgetting min-width: 0 on the fluid text column, so a long URL or code line stretches the layout.
  • Making all the cards a fixed width. Use flex: 1 1 <size> with flex-wrap, so they adapt.
  • Forgetting flex-direction: column on the card and wondering why margin-top: auto pushes the link sideways instead of down.
  • Using margin-top: auto on an item whose parent is not a flex container. Auto margins only absorb space inside flex (and grid) containers.
  • Layout with Flexbox when you need rows AND columns that align. Reach for Grid.

Practice

  1. Click Practice in Editor. Finish the navbar by adding the .navbar-cta button. Then try justify-content: space-between instead of margin-left: auto. How do they differ?
  2. Build the hero. Make it centre its content, then try min-height: 60vh.
  3. Write the .cards and .card rules. Resize the preview and watch how many cards fit on a line. Then add margin-top: auto to the links.
  4. Shorten the text of the second card. Do the links still line up?
  5. Build the comment pattern. Remove flex: 0 0 48px from the avatar and give the comment a very long text. What happens to the avatar?
  6. Style the search bar. Check that the input takes the free space, and that the button does not get squashed.
  7. Add flex-wrap: wrap to the navbar and shrink the preview to see how it behaves.

Recap

  • A real layout is nested flex containers: each element can be an item of its parent and a container for its own children.
  • Navbar: display: flex; align-items: center;, and margin-left: auto to push items to the right.
  • Hero: display: flex; justify-content: center; align-items: center; with min-height.
  • Cards: flex: 1 1 220px with flex-wrap: wrap for a responsive grid of equal-height cards. A column-direction card with margin-top: auto pins its last item to the bottom.
  • Media object: fixed item flex: 0 0 <size>, fluid item flex: 1; min-width: 0.
  • Search bar: flex: 1 on the input fills the row.
  • Next you will learn CSS Grid, which lays out rows and columns together.