CSS / LESSON 12

Display & positioning

Understand flow, Flexbox, Grid and where everything belongs.

Beginner25 min read · estimateUpdated Nov 1, 2026
In this lessonNormal flowdisplayThe box modelpositionz-index & stackingFlexboxGridCenteringTasks: how do I…?Common problemsUnits & sizingCheat sheet

1Normal flow

If you write no layout CSS at all, the browser still places things by a fixed rule called normal flow: block elements (div, p, h1, section, nav) stack top to bottom and each takes the full width. Inline elements (a, span, strong, img) sit side by side in a line and wrap like text.

Block elements stack. Inline elements share a line.
div (block)
div (block)
Text with span and a link in one line.
The whole game: every layout tool below either changes how elements take part in normal flow (display, flex, grid) or takes an element out of it (position). Decide which one you need before you write CSS.

2display

display decides what kind of box an element is, and how its children are laid out.

ValueBehavior
blockStarts a new line, fills the full width. Width, height, margin and padding all work.
inlineFlows inside a line of text. width and height do not apply to non-replaced inline elements, vertical margins do nothing.
inline-blockSits in a line like text, but accepts width, height, margin and padding.
noneRemoved completely: no space, not shown, not read by screen readers.
flexChildren line up in a row or column and you control spacing and alignment (section 6).
gridChildren are placed in rows and columns at the same time (section 7).
contentsThe element’s own box disappears and its children act as if they were in the parent.
inline vs inline-block vs block, all with width: 120px
inline inline-block block

display: none vs visibility: hidden vs opacity: 0

Takes spaceClickableUse for
display:noneNoNoMenus, tabs, hidden panels
visibility:hiddenYesNoHide without the layout jumping
opacity:0YesYesFade animations

3The box model

Every element is a box made of four layers, from inside out: content, padding (space inside the border), border, margin (space outside, between elements).

margin
border → padding
content (width × height)

Always set box-sizing: border-box

By default, width: 300px plus padding: 20px makes a box 340px wide, and this is the most common reason things overflow. With border-box, padding and border are counted inside the width.

*, *::before, *::after { box-sizing: border-box; }

Margin shorthand

margin: 10px;              /* all four sides */
margin: 10px 20px;         /* vertical | horizontal */
margin: 10px 20px 30px;    /* top | horizontal | bottom */
margin: 10px 20px 30px 40px; /* top right bottom left (clockwise) */
margin: 0 auto;            /* centers a block that has a width */
margin-left: auto;         /* pushes an element to the far right */
Margin collapsing: vertical margins between stacked blocks merge into the larger one (20px under 30px gives 30px, not 50px). Margins inside flex and grid containers never collapse. Padding never collapses.

4position

position controls whether an element stays in the normal flow. Together with top, right, bottom, left it nudges or pins it.

ValueIn flow?Offsets are measured from…Typical use
staticYesOffsets do nothing (default)Everything normal
relativeYes (keeps its space)Its own original spotSmall nudge, or to become an anchor for children
absoluteNoNearest ancestor establishing its containing blockBadges, corner buttons, dropdowns
fixedNoThe browser windowFloating buttons, always-visible bars
stickyYesThe scroll container, once you scroll past itHeaders, table headings, side menus
A useful starting point: an absolute element looks for the closest parent that has position: relative (or absolute, fixed, sticky). If none exists, it uses the whole page. So when an absolute element “jumps to the corner”, add position: relative to the parent you want it inside.

Try it

Page (outer, always positioned)
Parent
Sibling before
Target
Sibling after

Set the parent to static and watch the target measure from the outer page instead of its parent. Choose relative and notice the siblings do not move, because the original space is kept. Choose absolute and the siblings close the gap, because the box left the flow.

Offset shortcuts

inset: 0;                 /* top, right, bottom, left all 0: stretch to fill the parent */
position: absolute; top: 0; right: 0;     /* pin to top-right corner */
position: absolute; inset: 0; margin: auto; width: 200px; height: 100px; /* centered */

fixed

.chat-button { position: fixed; right: 20px; bottom: 20px; }

Stays in place while you scroll. Some ancestor properties can change its containing block; for example, if a parent has transform, filter or perspective, the fixed element measures from that parent instead.

sticky

Needs a threshold (such as top: 0) and room to move within its container. The page itself can provide the scrolling. Scroll the box below.

Sticky header (top: 0)

Row 1

Row 2

Row 3

Row 4

Row 5

Row 6

Row 7

Row 8

