Module: CSS
CSS·028·8 MIN READ

028: Flexbox II

TOPICS COVERED: Flexbox II

Learning outcomes

By the end, you can reason about flex basis, growth, shrinkage, and intrinsic minimums; size individual items; align one item; build a card whose action stays after flexible content; and debug common overflow without arbitrary widths.

Prerequisites and retrieval

Retrieve flex container, item, both axes, wrapping, and gap. Explain flex: 1 1 18rem in three parts. This exercise focuses on inspecting the sizing algorithm rather than collecting alignment recipes.

Terminology

Mental model: basis first, then negotiate free space

Flex sizing begins with each item's basis. If space remains, grow factors distribute positive free space. If items do not fit, shrink factors and base sizes influence reduction, bounded by min/max constraints. flex-grow: 1 does not mean “width: 100%,” and equal growth does not guarantee equal final widths when bases or content differ.

Common intentions:

css
flex: 0 1 auto; /* default: do not grow, may shrink, content/width basis */
flex: 1 1 0;    /* share from zero basis; useful for equal shares */
flex: 1 1 18rem;/* prefer 18rem, then negotiate */
flex: none;     /* 0 0 auto: do not grow or shrink */

Use shorthand so all three values are visible. Content still matters: flex items default to min-width: auto, often preserving their min-content width.

Beginner example: media object

html
<article class="profile">
  <img class="profile__photo" src="asha.jpg" width="160" height="160" alt="Asha Rao">
  <div class="profile__body">
    <h2>Asha Rao</h2>
    <p>Documenting accessible interface decisions and handling averyverylongunbrokenidentifier.</p>
  </div>
</article>
css
.profile {
  display: flex;
  align-items: flex-start;
  gap: 1rem;
  max-inline-size: 42rem;
}

.profile__photo {
  flex: 0 0 5rem;
  inline-size: 5rem;
  block-size: 5rem;
  border-radius: 50%;
  object-fit: cover;
}

.profile__body {
  flex: 1 1 auto;
  min-inline-size: 0;
}

.profile__body p { overflow-wrap: anywhere; }

The image is fixed to a deliberate avatar dimension and does not shrink. The body takes remaining space. min-inline-size: 0 permits the flex item to shrink below its automatic min-content minimum; the long token can then wrap. This line is not magic to paste everywhere. Use it when inspection confirms a flex child's intrinsic minimum causes overflow.

Remove it and test narrow widths. Observe whether the body forces the container wider. Restore it, then remove overflow-wrap; distinguish item shrink permission from text-breaking opportunity.

Intermediate example: cards with actions after flexible content

html
<article class="project-card">
  <img src="library.jpg" width="960" height="540" alt="Library finder results page">
  <div class="project-card__content">
    <p class="project-card__meta">Accessibility · HTML</p>
    <h2>Library finder</h2>
    <p>Search, opening hours, directions, and route accessibility.</p>
    <a class="button-link" href="#">Read case study</a>
  </div>
</article>
css
.project-list {
  display: flex;
  flex-wrap: wrap;
  align-items: stretch;
  gap: 1rem;
}

.project-card {
  display: flex;
  flex: 1 1 18rem;
  flex-direction: column;
  max-inline-size: 32rem;
  border: 1px solid rgb(203 213 225);
  background: white;
}

.project-card__content {
  display: flex;
  flex: 1;
  flex-direction: column;
  align-items: flex-start;
  padding: 1rem;
}

.project-card__content .button-link {
  margin-block-start: auto;
}

There are nested one-dimensional layouts. The list arranges cards; each card stacks image and content; content stacks text and action. The content flexes to fill stretched card height, and the action's auto block-start margin consumes remaining main-axis space. It follows content in DOM order and does not require absolute positioning.

If button alignment across cards is not required, omit the nested flex behavior and let normal flow remain simpler.

Optional advanced example: unequal sidebar relationship

css
.split {
  display: flex;
  flex-wrap: wrap;
  gap: 2rem;
}
.split__main { flex: 3 1 32rem; min-inline-size: 0; }
.split__aside { flex: 1 1 16rem; }

Factors are proportional only while distributing relevant free space; bases and constraints still shape results. Wrapping lets the aside move below when both preferred bases do not fit. Grid may express strict tracks more clearly in 029.

