Polyglot
Languages Web (HTML / CSS / JS) css layout
Web (HTML / CSS / JS) § css-layout

Layout (Flexbox and Grid)

The conventional contemporary CSS layout uses Flexbox (one-dimensional layout — rows or columns) and CSS Grid (two-dimensional layout — rows and columns simultaneously). Both replace the older mechanisms (floats, tables for layout, inline-block hacks) with substantial declarative layout. Flexbox is the conventional choice for one-dimensional alignment — navigation bars, button groups, card rows. Grid is the conventional choice for two-dimensional layout — page-level structure, dashboards, complex card grids. The combination — Flexbox for one-axis alignment, Grid for two-axis layout, the substantial alignment properties (justify-content, align-items, place-items), the responsive integration via media and container queries — is the substance of CSS’s modern layout surface.

Flexbox basics

Make an element a flex container via display: flex:

.container {
    display: flex;
}
<div class="container">
    <div>1</div>
    <div>2</div>
    <div>3</div>
</div>

The principal axes:

  • Main axis — direction of flex-direction (default row).
  • Cross axis — perpendicular.
.container {
    flex-direction: row;                           /* default: left-to-right */
    flex-direction: column;                        /* top-to-bottom */
    flex-direction: row-reverse;                   /* right-to-left */
    flex-direction: column-reverse;                /* bottom-to-top */
}

Flex alignment

.container {
    display: flex;

    /* Main axis: */
    justify-content: flex-start;                   /* default; pack at start */
    justify-content: flex-end;
    justify-content: center;
    justify-content: space-between;                /* spread; first/last at edges */
    justify-content: space-around;                 /* equal space around each */
    justify-content: space-evenly;                 /* equal space everywhere */

    /* Cross axis (single line): */
    align-items: stretch;                          /* default; fill cross axis */
    align-items: flex-start;
    align-items: flex-end;
    align-items: center;
    align-items: baseline;                         /* align text baselines */

    /* Cross axis (multi-line): */
    align-content: flex-start;                     /* applies when wrapping */
    align-content: space-between;
    align-content: center;
}

For individual item override:

.item {
    align-self: center;                            /* override container's align-items */
}

Flex item sizing

.item {
    flex: 1;                                       /* shorthand: 1 1 0 */
    flex: 0 0 auto;                                /* default */
    flex: 1 1 200px;                               /* grow shrink basis */

    /* Or individually: */
    flex-grow: 1;                                  /* admit growing */
    flex-shrink: 1;                                /* admit shrinking */
    flex-basis: 200px;                             /* preferred size */
}

The principal patterns:

  • flex: 1 — share remaining space equally.
  • flex: 0 0 auto — fixed size based on content (default).
  • flex: 0 0 200px — fixed 200px width.
  • flex: 1 1 200px — preferred 200px, grow/shrink as needed.
.sidebar { flex: 0 0 200px; }                      /* fixed width */
.main { flex: 1; }                                 /* fill remaining */

Flex wrap

.container {
    display: flex;
    flex-wrap: nowrap;                             /* default; single line */
    flex-wrap: wrap;                               /* multiple lines */
    flex-wrap: wrap-reverse;
}

.container {
    flex-flow: row wrap;                           /* shorthand for direction + wrap */
}

For gaps:

.container {
    display: flex;
    gap: 1rem;                                     /* gap between items */
    row-gap: 1rem;                                 /* between rows */
    column-gap: 0.5rem;                            /* between columns */
}

Flexbox common patterns

Centred element

.center {
    display: flex;
    justify-content: center;
    align-items: center;
    min-height: 100vh;
}
.nav {
    display: flex;
    justify-content: space-between;
    align-items: center;
    padding: 1rem;
}

.nav__brand { font-weight: bold; }

.nav__links {
    display: flex;
    gap: 1rem;
    list-style: none;
}

Card row

.card-row {
    display: flex;
    gap: 1rem;
    flex-wrap: wrap;
}

.card {
    flex: 1 1 250px;                               /* min 250px, grow */
    background: white;
    padding: 1rem;
    border-radius: 0.5rem;
}
.layout {
    display: flex;
    min-height: 100vh;
}

.sidebar {
    flex: 0 0 250px;
    background: lightgray;
}

.main {
    flex: 1;
    padding: 2rem;
}

Equal-height columns

.columns {
    display: flex;
    align-items: stretch;                          /* default; admits equal heights */
}

.column {
    flex: 1;
    background: white;
    padding: 1rem;
}

Push to right

.toolbar {
    display: flex;
    align-items: center;
}

.toolbar__spacer {
    margin-left: auto;                             /* push subsequent items right */
}
<div class="toolbar">
    <button>A</button>
    <button>B</button>
    <span class="toolbar__spacer"></span>
    <button>C</button>                              <!-- right-aligned -->
