Episode 1 — Fundamentals / 1.8 — CSS Layout Mastery
Interview Questions: CSS Layout Mastery
Practice questions with model answers for Flexbox, CSS Grid, positioning, z-index / stacking contexts, and container queries — topics commonly asked in front-end and full-stack interviews.
How to use this material (instructions)
- Read lessons in order —
README.md, then1.8.a→1.8.g. - Practice out loud — aim for 1–2 minutes per question, then compare to the model answer.
- Structure answers — definition → example → trade-off / when to use.
- Pair with exercises —
1.8-Exercise-Questions.md. - Quick review —
1.8-Quick-Revision.md.
Beginner (Q1–Q7)
Q1. What is the difference between Flexbox and CSS Grid?
Why interviewers ask: The most fundamental layout question — tests whether you understand the design intent of each tool.
Model answer:
| Flexbox | CSS Grid | |
|---|---|---|
| Dimensions | One-dimensional — row OR column | Two-dimensional — rows AND columns |
| Content vs layout | Content-driven — items determine their size, flex distributes remaining space | Layout-driven — you define the grid structure, items are placed into it |
| Best for | Component-level alignment (nav items, card content, button groups) | Page-level structure (header + sidebar + main + footer) |
| Wrapping | flex-wrap creates new flex lines, but lines are independent | Rows and columns are coordinated — items can span across both |
Rule of thumb: Flex for components, Grid for page layouts. In practice, you combine both — Grid for the page skeleton, Flex for the internals of each region.
Q2. How do you center a div both horizontally and vertically?
Why interviewers ask: Classic CSS question — tests practical knowledge and whether you know modern approaches.
Model answer:
The simplest modern approaches:
/* Flexbox */
.parent {
display: flex;
justify-content: center;
align-items: center;
}
/* Grid (shortest) */
.parent {
display: grid;
place-items: center;
}
Both require the parent to have a defined height (e.g. min-height: 100vh). The Grid approach is the shortest — place-items: center is a shorthand for align-items: center + justify-items: center.
Legacy approach: position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%) — still works but is more verbose and requires a positioned parent.
Q3. What does flex: 1 mean?
Why interviewers ask: Tests understanding of the flex shorthand — many developers use it without knowing what it expands to.
Model answer:
flex: 1 expands to flex-grow: 1; flex-shrink: 1; flex-basis: 0%.
| Component | Value | Meaning |
|---|---|---|
flex-grow | 1 | Item takes an equal share of extra space |
flex-shrink | 1 | Item can shrink if space is tight |
flex-basis | 0% | Starting size is zero — all space is distributed by flex-grow |
Because the basis is 0%, all items with flex: 1 get equal width regardless of content. Compare to flex: auto (1 1 auto) where items grow from their content size, so wider content gets more space.
Q4. What is the difference between justify-content and align-items?
Why interviewers ask: Confusing these two is one of the most common flexbox mistakes.
Model answer:
| Property | Controls | Axis (in flex-direction: row) |
|---|---|---|
justify-content | Distribution along the main axis | Horizontal |
align-items | Alignment along the cross axis | Vertical |
The key insight: these properties reference the main and cross axes, not "horizontal" and "vertical." When flex-direction: column, justify-content controls vertical distribution and align-items controls horizontal alignment — the axes swap.
Q5. What is the fr unit in CSS Grid?
Why interviewers ask: Tests understanding of Grid's fundamental sizing unit.
Model answer:
fr stands for fraction of available space. After all fixed-size tracks (px, rem, etc.) are resolved and gaps are subtracted, the remaining space is divided among fr tracks proportionally.
grid-template-columns: 200px 1fr 2fr;
/* 200px fixed, then remaining space split 1:2 */
fr is better than percentages because it automatically accounts for gap. With percentages, adding a 20px gap between three 33.33% columns causes overflow. With 1fr 1fr 1fr, the gap is subtracted before the fractions are calculated — no overflow.
Q6. Explain the five CSS position values.
Why interviewers ask: Positioning is foundational — misunderstanding it causes layout bugs.
Model answer:
| Value | In flow? | Positioned relative to | Creates stacking context? |
|---|---|---|---|
static | Yes | N/A — offsets have no effect | No |
relative | Yes | Its own normal position | With z-index |
absolute | No | Nearest positioned ancestor | With z-index |
fixed | No | Viewport | Always |
sticky | Yes | Scroll threshold within parent | Always |
Practical uses: relative → containing blocks and small offsets. absolute → tooltips, badges, dropdowns. fixed → persistent headers, floating buttons. sticky → scroll-aware headers and sidebars.
Gotcha: transform, filter, or perspective on an ancestor changes the containing block for fixed elements — it stops being relative to the viewport.
Q7. How do you make a footer stick to the bottom of the page?
Why interviewers ask: A common practical problem that tests layout thinking.
Model answer:
The footer should sit at the bottom of the viewport when content is short, and below the content when content is long.
body {
display: grid;
grid-template-rows: auto 1fr auto;
min-height: 100vh;
}
The 1fr row (main content) absorbs all extra space, pushing the footer down. The flexbox equivalent uses flex: 1 on the main element with flex-direction: column on the body.
The key is min-height: 100vh — without it, the container collapses to content height and there is no extra space to push the footer down.
Intermediate (Q8–Q13)
Q8. Explain auto-fill vs auto-fit in CSS Grid.
Why interviewers ask: Tests nuanced Grid knowledge — these two look similar but behave differently.
Model answer:
Both create as many columns as will fit. They differ when there are fewer items than available columns:
auto-fill: Creates empty tracks → items stay at their minimum size.auto-fit: Collapses empty tracks to zero → items stretch to fill the container.
/* Same container, 2 items, 1000px wide */
repeat(auto-fill, minmax(200px, 1fr)) /* 5 columns, 3 empty, items = 200px */
repeat(auto-fit, minmax(200px, 1fr)) /* 2 columns stretched, items = 500px */
When to use: auto-fit when you want items to stretch (most cases). auto-fill when you want consistent column widths regardless of how many items exist.
Q9. What is a stacking context and why does z-index: 9999 sometimes not work?
Why interviewers ask: One of the most confusing CSS topics — separates developers who truly understand layout from those who guess-and-check.
Model answer:
A stacking context is an invisible boundary for z-index competition. z-index only compares elements within the same stacking context — it is not a global layer system.
Why z-index: 9999 fails: If the element is inside a stacking context whose parent has a lower z-index than a sibling context, no z-index value on the child can escape above that sibling. The child is trapped inside its parent's context.
What creates stacking contexts: position + z-index, opacity < 1, transform, filter, isolation: isolate, position: fixed/sticky, flex/grid children with z-index.
Fix: Either restructure the DOM (move the element to a higher context), adjust ancestor z-index values, or use a portal pattern to render at the top of the DOM tree.
Q10. What is position: sticky and what are its common gotchas?
Why interviewers ask: Sticky is powerful but has surprising failure modes.
Model answer:
position: sticky behaves like relative until the element reaches a scroll threshold (defined by top, bottom, etc.), then it behaves like fixed — but only within its parent container's bounds.
.sticky-header {
position: sticky;
top: 0;
}
Common gotchas:
- No threshold set — without
top,bottom, etc., there is no trigger point and sticky does nothing. overflow: hidden/autoon an ancestor — changes the scrolling container, often breaking the sticky effect because the element's scroll context becomes the overflow ancestor, which may not scroll.- Parent height equals element height — sticky has no room to "stick" if its parent is exactly its own height.
- Parent ends — the element unsticks and scrolls away when it reaches the bottom of its parent.
Q11. How would you build a responsive card grid without media queries?
Why interviewers ask: Tests knowledge of intrinsic responsive design.
Model answer:
.card-grid {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(280px, 1fr));
gap: 24px;
}
This creates as many equal columns as will fit, each at least 280px. On a wide screen you get 4 columns; on mobile, 1 column — all without @media. The fr unit ensures leftover space is distributed evenly, and gap handles spacing without margin math.
Flexbox alternative:
.cards { display: flex; flex-wrap: wrap; gap: 16px; }
.card { flex: 1 1 280px; }
The Grid approach gives more consistent column widths. The Flex approach allows items to grow differently based on content.
Q12. What are container queries and when would you use them?
Why interviewers ask: Tests awareness of modern CSS features and component-driven thinking.
Model answer:
Container queries let a component style itself based on its container's size rather than the viewport. This solves a long-standing problem: a card component in a narrow sidebar should look different from the same card in a wide main area, regardless of viewport width.
.card-wrapper {
container-type: inline-size;
}
@container (min-width: 500px) {
.card { flex-direction: row; }
}
Use media queries for page-level layout (global breakpoints, mobile nav). Use container queries for reusable components that appear in multiple contexts (cards, widgets, data tables).
The two are complementary — container queries do not replace media queries.
Q13. Explain the flex shorthand. What is the difference between flex: 1, flex: auto, and flex: none?
Why interviewers ask: The flex shorthand has non-obvious defaults that trip up developers.
Model answer:
| Shorthand | Expands to | Behavior |
|---|---|---|
flex: 1 | 1 1 0% | Grow equally, shrink equally, start from zero (ignores content size) |
flex: auto | 1 1 auto | Grow from content size — wider content gets proportionally more space |
flex: none | 0 0 auto | Inflexible — item stays exactly at its content size |
flex: 0 1 auto | (the CSS default) | Won't grow, can shrink, starts at content size |
The critical difference between flex: 1 and flex: auto is the basis: 0% vs auto. With 0%, content size is ignored and space is divided purely by grow factors. With auto, content size is the starting point.
Advanced / Scenario (Q14–Q18)
Q14. A card grid has cards with different content lengths. Some cards have action buttons at the bottom, but the buttons don't align across the row. How do you fix this?
Why interviewers ask: Tests practical layout problem-solving with combined Grid + Flex.
Model answer:
Each card should use display: flex; flex-direction: column, and the content area above the button should have flex: 1. This pushes the button to the bottom of every card, and since Grid gives all cards in a row equal height, the buttons align.
.card-grid {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(280px, 1fr));
gap: 24px;
}
.card {
display: flex;
flex-direction: column;
}
.card .body {
flex: 1;
}
.card .actions {
margin-top: auto;
}
For even tighter alignment (e.g. aligning card titles, bodies, and footers across a row), use subgrid: each card becomes display: grid; grid-template-rows: subgrid; and inherits the parent's row tracks.
Q15. Why might a position: fixed element move when you scroll? Describe the bug and the fix.
Why interviewers ask: This is a real-world debugging question that reveals depth of CSS knowledge.
Model answer:
A position: fixed element should be fixed to the viewport. But if any ancestor has transform, filter, perspective, will-change: transform, or backdrop-filter, that ancestor becomes the containing block instead of the viewport. The "fixed" element is now fixed relative to that ancestor, not the viewport — so it scrolls with the ancestor.
Fix options:
- Remove the offending property from the ancestor.
- Move the fixed element outside the ancestor in the DOM (e.g. use a React portal to render it at the body level).
- If the ancestor property is necessary (e.g. for animation), restructure so the fixed element is not a descendant.
Q16. Design a z-index system for a complex web application.
Why interviewers ask: Tests architectural thinking — can you create maintainable systems, not just solve one-off problems.
Model answer:
Use CSS custom properties as semantic z-index tokens with gaps between values:
:root {
--z-base: 0;
--z-dropdown: 100;
--z-sticky: 200;
--z-overlay: 300;
--z-modal: 400;
--z-toast: 500;
--z-tooltip: 600;
}
Why this works:
- Semantic names make intent clear — no magic numbers.
- Gaps of 100 leave room for additions without renumbering.
- Single source of truth — search the codebase for
--z-to audit all stacking. - Use
isolation: isolateon component roots to prevent internal z-index from leaking out.
What to avoid: Arbitrary values like z-index: 9999 scattered through the codebase — this leads to z-index arms races.
Q17. How would you build a dashboard layout that works on both desktop and mobile without media queries?
Why interviewers ask: Tests whether you can leverage intrinsic responsive patterns.
Model answer:
Combine Grid's auto-fill / minmax with container queries:
.dashboard {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(320px, 1fr));
gap: 24px;
padding: 24px;
}
.widget {
container-type: inline-size;
}
@container (max-width: 400px) {
.chart { height: 200px; }
.data-table { font-size: 0.875rem; }
}
The grid handles column reflow (4 columns → 2 → 1). Container queries handle how each widget adapts its internal layout to its actual size. No breakpoints, no @media — the layout is fully intrinsic.
For the sidebar, use grid-template-columns: min(250px, 100%) 1fr — the sidebar collapses to full width on small screens.
Q18. Walk through how you would debug an element that is not appearing above another element despite having a higher z-index.
Why interviewers ask: Tests debugging methodology, not just knowledge.
Model answer:
Step 1: Verify z-index applies. Check if the element has position set (or is a flex/grid child). z-index has no effect on position: static elements in normal flow.
Step 2: Find the stacking context. Walk up the DOM from both elements and find which ancestor creates a stacking context for each. Look for position + z-index, transform, opacity < 1, filter, isolation: isolate.
Step 3: Compare contexts. If the two elements are in different stacking contexts, their individual z-index values do not compete. The stacking is determined by the z-index of their context ancestors.
Step 4: Use DevTools. Chrome's Layers panel (Ctrl+Shift+P → "Show Layers") visualizes stacking contexts as 3D layers. The Computed tab shows the resolved z-index value.
Step 5: Fix. Either restructure the DOM to put both elements in the same stacking context, adjust ancestor z-index values, or use a portal to render the element at a higher DOM level.
Quick-Fire (Yes / No + one line)
| Question | Answer | One-line rationale |
|---|---|---|
Can z-index work on a static element? | No (mostly) | Requires positioned, flex-child, or grid-child context. |
Does flex-direction: column change what justify-content controls? | Yes | justify-content always controls the main axis, which is now vertical. |
Is gap supported in both Flexbox and Grid? | Yes | Unified property — works in both layout models in all modern browsers. |
Does position: sticky remove the element from flow? | No | It stays in flow and preserves its space — unlike fixed and absolute. |
| Can container queries respond to height? | Yes | With container-type: size, but inline-size (width-only) is more common. |
Does auto-fit and auto-fill differ when all columns are filled? | No | They behave identically when there are enough items to fill all tracks. |
Is fr calculated before or after gap? | After | gap space is subtracted first, then remaining space is divided among fr tracks. |
Does transform on a parent affect position: fixed children? | Yes | It makes the parent the containing block, breaking viewport-relative behavior. |
Interview Tips
- Start with the mental model. "Flexbox is 1D, Grid is 2D" — then give examples.
- Draw it out. Sketch axes, grid lines, stacking contexts — visual answers are convincing.
- Name the trade-offs. "Grid gives consistent columns; Flex gives content-driven sizing."
- Mention the gotchas. Bringing up sticky pitfalls or stacking context side effects shows real experience.
- Connect to real patterns. "In our dashboard, we use Grid for the widget layout and Flex for each widget's internals."
- Know container queries. They are modern and show you keep up with CSS evolution.
- Have a z-index strategy. Token-based systems show architectural thinking.
- Debug out loud. Walk through your process: "First I would check if position is set, then find the stacking context…"
Use this alongside the 1.8 Exercise Questions for hands-on practice and deeper review.