Skip to content
EzzyWeb

Level 2, Styling & Logic, Chapter 5 of 24

CSS Layout — Flexbox

Why this matters

Before Flexbox, arranging elements side by side in CSS was surprisingly awkward, requiring tricks that were hard to predict and harder to maintain. Flexbox changed that by giving developers a purpose-built layout system: turn on a single property, and suddenly a group of boxes can line up in a row, wrap onto new lines, center themselves, or space themselves out evenly, all with a small number of properties.

Almost every modern website uses Flexbox somewhere: a navigation bar with links spread across the top, a row of product cards, a footer with columns of links. Before getting there, this chapter also fills in the visual vocabulary you need everywhere: how to describe color precisely, how to size things with the right unit for the job, and how backgrounds and borders bring a design to life. If you've ever wondered how a set of buttons stays perfectly aligned no matter the screen size, or how a hero section gets a smooth color gradient behind it, the answers are in this chapter.

The lesson

Color, in full

CSS supports several ways to describe color, and each has its place. Named colors like red or steelblue are quick and readable but limited to a fixed list. Hex codes like #ff6600 (6-digit) or #f60 (3-digit shorthand) are the most common format in professional work because design tools export them directly; each pair of digits represents red, green, and blue intensity from 00 to ff. rgb(255, 102, 0) describes the same color using decimal numbers instead of hex, and rgba(255, 102, 0, 0.5) adds a fourth value for opacity, from 0 (invisible) to 1 (fully solid), which is the easiest way to get a semi-transparent color. hsl() describes color differently and often more intuitively: hue is a position on the color wheel from 0 to 360 degrees (0 is red, 120 is green, 240 is blue), saturation is how vivid the color is from 0% (gray) to 100% (full color), and lightness runs from 0% (black) through the pure color to 100% (white). hsl() is especially handy for building color variations, since sliding the lightness value up or down gives you a lighter or darker shade of the exact same hue.

