Series Build a Website from Scratch Part 5 of 10

Layout Techniques —
Arranging Elements with Flexbox

In Part 4 we gave café SOEL colors, fonts, and spacing — but everything is still stacked vertically. Today we'll use Flexbox to place the logo and nav side by side, align images next to text, and finally make the site look like a real layout.

Quick Recap

📖 Quick Recap
  • Learned CSS syntax (selector { property: value; }) and linked an external stylesheet
  • Defined CSS custom properties in :root to manage the site's color palette in one place
  • Understood the box model and styled the hero section as a full-viewport image with an overlay

The hero fills the screen, sections alternate background colors, and the footer is dark — it's starting to look like a real site. But the header logo and nav are stacked on top of each other, and the concept image sits above the text instead of beside it. Today, we fix all of that.

Goals for This Part

🎯 Goals for This Part
  • Understand the core Flexbox concept: flex containers and flex items
  • Control item placement with justify-content, align-items, and gap
  • Apply Flexbox to the header, concept, access, and footer sections of café SOEL
  • Refactor the hero's center alignment from position + transform to cleaner Flexbox
👀 Preview the Result
By the end of this part, your site will look like this. Feel free to preview it before we start.
See the Part 5 result →

What Is Flexbox?

Flexbox — short for CSS Flexible Box Layout — is a CSS layout system designed to distribute space and align items within a container. The concept is beautifully simple: add display: flex to a parent element, and its children line up in a row automatically.

The parent is called the flex container; the children are called flex items. Think of it like a bookshelf (container) and the books on it (items).

Here's what elements look like without Flexbox — plain block stacking:

▶ Live Demo — No Flexbox (block stacking)
A
B
C

This is exactly the state café SOEL is in right now. Now add display: flex to the parent:

▶ Live Demo — With display: flex (horizontal row)
A
B
C

One line of CSS, and everything goes horizontal. That's the power of Flexbox.

Write display: flex on the parent of the things you want to arrange. Flexbox always starts with that single step.

Key Flexbox Properties

Once you've turned on Flexbox with display: flex, a handful of properties let you fine-tune how items are arranged. Let's walk through the ones we'll use today.

justify-content — Main-Axis Alignment

This controls how flex items are distributed along the main axis (left to right when items are in a row).

Common justify-content values
/* Pack items to the start (default) */
justify-content: flex-start;

/* Pack items to the end */
justify-content: flex-end;

/* Center items */
justify-content: center;

/* Push items to opposite ends */
justify-content: space-between;

space-between is especially handy — it's perfect for putting a logo on the left and navigation on the right.

▶ Live Demo — justify-content: space-between
Logo
Nav

align-items — Cross-Axis Alignment

This controls the perpendicular axis (top to bottom when items are in a row). Use center to vertically center items of different heights.

Common align-items values
/* Stretch to fill (default) */
align-items: stretch;

/* Align to the top */
align-items: flex-start;

/* Center vertically */
align-items: center;

/* Align to the bottom */
align-items: flex-end;

gap — Spacing Between Items

Instead of adding margins to individual items, gap creates uniform spacing between flex items. Clean, simple, and no extra margin on the first or last item.

Using gap
.nav-list {
  display: flex;
  gap: 36px;  /* 36px between each item */
}

flex: 1 — Share Remaining Space Equally

When you give a flex item flex: 1, it grows to fill the available space. Give it to two siblings, and they split the container width 50/50.

flex-direction — Change the Axis

The default direction is row (horizontal). Set it to column and items stack vertically — but you still get all the alignment powers of Flexbox. Great for centering a column of content.

▶ Live Demo — flex-direction: column + align-items: center
Logo
Nav Links
© 2025

flex-shrink: 0 — Don't Let It Shrink

By default, flex items can shrink when space is tight. Setting flex-shrink: 0 says "keep this item at its specified width, no matter what." Useful for fixed-width labels.

📌 Remember This
Flexbox boils down to four main properties: display: flex to activate it, justify-content for the main axis, align-items for the cross axis, and gap for spacing. Master these four, and you can handle the vast majority of layout challenges.

Applying Flexbox to the Header

Let's start applying Flexbox to café SOEL. Right now the logo and navigation are stacked vertically. We want "logo on the left, nav on the right."

Add three lines to .header-inner in your css/style.css:

css/style.css — Add to .header-inner
.header-inner {
  max-width: var(--container-width);
  margin: 0 auto;
  padding: 0 var(--container-padding);
  height: 72px;
  display: flex;              /* ← add */
  align-items: center;        /* ← add */
  justify-content: space-between; /* ← add */
}

