Episode 1 — Fundamentals / 1.6 — CSS Core Fundamentals
Interview Questions: CSS Core Fundamentals
Practice questions with model answers for CSS selectors, specificity, cascade, inheritance, the box model, units, responsive functions, custom properties, design systems, and design tokens — topics commonly asked in front-end and full-stack interviews.
How to use this material (instructions)
- Read lessons in order —
README.md, then1.6.a→1.6.i. - Practice out loud — aim for 1–2 minutes per question, then compare to the model answer.
- Structure answers — definition → example → trade-off / accessibility note.
- Pair with exercises —
1.6-Exercise-Questions.md. - Quick review —
1.6-Quick-Revision.md.
Beginner (Q1–Q8)
Q1. What are CSS selectors and what types do you know?
Why interviewers ask: Foundational knowledge — tests whether you understand how CSS targets elements.
Model answer:
A CSS selector is the pattern before the { } block that determines which elements the declarations apply to. Major types:
| Type | Example | What it targets |
|---|---|---|
| Element (type) | p { } | All <p> elements |
| Class | .card { } | Elements with class="card" |
| ID | #hero { } | The element with id="hero" |
| Universal | * { } | Every element |
| Attribute | [type="email"] { } | Elements with that attribute value |
| Descendant | nav a { } | <a> anywhere inside <nav> |
| Child | ul > li { } | Direct <li> children of <ul> |
| Adjacent sibling | h2 + p { } | <p> immediately after <h2> |
| Pseudo-class | :hover, :focus, :nth-child() | State or position |
| Pseudo-element | ::before, ::after | Generated content |
Best practice: use classes as the primary styling hook — predictable, reusable, low specificity.
Q2. Explain CSS specificity and how it determines which styles apply.
Why interviewers ask: The #1 CSS debugging concept — if you don't understand specificity, you'll fight the cascade constantly.
Model answer:
Specificity is a weight tuple the browser calculates for each selector: (inline, IDs, classes/attrs/pseudo-classes, elements/pseudo-elements).
| Selector | Specificity | Explanation |
|---|---|---|
p | 0,0,0,1 | 1 element |
.card | 0,0,1,0 | 1 class |
#hero | 0,1,0,0 | 1 ID |
style="…" | 1,0,0,0 | Inline |
#sidebar .widget h3 | 0,1,1,1 | 1 ID + 1 class + 1 element |
Key rules:
- Categories never overflow — 1 ID beats 100 classes
- When specificity is equal, last rule in source order wins
!importantjumps the declaration to the top of the origin tier — avoid it unless necessary:where()has zero specificity;:is()takes the highest argument's specificity
Q3. What is the CSS cascade?
Why interviewers ask: Specificity is just one part — the cascade is the full conflict resolution system.
Model answer:
The cascade is the algorithm the browser uses to determine which CSS declaration wins when multiple rules target the same property on the same element. Resolution order (highest priority first):
- Origin & importance — transitions → user
!important→ author!important→ author normal → user normal → user-agent - Inline styles —
style="…"attribute - Cascade layers (
@layerorder — last declared layer wins) - Specificity — selector weight comparison
- Source order — later declaration wins (tie-breaker)
In daily work, most conflicts come down to specificity and source order. Knowing the full cascade explains why !important in a user stylesheet can override author styles (intentional for accessibility).
Q4. Explain the CSS box model.
Why interviewers ask: Every layout bug traces back to box model misunderstanding. This is asked in virtually every front-end interview.
Model answer:
Every element generates a rectangular box with four layers, from inside out:
- Content — text, images, children
- Padding — space between content and border (background fills it)
- Border — visible edge (has its own width, style, color)
- Margin — space outside the border (always transparent)
┌─────── Margin ───────┐
│ ┌───── Border ─────┐ │
│ │ ┌─── Padding ──┐ │ │
│ │ │ Content │ │ │
│ │ └──────────────┘ │ │
│ └──────────────────┘ │
└──────────────────────┘
The critical distinction — box-sizing:
| Value | width includes | An element with width: 200px; padding: 20px; border: 2px occupies… |
|---|---|---|
content-box (default) | Content only | 244px (200 + 40 padding + 4 border) |
border-box | Content + padding + border | 200px (content shrinks to fit) |
Always use: *, *::before, *::after { box-sizing: border-box; } — every modern reset does this.
Q5. What is the difference between margin and padding?
Model answer:
| Padding | Margin | |
|---|---|---|
| Position | Inside the border | Outside the border |
| Background | Fills with element background | Always transparent |
| Click area | Included in click/tap target | Not part of the element |
| Collapsing | Never collapses | Vertical margins collapse between siblings |
| Use when | You want space between content and border | You want space between elements |
Margin collapsing: when two adjacent block elements have vertical margins, the larger margin wins (they don't add). This only happens vertically, never horizontally, and never inside flex or grid containers.
Q6. What are the differences between px, rem, and em?
Why interviewers ask: Unit choice directly affects accessibility and responsive design — tests practical awareness.
Model answer:
| Unit | Relative to | Scales with user preference? | Best for |
|---|---|---|---|
px | Nothing (absolute) | No | Borders, shadows, thin details |
rem | :root font-size | Yes | Typography, spacing, layout |
em | Element's own font-size | Yes (but compounds) | Component-internal spacing |
Why rem matters for accessibility: users who set their browser font-size to 20px (for readability) expect text and spacing to scale. 1rem = user's chosen size. If you use px, the text ignores their preference — this violates WCAG 1.4.4.
em compounding pitfall: 1.2em inside 1.2em = 1.44× root — nested elements multiply. rem always references root, so it never compounds.
Q7. What is CSS inheritance?
Model answer:
Inheritance is how certain CSS property values flow from parent to child elements automatically. Properties related to text and typography inherit by default:
font-family,font-size,font-weight,line-heightcolor,text-align,letter-spacinglist-style,cursor,visibility
Properties related to layout and box do not inherit:
margin,padding,border,width,heightbackground,display,position,overflow
Why this design makes sense: setting color: navy on <body> should color all text — but giving every child the same margin would be chaotic.
Control keywords: inherit (force), initial (spec default), unset (inherit if inheritable, initial otherwise), revert (browser default).
Q8. What are CSS custom properties (variables)?
Model answer:
CSS custom properties are author-defined values prefixed with --:
:root {
--color-primary: #2563eb;
--spacing-md: 1rem;
}
.button {
background: var(--color-primary);
padding: var(--spacing-md);
}
Key features:
- Inherit down the DOM tree (like
color) - Override at any scope:
.dark { --color-primary: #60a5fa; } - Fallback:
var(--x, fallback-value) - Runtime-updatable via JavaScript:
el.style.setProperty('--x', val)
vs Sass/Less variables: preprocessor variables are compiled away at build time — they can't change at runtime, don't inherit, and can't be themed per-context. CSS custom properties are live in the browser.
Intermediate (Q9–Q15)
Q9. What is box-sizing: border-box and why is it used in every CSS reset?
Why interviewers ask: Tests whether you've worked with real CSS and understand sizing pain points.
Model answer:
With the default content-box, width sets only the content area — padding and border are added on top, making total element size unpredictable. A width: 50% column with padding doesn't fit alongside another 50% column.
border-box includes padding and border within the declared width — the math becomes intuitive. The universal reset applies this to all elements:
*, *::before, *::after {
box-sizing: border-box;
}
Without this reset: every width calculation must account for padding and border manually — a constant source of layout bugs, especially in responsive designs.
Q10. Explain clamp(), min(), and max() in CSS.
Why interviewers ask: Modern CSS literacy — tests whether you know current responsive techniques beyond media queries.
Model answer:
| Function | Returns | Plain English |
|---|---|---|
clamp(min, preferred, max) | Preferred value, clamped to bounds | "Be this, but never less than X or more than Y" |
min(a, b) | Smallest value | "Be A or B, whichever is smaller" (caps at max) |
max(a, b) | Largest value | "Be A or B, whichever is larger" (enforces floor) |
Real examples:
/* Fluid typography: scales with viewport, stays between 1rem and 2rem */
h1 { font-size: clamp(1rem, 2.5vw, 2rem); }
/* Container: never wider than 1200px, but shrinks on small screens */
.container { width: min(90%, 1200px); }
/* Sidebar: at least 250px, but grows to 25% on large screens */
.sidebar { width: max(250px, 25%); }
These functions accept mixed units (rem, vw, px, %) and eliminate many media queries.
Q11. What is margin collapsing?
Why interviewers ask: One of CSS's most surprising behaviors — tests depth of understanding.
Model answer:
When two adjacent block-level elements have vertical margins, the margins merge instead of adding — the larger margin wins:
Element A: margin-bottom: 30px
Element B: margin-top: 20px
Gap between them: 30px (not 50px)
When margins collapse:
- Adjacent siblings (vertical only)
- Parent and first/last child (if no border, padding, or BFC separates them)
- Empty blocks with no content, padding, or border
When they do NOT collapse:
- Horizontal margins (never)
- Flex or grid children
- Elements creating a BFC (
overflow: hidden/auto,display: flow-root) - Floated or absolutely positioned elements
Fix when unwanted: add padding or border on the parent, use display: flow-root, or switch to flexbox/grid.
Q12. Compare :is(), :where(), and :has().
Model answer:
| Pseudo-class | Purpose | Specificity |
|---|---|---|
:is() | Match any selector in list | Takes highest argument's weight |
:where() | Match any selector in list | Always zero |
:has() | Select element based on descendants | Takes argument's weight |
/* :is() — high specificity (takes #id weight) */
:is(#hero, .card) h2 { color: navy; }
/* :where() — zero specificity (easy to override) */
:where(article, section) p { line-height: 1.6; }
/* :has() — parent selector */
.card:has(img) { padding: 0; }
When to use each:
:is()— replacing repetitive selectors with a compact list:where()— setting defaults that consumers should easily override (resets, library styles):has()— styling a parent based on its children (previously required JS)
Q13. How do viewport units work, and what is the mobile 100vh problem?
Model answer:
Viewport units are percentages of the browser viewport:
| Unit | Meaning |
|---|---|
vw | 1% of viewport width |
vh | 1% of viewport height |
vmin | 1% of the smaller dimension |
vmax | 1% of the larger dimension |
The mobile 100vh problem: on mobile browsers, the address bar appears and disappears as the user scrolls. 100vh is calculated as the largest viewport (address bar hidden), so elements set to 100vh overflow the visible area when the address bar is showing.
Fix — dynamic viewport units:
| Unit | Adjusts with address bar? |
|---|---|
dvh | Yes — updates dynamically |
svh | No — always smallest viewport |
lvh | No — always largest viewport |
Use 100dvh for "fill the visible screen" on mobile.
Q14. How would you implement dark mode using CSS custom properties?
Why interviewers ask: Tests practical theming knowledge — a real-world requirement.
Model answer:
:root {
--color-bg: #ffffff;
--color-text: #1a1a2e;
--color-surface: #f1f5f9;
--color-primary: #2563eb;
}
[data-theme="dark"] {
--color-bg: #0f172a;
--color-text: #e2e8f0;
--color-surface: #1e293b;
--color-primary: #60a5fa;
}
body {
background-color: var(--color-bg);
color: var(--color-text);
}
Theme switching: toggle a data-theme attribute on <html> via JavaScript — every element using these variables updates instantly through inheritance.
Respecting user preference:
@media (prefers-color-scheme: dark) {
:root {
--color-bg: #0f172a;
--color-text: #e2e8f0;
}
}
This approach uses zero !important, requires no component CSS changes, and the toggle is one attribute swap.
Q15. What is a design token?
Model answer:
A design token is a named, platform-agnostic design decision — the atomic unit of a design system:
| Token example | Category | Value |
|---|---|---|
color-primary | Color | #2563eb |
space-md | Spacing | 1rem |
radius-sm | Border radius | 0.25rem |
font-body | Typography | system-ui, sans-serif |
Three tiers:
Primitive: --blue-500: #2563eb ← raw palette value
Semantic: --color-primary: var(--blue-500) ← maps to a role
Component: --button-bg: var(--color-primary) ← binds to a component
Why tiers matter: when the brand color changes from blue to green, you update one semantic mapping — not hundreds of component files. Tokens can be exported from a central JSON source to CSS, iOS, Android, and Tailwind configs simultaneously.
Advanced / Scenario (Q16–Q20)
Q16. A developer is fighting specificity — their styles keep being overridden. How do you diagnose and fix this?
Why interviewers ask: Tests real-world CSS debugging skills.
Model answer:
Diagnosis workflow:
- DevTools → Elements → Styles — find which rule is winning (it's at the top, un-struck). Identify its specificity.
- Check for
!importanton the winning declaration - Check for inline styles (
style="…") - Compare specificity scores — the losing selector might be too low
Common fixes (in order of preference):
| Fix | When to use |
|---|---|
| Lower the winning selector's specificity | If it's unnecessarily complex (e.g., #app .container .sidebar .widget h3) |
| Raise the losing selector's specificity | Add a class instead of using an element selector |
Use :where() for defaults | Zero-specificity defaults are always overridable |
Use @layer | Organize cascade order explicitly |
!important | Last resort — document why |
Root cause: the project likely lacks a specificity convention. Introduce flat, single-class selectors (like BEM) and a layer strategy.
Q17. Explain how em and rem interact in a component with nested elements.
Why interviewers ask: Tests whether you truly understand relative units, not just definitions.
Model answer:
html { font-size: 16px; }
.parent {
font-size: 1.25rem; /* 20px (1.25 × 16px root) */
padding: 1em; /* 20px (1 × parent's own 20px) */
}
.parent .child {
font-size: 1.25em; /* 25px (1.25 × parent's 20px) — COMPOUNDED */
padding: 1rem; /* 16px (1 × root 16px) — NOT compounded */
}
.parent .child .grandchild {
font-size: 1.25em; /* 31.25px (1.25 × 25px) — compounded again! */
}
em on font-size references the parent's computed font-size — each nesting level multiplies. rem always references root — stable across nesting depth.
Recommendation: use rem by default; reserve em for component padding/margins where you intentionally want spacing to scale with the component's own text size.
Q18. How would you build a spacing and color token system for a new project?
Why interviewers ask: Tests architectural thinking — can you design CSS for scale, not just write it?
Model answer:
Spacing scale (4px base):
:root {
--space-1: 0.25rem; /* 4px */
--space-2: 0.5rem; /* 8px */
--space-3: 0.75rem; /* 12px */
--space-4: 1rem; /* 16px */
--space-6: 1.5rem; /* 24px */
--space-8: 2rem; /* 32px */
--space-12: 3rem; /* 48px */
--space-16: 4rem; /* 64px */
}
Color system (primitive + semantic):
:root {
/* Primitives */
--blue-500: #2563eb;
--blue-600: #1d4ed8;
--gray-100: #f1f5f9;
--gray-900: #0f172a;
--red-500: #ef4444;
/* Semantic */
--color-primary: var(--blue-500);
--color-primary-hover: var(--blue-600);
--color-bg: var(--gray-100);
--color-text: var(--gray-900);
--color-danger: var(--red-500);
}
/* Dark override */
[data-theme="dark"] {
--color-bg: var(--gray-900);
--color-text: var(--gray-100);
--color-primary: #60a5fa;
}
Usage rule: components reference semantic tokens only — never hardcode values or use primitives directly. This makes theming a mapping change, not a rewrite.
Q19. A design uses fluid typography that shrinks too small on mobile. How would you fix it with modern CSS?
Model answer:
Use clamp() to set a floor, a preferred scaling value, and a ceiling:
body {
font-size: clamp(1rem, 0.875rem + 0.5vw, 1.25rem);
}
h1 {
font-size: clamp(1.5rem, 1rem + 2vw, 3rem);
}
Why this works:
- Minimum (
1rem) ensures text never drops below the user's base size — critical for accessibility - Preferred (
0.875rem + 0.5vw) scales smoothly with viewport - Maximum (
1.25rem) prevents oversized text on ultrawide monitors
Accessibility: both bounds use rem, not px — so they still respect user font-size preferences (WCAG 1.4.4).
What it replaces: multiple @media breakpoints with discrete font-size changes — clamp() makes the transition continuous.
Q20. What are cascade layers (@layer) and when would you use them?
Why interviewers ask: Tests modern CSS knowledge and ability to architect styles at scale.
Model answer:
@layer defines explicit priority tiers in the cascade. Declarations in later-listed layers override earlier ones (for normal rules; !important inverts the order):
@layer reset, base, components, utilities;
@layer reset {
*, *::before, *::after { box-sizing: border-box; margin: 0; }
}
@layer base {
body { font-family: system-ui; line-height: 1.5; }
}
@layer components {
.button { padding: 0.5rem 1rem; border-radius: 0.25rem; }
}
@layer utilities {
.sr-only { position: absolute; width: 1px; height: 1px; overflow: hidden; }
}
When to use:
- Large codebases with third-party CSS — layer them below your styles
- Utility-first frameworks — utilities in the highest layer always win without
!important - Component libraries — ship in a lower layer so consumers can override easily
- Team convention — makes the cascade explicit and reviewable
Key insight: layers resolve before specificity — a 0,0,0,1 selector in a later layer beats 0,1,0,0 in an earlier layer.
Quick-Fire (Yes / No + one line)
| Question | Answer | One-line rationale |
|---|---|---|
| Does 1 ID selector beat 100 class selectors? | Yes | Specificity categories never overflow into each other. |
Does * add specificity? | No | Universal selector has 0,0,0,0 weight. |
Does margin inherit? | No | Box model properties do not inherit. |
Does color inherit? | Yes | Typography properties inherit by default. |
Is box-sizing: border-box a CSS default? | No | Default is content-box — resets override to border-box. |
Does rem respect user font-size preferences? | Yes | rem is relative to root font-size, which reflects user settings. |
Does em compound in nested elements? | Yes | Each nesting level multiplies by the parent's font-size. |
| Can CSS custom properties be changed at runtime? | Yes | Via setProperty() in JavaScript — all dependent styles update. |
Is :where() the same as :is() functionally? | Yes | Same matching logic — but :where() has zero specificity. |
Can clamp() mix rem and vw? | Yes | CSS math functions accept mixed units freely. |
Interview Tips
- Draw the box model. Interviewers love visual explanations — content → padding → border → margin.
- Know the specificity tuple.
(inline, IDs, classes, elements)— practice scoring selectors mentally. - Explain cascade order. Origin → specificity → source order — shows depth beyond "ID beats class."
- Connect units to accessibility. "
remrespects user preferences;pxdoes not" — interviewers notice a11y awareness. - Use real examples. "I'd use
clamp()for the heading so it scales smoothly" is better than abstract definitions. - Mention design tokens proactively. Shows you think about systems, not just individual pages.
- Know
border-box. If asked about box model, immediately mention the reset — it shows production experience. - Explain trade-offs. "
!importantis powerful but creates specificity escalation" — balanced answers score higher.
Use this alongside the 1.6 Exercise Questions for hands-on practice and deeper review.