
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
- 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
- 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
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:
This is exactly the state café SOEL is in right now. Now add display: flex to the parent:
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).
/* 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.
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.
/* 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.
.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.
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.
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:
.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:
.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.
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:
.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:
.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);
}
.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.
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.
.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.
.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.
.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.
.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.
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)
/* ===================================
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;
}
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