Then make the nav links horizontal by adding two lines to a new .nav-list rule:

css/style.css — Add .nav-list
.nav-list {
  display: flex;   /* ← add */
  gap: 36px;       /* ← add */
}

Save and refresh. The logo snaps to the left, the nav links line up on the right — instantly recognizable as a navigation bar.

💡 KANON's Tip
You could achieve the same look by adding margin-left: 36px to each <li>, but then you'd need to remove the margin from the first item. With gap, spacing only appears between items — no extra margins to worry about.

Refactoring the Hero with Flexbox

In Part 4, we centered the hero text using position: absolute and transform: translate(-50%, -50%). It worked, but it was a bit of a juggling act. Now that we know Flexbox, we can do it in a much cleaner way.

First, add three lines to .hero:

css/style.css — Add to .hero
.hero {
  position: relative;
  height: 100vh;
  min-height: 600px;
  max-height: 1000px;
  overflow: hidden;
  display: flex;              /* ← add */
  align-items: center;        /* ← add */
  justify-content: center;    /* ← add */
}

Then simplify .hero-content — remove the four positioning lines and replace them with position: relative:

❌ Part 4 code (before)
.hero-content {
  position: absolute;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
  z-index: 2;
  text-align: center;
  color: var(--color-white);
  padding: 0 var(--container-padding);
}
✅ Flexbox version (after)
.hero-content {
  position: relative;       /* absolute → relative */
  z-index: 2;
  text-align: center;
  color: var(--color-white);
  padding: 0 var(--container-padding);
}

The result looks identical, but the code is far simpler. Flexbox says "center this child both horizontally and vertically" — no coordinate math needed.

We keep position: relative so that z-index: 2 still pushes the text in front of the background image. The background image uses position: absolute, so it's removed from the normal flow and doesn't participate as a flex item.

💡 KANON's Tip
I deliberately used position + transform in Part 4 because we hadn't covered Flexbox yet. Rewriting code as you learn better techniques is a normal — even enjoyable — part of web development. Your code isn't set in stone; it improves as your knowledge grows.

Laying Out the Content Sections

With the header and hero sorted, let's move on to the main content areas.

Concept Section — Image and Text Side by Side

The concept section should show the image on the left and text on the right, each taking half the width.

css/style.css — Add to Concept
.concept-inner {
  max-width: var(--container-width);
  margin: 0 auto;
  display: flex;          /* ← add */
  align-items: center;    /* ← add */
  gap: 60px;              /* ← add */
}

.concept-image {
  border-radius: 12px;
  overflow: hidden;
  flex: 1;                /* ← add */
}

.concept-text {
  flex: 1;                /* ← add */
}

flex: 1 on both children makes them share the container width equally. gap: 60px provides clean spacing between them, and align-items: center vertically centers the image with the text block.

Access Section — Photo and Info Side by Side

The access section follows the same pattern: photo on the left, shop info on the right.

css/style.css — Add to Access
.access-inner {
  max-width: var(--container-width);
  margin: 0 auto;
  display: flex;          /* ← add */
  align-items: center;    /* ← add */
  gap: 60px;              /* ← add */
}

.access-image {
  border-radius: 12px;
  overflow: hidden;
  flex: 1;                /* ← add */
}

.access-info {
  flex: 1;                /* ← add */
}

Info Items — Horizontal Label + Value

The definition list items (dt/dd pairs) also benefit from Flexbox. Lining up the label and value horizontally makes the info much easier to scan.

css/style.css — Add to .info-item
.info-item {
  padding: 16px 0;
  border-bottom: 1px solid var(--color-border);
  display: flex;          /* ← add */
  gap: 24px;              /* ← add */
}

.info-item dt {
  font-size: 13px;
  font-weight: 500;
  color: var(--color-text-muted);
  letter-spacing: 0.5px;
  margin-bottom: 4px;
  width: 100px;           /* ← add */
  flex-shrink: 0;         /* ← add */
}

width: 100px locks the label to a fixed width. flex-shrink: 0 prevents the label from squishing even if the adjacent value is long. The result is a neat, table-like layout without an actual table.

Styling the Footer with Flexbox

Finally, the footer. Here we want three elements — logo, nav links, and copyright — stacked vertically and centered. That's a job for flex-direction: column.

css/style.css — Add to Footer
.footer-inner {
  max-width: var(--container-width);
  margin: 0 auto;
  display: flex;                /* ← add */
  flex-direction: column;        /* ← add */
  align-items: center;          /* ← add */
  gap: 24px;                    /* ← add */
}

