Cheat SheetsHTML, CSS & ResponsiveLayout

Layout — Cheat Sheet

HTML, CSS & Responsive · 6 topics. Download the PDF or the Instagram carousel and share it.

Cheat Sheet · AiCanCode.org
Layout
HTML, CSS & Responsive6 topicsQuick revision reference
1

Display, Normal Flow and Formatting Contexts

Before flexbox and grid there is normal flow, and every layout question starts with which formatting context an element is in. This is the model that makes the rest predictable.

  • Inline elements ignore width, height and vertical margins; inline-block accepts them but inherits whitespace gaps
  • display: none removes an element from the accessibility tree, while .sr-only hides it visually but keeps it announced
  • A parent's display value decides which layout model its children follow, which is why flex and grid properties do nothing elsewhere
  • display: flow-root creates a block formatting context without the clipping side effects of overflow: hidden
  • Logical properties (margin-inline, block-size) mirror automatically for right-to-left languages
What inline ignores, and display: none vs sr-only
/* block — new line, fills the width, respects every box property */
div, p, h1, section, ul, li

/* inline — flows in the line, and IGNORES width, height and
   vertical margins. Horizontal padding works but does not push
   siblings apart vertically. */
span, a, strong, em, code
span { width: 200px }             /* has no effect */

/* inline-block — flows in the line, but accepts every box property */
.chip { display: inline-block; width: 6rem; padding: .5rem }

/* The mysterious gap between inline-block elements is the
   WHITESPACE in your HTML being rendered as a space character.
   Use flex with gap instead of fighting it. */

/* Two special values worth knowing */
display: none        /* removed from the layout AND from the
                        accessibility tree — screen readers skip it */
visibility: hidden   /* invisible but still occupies its space */
.sr-only             /* visually hidden but STILL announced —
                        the pattern for screen-reader-only text */

/* display: contents removes the box but keeps the children, useful
   for making a wrapper disappear from a grid — but it also removed
   semantics in older browsers, so avoid it on lists and tables. */
2

Flexbox

One-dimensional layout: a row or a column, with control over how the free space is distributed. It is the right tool for components, toolbars and anything that must adapt to its content.

  • justify-content works along the main axis and align-items along the cross axis — flex-direction swaps which is which
  • align-items: stretch is the default, which is why flex siblings become equal height for free
  • flex: 1 means grow from a zero basis, giving equal widths; flex: auto grows from the content width
  • min-width: 0 on a flex item is the fix for content that refuses to shrink and overflows the row
  • An auto margin absorbs all free space on that side, which pushes an item to the end without floats
Main axis vs cross axis
.row {
  display: flex;
  flex-direction: row;          /* main = horizontal, cross = vertical */
  justify-content: space-between;   /* along the MAIN axis */
  align-items: center;              /* along the CROSS axis */
  gap: 1rem;                        /* never margins between items */
}

/* Change the direction and both meanings swap: */
.col { flex-direction: column }   /* main = vertical, cross = horizontal */
/* now justify-content moves items UP and DOWN */

/* justify-content: flex-start | center | flex-end
                    space-between | space-around | space-evenly
   align-items:     stretch (default) | flex-start | center | flex-end
                    baseline                                        */

/* align-items: stretch is the default, which is why two cards in a
   row are automatically equal height — a genuinely useful default. */

/* Per-item override */
.logo { align-self: flex-start }

/* The classic header, in three lines */
header { display: flex; justify-content: space-between; align-items: center }
3

CSS Grid

Two-dimensional layout: you define rows and columns, and place items into them. It handles page structure and card grids that flexbox can only approximate.

  • fr distributes free space after fixed tracks and gaps, which is why it behaves where percentages overflow
  • minmax(0, 1fr) prevents wide content stretching a track, the grid equivalent of min-width: 0
  • repeat(auto-fit, minmax(min(16rem, 100%), 1fr)) gives a fully responsive card grid with no media queries
  • grid-template-areas expresses a page layout readably and can be redefined per breakpoint
  • Visual reordering does not change tab order — keep DOM order and visual order aligned for keyboard users
fr, repeat, minmax(0, 1fr), line placement
.grid {
  display: grid;
  grid-template-columns: 200px 1fr 200px;   /* three columns */
  grid-template-rows: auto 1fr auto;
  gap: 1rem;                                 /* row and column gap */
}

/* fr = a fraction of the FREE space, after fixed tracks and gaps.
   This is why 1fr behaves and 33.33% overflows once gap is added. */
grid-template-columns: 1fr 2fr;      /* one third, two thirds */
grid-template-columns: repeat(3, 1fr);
grid-template-columns: repeat(4, minmax(0, 1fr));   /* see below */

/* minmax(0, 1fr) instead of 1fr is the grid equivalent of the flex
   min-width: 0 fix — a track's default minimum is 'auto', so wide
   content stretches the column and breaks the layout. */

/* Placing items by line number (lines, not tracks — 4 lines for
   3 columns) */
.item { grid-column: 1 / 3 }        /* from line 1 to line 3 */
.item { grid-column: span 2 }       /* however many, span two */
.item { grid-column: 1 / -1 }       /* full width, whatever the count */
.item { grid-row: 2 / 4 }