</div>

CSS Grid basics

Make an element a grid container via display: grid:

.container {
    display: grid;
    grid-template-columns: 1fr 1fr 1fr;             /* three equal columns */
    gap: 1rem;
}
<div class="container">
    <div>1</div>
    <div>2</div>
    <div>3</div>
    <div>4</div>
    <div>5</div>
    <div>6</div>
</div>

Grid template

.container {
    display: grid;

    /* Columns: */
    grid-template-columns: 200px 1fr 200px;        /* fixed-flex-fixed */
    grid-template-columns: repeat(3, 1fr);          /* three equal */
    grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));  /* responsive */
    grid-template-columns: 200px auto 1fr;
    grid-template-columns: 25% 25% 25% 25%;

    /* Rows: */
    grid-template-rows: auto 1fr auto;              /* header, content, footer */
    grid-template-rows: repeat(3, 100px);

    /* Gaps: */
    gap: 1rem;
    column-gap: 0.5rem;
    row-gap: 1rem;
}

The fr unit admits fractional size — divides remaining space proportionally.

The repeat(auto-fit, minmax(200px, 1fr)) admits responsive grid — fills as many columns as fit, each at least 200px:

.gallery {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
    gap: 1rem;
}

Grid items

.item {
    /* By line numbers: */
    grid-column: 1 / 3;                            /* span columns 1 to 3 */
    grid-column: 1 / span 2;                       /* start at 1, span 2 */
    grid-column-start: 1;
    grid-column-end: 3;

    grid-row: 2 / 4;
    grid-row: span 2;

    /* Shorthand: */
    grid-area: 1 / 2 / 3 / 4;                      /* row-start / col-start / row-end / col-end */
}

Named grid areas

.container {
    display: grid;
    grid-template-areas:
        "header header header"
        "sidebar main main"
        "footer footer footer";
    grid-template-columns: 200px 1fr 1fr;
    grid-template-rows: auto 1fr auto;
    min-height: 100vh;
}

.header { grid-area: header; }
.sidebar { grid-area: sidebar; }
.main { grid-area: main; }
.footer { grid-area: footer; }

The mechanism admits substantial visual layout in CSS — the template strings look like the layout.

Grid alignment

.container {
    display: grid;

    /* Items within their cells: */
    justify-items: stretch;                        /* horizontal, default */
    align-items: stretch;                          /* vertical, default */
    place-items: center;                           /* shorthand */

    /* The grid within the container: */
    justify-content: start;                        /* horizontal */
    align-content: start;                          /* vertical */
    place-content: center;                         /* shorthand */
}

.item {
    justify-self: end;                             /* per-item horizontal */
    align-self: center;                            /* per-item vertical */
    place-self: center end;                        /* shorthand: align-self justify-self */
}

Implicit grid

When items overflow the explicit grid:

.container {
    display: grid;
    grid-template-columns: repeat(3, 1fr);

    /* Implicit row sizes: */
    grid-auto-rows: minmax(100px, auto);

    /* Direction of auto-placement: */
    grid-auto-flow: row;                           /* default */
    grid-auto-flow: column;
    grid-auto-flow: dense;                         /* fill gaps */
}

Grid common patterns

Holy Grail layout

.layout {
    display: grid;
    grid-template-areas:
        "header  header  header"
        "nav     main    aside"
        "footer  footer  footer";
    grid-template-columns: 200px 1fr 200px;
    grid-template-rows: auto 1fr auto;
    min-height: 100vh;
}

.header { grid-area: header; }
.nav { grid-area: nav; }
.main { grid-area: main; }
.aside { grid-area: aside; }
.footer { grid-area: footer; }

Responsive card grid

.gallery {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
    gap: 1rem;
}

The auto-fit admits as many columns as fit; each card is min 250px, growing equally.

auto-fill vs auto-fit

.with-auto-fill {
    grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
    /* Empty tracks remain at minmax min when fewer items */
}

.with-auto-fit {
    grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
    /* Empty tracks collapse; remaining items grow */
}

The conventional contemporary choice is auto-fit — admits substantial filling of available space.

Form layout

.form {
    display: grid;
    grid-template-columns: max-content 1fr;
    gap: 1rem;
    align-items: center;
}

label { justify-self: end; }
.gallery {
    display: grid;
    grid-template-columns: repeat(4, 1fr);
    grid-auto-rows: 200px;
    gap: 0.5rem;
}

.featured {
    grid-column: span 2;
    grid-row: span 2;
}

Subgrid (modern)

.parent {
    display: grid;
    grid-template-columns: 1fr 2fr 1fr;
}

.child {
    grid-column: 1 / -1;                           /* full width */
    display: grid;
    grid-template-columns: subgrid;                /* inherit parent's columns */
}