Mistakes, debugging, and DevTools

  • Treating grow factors as percentages.
  • Setting only flex-grow and forgetting the auto basis.
  • Applying min-inline-size: 0 blindly instead of finding the overflowing item.
  • Using flex: none on long navigation labels so they cannot shrink or wrap.
  • Setting equal heights explicitly rather than using stretch and natural growth.
  • Applying align-self expecting main-axis movement; it affects cross-axis alignment.
  • Nesting flex containers where normal flow would do.
  • Using order for responsive rearrangement and breaking focus sequence.

Enable the flex overlay. Inspect item base sizes, final sizes, and constraints. Change flex-basis to 0, auto, and 18rem; observe the distribution. DevTools may display a Flexbox editor and growth/shrink calculations. Test a long word, larger image, missing image, and 200% zoom.

Accessibility and performance

DOM order remains the authoritative reading and focus order. Keep actions after descriptions in markup. Cropped images need meaningful alt text when informative; decorative images use empty alt. Do not fix card heights, because enlarged text must remain visible. A pushed action must not overlap content.

Nested flex layout is normal, but do not add containers without purpose. Avoid continuously animating flex-basis, width, or height across many items; these trigger layout. Prefer restrained transforms for optional motion and honor reduced-motion preferences when animation exists.

Deep dive: what flex shorthand really means

The shorthand:

css
.item {
  flex: 1;
}

is commonly used, but learners should know it expands to a grow/shrink/basis combination rather than “make equal columns” as a magical command.

Prefer explicit values while reasoning:

css
.item {
  flex: 1 1 0;
}

versus:

css
.item {
  flex: 1 1 auto;
}

The important difference is the basis used before free space is distributed.

  • flex-basis: 0 starts negotiation from zero-sized bases, so equal grow factors tend to produce equal shares.
  • flex-basis: auto uses the item's main-size/intrinsic basis when available, so content can influence the starting size.

Deep dive: flex sizing algorithm as a practical story

You do not need to reproduce the specification algorithm from memory, but you should understand the sequence:

  1. Determine each item's flex base size.
  2. Add gaps and bases to see whether the container has positive or negative free space.
  3. If free space is positive, flex-grow participates.
  4. If negative, flex-shrink participates relative to base sizes.
  5. Min/max constraints can freeze items and force redistribution.
  6. Alignment happens after sizes are resolved.

This explains why “all children have flex: 1” can still surprise you when one child has a large minimum width or unbreakable content.

Worked example: fixed-ish sidebar with flexible content

html
<div class="dashboard">
  <aside class="dashboard__sidebar">...</aside>
  <main class="dashboard__main">...</main>
</div>
css
.dashboard {
  display: flex;
  flex-wrap: wrap;
  gap: 2rem;
}

.dashboard__sidebar {
  flex: 0 1 18rem;
}

.dashboard__main {
  flex: 1 1 32rem;
  min-inline-size: 0;
}

Interpretation:

  • Sidebar does not grow, can shrink, and prefers 18rem.
  • Main area grows, can shrink, and prefers 32rem.
  • Wrapping lets the main area move below when the combined preferred sizes no longer fit.

This can produce a responsive relationship without a breakpoint.

Worked example: equal buttons versus content-sized buttons

Equal distribution:

css
.button-row {
  display: flex;
  gap: 0.75rem;
}

.button-row > * {
  flex: 1 1 0;
}

Content-sized:

css
.button-row {
  display: flex;
  gap: 0.75rem;
}

.button-row > * {
  flex: 0 0 auto;
}

The correct choice depends on the interface. Equal widths can look balanced for a compact dialog; content-sized controls often work better in toolbars.

Deep dive: shrinking is constrained by content

This fails surprisingly often:

css
.row {
  display: flex;
}

.row__content {
  flex: 1;
}

If .row__content contains a long URL or wide preformatted content, its automatic minimum size can block shrinking.

Add:

css
.row__content {
  flex: 1 1 auto;
  min-inline-size: 0;
}

.row__content p {
  overflow-wrap: anywhere;
}

min-inline-size: 0 is not a ritual; it says this flex item may shrink below its content-based automatic minimum.

Deep dive: align-self and exceptional items