header { position: sticky; top: 0; z-index: 10; background: #fff; }
Sticky does nothing? Check that you set top, that no ancestor has overflow: hidden/auto/scroll (it becomes the scroll container), and that the parent is taller than the sticky element.

5z-index & stacking

z-index decides which overlapping element is on top. Higher wins. It only works on elements that are positioned (not static) or that are flex/grid children.

z-index: 2
z-index: 1
Why does a huge z-index lose? Within the same stacking context, higher values appear above lower ones. Some properties create a stacking context: position with a z-index, opacity below 1, transform, filter, isolation: isolate. A child cannot escape its parent’s stacking context. Compare the parent contexts, not just the child values.
/* a simple scale that avoids chaos */
--z-dropdown: 100;
--z-sticky: 200;
--z-modal: 300;
--z-toast: 400;

6Flexbox

Use flexbox to lay items out along one direction: navbars, button groups, cards in a row, a label next to an input. Set display: flex on the parent; its direct children become flex items.

.row { display: flex; gap: 16px; }

Two axes matter. The main axis is the direction of the row (horizontal by default). The cross axis is the other one. justify-content works on the main axis and align-items on the cross axis. If you flip to flex-direction: column, they swap.

Try it

1
2
3

Container properties

PropertyWhat it does
flex-directionrow | column | row-reverse | column-reverse
justify-contentDistributes items along the main axis
align-itemsAligns items on the cross axis
flex-wrapLets items drop to a new line instead of shrinking
gapSpace between items (use this instead of margins)
align-contentSpacing between lines when items wrapped

Item properties

PropertyWhat it does
flex: 1Item grows to take the free space. Two items with flex: 1 share it equally.
flex: 0 0 200pxDo not grow, do not shrink, start at 200px (a fixed-width item).
flex-shrink: 0Never get squeezed (good for logos and icons).
align-selfOverride align-items for a single item.
orderChange visual order without touching the HTML.
margin-left: autoAbsorbs all free space on that side: pushes the item (and everything after it) to the end.
Text overflowing a flex item? Flex items refuse to shrink below their content. Add min-width: 0 to the item and the text can wrap or truncate.

7Grid

Use grid when you need rows and columns together: page layouts, card galleries, dashboards, forms. Set display: grid on the parent and define the columns.

1fr
2fr
1fr
spans 2 columns
1fr
.grid {
  display: grid;
  grid-template-columns: 1fr 2fr 1fr;   /* three columns, middle twice as wide */
  gap: 16px;
}
.wide { grid-column: 1 / 3; }           /* from line 1 to line 3 = 2 columns */

Responsive cards with no media query

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

Each card is at least 240px wide, and the browser fits as many per row as it can.

Page layout with named areas

.page {
  display: grid;
  grid-template-columns: 240px 1fr;
  grid-template-rows: auto 1fr auto;
  grid-template-areas:
    "header header"
    "side   main"
    "footer footer";
  min-height: 100vh;
}
header { grid-area: header; }
aside  { grid-area: side; }
main   { grid-area: main; }
footer { grid-area: footer; }
Flex or grid? One direction, content decides the size: flex. Two directions, layout decides the size: grid. They combine well: grid for the page, flex inside each component.

8Centering

/* 1. Center anything, horizontally and vertically (best default) */
.parent { display: grid; place-items: center; }

/* 2. Same with flex */
.parent { display: flex; justify-content: center; align-items: center; }

/* 3. Center a block horizontally only */
.box { width: 600px; max-width: 100%; margin: 0 auto; }

/* 4. Center text */
.text { text-align: center; }

/* 5. Center an absolutely positioned element */
.box { position: absolute; inset: 0; margin: auto; width: 200px; height: 100px; }

/* 6. Center on the full screen */
.parent { min-height: 100vh; display: grid; place-items: center; }
Vertical centering needs a parent with a height. If the parent is only as tall as its content, there is nothing to center inside.

9Tasks: how do I…?

Put the logo on the left and the links on the right (navbar)

Flex on the nav, then let the space between do the work.

<nav class="nav"><a class="logo">DECT</a><ul class="links">…</ul></nav>

.nav   { display: flex; justify-content: space-between; align-items: center; padding: 16px 32px; }
.links { display: flex; gap: 24px; list-style: none; margin: 0; padding: 0; }

Put the logo left, links in the center, a button right

Three-column grid, where the middle column stays truly centered.

.nav { display: grid; grid-template-columns: 1fr auto 1fr; align-items: center; }
.nav .cta { justify-self: end; }

Keep the navbar at the top while scrolling

.nav { position: sticky; top: 0; z-index: 100; background: #fff; }

Push one link to the far right

Auto margin on that item.

.links { display: flex; gap: 16px; }
.links .login { margin-left: auto; }

Put a button in the top-right corner of a card

The card is the anchor (relative); the button is pinned (absolute).

.card  { position: relative; padding: 24px; }
.close { position: absolute; top: 12px; right: 12px; }

Put a notification badge on an icon

.icon  { position: relative; display: inline-block; }
.badge { position: absolute; top: -6px; right: -6px; }

Make a floating button stay in the bottom-right corner

.fab { position: fixed; right: 20px; bottom: 20px; z-index: 50; }

Show a dropdown menu under a link

This CSS illustrates placement only. A complete dropdown also needs a keyboard- and touch-operable button, focus handling and an expanded state.

.item     { position: relative; }
.dropdown { position: absolute; top: 100%; left: 0; display: none; min-width: 180px; z-index: 100; }
.item:hover .dropdown { display: block; }

Build a modal with a dark overlay

This is a layout example, not a complete accessible modal. A real modal also needs focus management, a clear close action and Escape-key handling; consider the native dialog element.

.overlay { position: fixed; inset: 0; background: rgba(0,0,0,.5); display: grid; place-items: center; z-index: 300; }
.modal   { background: #fff; padding: 24px; width: min(480px, 92vw); border-radius: 8px; }

Keep the footer at the bottom when the page is short

body { min-height: 100vh; display: flex; flex-direction: column; margin: 0; }
main { flex: 1; }   /* takes all leftover space, footer is pushed down */

Make a sidebar plus main content

.layout  { display: grid; grid-template-columns: 260px 1fr; gap: 24px; }
.sidebar { position: sticky; top: 16px; align-self: start; }

Make cards the same height, with the button at the bottom

.cards { display: grid; grid-template-columns: repeat(auto-fit, minmax(240px, 1fr)); gap: 20px; }
.card  { display: flex; flex-direction: column; }
.card .btn { margin-top: auto; }

Place two buttons side by side with a space

.actions { display: flex; gap: 12px; }

Make a full-screen hero with centered text

.hero { min-height: 100vh; display: grid; place-items: center; text-align: center; }

Stack columns on mobile, side by side on desktop

.row { display: flex; flex-direction: column; gap: 20px; }
@media (min-width: 768px) { .row { flex-direction: row; } .row > * { flex: 1; } }

Hide the menu on mobile and show a burger button

.burger { display: none; }
@media (max-width: 767px) { .links { display: none; } .burger { display: block; } }

Put text over an image

.hero { position: relative; }
.hero img { width: 100%; display: block; }
.hero .text { position: absolute; left: 24px; bottom: 24px; color: #fff; }

10Common problems

SymptomCauseFix
Absolute element jumps to the page cornerNo positioned parentposition: relative on the parent
Parent collapses to zero heightAll children are absolute or floatedGive the parent a height, or keep one child in flow
z-index has no effectElement is static, or in a different stacking contextAdd position: relative; raise the parent’s z-index
Horizontal scrollbar appearsSomething is wider than the screen (padding with content-box, 100vw, long words)box-sizing: border-box; use width: 100%; overflow-wrap: anywhere
Width and height do nothingElement is inline (span, a)display: inline-block or block
Margin-top on a child moves the parentMargin collapsingUse padding on the parent, or display: flow-root
Small gap under an imageImages are inline and sit on the text baselineimg { display: block; }
Sticky doesn’t stickMissing top, or an ancestor has overflow setAdd top: 0; remove overflow from ancestors
Flex item overflows with long textItems won’t shrink below content sizemin-width: 0 on the item
100vh is too tall on phonesThe browser toolbar counts in 100vhUse 100dvh
Fixed element not fixedAn ancestor has transform or filterMove the element outside, or remove the transform
Centering fails verticallyParent has no heightGive it min-height

Debug routine

/* 1. Show every box */
* { outline: 1px solid rgba(255,0,0,.4); }

/* 2. In the browser: right-click → Inspect → hover the element
      The colors show margin, border, padding and content.
   3. Check the "Computed" tab for the real display and position. */

11Units & sizing

UnitMeaningUse for
pxFixed pixelsBorders, shadows, small details
%Percent of the parentFluid widths
remMultiple of the root font size (often 16px)Font sizes, spacing
emMultiple of the element’s font sizePadding that scales with text
vw / vh1% of the viewport width / heightFull-screen sections
dvhViewport height that adapts to mobile toolbarsFull-height on phones
frShare of free space in gridGrid columns
width: min(1100px, 92%);               /* never wider than 1100px, never touching the edges */
font-size: clamp(1rem, 2.5vw, 1.5rem); /* min | preferred | max */
.container { max-width: 1100px; margin-inline: auto; padding-inline: 20px; }
img { max-width: 100%; height: auto; }  /* images never overflow */

12Cheat sheet

I want to…Use
Arrange items in a row or columndisplay: flex + gap
Build rows and columnsdisplay: grid
Center anythingdisplay: grid; place-items: center
Center a block horizontallymargin: 0 auto + a width
Space items apartgap (parent) rather than margins on children
Push an item to the endmargin-left: auto (flex)
Pin to a corner of its parentparent relative + child absolute
Pin to the screenposition: fixed
Stick while scrollingposition: sticky; top: 0
Cover an area completelyposition: absolute; inset: 0
Show or hidedisplay: none / block
Change which overlaps whichz-index on a positioned element
Keep padding inside the widthbox-sizing: border-box
Order of thinking: 1) Is it normal flow? Leave it alone. 2) Items in a line or a grid? Flex or grid on the parent. 3) Does it need to overlap or stay on screen? Then, and only then, use position.

Further reading

MDN CSS reference · W3C CSS resources

ONE STEP FURTHER

Small steps. Real understanding.

Try the example in your own project before moving on.

Progress is saved only in this browser.