Build Your Portfolio
Beginner

The Top Bar with Flexbox

Two things on one line, pushed to opposite edges, perfectly centered vertically. It sounds simple, and for years it wasn't. Flexbox makes it three lines of CSS. Build the top bar: a round avatar with an online dot, a name, and links with an active pill.

The Top Bar with Flexbox

Look at the top of almost any website. There's a logo on the left and links on the right, and both sit exactly in the middle of the bar's height. It's so common you never notice it.

Getting that right used to take floats, clearfixes, and line-height tricks that broke as soon as the text changed size. Today it takes flexbox, and once you understand three properties, you'll use it every day.

Flexbox in one picture

Add display: flex to a container, and its direct children line up in a row. Then two properties decide where they go:

  • justify-content places them along the row (left to right).
  • align-items places them across it (top to bottom).

Here's our top bar trying three values of justify-content:

Animation: the profile chip and the nav links first huddle on the left (flex-start), then in the middle (center), then get pushed to opposite edges (space-between) with all the spare space between them
space-between gives all the leftover room to the gap in the middle.

space-between is the one we want: the first child hugs the left edge, the last hugs the right, and all the spare space goes between them.


The bar

/* ---------- 3. Top bar ---------- */
.topbar {
  max-width: 960px;
  margin: 0 auto;
  padding: 32px 24px 24px;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 24px;
}
  • max-width: 960px; margin: 0 auto keeps the bar the same width as the grid below and centers it. auto left and right margins split the leftover space equally.
  • align-items: center lines both children up on their vertical middles, so the 44px avatar and the shorter links sit on the same center line.
  • gap: 24px guarantees at least 24px between them, even on a narrow screen where space-between has no spare room left.

The profile chip

The chip is itself a flex container. That's normal: flexbox inside flexbox is how most layouts are built.

.profile-chip {
  display: flex;
  align-items: center;
  gap: 12px;
  text-decoration: none;
  color: var(--paper);
}

.profile-name {
  display: flex;
  flex-direction: column;
  line-height: 1.2;
}

.profile-name strong {
  font-size: 1.05rem;
}

.profile-name small {
  font-size: 0.75rem;
  opacity: 0.85;
}

.profile-name uses flex-direction: column to stack the name above "Hololive EN". Flexbox works in either direction; column just turns the row on its side.

The round avatar and its online dot

.avatar {
  position: relative;
  flex-shrink: 0;
}

.avatar img {
  width: 44px;
  height: 44px;
  border-radius: 50%;
  border: 2px solid var(--paper);
  object-fit: cover;
}

.online-dot {
  position: absolute;
  right: -2px;
  bottom: -2px;
  width: 14px;
  height: 14px;
  border-radius: 50%;
  background: var(--online);
  border: 2px solid var(--paper);
}

Three ideas in a few lines:

  1. border-radius: 50% turns any square into a circle.
  2. object-fit: cover makes the image fill the circle without stretching, even if the original isn't square.
  3. The green dot uses a pair you'll meet again and again: a parent with position: relative and a child with position: absolute. The child is taken out of the normal flow and placed relative to the parent's edges. right: -2px; bottom: -2px tucks it onto the bottom-right curve of the avatar.

flex-shrink: 0 stops flexbox from squashing the avatar when space gets tight. Without it, a long name could make the avatar shrink into an oval.


The links and the active pill

.topbar nav {
  display: flex;
  gap: 8px;
}

.topbar nav a {
  padding: 8px 14px;
  border-radius: 6px;
  font-size: 0.9rem;
  font-weight: 500;
  text-decoration: none;
}

.topbar nav a[aria-current="page"] {
  background: var(--blue-deep);
  color: var(--paper);
}

.topbar nav a:hover:not([aria-current]) {
  background: rgba(255, 255, 255, 0.35);
}

The interesting selector is a[aria-current="page"], an attribute selector. In lesson 2 we put aria-current="page" on the About link so screen readers would announce "current page". Now the same attribute draws the pill. One source of truth: if you move the attribute, the highlight moves with it, and the two can never disagree.

:hover:not([aria-current]) means "on hover, but not the current link", so the active pill doesn't flicker when you point at it.


Checkpoint

The top bar finished: round avatar with a green online dot, Gawr Gura and Hololive EN stacked beside it, and Home, About (in a dark blue pill) and Gallery on the right
The first piece that looks like the design.

If something looks wrong

  • The links are under the name, not to the right. display: flex is missing on .topbar, or it's on the wrong element. It belongs on the parent of the two things you want in a row.
  • The green dot is in the corner of the page. The .avatar wrapper needs position: relative. Without it, absolute positions against the nearest positioned ancestor, which might be the whole page.
  • The avatar is an oval. Add object-fit: cover and check that width and height are equal.

What to remember

  • display: flex puts children in a row. flex-direction: column stacks them instead.
  • justify-content spaces along the row; align-items aligns across it; gap sets a minimum space.
  • position: relative on the parent + absolute on the child pins badges and decorations.
  • Style states with attributes (aria-current) so meaning and look stay in sync.

Next, every card finds its place at once: The Bento Grid.