css
.row {
  display: flex;
  align-items: center;
}

.row__badge {
  align-self: flex-start;
}

Use align-self when one item genuinely differs from the container's default. If every child receives a different align-self, the container's alignment model may be wrong.

Flexbox versus Grid decision examples

Choose Flexbox when:

  • navigation items need one-axis distribution;
  • buttons need wrapping;
  • an avatar and text need a media-object relationship;
  • a card needs a column with action pushed to the bottom.

Choose Grid when:

  • card columns must line up across rows;
  • page regions need two-dimensional placement;
  • repeated items need explicit rows and columns;
  • overlapping named areas are clearer than independent flex lines.

A component can use Grid outside and Flexbox inside. Layout systems are composable.

Tiered exercises

Checkpoint: trace the sizing negotiation

Imagine a 60rem container with a 2rem total gap and two items using bases of 20rem and 10rem, both with grow factor 1. Before constraints, 28rem remains. Equal grow factors allocate 14rem to each, producing 34rem and 24rem, not equal widths. Equal factors share free space; they do not erase different bases. With a zero basis, the starting relationship changes.

Shrinkage is also not a simple equal subtraction. The algorithm considers scaled shrink factors based on base sizes, then freezes items at min/max constraints as necessary. You rarely calculate every iteration by hand, but this mental model explains why a large item often surrenders more space and why content minimums can stop negotiation.

Create a reproducible overflow test: put a long unbroken identifier inside the flexible body while a fixed avatar occupies space. Inspect the body's computed minimum and used width. Set min-inline-size: 0; if the item now shrinks but text still paints outside, add an appropriate wrapping rule to the text. The two declarations solve different layers of the problem.

For card actions, compare auto margin with absolute positioning. Auto margin participates in flex layout, follows growing descriptions, and preserves card height. Absolute positioning leaves flow, forcing guessed bottom padding and risking overlap. The Flexbox solution is both shorter and content-aware.

Constraints can freeze an item during negotiation. Add max-inline-size to one growing item and observe it stop accepting space while siblings continue. Add a realistic min-inline-size to a sidebar and watch wrapping occur sooner. Min/max values should represent content needs, not an attempt to reproduce one screenshot.

align-self overrides the container's align-items for one item. Use it when one action or image has a genuine cross-axis alignment difference, not to patch inconsistent markup. It cannot distribute main-axis free space; use auto margins or container justification for that axis.

Nested flex layouts can obscure which container controls which property. In DevTools, climb one level at a time and label each direct-child set. A card may simultaneously be a flex item in the gallery and a flex container for its content. Its flex shorthand belongs to the outer relationship; its flex-direction governs the inner relationship. Keeping those roles separate makes debugging substantially faster.

Foundation: Build the profile media object with a nonshrinking 5rem image and flexible body. Introduce a long token and repair overflow intentionally.

Core: Make project cards column flex containers. Push each action after flexible content without positioning or fixed heights.

Stretch: Build a wrapping main/aside split with different basis and grow values. Explain what happens at wide, medium, and narrow available widths.

css
.profile { display: flex; align-items: flex-start; gap: 1rem; }
.profile__photo { flex: 0 0 5rem; inline-size: 5rem; block-size: 5rem; object-fit: cover; }
.profile__body { flex: 1 1 auto; min-inline-size: 0; }
.profile__body p { overflow-wrap: anywhere; }
.project-card { display: flex; flex: 1 1 18rem; flex-direction: column; }
.project-card__content { display: flex; flex: 1; flex-direction: column; align-items: flex-start; padding: 1rem; }
.project-card__content .button-link { margin-block-start: auto; }
.split { display: flex; flex-wrap: wrap; gap: 2rem; }
.split__main { flex: 3 1 32rem; min-inline-size: 0; }
.split__aside { flex: 1 1 16rem; }

Recap and exit questions

Flex items begin from a basis and negotiate positive or negative free space under constraints. Intrinsic minimums explain many “Flexbox overflow” bugs. Nested flex is useful when each level has a clear one-axis job.

  1. Why do equal grow factors not always create equal final widths?
  2. What does min-inline-size: 0 permit?
  3. How does an auto main-axis margin behave?
  4. Why is the card action not absolutely positioned?
  5. When is flex: none appropriate?

Official references