Learn Frontend Part 3: CSS Fundamentals - Cascade, Specificity, Box Model and Positioning

Learn Frontend Part 3: CSS Fundamentals - CodingHumans

CSS looks simple until a style "mysteriously" refuses to apply, an element overflows its box, or a z-index of 9999 still sits behind something. Nearly all of these surprises come from a few core rules: the cascade, specificity, inheritance, the box model and positioning. Understanding them turns CSS from trial and error into something predictable.

This is Part 3 of the Learn Frontend series. Layout with Flexbox and Grid follows in Part 4.

1. The cascade: which rule wins?

When several rules set the same property on the same element, the browser decides the winner in this order:

  1. Origin and importance: user-agent (browser) styles, then your styles; !important declarations override normal ones.
  2. Cascade layers (@layer), if you use them: later layers win.
  3. Specificity: the more specific selector wins.
  4. Order of appearance: if everything else ties, the rule that comes last wins.

Specificity is counted as three numbers (IDs, classes/attributes/pseudo-classes, elements/pseudo-elements) and compared left to right. Inline style attributes beat all selectors.

/* Specificity: (IDs, classes, elements) */
p                  { color: black; }   /* (0, 0, 1) */
.note              { color: blue; }    /* (0, 1, 0)  beats p            */
article p.note     { color: green; }   /* (0, 1, 2)  beats .note        */
#intro .note       { color: red; }     /* (1, 1, 0)  beats all of above */
:where(#intro) p   { color: gray; }    /* (0, 0, 1)  :where adds nothing */
Tip: keep specificity low and flat (mostly single classes). Fighting specificity with IDs and !important is the main cause of unmaintainable CSS.

2. Inheritance and the computed value

Some properties are inherited by children automatically, mainly typography: color, font-family, font-size, line-height, text-align. Layout properties such as margin, padding, border, width and background are not inherited.

You can control this explicitly with the keywords inherit, initial, unset and revert. DevTools' Computed panel shows the final value of every property and which rule produced it, which is the fastest way to debug "why is this style not applied?".

3. The box model

Every element is a rectangle made of content, padding, border and margin. By default (box-sizing: content-box), width sets only the content, so padding and border are added on top and a 100%-wide box with padding overflows its parent. Almost every modern codebase switches to border-box, where width includes padding and border.

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

.card {
  width: 300px;           /* total width stays 300px */
  padding: 16px;          /* included inside the 300px */
  border: 1px solid #ddd; /* also included */
}
  • Margin collapsing: vertical margins between block siblings collapse to the larger one rather than adding up. It does not happen inside flex or grid containers.
  • display decides how the box behaves: block (full width, new line), inline (flows with text; width and vertical margins ignored), inline-block (flows inline but respects size), flex, grid, none (removed from layout).
  • visibility: hidden hides an element but keeps its space; display: none removes it from layout entirely.

4. Units and sizing

UnitRelative toTypical use
pxFixed (CSS pixels)Borders, small fixed details
remRoot font sizeFont sizes and spacing that scale with user settings
emThe element's own font sizePadding that should grow with text size
%The parent (for width)Fluid widths
vw / vh / dvhViewport sizeFull-screen sections (dvh handles mobile browser bars)
chWidth of the "0" characterReadable text widths, such as max-width: 65ch

Functions like min(), max() and clamp() make values responsive without media queries, for example font-size: clamp(1rem, 2.5vw, 1.5rem).

5. Positioning and stacking

  • static: the default, normal flow.
  • relative: stays in flow but can be nudged with top/left; also becomes the reference for absolutely positioned children.
  • absolute: removed from flow and placed relative to the nearest positioned ancestor.
  • fixed: placed relative to the viewport; stays put while scrolling.
  • sticky: behaves like relative until a scroll threshold, then sticks like fixed within its container.

z-index only works on positioned elements (and flex/grid items), and it only competes within the same stacking context. Properties such as opacity below 1, transform, filter and position: fixed create new stacking contexts, which is why a child with z-index 9999 can still appear below an element outside its parent.

6. Modern CSS you should know

  • Custom properties (CSS variables): define design tokens once and reuse them; they cascade and can change at runtime, which makes theming and dark mode easy.
  • Pseudo-classes: :hover, :focus-visible, :nth-child(), :not(), :is(), :where() (zero specificity) and :has(), the "parent selector".
  • Pseudo-elements: ::before, ::after, ::placeholder, ::selection.
  • Transitions and transforms: animate transform and opacity for smooth, GPU-friendly motion (Part 9 explains why).
  • Nesting is now supported natively in modern browsers, reducing the need for preprocessors.
:root {
  --bg: #ffffff;
  --text: #1f2937;
  --brand: #3B37B0;
  --radius: 12px;
}
@media (prefers-color-scheme: dark) {
  :root { --bg: #0f172a; --text: #e5e7eb; }
}
.button {
  background: var(--brand);
  color: #fff;
  border-radius: var(--radius);
  transition: transform 0.15s ease;
}
.button:hover { transform: translateY(-2px); }
.button:focus-visible { outline: 3px solid var(--text); outline-offset: 2px; }
Key takeaways
  • The cascade decides conflicts by importance, layers, specificity and finally source order.
  • Keep specificity low and flat; avoid ID selectors and !important for everyday styling.
  • Typography properties inherit; layout properties do not. Use DevTools Computed to debug.
  • Use box-sizing: border-box everywhere, and remember vertical margins collapse in normal flow.
  • z-index works within stacking contexts; transform, opacity and fixed positioning create new ones.

Practice problems

Try these exercises in a small HTML file or on CodePen, using DevTools to check your understanding.

  1. Write four rules targeting the same element and predict which colour wins; then check in DevTools.
  2. Create a 100%-wide box with padding and observe the overflow with content-box, then fix it with border-box.
  3. Make two stacked paragraphs with margins of 20px and 30px and measure the gap to see margin collapsing.
  4. Build a sticky header and a fixed "back to top" button on a long page.
  5. Reproduce a z-index bug with a transformed parent, then fix it by changing the stacking context.
  6. Build a light and dark theme using only CSS custom properties and prefers-color-scheme.

Interview questions and FAQ

How is CSS specificity calculated?

Count ID selectors, then class, attribute and pseudo-class selectors, then element and pseudo-element selectors, and compare those three numbers from left to right. Inline styles beat any selector, and !important overrides normal declarations.

What is the difference between content-box and border-box?

With content-box, width and height apply only to the content, so padding and border are added on top. With border-box, width and height include padding and border, which makes sizing far more predictable.

Why is my z-index not working?

Either the element is not positioned (static), or it is inside a different stacking context than the element it should cover. Parents with transform, opacity below 1, filter or certain position values create new stacking contexts.

What is the difference between display: none and visibility: hidden?

display: none removes the element from the layout and the accessibility tree. visibility: hidden hides it visually but keeps its space in the layout.

What is the difference between em and rem?

em is relative to the current element's font size, so it compounds when nested. rem is always relative to the root element's font size, which makes it predictable for spacing and typography.

Test yourself with MCQs: HTML5 and CSS3 MCQs | Web Development MCQs

Comments

Answered 0/0Correct 0