The subgrid admits substantial alignment of nested grid children with the parent.

Centred page

.page {
    display: grid;
    place-items: center;
    min-height: 100vh;
}

The place-items: center admits both horizontal and vertical centring.

Dashboard

.dashboard {
    display: grid;
    grid-template-columns: repeat(12, 1fr);
    gap: 1rem;
}

.widget-1 { grid-column: span 6; }
.widget-2 { grid-column: span 3; }
.widget-3 { grid-column: span 3; }
.widget-large { grid-column: span 12; }

The 12-column grid admits substantial flexibility — substantial parallel to traditional grid frameworks.

Container queries

Container queries admit responsive design based on parent size:

.card {
    container-type: inline-size;
    container-name: card;
}

@container card (min-width: 400px) {
    .card__title {
        font-size: 1.5rem;
    }
}

@container (min-width: 600px) {
    .card {
        display: grid;
        grid-template-columns: 1fr 2fr;
    }
}

The mechanism admits substantial component-driven responsive design — substantial improvement over media-query-only approaches.

Choosing Flexbox vs Grid

The conventional discipline:

  • Flexbox — for one-dimensional layout (single row or column).
  • Grid — for two-dimensional layout (rows and columns simultaneously).
  • Flexbox — when items have variable sizes and should adapt to content.
  • Grid — when overall layout structure matters; substantial alignment across both axes.

Examples:

UseFlexboxGrid
Navigation bar
Button group
Card row
Page layout
Dashboard
Image gallery
Form layout
Sidebar + main

Often both are used together — Grid for page structure, Flexbox for components within.

Common patterns

Centring (multiple methods)

/* Flexbox: */
.center {
    display: flex;
    justify-content: center;
    align-items: center;
}

/* Grid: */
.center {
    display: grid;
    place-items: center;
}

/* Margin auto: */
.center {
    width: 800px;
    margin: 0 auto;                                /* horizontal only */
}

Two-column with stack on mobile

.layout {
    display: grid;
    gap: 1rem;
    grid-template-columns: 1fr;                    /* stacked default */
}

@media (min-width: 768px) {
    .layout {
        grid-template-columns: 250px 1fr;           /* sidebar + main */
    }
}

Equal-height cards

.cards {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
    gap: 1rem;
}

.card {
    display: flex;
    flex-direction: column;
}

.card__body {
    flex: 1;                                       /* push footer to bottom */
}

Sticky header with grid

.layout {
    display: grid;
    grid-template-rows: auto 1fr;
    min-height: 100vh;
}

.header {
    position: sticky;
    top: 0;
    z-index: 10;
    background: white;
}

Masonry-like layout

CSS Grid does not natively admit masonry until Grid Level 3; the conventional substitute uses Flexbox columns:

.masonry {
    columns: 250px;                                /* multi-column layout */
    column-gap: 1rem;
}

.masonry > * {
    margin-bottom: 1rem;
    break-inside: avoid;                           /* don't split items */
}

Holy Grail (modern)

body {
    display: grid;
    grid-template-rows: auto 1fr auto;
    min-height: 100vh;
}

main {
    display: grid;
    grid-template-columns: 200px 1fr 200px;
    gap: 1rem;
}

@media (max-width: 768px) {
    main {
        grid-template-columns: 1fr;
    }
}

Magazine layout

.article {
    display: grid;
    grid-template-columns: 1fr min(65ch, 100%) 1fr;
}

.article > * {
    grid-column: 2;                                /* default to centre column */
}

.article > .full-bleed {
    grid-column: 1 / -1;                           /* full width */
}

The pattern admits substantial readable text width with full-bleed images.

Auto-grid utility

.auto-grid {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(min(100%, var(--min-size, 250px)), 1fr));
    gap: var(--gap, 1rem);
}
<div class="auto-grid" style="--min-size: 200px; --gap: 0.5rem;">
    <!-- items -->
</div>

A note on the conventional discipline

The contemporary CSS layout advice:

  • Use Flexbox for one-dimensional layouts.
  • Use Grid for two-dimensional layouts.
  • Use gap over margins for spacing in flex/grid.
  • Use repeat(auto-fit, minmax(...)) for responsive grids.
  • Use named grid-template-areas for substantial readable layout.
  • Use container queries for component-level responsive design.
  • Use min-height: 100vh (or 100dvh) for full-viewport sections.
  • Use place-items: center for substantial centring.
  • Use logical properties (block/inline) for internationalisation.
  • Use Grid for page structure, Flexbox for components — they compose well.

The combination — Flexbox for one-axis, Grid for two-axis, the substantial alignment properties, container queries for component-level responsiveness, the subgrid for substantial nested alignment — is the substance of CSS’s modern layout surface. The discipline produces substantial responsive layouts with substantial declarative control over spatial arrangement.