.footer-nav {
  display: flex;                /* ← add */
  gap: 28px;                    /* ← add */
}

flex-direction: column stacks items vertically while align-items: center centers them horizontally. The footer nav links get their own display: flex with gap: 28px to line up in a row.

Reviewing the Full Result

Save your file and refresh the browser.

👀 Preview the Result
The header logo and nav are side by side, the concept section is a two-column layout, and the footer is neatly centered. The menu cards and gallery are still stacked — that's expected. We'll fix those with CSS Grid in the next part. Does your screen match?
See the Part 5 result →

We didn't touch index.html at all this time. Every change was CSS only — that's the beauty of Flexbox.

If something looks off, copy the complete CSS below into your file to get back on track.

index.html (unchanged from Part 4)

index.html is identical to Part 4. No changes were made.

css/style.css (Part 5 complete)

css/style.css (Part 5 complete)
/* ===================================
   café SOEL — Style Sheet
   Part 5: Base CSS + Flexbox Layout
   =================================== */

/* ---------- CSS Custom Properties ---------- */
:root {
  --color-bg: #ffffff;
  --color-bg-soft: #faf9f7;
  --color-bg-accent: #f5f0eb;
  --color-text: #2c2c2c;
  --color-text-light: #6b6b6b;
  --color-text-muted: #999999;
  --color-accent: #8b7355;
  --color-accent-light: #c4a882;
  --color-border: #e8e4df;
  --color-white: #ffffff;
  --color-black: #1a1a1a;

  --section-padding: 120px;
  --container-width: 1200px;
  --container-padding: 24px;
}

/* ---------- Reset & Base ---------- */
*,
*::before,
*::after {
  margin: 0;
  padding: 0;
  box-sizing: border-box;
}

html {
  scroll-behavior: smooth;
  scroll-padding-top: 80px;
}

body {
  font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI',
               'Hiragino Kaku Gothic ProN', 'Noto Sans JP', sans-serif;
  font-weight: 400;
  font-size: 16px;
  line-height: 1.9;
  color: var(--color-text);
  background-color: var(--color-bg);
}

img {
  display: block;
  max-width: 100%;
  height: auto;
}

a {
  color: inherit;
  text-decoration: none;
}

ul, ol {
  list-style: none;
}

/* ---------- Utility ---------- */
.section {
  padding: var(--section-padding) var(--container-padding);
}

.section-label {
  display: inline-block;
  font-size: 14px;
  font-weight: 600;
  letter-spacing: 3px;
  text-transform: uppercase;
  color: var(--color-accent);
  margin-bottom: 16px;
}

.section-heading {
  font-size: clamp(24px, 4vw, 36px);
  font-weight: 700;
  line-height: 1.4;
  letter-spacing: 0.02em;
  color: var(--color-black);
  margin-bottom: 20px;
}

.section-header {
  text-align: center;
  max-width: 600px;
  margin: 0 auto 60px;
}

.section-desc {
  font-size: 15px;
  color: var(--color-text-light);
  line-height: 1.8;
}

/* ---------- Header ---------- */
.header {
  position: fixed;
  top: 0;
  left: 0;
  width: 100%;
  z-index: 100;
  background-color: var(--color-white);
  border-bottom: 1px solid var(--color-border);
}

.header-inner {
  max-width: var(--container-width);
  margin: 0 auto;
  padding: 0 var(--container-padding);
  height: 72px;
  display: flex;
  align-items: center;
  justify-content: space-between;
}

.logo img {
  height: 32px;
  width: auto;
}

.nav-list {
  display: flex;
  gap: 36px;
}

.nav-link {
  font-size: 15px;
  font-weight: 600;
  letter-spacing: 2px;
  text-transform: uppercase;
  color: var(--color-text);
}

/* ---------- Hero ---------- */
.hero {
  position: relative;
  height: 100vh;
  min-height: 600px;
  max-height: 1000px;
  overflow: hidden;
  display: flex;
  align-items: center;
  justify-content: center;
}

.hero-image {
  position: absolute;
  inset: 0;
  z-index: 1;
}

.hero-image img {
  width: 100%;
  height: 100%;
  object-fit: cover;
}

.hero-image::after {
  content: '';
  position: absolute;
  inset: 0;
  background: linear-gradient(
    to bottom,
    rgba(0, 0, 0, 0.15) 0%,
    rgba(0, 0, 0, 0.35) 100%
  );
}

.hero-content {
  position: relative;
  z-index: 2;
  text-align: center;
  color: var(--color-white);
  padding: 0 var(--container-padding);
}