/* Alignment works on both axes, with the same vocabulary */
place-items: center;                /* align-items + justify-items */
place-content: center;              /* the whole grid within the box */
4

Positioning — relative, absolute, fixed and sticky

Positioning takes an element out of the normal flow, or anchors it while scrolling. The recurring question is always the same: positioned relative to what?

  • An absolutely positioned element is measured against the nearest positioned ancestor, or the page if there is none
  • inset: 0 fills the containing block and is the shorthand for all four offsets
  • Sticky needs a threshold, an ancestor without overflow hidden/auto, and a parent taller than itself
  • A sticky header needs scroll-padding so anchor links do not land underneath it
  • Absolutely positioned menus are clipped by overflow and trapped by stacking contexts, which is why libraries use portals or the popover top layer
inset: 0, and the positioned ancestor
/* static — the default. top/left/z-index do nothing. */

/* relative — stays in the flow and reserves its original space,
   but is visually nudged. Its main job is to be a positioning
   ancestor for something absolute inside it. */
.wrapper { position: relative }

/* absolute — REMOVED from the flow, positioned against the nearest
   positioned ancestor (or the page if there is none). */
.badge { position: absolute; top: -.5rem; inset-inline-end: -.5rem }

/* fixed — removed from the flow, positioned against the VIEWPORT,
   so it does not scroll. */
.fab { position: fixed; inset-block-end: 1rem; inset-inline-end: 1rem }

/* sticky — in the flow until it hits its threshold, then it stays. */
thead th { position: sticky; top: 0 }

/* inset is the shorthand for all four offsets */
.overlay { position: absolute; inset: 0 }        /* fill the parent */
.modal { position: fixed; inset: 0; display: grid; place-items: center }

/* Centring something absolute */
.center { position: absolute; inset: 0; margin: auto; width: 200px;
          height: 100px }
5

Stacking Contexts and z-index

z-index only compares siblings within the same stacking context. That single rule explains why a z-index of 9999 can still render behind something set to 1.

  • z-index only compares elements within the same stacking context, which is why a huge value can still lose
  • transform, opacity below 1, filter and will-change all create a stacking context with no z-index involved
  • The fix for a trapped element is a portal or removing the context, never a larger number
  • isolation: isolate deliberately contains a component's z-indexes so they cannot leak
  • Native dialog and the popover attribute render in the top layer, above everything, sidestepping stacking entirely
Why 9999 loses to 2
<div class="a">        <!-- z-index: 1, creates a context -->
  <div class="modal">  <!-- z-index: 9999 -->
</div>
<div class="b">        <!-- z-index: 2 -->

.a { position: relative; z-index: 1 }
.modal { position: fixed; z-index: 9999 }
.b { position: relative; z-index: 2 }

/* .modal renders BEHIND .b.

   Because .a created a stacking context, .modal's 9999 only ranks it
   among .a's descendants. The whole of .a — modal included — is then
   placed at level 1, behind .b at level 2. No number inside .a can
   change that. */

/* The fix is never a bigger number. It is one of:
     - render the modal outside .a (a portal to document.body)
     - remove whatever made .a a stacking context
     - raise .a itself                                        */

/* Within one context, the paint order is:
     the element's background, negative z-index, block boxes,
     floats, inline content, z-index: 0/auto, then positive z-index */
6

Overflow and Scrolling

Content that does not fit has to go somewhere. Choosing deliberately — clip, scroll, wrap or truncate — and making the result keyboard-accessible is the difference between a bug and a feature.

  • overflow: hidden clips content with no way to reach it; auto is usually what was intended
  • Any non-visible overflow creates a formatting context and breaks position: sticky for descendants
  • A scrollable container needs tabindex and a label, or keyboard users cannot scroll it
  • width: 100vw includes the scrollbar and is the most common cause of an unexpected horizontal scrollbar
  • scroll-padding offsets anchors under a sticky header, and overscroll-behavior: contain stops scroll chaining out of a modal
auto vs hidden vs clip
overflow: visible   /* default — content spills, visibly */
overflow: hidden    /* clipped, NOT scrollable — even by keyboard */
overflow: scroll    /* always shows a scrollbar track */
overflow: auto      /* scrollbar only when needed — usually correct */
overflow: clip      /* clips without creating a scroll container */

/* Separate axes */
overflow-x: auto; overflow-y: hidden;
/* Note: setting one axis to a non-visible value forces the other to
   'auto' — you cannot have visible on one and hidden on the other. */

/* Side effects of overflow != visible, all of which bite:
     - creates a block formatting context (stops margin collapsing)
     - BREAKS position: sticky for descendants
     - becomes the containing block for scroll calculations       */

/* overflow: clip avoids the scroll-container side effects, which
   makes it the better choice for pure clipping: */
.mask { overflow: clip }

/* Text overflow */
.truncate { white-space: nowrap; overflow: hidden; text-overflow: ellipsis }
.clamp-3 { display: -webkit-box; -webkit-line-clamp: 3;
           -webkit-box-orient: vertical; overflow: hidden }
.break { overflow-wrap: break-word }      /* long URLs and tokens */
Learn this free with Aria, your AI tutor → AiCanCode.org/learn/html-css