The same blue, four ways
CSS
1.a { color: steelblue; }2.b { color: #4682b4; }3.c { color: rgb(70, 130, 180); }4.d { color: hsl(207, 44%, 49%); }56/* rgba adds transparency, useful for overlays */7.overlay {8  background-color: rgba(0, 0, 0, 0.5);9}
hsl is often easiest to reason about when you want lighter or darker versions of the same color.

Units, in full

CSS units differ in what they're relative to. px is an absolute pixel, the most predictable but the least flexible unit. % is relative to the parent element's size, useful for fluid widths that shrink and grow with their container. em is relative to the font size of the current element (or its parent, for non-font properties), which means it can compound unexpectedly when nested. rem is relative to the root element's font size (the <html> element) no matter how deeply nested it is, which makes it the standard choice for building a consistent type scale across a whole page. vw and vh are relative to the viewport (the visible browser window): 1vw is 1% of the viewport width, 1vh is 1% of the viewport height, both useful for elements that should scale with the screen itself, like a full-height hero section.

  • px — a fixed, absolute size; predictable but not flexible.
  • % — relative to the parent element; good for fluid widths.
  • em — relative to the current element's font size; can compound when nested.
  • rem — relative to the root font size; the standard choice for consistent type sizing.
  • vw / vh — relative to the viewport width/height; good for full-screen sections.
Choosing units for the job
CSS
1html {2  font-size: 16px; /* 1rem now equals 16px everywhere */3}45h1 {6  font-size: 2.5rem; /* scales with the root, not a parent */7}89.container {10  max-width: 1100px;11  width: 90%; /* fluid until it hits the max-width */12  margin: 0 auto;13}1415.hero {16  min-height: 60vh; /* always 60% of the visible window height */17}
rem keeps type sizes consistent; % combined with max-width creates a layout that's fluid but never too wide.

Backgrounds

The background-color property fills an element's background with a solid color, using any of the color formats above. background-image accepts a url() pointing to an image file. Once an image is set, background-size controls how it fits: cover scales the image to fill the entire element, cropping if necessary, while contain scales it to fit entirely inside the element, possibly leaving empty space. background-position controls which part of the image is shown, commonly center. background-repeat controls whether a smaller image tiles to fill the space, and setting it to no-repeat is common when using cover or contain. CSS can also generate backgrounds without any image file at all using linear-gradient(), which blends between two or more colors along a direction you specify.

Backgrounds and a gradient
CSS
1.hero {2  background-image: url("hero.jpg");3  background-size: cover;4  background-position: center;5  background-repeat: no-repeat;6  min-height: 40vh;7}89.banner {10  background-image: linear-gradient(to right, #ff7e5f, #feb47b);11  color: white;12  padding: 40px;13}
linear-gradient(to right, colorA, colorB) blends smoothly from the first color to the second.

Turning on Flexbox

Flexbox works on a parent-child relationship. You set display: flex on a container element, and every direct child of that container becomes a flex item, automatically arranged in a row by default. This is different from styling individual elements in isolation; here, the container's CSS controls how its children behave as a group. Imagine a <div> wrapping three <button> elements: without Flexbox they stack in whatever order the browser defaults to, but with display: flex on the wrapping div, the buttons line up neatly side by side.

Turning a row of items into a flex row
CSS
1.toolbar {2  display: flex;3}
Adding display: flex to the parent arranges its direct children in a horizontal row.

Once a container is a flex container, flex-direction decides the main axis: row (the default, left to right) or column (top to bottom). justify-content controls alignment along that main axis, and its most useful values are flex-start, center, space-between (pushes the first and last item to the edges, spacing the rest evenly), and space-around (spaces items with equal room on every side). align-items controls alignment along the cross axis, perpendicular to the main axis; common values are flex-start, center, and stretch. gap adds consistent spacing between every item without needing margin on each one. flex-wrap: wrap lets extra items move onto new lines instead of squeezing onto one, which matters as soon as a row has more content than fits.

  • justify-content: center — groups items together in the middle of the main axis.
  • justify-content: space-between — pushes the first and last item to the edges, spacing the rest evenly.
  • align-items: center — centers items along the cross axis (vertically, in a row).
  • flex-direction: column — switches the main axis to vertical, stacking items top to bottom.
  • flex-wrap: wrap — allows items to move to a new line instead of shrinking to fit one.
Centering and spacing items
CSS
1.navbar {2  display: flex;3  justify-content: space-between;4  align-items: center;5  padding: 16px;6}
This is a classic navigation bar pattern: a logo on the left, links on the right, vertically centered.
A wrapping card row
CSS
1.gallery {2  display: flex;3  flex-wrap: wrap;4  gap: 16px;5}67.gallery .card {8  width: 200px;9  padding: 12px;10  border: 1px solid #ddd;11}
gap adds consistent spacing between flex items without needing margin on every card.

Individual flex items can also be given their own properties. The flex-grow property lets an item expand to fill leftover space, useful for a search input that should stretch while its neighboring button stays a fixed size. The flex shorthand combines flex-grow, flex-shrink, and flex-basis into one declaration; writing flex: 1 on a child is a very common shortcut meaning "grow to share the available space equally with any other flex: 1 siblings." In practice, most everyday layouts only need display: flex, flex-direction, justify-content, align-items, gap, flex-wrap, and occasionally flex on a child. Mastering these will let you build navigation bars, card grids, form rows, and footers confidently.

flex-grow and the flex shorthand
CSS
1.search-row {2  display: flex;3  gap: 8px;4}56.search-row input {7  flex-grow: 1; /* stretches to fill remaining space */8}910.columns {11  display: flex;12  gap: 16px;13}1415.columns .col {16  flex: 1; /* every column shares the row equally */17}
flex: 1 on every child makes them split the available width equally.

Flexbox or Grid — which do I reach for?

CSS Grid is another layout system that works well alongside Flexbox rather than replacing it. A simple rule of thumb: reach for Flexbox when you're arranging items along a single dimension, a row or a column, like a navigation bar, a button group, or a stack of cards. Reach for Grid when you need to control rows and columns at the same time, like a photo gallery or a page layout with a sidebar, header, and main content area all positioned relative to each other. Many real interfaces use both: Grid for the overall page skeleton, and Flexbox for aligning the smaller groups of elements inside each grid area.

Try it yourself

Arrange the navigation links into a horizontal row using Flexbox, then give the nav a gradient background using linear-gradient and space its links with justify-content.

Live preview

Chapter boss project

Boss Project: Build a Responsive Card Gallery

Create a gallery of at least four cards, each containing an image (or a background-image with background-size: cover), a heading, and a short paragraph, and arrange them with Flexbox so they sit in a row and wrap onto new lines when the screen is narrow.

Use gap to space the cards evenly instead of manually adding margin to each one. Each card should have consistent padding, use rem for its text sizes, and a border or background color chosen using hex, rgb, or hsl rather than only named colors.

Add one decorative touch using either a linear-gradient background on the page or section header, or an rgba overlay color, to show you understand both solid and semi-transparent backgrounds.

  • Wrap all your cards in one parent container and apply display: flex plus flex-wrap: wrap to that parent.
  • Give each card a fixed width, like 220px, so several fit per row before wrapping.
  • Use gap on the parent container instead of margin on each card to keep spacing consistent.
  • Try hsl() for your color choices so you can easily create a lighter or darker variant by changing only the lightness value.
  • Reuse the box model skills from the previous chapter for padding and borders on each card.

Level-up checklist

Tick these off once each one is true for you.