.hero-sub {
  font-size: 14px;
  font-weight: 600;
  letter-spacing: 4px;
  text-transform: uppercase;
  margin-bottom: 20px;
  opacity: 0.9;
}

.hero-title {
  font-size: clamp(28px, 5vw, 52px);
  font-weight: 700;
  line-height: 1.5;
  letter-spacing: 0.06em;
  margin-bottom: 40px;
  text-shadow: 0 2px 20px rgba(0, 0, 0, 0.2);
}

.hero-cta {
  display: inline-block;
  font-size: 13px;
  font-weight: 600;
  letter-spacing: 3px;
  text-transform: uppercase;
  color: var(--color-white);
  padding: 12px 36px;
}

/* ---------- Concept ---------- */
.concept {
  background-color: var(--color-bg);
}

.concept-inner {
  max-width: var(--container-width);
  margin: 0 auto;
  display: flex;
  align-items: center;
  gap: 60px;
}

.concept-image {
  border-radius: 12px;
  overflow: hidden;
  flex: 1;
}

.concept-text {
  flex: 1;
}

.concept-text p {
  font-size: 15px;
  line-height: 2;
  color: var(--color-text-light);
  margin-bottom: 16px;
}

.concept-text p:last-child {
  margin-bottom: 0;
}

/* ---------- Menu ---------- */
.menu {
  background-color: var(--color-bg-soft);
}

.menu-card-body {
  padding: 24px;
}

.menu-card-title {
  font-size: 20px;
  font-weight: 700;
  letter-spacing: 1px;
  margin-bottom: 8px;
  color: var(--color-black);
}

.menu-card-desc {
  font-size: 14px;
  line-height: 1.8;
  color: var(--color-text-light);
  margin-bottom: 12px;
}

.menu-card-price {
  font-size: 18px;
  font-weight: 600;
  color: var(--color-accent);
  letter-spacing: 1px;
}

/* ---------- Gallery ---------- */
.gallery {
  background-color: var(--color-bg);
}

/* ---------- Access ---------- */
.access {
  background-color: var(--color-bg-soft);
}

.access-inner {
  max-width: var(--container-width);
  margin: 0 auto;
  display: flex;
  align-items: center;
  gap: 60px;
}

.access-image {
  border-radius: 12px;
  overflow: hidden;
  flex: 1;
}

.access-info {
  flex: 1;
}

.info-list {
  margin-top: 12px;
}

.info-item {
  padding: 16px 0;
  border-bottom: 1px solid var(--color-border);
  display: flex;
  gap: 24px;
}

.info-item:last-child {
  border-bottom: none;
}

.info-item dt {
  font-size: 13px;
  font-weight: 500;
  color: var(--color-text-muted);
  letter-spacing: 0.5px;
  margin-bottom: 4px;
  width: 100px;
  flex-shrink: 0;
}

.info-item dd {
  font-size: 15px;
  line-height: 1.8;
  color: var(--color-text);
}

.info-item dd a {
  color: var(--color-accent);
}

/* ---------- Footer ---------- */
.footer {
  background-color: var(--color-black);
  color: var(--color-white);
  padding: 60px var(--container-padding);
}

.footer-inner {
  max-width: var(--container-width);
  margin: 0 auto;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 24px;
}

.footer-logo img {
  height: 24px;
  width: auto;
  filter: brightness(0) invert(1);
  opacity: 0.8;
}

.footer-nav {
  display: flex;
  gap: 28px;
}

.footer-nav a {
  font-size: 13px;
  font-weight: 600;
  letter-spacing: 2px;
  text-transform: uppercase;
  color: rgba(255, 255, 255, 0.5);
}

.footer-copy {
  font-size: 12px;
  color: rgba(255, 255, 255, 0.35);
  letter-spacing: 0.5px;
}
⚠️ Common Mistake
If Flexbox doesn't seem to work, the most common cause is writing display: flex on the wrong element. Flexbox goes on the parent — not on the items you want to arrange, but on the element one level above them. For example, to line up nav links horizontally, write it on .nav-list (the <ul>), not on .nav-link.

What You Learned

  • Flexbox activates with display: flex on a parent element, turning its children into a horizontal row
  • justify-content controls the main axis (left/right); align-items controls the cross axis (top/bottom)
  • gap sets spacing between items cleanly — no extra margins on the first or last item
  • flex: 1 distributes remaining space equally; flex-shrink: 0 prevents an item from shrinking
  • flex-direction: column stacks items vertically while still giving you alignment control
  • The hero's center alignment was refactored from position + transform to simpler Flexbox
  • Menu cards and the gallery are still stacked — next time we'll arrange them with CSS Grid and add responsive breakpoints