CSS Interview Questions

30+ CSS interview questions and answers in quiz-style format, answered by ex-FAANG interviewers
Questions and solutions by ex-interviewers
Covers critical topics

Looking to ace your next CSS interview questions? You’re in the right place.

CSS interview questions test your core styling expertise. Interviewers typically focus on topics such as:

  • Specificity & Cascade: Understanding how CSS rules compete and how to control which styles win.
  • Box Model: Mastering content, padding, border, and margin to build precise layouts.
  • Flexbox & Grid: Creating flexible, responsive layouts with modern CSS layout systems.
  • Responsive Design: Making designs adapt gracefully across screen sizes using media queries and fluid units.
  • Selectors & Combinators: Targeting elements efficiently with class, attribute, pseudo-class, and pseudo-element selectors.
  • Performance & Optimization: Writing lean, maintainable CSS and minimizing repaint/reflow overhead.

Below, you’ll find 30+ curated CSS interview questions, covering everything from foundational concepts to advanced layout and optimization strategies. Each question includes:

  • Quick Answers (TL;DR): Concise responses to help you answer on the spot.
  • Detailed Explanations: In-depth insights to ensure you understand not just the “how,” but the “why”.

These questions are crafted by senior and staff engineers from top tech companies, not anonymous contributors or AI-generated content. Start practicing below and get ready to stand out in your CSS interview!

If you're looking for CSS coding questions -We've got you covered as well, with:
Javascript coding
  • 40+ CSS coding interview questions
  • An in-browser coding workspace that mimics real interview conditions
  • Reference solutions from ex-interviewers at Big Tech companies
  • One-click automated, transparent test cases
  • Instant UI preview for UI-related questions
Get Started
Join 50,000+ engineers

Explain your understanding of the box model and how you would tell the browser in CSS to render your layout in different box models.

Topics
CSS

TL;DR

Every rendered CSS box has a content area surrounded by padding, a border, and margins. With the default box-sizing: content-box, a declared width applies only to the content. With box-sizing: border-box, it includes the content, padding, and border; margins are excluded in both cases. border-box usually makes component sizing easier to reason about.


Explain your understanding of the box model and how you would tell the browser in CSS to render your layout in different box models.

The four areas

From the inside out, the box model consists of:

  1. Content: Text, images, or child elements.
  2. Padding: Space between the content and border; the element's background extends through it.
  3. Border: The line surrounding the padding and content.
  4. Margin: Transparent space outside the border that separates the element from others.

The width and height properties control different areas depending on box-sizing.

content-box and border-box

Both boxes below declare width: 100px, padding: 10px, and a 5px border:

.content-box {
box-sizing: content-box; /* Initial value. */
width: 100px;
padding: 10px;
border: 5px solid;
}
.border-box {
box-sizing: border-box;
width: 100px;
padding: 10px;
border: 5px solid;
}
Box sizingContent widthBorder-box width
content-box100px100px + 20px + 10px = 130px
border-box100px - 20px - 10px = 70px100px

Neither calculation includes margins. A common project-wide default is:

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

This is a sizing convention, not a complete layout system. Intrinsic sizes, min-width, max-width, overflow, and the formatting context can still affect the final used size. For example, width and height normally do not apply to a non-replaced inline element such as a span.

Margins and debugging

Adjacent vertical margins of block boxes in normal flow can collapse into one margin. Margins do not collapse in Flexbox or Grid layout, and box-sizing does not change margin-collapsing behavior.

When a component is unexpectedly too wide, inspect its box-model diagram and computed box-sizing in browser DevTools. This quickly reveals whether padding or borders were added outside a content-box width, or whether a minimum size or overflowing child is the real cause.

Further reading

Exercises

Check your understanding
Beta
Check your understanding Exercise
Check your understanding Exercise

An element has width: 200px, padding: 20px on each side, and a 5px border on each side. What is its border-box width with box-sizing: content-box?

What does `* { box-sizing: border-box; }` do?

What are its advantages?
Topics
CSS

TL;DR

* { box-sizing: border-box; } makes every element selected by * include its padding and border within its declared width and height; margins remain outside. This prevents padding from unexpectedly increasing a percentage-sized box. The selector does not include pseudo-elements, so a project-wide rule commonly includes *::before and *::after or uses inheritance from html.


What does * { box-sizing: border-box; } do?

How the calculation changes

The initial content-box value applies a declared size to the content box. Padding and borders are added outside it. With border-box, the declared size describes the border box and the content area shrinks to make room for padding and borders.

.field {
box-sizing: border-box;
width: 100%;
padding: 0.75rem;
border: 2px solid;
}

Here, the field's border box remains 100% wide. With content-box, its border box would be 100% plus the inline padding and borders, which can overflow its container.

For a 100px width with 10px of padding and a 5px border on each side:

ValueContent widthBorder-box width
content-box100px130px
border-box70px100px

Margins are not included in either width.

A project-wide convention

The literal universal rule applies to elements but not their ::before and ::after pseudo-elements. One common reset is:

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

Using inheritance makes a subtree easier to opt back into content-box if an embedded widget depends on that model. A direct border-box declaration on all three selectors is also valid.

Advantages and limits

border-box makes component and grid sizing easier to calculate, particularly when percentages, padding, and borders are combined. It does not guarantee that an element fits: margins, intrinsic minimum sizes, long unbreakable content, min-width, transforms, and overflowing descendants can still extend beyond the container.

Switching an established application globally can break components written around content-box, especially third-party widgets. Introduce the convention early or test the migration. Use content-box deliberately when the content area itself must retain the exact declared dimensions.

Further reading

Exercises

Check your understanding
Beta
Check your understanding Exercise
Check your understanding Exercise

Which statements about box-sizing: border-box are correct? Select all that apply.

What is the CSS `display` property and can you give a few examples of its use?

Topics
CSS

TL;DR

The display property controls which boxes an element generates and how those boxes participate in layout. A value can describe both the element's outer role—block-level or inline-level—and how its children are laid out—flow, Flexbox, Grid, table layout, and so on. display: none generates no boxes for the element or descendants and normally removes them from the accessibility tree as well.


What is the CSS display property and can you give a few examples of its use?

Outer and inner display types

Many common values combine an outer display type with an inner formatting context:

ValueOuter behaviorChild layout
blockBlock-level boxNormal flow
inlineInline-level box that can fragment across linesNormal inline flow
inline-blockAtomic inline-level boxFlow-root formatting context
flexBlock-level boxFlexbox
inline-flexInline-level boxFlexbox
gridBlock-level boxGrid
inline-gridInline-level boxGrid
list-itemBlock or inline principal box plus a markerNormal flow

For example, a navigation list can remain a semantic list while its children use Flexbox:

<nav aria-label="Primary">
<ul class="nav-list">
<li><a href="/products">Products</a></li>
<li><a href="/pricing">Pricing</a></li>
</ul>
</nav>
.nav-list {
display: flex;
flex-wrap: wrap;
gap: 0.75rem;
padding: 0;
list-style: none;
}

Changing display changes layout boxes, not the HTML element's meaning. A div with display: table does not become a semantic HTML table, and a list needs appropriate markup even if its marker is visually removed. Because suppressing markers can affect how some browser and assistive-technology combinations expose a list, verify the accessibility tree as well as the DOM.

Hiding and suppressing boxes

display: none suppresses the element's entire box subtree. It is appropriate when content should be unavailable in the current state, but not for text intended only for screen readers or for an animation that needs a rendered start and end state.

display: contents suppresses the element's own box while its children participate as though they were direct children of the surrounding layout. It can help with wrapper-heavy Grid layouts, but it does not remove the element from the DOM and has had accessibility interoperability issues for some element and browser combinations. Test its semantics before using it on meaningful containers.

Table and internal values

Values such as table, table-row, and table-cell use the CSS table formatting model. More specialized internal values normally belong to that model and are rarely chosen for general application layout. Grid and Flexbox are clearer for most non-tabular arrangements.

Use browser layout overlays and the computed display value when debugging. The element's default user-agent style may differ by element, and some declared values compute to a pair of outer and inner types.

Further reading

Exercises

Check your understanding
Beta
Check your understanding Exercise
Check your understanding Exercise

Which statements about display values are correct? Select all that apply.

What is CSS selector specificity and how does it work?

Topics
CSS

TL;DR

Specificity is the selector weight the cascade compares when competing declarations are in the same relevant origin, importance level, encapsulation context, and cascade layer. Count IDs, then classes/attributes/pseudo-classes, then type selectors/pseudo-elements, and compare those columns from left to right. If specificity is equal, scoping proximity and then source order can decide. !important and cascade layers change an earlier cascade stage; they do not add specificity.


What is CSS selector specificity and how does it work?

Specificity is one part of the cascade

The browser does not simply choose the selector with the largest score. It first discards irrelevant rules, then considers cascade origin and importance, encapsulation context, and cascade layer. Only declarations still competing at the same level are compared by specificity. Active animations and transitions also have defined positions in the cascade.

This matters because a low-specificity selector in a later normal layer can beat a high-specificity selector in an earlier layer:

@layer base, components;
@layer base {
#checkout .button {
color: red; /* 1-1-0 */
}
}
@layer components {
.button.primary {
color: green; /* 0-2-0, but the later normal layer wins. */
}
}

Normal declarations outside a layer take precedence over normal layered declarations. For important declarations, layer and origin order are intentionally reversed; for example, important user styles can override important author styles. !important should therefore be understood as a change in cascade importance, not an infinitely large specificity score.

Calculating selector weight

Specificity is commonly written as ID-CLASS-TYPE:

  1. ID: ID selectors such as #checkout.
  2. CLASS: Class selectors, attribute selectors, and pseudo-classes such as .button, [aria-current], and :hover.
  3. TYPE: Type selectors and pseudo-elements such as button and ::before.

Combinators and the universal selector * add no weight. The columns are compared lexicographically, so 1-0-0 beats 0-20-20; do not treat the value as a base-ten number.

SelectorSpecificity
button0-0-1
.dialog button0-1-1
#checkout .dialog button1-1-1
button[aria-pressed='true']0-1-1

Inline style declarations participate specially in the author cascade and take precedence over normal declarations in author stylesheets. An important author rule can override a normal inline declaration.

Functional pseudo-classes

Some pseudo-classes need special treatment:

  • :where() and its arguments always contribute 0-0-0.
  • :is(), :not(), and :has() contribute the specificity of their most specific selector argument; the pseudo-class itself adds nothing.
  • CSS nesting follows similar specificity behavior to :is() for a nested selector list.

This makes :where() useful for defaults that consumers should be able to override easily:

:where(.prose) :where(h2, h3) {
margin-block-start: 1.5em;
}

Keeping overrides manageable

Prefer classes and intentionally low specificity for reusable components, and use cascade layers to define relationships between resets, components, and utilities. Avoid IDs in reusable selectors and repeated !important escalation. When a declaration loses unexpectedly, browser DevTools can show the winning rule and whether origin, a layer, specificity, or source order caused the result.

Further reading

Exercises

Check your understanding
Beta
Check your understanding Exercise 1 of 2
Check your understanding Exercise 1 of 2

Assume all declarations have the same origin, importance, layer, and scope. Which selector has the greatest specificity?

What's the difference between `block`, `inline`, and `inline-block`?

Topics
CSS

TL;DR

A block box participates in block layout and, with width: auto, normally fills the available inline space. An inline box participates inside a line and can wrap across lines; width and height do not apply to ordinary non-replaced inline boxes. An inline-block is a single atomic inline-level box whose dimensions and all box-model sides can be controlled. For groups of interface controls, Flexbox is often clearer than relying on inline-block whitespace and baseline alignment.


What's the difference between block, inline, and inline-block?

Behavior in normal flow

Behaviorblockinlineinline-block
Outer participationBlock-levelInline-level and can fragment across linesAtomic inline-level box
Default width: auto behaviorFills available inline space in normal block flowFits inline contentShrink-to-fit
width and height on a non-replaced elementApplyNormally do not applyApply
Line alignmentNot controlled by vertical-align in block flowParticipates in baseline and vertical-align behaviorParticipates as one box in baseline and vertical-align behavior
Typical useSections and vertically stacked regionsText-level phrasing contentA sized box that must sit in a text line

“Block” and “inline” refer to logical axes, so they continue to make sense in vertical writing modes; they do not inherently mean horizontal or vertical pixels.

Box-model details

Inline non-replaced content such as a span can split into multiple boxes when it wraps. Inline-start and inline-end margins, padding, and borders affect spacing. Block-axis padding and borders are painted, but they do not expand the line box in the same way a block or inline-block box does and can overlap adjacent lines. Block-axis margins on such an inline box have no effect.

Replaced inline elements such as img are an important exception: they have intrinsic dimensions and accept width and height even with display: inline.

An inline-block behaves as one indivisible item in its surrounding line while laying out its own contents in a flow-root formatting context. It is useful for a compact badge that needs padding and a minimum size:

.status-badge {
display: inline-block;
min-inline-size: 4rem;
padding: 0.25rem 0.5rem;
border-radius: 999px;
text-align: center;
vertical-align: middle;
}

Practical layout choice

Whitespace between inline-block elements in HTML is rendered as text spacing, and baseline alignment can produce an unexpected gap below image-like boxes. Removing source whitespace or changing font size only treats symptoms. When laying out a toolbar, navigation, or button group, use Flexbox with an explicit gap; reserve inline-block for boxes that genuinely belong in inline formatting.

Changing an element's CSS display type does not change its HTML semantics. Choose button, a, headings, and structural elements for meaning first, then select the layout behavior.

Further reading

Exercises

Check your understanding
Beta
Check your understanding Exercise
Check your understanding Exercise

A badge should sit within a line of text as one atomic box while honoring explicit width, height, and padding. Which display value fits?

What's the difference between a `relative`, `fixed`, `absolute`, `sticky` and `static`-ally positioned element?

Topics
CSS

TL;DR

static participates in normal flow. relative stays in flow but can be visually offset and establish a containing block. absolute and fixed are out of flow; absolute positioning uses its containing block, while fixed positioning usually uses the viewport. sticky stays in flow and is constrained within its scrolling container after crossing an inset threshold. Transforms and other properties can change the containing block, so “fixed means viewport” and “absolute means nearest positioned ancestor” have exceptions.


What's the difference between a relative, fixed, absolute, sticky and static-ally positioned element?

Comparison

ValueNormal-flow space retained?Positioning referenceCommon use
staticYesNormal flowOrdinary document content
relativeYesIts own normal positionSmall offsets or an anchor for descendants
absoluteNoIts containing blockBadges, anchored overlays, decorative layers
fixedNoUsually the viewport or page boxViewport-level controls and overlays
stickyYesIts normal position, then the nearest relevant scrollport and containing blockSection headers and table headers

An element is “positioned” when its computed position is not static.

Static and relative positioning

position: static is the initial value. Physical or logical inset properties do not move a statically positioned box.

A relatively positioned box is laid out normally, so its original space remains reserved. Insets then shift the rendered box without causing siblings to occupy the vacated area. It also becomes a containing block for many absolutely positioned descendants:

.card {
position: relative;
}
.card__badge {
position: absolute;
inset-block-start: 0;
inset-inline-end: 0;
transform: translate(50%, -50%);
}

Absolute and fixed positioning

An absolutely positioned box is removed from normal flow. Its containing block is often the nearest ancestor whose position is not static, but transforms, containment, and several other properties can also establish one. If no qualifying ancestor exists, it uses the initial containing block.

A fixed box is also out of flow and is normally positioned relative to the viewport, so it stays in place during document scrolling. However, an ancestor with a transform, perspective, or certain containment properties can establish its fixed-position containing block instead. Fixed UI should be tested at zoomed and narrow sizes so it does not cover content or trap controls off-screen.

Sticky positioning

Sticky positioning is not simply a switch from relative to fixed. The box remains in flow, then its position is adjusted to stay within inset constraints as its nearest scrolling ancestor moves. At least one inset on the relevant axis, such as inset-block-start, must be non-auto:

.section-heading {
position: sticky;
inset-block-start: 0;
z-index: 1;
background: Canvas;
}

Sticky behavior can appear broken when an ancestor establishes an unexpected scrolling mechanism, when the containing block is too short, or when no inset is set. Inspect ancestor overflow values and the available scroll distance before adding arbitrary offsets.

Positioning also interacts with stacking contexts. fixed and sticky boxes create stacking contexts, and a relative or absolute box does so when its z-index is not auto.

Further reading

Exercises

Check your understanding
Beta
Check your understanding Exercise
Check your understanding Exercise

Which statements about CSS positioning are correct? Select all that apply.

Are you familiar with styling SVG?

Topics
CSS

TL;DR

Inline SVG participates in the document's CSS cascade, so shapes can be styled with properties such as fill, stroke, and color, including states like :hover. Presentation attributes such as fill="red" have low cascade priority and can be overridden by CSS, while a style attribute has inline-style priority. An SVG loaded through <img> is a separate document and cannot normally be styled from the page's stylesheet.

<button class="icon-button" type="button">
<svg class="icon" viewBox="0 0 24 24" aria-hidden="true">
<path d="M5 12h14M12 5l7 7-7 7" />
</svg>
Continue
</button>
.icon {
width: 1.25em;
fill: none;
stroke: currentColor;
stroke-width: 2;
}

Are you familiar with styling SVG?

SVG is an XML-based vector format. How it can be styled depends on how it is embedded.

Inline SVG

An inline <svg> is part of the page's DOM. CSS can target its elements, inherit values, use custom properties, and react to states on the SVG or its ancestors. currentColor is especially useful for icons because it makes SVG paint follow the surrounding text color.

SVG presentation attributes such as fill, stroke, stroke-width, and opacity behave like low-priority author declarations. A stylesheet rule can override them:

<svg
class="status-icon"
viewBox="0 0 20 20"
role="img"
aria-labelledby="status-title">
<title id="status-title">Payment approved</title>
<circle cx="10" cy="10" r="8" fill="green" />
</svg>
.status-icon circle {
fill: var(--status-color, seagreen);
}

Avoid broad selectors such as svg { fill: ... }, which can accidentally fill paths intended to use strokes or their own colors.

External SVG

  • <img src="icon.svg" alt="..."> treats the SVG as an image. Page CSS cannot reach its internal shapes, but the image is cacheable and easy to use.
  • A CSS background-image is appropriate for decoration, not meaningful content, because it has no HTML alternative text.
  • <object> or <iframe> creates a separate document with additional loading, security, and scripting considerations.
  • An external <use> symbol sprite can share icons, but styling behavior depends on how the symbol is authored; prefer currentColor and test the target browsers.

For decorative inline icons, use aria-hidden="true" and ensure the adjacent control has an accessible name. For a standalone informative SVG, provide an accessible name using surrounding HTML or an SVG <title> relationship. Do not put essential text only in generated or decorative graphics.

Further reading

Exercises

Check your understanding
Beta
Check your understanding Exercise
Check your understanding Exercise

Which statements about styling SVG are correct? Select all that apply.

How do you manipulate CSS styles using JavaScript?

Topics
CSSWeb APIsJavaScript

TL;DR

You can manipulate CSS styles using JavaScript by accessing the style property of an HTML element. For example, to change the background color of a div element with the id myDiv, you can use:

document.getElementById('myDiv').style.backgroundColor = 'blue';

You can also add, remove, or toggle CSS classes using the classList property:

document.getElementById('myDiv').classList.add('newClass');
document.getElementById('myDiv').classList.remove('oldClass');
document.getElementById('myDiv').classList.toggle('toggleClass');

Manipulating CSS styles using JavaScript

Accessing and modifying inline styles

You can directly manipulate the inline styles of an HTML element using the style property. This property allows you to set individual CSS properties.

// Select the element
const myDiv = document.getElementById('myDiv');
// Change the background color
myDiv.style.backgroundColor = 'blue';
// Set multiple styles
myDiv.style.width = '100px';
myDiv.style.height = '100px';
myDiv.style.border = '1px solid black';

Using the classList property

The classList property provides methods to add, remove, and toggle CSS classes on an element. This is useful for applying predefined styles from your CSS files.

// Select the element
const myDiv = document.getElementById('myDiv');
// Add a class
myDiv.classList.add('newClass');
// Remove a class
myDiv.classList.remove('oldClass');
// Toggle a class
myDiv.classList.toggle('toggleClass');

Modifying styles using CSS variables

CSS variables (custom properties) can be manipulated using JavaScript. This is particularly useful for theming and dynamic styling.

// Set a CSS variable
document.documentElement.style.setProperty('--main-color', 'blue');
// Get the value of a CSS variable
const mainColor = getComputedStyle(document.documentElement).getPropertyValue(
'--main-color',
);
console.log(mainColor);

Using external stylesheets

You can also manipulate styles by dynamically adding or removing stylesheets.

// Create a new link element
const link = document.createElement('link');
link.rel = 'stylesheet';
link.href = 'styles.css';
// Append the link element to the head
document.head.appendChild(link);
// Remove the link element
document.head.removeChild(link);

Further reading

Exercises

Check your understanding
Beta
Check your understanding Exercise
Check your understanding Exercise

A component needs to toggle a predefined .is-open visual state while keeping styling rules in CSS. Which update best expresses that behavior?

Can you explain the difference between coding a website to be responsive versus using a mobile-first strategy?

Topics
CSS

TL;DR

Responsive design is the outcome: a page adapts to available space, input methods, user preferences, and content needs. Mobile-first is one implementation strategy: start with the constrained layout as the base CSS, then add enhancements with min-width queries. A responsive page can be mobile-first or desktop-first; mobile-first does not automatically make it faster, and breakpoints should be chosen where the content needs them rather than for named devices.


Can you explain the difference between coding a website to be responsive versus using a mobile-first strategy?

The terms describe different dimensions of a design decision.

Responsive design

A responsive interface uses flexible sizing, wrapping, Grid or Flexbox, responsive media, and conditional rules so the same document works across different environments. Viewport width is common, but media queries can also respond to motion preferences, pointer precision, hover support, orientation, contrast, and print output. Container queries adapt a component to its own available space.

Mobile-first CSS

Mobile-first CSS puts the simplest narrow-layout rules outside a query and layers on changes as space becomes available:

.card-list {
display: grid;
gap: 1rem;
grid-template-columns: 1fr;
}
@media (width >= 48rem) {
.card-list {
grid-template-columns: repeat(3, minmax(0, 1fr));
}
}

This often encourages teams to prioritize content and progressive enhancement. It can also reduce overrides when the narrow layout is the simplest default. The performance benefit is not the cost of evaluating fewer media queries—browsers evaluate media queries efficiently, and all downloaded CSS still has transfer and parsing cost.

Choosing an approach

A desktop-first approach may be reasonable when incrementally adapting an existing desktop product or when the wide layout is genuinely the simplest baseline. A component library may benefit more from container queries than from page-wide mobile or desktop breakpoints.

Whichever direction is used:

  • Choose breakpoints by resizing until the content or interaction no longer works well.
  • Keep source order logical; CSS reordering should not create a confusing keyboard or reading order.
  • Test zoom, long content, touch and keyboard input, reduced motion, and real devices—not only a few viewport presets.
  • Avoid hiding essential functionality merely because the viewport is narrow.

Further reading

Exercises

Check your understanding
Beta
Check your understanding Exercise
Check your understanding Exercise

Which statement correctly distinguishes responsive design from a mobile-first strategy?

Can you give an example of an `@media` property other than `screen`?

Topics
CSS

TL;DR

screen is a media type, not a property. A common alternative is print, which lets a page remove interactive chrome, expand useful URLs, and avoid layouts that waste paper. Most responsive CSS uses media features such as width, hover, or prefers-reduced-motion rather than relying on a media type.

@media print {
nav,
.advertisement {
display: none;
}
main {
max-width: none;
}
}

Can you give an example of an @media property other than screen?

The current general-purpose media types are:

  • all, which matches every device.
  • print, which represents paged output and print preview.
  • screen, which represents screens that do not match print.

Older types such as handheld, projection, tty, and speech are deprecated. In particular, speech should not be treated as a reliable way to target screen readers. Accessible markup must work independently of whether assistive technology exposes a media type.

Practical print styles

Print CSS can remove controls that have no printed function, prevent awkward page breaks, and expose destinations that would otherwise be hidden behind links:

@media print {
a[href^='http']::after {
content: ' (' attr(href) ')';
}
article {
break-inside: avoid;
}
}

Test in the browser's print preview. Do not remove content users may need, and remember that users can choose whether to print backgrounds and colors.

Media features are usually more useful for application behavior:

@media (prefers-reduced-motion: reduce) {
.animated-panel {
animation: none;
}
}

Further reading

Exercises

Check your understanding
Beta
Check your understanding Exercise
Check your understanding Exercise

Which example uses a media type other than screen?

Describe `float`s and how they work.

Topics
CSS

TL;DR

float moves a box to the inline start or end of its containing block and lets following inline content wrap around it. It was historically used for page layouts, but Flexbox and Grid are better for arranging interface regions. Floats remain useful for editorial effects such as wrapping article text around an image. Use clear to move later content below floats and display: flow-root when a parent should contain its floated children.


Describe floats and how they work.

A floated box is shifted to one side and taken out of normal block-flow calculations, but it still affects surrounding line boxes. This is why text wraps around a float while an absolutely positioned element is simply overlaid without reserving wrapping space.

Editorial use

<article class="story">
<img
class="story__photo"
src="speaker.jpg"
alt="Ada speaking at a conference" />
<p>Article text wraps around the photograph until it passes the float.</p>
</article>
.story {
display: flow-root;
}
.story__photo {
float: inline-start;
width: 12rem;
margin-inline-end: 1rem;
margin-block-end: 0.5rem;
}

The logical value inline-start follows the writing direction. Where support requirements favor physical values, left and right remain available.

Clearing and containing floats

clear moves a block below earlier floats on the specified side. If a container has only floated children, its normal-flow height may not include them. Creating a BFC with display: flow-root makes the container enclose those floats.

Legacy code may use a generated ::after clearfix or overflow: hidden. Preserve those when maintaining an older layout, but prefer flow-root for a new float container because the intent is clearer and it does not clip overflow.

When not to use floats

Do not use floats to build application grids, navigation bars, or general alignment. Flexbox handles one-dimensional distribution and Grid handles two-dimensional tracks without clearfixes or source-order tricks. Keep the HTML source order meaningful for reading and keyboard navigation regardless of the visual layout.

Further reading

Exercises

Check your understanding
Beta
Check your understanding Exercise
Check your understanding Exercise

Which scenario remains a natural use for float?

Describe pseudo-elements and discuss what they are used for.

Topics
CSS

TL;DR

A pseudo-element selects a part of an element or a generated box that is not represented by a separate HTML element, such as ::first-letter, ::marker, ::selection, ::before, or ::after. Use pseudo-elements for presentation and generated decoration, not for essential content or controls. Pseudo-classes such as :hover and :focus-visible instead select an existing element in a particular state.


Describe pseudo-elements and discuss what they are used for.

Pseudo-elements extend a selector with ::name and let CSS address content that ordinary element selectors cannot target directly.

Common uses

  • ::before and ::after generate child-like boxes for decoration.
  • ::first-letter and ::first-line style typographic fragments.
  • ::marker styles a list item's marker.
  • ::selection styles user-selected text.
  • Form-control pseudo-elements expose browser-defined parts where supported.
.external-link::after {
content: '';
display: inline-block;
width: 0.75em;
height: 0.75em;
margin-inline-start: 0.25em;
background: currentColor;
mask: url('/icons/external-link.svg') center / contain no-repeat;
}
li::marker {
color: rebeccapurple;
}

Generated content is not semantic HTML

::before and ::after do not create DOM elements, cannot replace a real button or link, and have inconsistent exposure to accessibility APIs. Keep important labels, instructions, and status messages in HTML. Decorative generated content should not be the only way meaning is conveyed.

Use the double-colon syntax for pseudo-elements. Browsers retain the old single-colon form for the original :before, :after, :first-line, and :first-letter names for compatibility, but new pseudo-elements use ::.

Further reading

Exercises

Check your understanding
Beta
Check your understanding Exercise
Check your understanding Exercise

Which selectors target pseudo-elements rather than element states? Select all that apply.

Describe what you like and dislike about the CSS preprocessors you have used.

Topics
CSS

TL;DR

A strong answer should name a preprocessor actually used and connect its features to a concrete codebase. Sass or Less can provide modules, functions, mixins, loops, and build-time validation, which remain useful for generated design systems and mature projects. The tradeoffs are an extra build dependency, source-map debugging, possible output bloat, and abstractions that can obscure the resulting CSS. Native custom properties, nesting, cascade layers, and modern color functions now cover many simpler use cases.


Describe what you like and dislike about the CSS preprocessors you have used.

This is an experience question, so the best response describes decisions and outcomes rather than listing syntax. Sass is a useful example.

Useful capabilities

  • Modules and organization: Sass's module system can expose a deliberate public API for tokens and utilities instead of relying on global imports.
  • Functions, mixins, and loops: Build-time logic can generate a controlled family of themes, spacing utilities, or compatibility rules.
  • Validation: A compiler can catch invalid variables and function calls before deployment.
  • Existing ecosystem: A mature codebase may already encode important design-system logic in Sass or Less.

Costs and failure modes

  • The project needs a compiler, configuration, CI integration, and accurate source maps.
  • Indirection can make it difficult to identify which source produced a declaration in DevTools.
  • Deep nesting mirrors the DOM, increases specificity, and produces fragile selectors.
  • Loops and mixins can silently generate much more CSS than authors expect.
  • Preprocessor variables exist only at build time and cannot respond to runtime cascade or DOM context like CSS custom properties can.

The old node-sass package was based on LibSass and is end-of-life. Current Sass projects should use Dart Sass. Less is implemented in JavaScript and remains supported, but choosing a tool should depend on the existing stack and the capabilities the project actually needs.

For a new project with straightforward styling, native CSS may be sufficient. A preprocessor is still reasonable when it removes meaningful repetition or supports an established design-system pipeline; it should not be added merely to use nesting or variables.

Further reading

Exercises

Check your understanding
Beta
Check your understanding Exercise
Check your understanding Exercise

Describe a CSS preprocessor you have used, including the project context, one concrete benefit, one cost, and whether you would choose it for a new project today.

Have you ever used a grid system, and if so, what do you prefer?

Topics
CSS

TL;DR

A strong modern answer is to use CSS Grid for two-dimensional page or component layouts, Flexbox for one-dimensional alignment, and a framework grid when its shared tokens and conventions genuinely help the team. The choice should preserve logical source order and respond to the component's content, not merely reproduce a fixed 12-column system.


Have you ever used a grid system, and if so, what do you prefer?

Choosing the layout tool

Older grid systems divided a page into columns using floats and calculated gutters. Native layout now covers most of those use cases:

ToolBest fitMain tradeoff
CSS GridRows and columns that need coordinated tracksMore layout decisions are defined by the container
FlexboxA row or column whose items align or distribute along one main axisWrapped rows do not share column tracks
Framework gridA project that benefits from an established spacing scale, breakpoints, and team conventionsAdds framework-specific markup or utility conventions

Grid and Flexbox are complementary. A page can use Grid for its card layout and Flexbox for the actions inside each card.

A content-responsive grid

This layout creates as many columns as fit without naming device-specific breakpoints:

.cards {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(min(100%, 16rem), 1fr));
gap: 1rem;
}
.card__actions {
display: flex;
flex-wrap: wrap;
gap: 0.5rem;
}

The min(100%, 16rem) expression prevents a track from overflowing a container narrower than 16rem. An explicit breakpoint is still appropriate when the content needs a deliberate layout change rather than simply more available columns.

Practical selection criteria

Whichever system is used:

  • Keep the DOM in a sensible reading and focus order; do not use visual reordering to repair poor source order.
  • Prefer design-system spacing and track tokens when consistency matters across teams.
  • Check narrow containers, zoomed text, long translations, and missing or unusually long content.
  • Avoid importing an entire framework only for a layout that a few native declarations can express.

Further reading

Exercises

Check your understanding
Beta
Check your understanding Exercise
Check your understanding Exercise

A team is choosing between CSS Grid, Flexbox, and a framework’s 12-column grid for a new dashboard. Explain how you would decide.

Have you played around with the new CSS Flexbox or Grid specs?

Topics
CSS

TL;DR

Flexbox and Grid are established CSS layout systems rather than experimental “new specs.” Flexbox is best when a container mainly lays items out along one axis; Grid is best when rows and columns need coordinated tracks. They are commonly combined, and the choice should follow the layout relationship rather than a rule that one replaces the other.


Have you played around with the new CSS Flexbox or Grid specs?

Flexbox for one-dimensional relationships

Flexbox distributes and aligns items along a main axis, with a cross axis perpendicular to it. It works well for navigation, toolbars, button groups, and component internals where content size influences the layout.

.toolbar {
display: flex;
align-items: center;
gap: 0.75rem;
}
.toolbar__search {
flex: 1 1 16rem;
}

Items can wrap, but each flex line lays itself out independently. Flexbox does not create shared column tracks across those lines.

Grid for two-dimensional relationships

Grid defines rows and columns at the container level. It is useful when items must align in both dimensions:

.dashboard {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(min(100%, 18rem), 1fr));
gap: 1rem;
}
.dashboard__wide-card {
grid-column: 1 / -1;
}

The wide card spans every explicit column, including when the auto-fitting grid collapses to one. If it should span exactly two tracks only when two tracks fit, add a content-driven breakpoint and test it with real content.

Combining them safely

A typical page uses Grid for the overall cards and Flexbox inside a card to align its title and actions. Features such as gap, minmax(), auto-fit, and subgrid can reduce wrapper elements and breakpoints, subject to the project's actual browser support requirements.

Visual placement is not a substitute for semantic source order. Grid and Flexbox can display items in a different order without changing reading, focus, or accessibility-tree order, so the DOM should remain logical.

Further reading

Exercises

Check your understanding
Beta
Check your understanding Exercise
Check your understanding Exercise

A card gallery must align card columns and rows across the whole container. Which layout is the clearest starting point?

Have you used or implemented media queries or mobile-specific layouts/CSS?

Topics
CSS

TL;DR

Media queries conditionally apply CSS using viewport, output, capability, or user-preference features. Use content-driven breakpoints for page-level changes, preference queries such as prefers-reduced-motion for accessibility, and capability queries such as hover without assuming they identify a particular device. Container queries are often better when a reusable component should respond to its own available space.


Have you used or implemented media queries or mobile-specific layouts/CSS?

Responsive layout

A mobile-first stylesheet can start with a single-column layout and add columns when the content has enough room:

.cards {
display: grid;
gap: 1rem;
}
@media (width >= 48rem) {
.cards {
grid-template-columns: repeat(2, minmax(0, 1fr));
}
}

The breakpoint should be chosen where the design becomes cramped, not because it matches a named phone or tablet. A desktop-first max-width approach can still be reasonable when adapting an existing wide-screen design; mobile-first is a strategy, not a browser requirement.

Flexbox and Grid can also make a layout fluid with fewer explicit breakpoints. Images need their own resource-selection mechanism: use srcset and sizes rather than relying on CSS media queries to hide a large image after it has downloaded.

Preferences and input capabilities

Media queries can reflect user settings and input characteristics:

.carousel {
scroll-behavior: smooth;
}
@media (prefers-reduced-motion: reduce) {
.carousel {
scroll-behavior: auto;
}
.decorative-spinner {
animation: none;
}
}
@media (hover: hover) and (pointer: fine) {
.card:hover {
box-shadow: 0 0.25rem 1rem rgb(0 0 0 / 15%);
}
}

Removing nonessential motion deliberately is safer than globally forcing every animation and transition to an almost-zero duration, which can break state changes that depend on transition events. A hover query should enhance an already usable interface; content and controls still need keyboard and touch-accessible paths. Hybrid devices make “touch device” assumptions unreliable; pointer describes the primary pointing device, while any-pointer considers any available one.

Component-level adaptation

When the same card can appear in a narrow sidebar or a wide main column, its container is more relevant than the viewport:

.card-shell {
container-type: inline-size;
}
@container (width >= 30rem) {
.card {
display: grid;
grid-template-columns: 10rem 1fr;
gap: 1rem;
}
}

Media and container queries complement each other: media queries describe the browsing environment, while container queries describe the space allocated to a component.

Testing the result

Resize emulation is useful, but also test zoom, long translated text, landscape and portrait orientations, keyboard navigation, real touch input where relevant, and the operating system's motion and color preferences. A layout that fits at one viewport width can still fail when text grows or a scrollbar changes the available inline size.

Further reading

Exercises

Check your understanding
Beta
Check your understanding Exercise
Check your understanding Exercise

Which query choices match their requirements? Select all that apply.

What are some of the "gotchas" for writing efficient CSS?

Topics
CSS

TL;DR

Efficient CSS starts with measurement. Check unused and render-blocking CSS, style recalculation, layout, paint, and compositing in browser DevTools before optimizing selectors. Keep the cascade predictable, avoid unnecessary invalidation and layout work, animate suitable properties, and use containment or will-change only when profiling shows that their tradeoffs help.


What are some of the "gotchas" for writing efficient CSS?

Delivery and unused CSS

CSS can delay rendering because the browser needs style information before it can paint. Common high-impact issues include a large framework bundle used by only a few components, route-specific styles loaded everywhere, duplicate rules, and fonts or background images referenced by CSS but downloaded too early.

Use the Network panel to inspect transfer and blocking time and the Coverage panel to find candidates for removal or splitting. Minification and compression help transfer size, but deleting unused declarations and loading critical route styles intentionally usually has more leverage.

Selector matching and the cascade

Engines commonly use the rightmost compound selector to find candidate elements, then verify the rest of the selector relationship. However, browsers optimize matching heavily, so “shorter selectors are always faster” is not a useful universal rule. A selector becomes a performance problem only when measurements show expensive style recalculation for the actual DOM and mutation pattern.

Write selectors primarily for maintainability and controlled invalidation:

/* A component class with deliberately low-specificity defaults. */
:where(.card) {
padding: 1rem;
}
/* A narrow state change rather than a broad descendant override. */
.card[data-state='selected'] {
outline: 2px solid Highlight;
}

Classes, BEM, cascade layers, and low-specificity selectors can make overrides predictable, but none of them guarantees faster rendering. Deeply coupled selectors and repeated !important rules are primarily architecture and debugging problems.

Layout, paint, and compositing

Changing geometry can cause layout; changing visual properties can require painting; and composited layers then need to be assembled. Avoid JavaScript that repeatedly alternates layout reads and style writes across many elements, because it can force synchronous layout. Batch reads and writes and update only the elements that changed.

For animations, transform and opacity are often good candidates because they can avoid layout and paint, but layer promotion is browser-dependent and large layers still cost memory and compositing time. Expensive shadows, filters, clipping, and large painted areas should be judged with paint flashing and a performance recording, not forbidden categorically.

Containment and content-visibility can let the browser skip work outside a component or off-screen subtree. They also affect layout and sizing and need deliberate focus and accessibility testing. Likewise, will-change should be applied sparingly and removed when no longer needed.

What to measure

Record the slow interaction in the target browser and representative hardware. Look for:

  • Long “Recalculate Style” or layout events and the number of affected elements.
  • Large or frequent paint regions.
  • Excessive layer count or memory use.
  • Unused CSS and render-blocking resources.
  • Layout shifts and interaction latency caused by late styles or content.

Optimize the dominant cost, repeat the trace, and keep the change only if the measured result improves without harming correctness.

Further reading

Exercises

Check your understanding
Beta
Check your understanding Exercise
Check your understanding Exercise

A page feels slow during scrolling and interaction, and a teammate proposes shortening every CSS selector. Describe a better performance investigation.

What are the advantages/disadvantages of using CSS preprocessors?

Topics
CSS

TL;DR

CSS preprocessors such as Sass and Less add build-time modules, variables, mixins, functions, and control flow. They can remove repetition and generate consistent CSS, but they add a compiler, another language to learn, source-map and debugging concerns, and the risk of producing much more CSS than the source suggests. Native custom properties, nesting, calculations, and cascade layers now cover some former use cases, so adopt a preprocessor for a concrete remaining need.


What are the advantages/disadvantages of using CSS preprocessors?

Advantages

  • Build-time abstraction: Mixins and functions can express repeated patterns that plain declarations would duplicate.
  • Structured source files: A module system can split a large stylesheet into focused files while producing an intentional set of deployable CSS assets.
  • Compile-time data and logic: Maps, loops, and conditionals can generate design-token utilities or a family of related selectors.
  • Mature tooling: Established preprocessors provide diagnostics, package ecosystems, and source maps.

For example, a Sass mixin named focus-ring($color) can emit the same focus declarations wherever it is included. This removes source repetition, but every inclusion still adds declarations to the compiled CSS, so inspect the output rather than assuming the abstraction is free.

A Sass variable exists only while compiling. By contrast, a CSS custom property remains in the browser and can change at runtime through the cascade:

:root {
--focus-color: Highlight;
}
.button:focus-visible {
outline: 0.2rem solid var(--focus-color);
outline-offset: 0.2rem;
}

Disadvantages

  • Build dependency: Development, production, and any consuming package must use a compatible compiler and configuration.
  • Abstraction cost: Deep nesting, inheritance helpers, and clever loops can hide the emitted selectors and their specificity.
  • Output growth: A small loop or repeated mixin call can generate a large stylesheet. The compiled output, not source-line count, determines transfer and browser work.
  • Debugging indirection: Source maps help, but DevTools ultimately applies the generated CSS.
  • Language overlap: Native CSS evolves independently. A preprocessor feature with similar syntax may have different semantics from the eventual platform feature.

When to use one

Use a preprocessor when its build-time capabilities materially simplify an existing codebase or token pipeline. Prefer native CSS when runtime theming, inheritance, browser-computed values, or direct platform interoperability matters. Many projects use both—for example, Sass modules to organize files and CSS custom properties for runtime themes.

Before adding a preprocessor to a new project, compare the requirement with native custom properties, nesting, calc(), cascade layers, and the existing bundler's file handling. If plain CSS already expresses the design clearly, another compilation layer may not pay for itself.

Further reading

Exercises

Check your understanding
Beta
Check your understanding Exercise
Check your understanding Exercise

Which statements are sound reasons for or against a CSS preprocessor? Select all that apply.

What are the different ways to visually hide content (and make it available only for screen readers)?

Topics
AccessibilityCSS

TL;DR

Use a tested visually-hidden utility that clips an element to a tiny, absolutely positioned box while leaving it in the accessibility tree. Do not use display: none, visibility: hidden, or the hidden attribute for screen-reader-only text because they normally hide it from assistive technology too. If the hidden element is focusable, reveal it on :focus or :focus-within so sighted keyboard users can see it.


What are the different ways to visually hide content (and make it available only for screen readers)?

A visually-hidden utility

A robust utility combines several declarations instead of moving content thousands of pixels off-screen:

.visually-hidden:not(:focus, :focus-within, :active) {
position: absolute;
width: 1px;
height: 1px;
padding: 0;
margin: -1px;
overflow: hidden;
clip-path: inset(50%);
white-space: nowrap;
border: 0;
}

The :not(:focus, :focus-within, :active) exception makes a focusable element such as a “Skip to main content” link—or a container with a focused descendant—visible when a keyboard user reaches it. A project can instead use the maintained visually-hidden or sr-only utility from its existing framework, but should verify the exact version and focus behavior.

Typical uses include supplying a text label for an icon-only control or adding context to a terse link. Prefer an ordinary visible label whenever the information helps everyone.

<button type="button">
<svg aria-hidden="true" focusable="false"><!-- Icon paths. --></svg>
<span class="visually-hidden">Close dialog</span>
</button>

Techniques with different semantics

Choose a technique based on who should receive the content:

TechniqueVisibleUsually exposed to assistive technologyAppropriate use
Visually-hidden utilityNoYesSupplemental accessible text
display: none, visibility: hidden, or hiddenNoNoContent unavailable to everyone in the current state
aria-hidden="true"YesNoRedundant or decorative content, never a focusable control

opacity: 0, off-screen positioning, and large negative text-indent values can leave an invisible element interactive or create confusing scroll and focus behavior. A zero-sized element can also be omitted or handled inconsistently by assistive technology. These are poor substitutes for a tested utility.

Test the accessible result

Inspect the accessibility tree and accessible name in browser DevTools, navigate with only the keyboard, and test representative screen-reader and browser combinations. Confirm that hidden text does not duplicate a visible label and that every focusable element becomes visually apparent when focused.

Further reading

Exercises

Check your understanding
Beta
Check your understanding Exercise
Check your understanding Exercise

A visible icon-only link needs an accessible text label without displaying that text in the normal layout. Which technique is appropriate?

What are the various clearing techniques and which is appropriate for what context?

Topics
CSS

TL;DR

For a container that must contain floated children, use display: flow-root; it creates a new block formatting context without pretending that overflow should be clipped or scrolled. Use clear on an element that must move below preceding floats. Clearfix pseudo-elements remain useful in legacy code, while empty clearing elements and overflow: hidden are usually avoidable.


What are the various clearing techniques and which is appropriate for what context?

clear moves a following box

The clear property moves a block box below relevant earlier floats in the same block formatting context:

<img class="thumbnail" src="avatar.jpg" alt="" />
<p>Text wraps beside the floated image.</p>
<h2 class="next-section">Account activity</h2>
.thumbnail {
float: inline-start;
margin-inline-end: 1rem;
}
.next-section {
clear: both;
}

Use this when the following element—not the parent—must start below floats.

flow-root contains floats

A float is taken out of normal flow, so a parent containing only floated children can appear to have zero height. display: flow-root creates a new block formatting context whose height contains those floats:

.media-object {
display: flow-root;
}

This states the layout intent directly. overflow: auto or overflow: hidden also establishes a block formatting context in common cases, but it can add scrollbars or clip overflowing content. Use an overflow value because overflow behavior is desired, not merely as a clearing trick.

Legacy techniques

A traditional clearfix inserts a generated block after the contents:

.clearfix::after {
display: block;
clear: both;
content: '';
}

It remains reasonable when maintaining a legacy float-based grid or supporting a target without flow-root. An empty <div style="clear: both"></div> changes the document structure only for presentation and is harder to maintain.

For new multi-column component or page layout, use Grid or Flexbox rather than floats and clearing. Floats still have a valid role when content should wrap around an image or other editorial element.

Further reading

Exercises

Check your understanding
Beta
Check your understanding Exercise
Check your understanding Exercise

A container should expand to contain its floated image without clipping overflow or adding empty markup. What is the clearest modern rule?

What existing CSS frameworks have you used locally, or in production?

How would you change/improve them?
Topics
CSS

TL;DR

This is an experience question: name the framework, version or era, product context, and the tradeoff you observed. A useful answer explains whether the framework is component-based or utility-first, how it affected consistency, accessibility, bundle size, customization, and upgrades, and one concrete improvement. Do not present a personal preference or an old release problem as a timeless property of the framework.


What existing CSS frameworks have you used locally, or in production?

Compare the layer each framework provides

Frameworks solve different problems, so compare them against the project's needs rather than ranking them generically:

ExampleWhat it providesQuestions to ask
BootstrapLayout utilities and prebuilt interactive component stylesCan its Sass variables or CSS custom properties express the product's design without extensive overrides?
BulmaSass-based layout and component classesDoes the class and markup convention fit the application, and is the team prepared to test upgrades?
Semantic UIA themed component vocabulary and JavaScript integrationsIs the theming and integration model maintainable for the versions already in the product?
Tailwind CSSLow-level utility classes generated from a configured design systemAre repeated component patterns extracted appropriately, and is generated CSS limited to what the product uses?

The named products illustrate different approaches; their capabilities and release behavior change, so an interview answer should identify the version or time period behind any specific criticism.

A practical answer shape

A strong answer might say:

I used Bootstrap in an internal application where predictable form and layout conventions helped a small team deliver quickly. The main cost was overriding component styles to match a distinct brand. I would first customize framework tokens and wrap repeated application patterns instead of adding more-specific selectors throughout the codebase. I would also test the exact component states we use—keyboard focus, validation errors, zoom, and long translated labels—rather than assuming the framework makes them accessible automatically.

That answer connects the tool to a real constraint, acknowledges both value and cost, and proposes a change at the correct abstraction layer.

Improving framework use

  • Configure documented tokens, themes, and build-time options before overriding generated selectors.
  • Keep application components behind a small wrapper API so framework upgrades do not require editing every call site.
  • Import or generate only the CSS the product uses, then verify the result with Network and Coverage tooling.
  • Add regression tests for the application's supported component states and target browsers.
  • Follow the framework's migration guides and review generated markup and accessibility behavior after an upgrade.
  • Avoid maintaining a private fork unless the project can sustain merging upstream fixes; contribute a generally useful fix upstream when practical.

A framework accelerates common work but does not replace semantic HTML, product-specific usability testing, or knowledge of the underlying cascade and layout systems.

Further reading

Exercises

Check your understanding
Beta
Check your understanding Exercise
Check your understanding Exercise

Describe a CSS framework you have used in production and one improvement you would make to its use in that codebase.

Describe Block Formatting Context (BFC) and how it works.

Topics
CSS

TL;DR

A block formatting context (BFC) is an independent region for normal-flow block layout and float interaction. A new BFC contains its internal floats, prevents external floats from intruding into it, and stops certain vertical margins from collapsing across its boundary. When the goal is to create a BFC intentionally, display: flow-root communicates that intent without clipping content or creating scrollbars.

.media-object {
display: flow-root;
}
.media-object > img {
float: inline-start;
margin-inline-end: 1rem;
}

Describe Block Formatting Context (BFC) and how it works.

A BFC defines where block boxes participate in flow layout and how floats affect nearby content. It is a layout boundary, not a visible element or a new DOM subtree.

Why a BFC matters

Creating a BFC can:

  • Make a container's height include floated descendants.
  • Keep a normal-flow box from wrapping around a neighboring float.
  • Prevent a descendant's vertical margin from collapsing through the container boundary.

Common ways a BFC is created

The full list is longer, but common triggers include:

  • The root <html> element.
  • A floated element.
  • An absolutely or fixed-positioned element.
  • display: inline-block, table-cell, or table-caption.
  • display: flow-root.
  • A block with overflow other than visible or clip.
  • Certain containment and container-query settings.

Flex and Grid containers establish their own flex and grid formatting contexts. They have some similar boundary effects, but describing them simply as BFCs hides important differences, such as how their children are laid out.

Prefer an intentional trigger

Older code often uses overflow: hidden or overflow: auto solely to contain floats. That creates a BFC, but it may clip shadows or produce unwanted scrolling. display: flow-root states the layout intention directly and avoids those overflow side effects.

In DevTools, inspect the computed display, float, position, overflow, contain, and container-type values when a float escapes a parent or margins collapse unexpectedly.

Further reading

Exercises

Check your understanding
Beta
Check your understanding Exercise
Check your understanding Exercise

Which effects can result from establishing a new block formatting context? Select all that apply.

Describe `z-index` and how stacking context is formed.

Topics
CSS

TL;DR

z-index orders a box only within its current stacking context; it is not a global page-wide height. A stacking context is painted as one atomic unit in its parent, so a descendant with z-index: 9999 cannot escape an ancestor that is below a sibling context. Positioned elements with a non-auto z-index, flex or grid items with a non-auto z-index, transforms, opacity below 1, containment, and several other features create contexts. Elements in the browser's top layer, such as an open modal <dialog>, are above ordinary document stacking contexts.


Describe z-index and how stacking context is formed.

When boxes overlap, CSS applies a defined painting order. z-index influences that order for positioned boxes and for flex and grid items, but its values are compared only among relevant siblings in the same stacking context.

Why a large z-index can still lose

<div class="page-header">Header</div>
<main class="content">
<div class="tooltip">Tooltip</div>
</main>
.page-header {
position: relative;
z-index: 2;
}
.content {
position: relative;
z-index: 1;
transform: translateZ(0);
}
.tooltip {
position: absolute;
z-index: 9999;
}

The tooltip remains below the header because the entire .content context is at level 1, below the header at level 2. Increasing the tooltip's value cannot move it into the header's parent context. Fix the context structure or render the overlay in an appropriate shared layer rather than escalating arbitrary numbers.

Common stacking-context triggers

Common examples include:

  • The root element.
  • position: absolute or relative with z-index other than auto.
  • position: fixed or sticky.
  • A flex or grid item with z-index other than auto.
  • opacity less than 1.
  • A non-none transform, filter, perspective, mask, or blend mode.
  • Relevant isolation, contain, container-type, and will-change values.

The full list evolves with CSS features, so use the browser's stacking-context inspection rather than memorizing only a few triggers. Also distinguish stacking contexts from compositing layers: a browser may promote content to a GPU layer as an implementation optimization, but that does not redefine CSS painting order.

For application overlays, use a small documented layer scale and native top-layer APIs such as <dialog> or popovers where their behavior fits the interaction.

Further reading

Exercises

Check your understanding
Beta
Check your understanding Exercise 1 of 2
Check your understanding Exercise 1 of 2

Which declarations can cause an element to establish a stacking context? Select all that apply.

Explain CSS sprites, and how you would implement them on a page or site.

Topics
CSSPerformance

TL;DR

A CSS sprite packs several raster images into one file and reveals one region with background-position, fixed dimensions, and sometimes background-size. It historically reduced request overhead, but HTTP/2 and HTTP/3, caching, SVG symbols, and ordinary image files often make sprites a poor default today. Sprites can still be useful for tightly coupled small assets or frame-based game animation when one decoded sheet is operationally convenient.


Explain CSS sprites, and how you would implement them on a page or site.

A build tool usually packs the source images and records each region's coordinates. Every displayed sprite uses the same background image but shifts it so only the intended region appears.

.icon {
display: inline-block;
width: 24px;
height: 24px;
background-image: url('/images/icons.png');
background-repeat: no-repeat;
}
.icon--cart {
background-position: 0 0;
}
.icon--arrow {
background-position: -24px 0;
}
<button type="button">
<span class="icon icon--cart" aria-hidden="true"></span>
Add to cart
</button>

The element's dimensions must match the sprite cell. A high-density sheet also needs a deliberate background-size so its device pixels map to the intended CSS-pixel grid.

Tradeoffs

Sprites reduce the number of independently requested files and ensure all regions arrive together. They also couple unrelated assets: changing one icon invalidates the whole sheet, unused regions are downloaded, coordinates are brittle, and responsive or differently colored icons are awkward.

Use backgrounds only for decorative imagery. A meaningful icon needs an accessible name from visible text or the containing control; the background itself cannot provide alternative text. SVG sprites or inline SVG are usually more flexible for single-color interface icons, while ordinary <img> elements are better for content images with alt, responsive candidates, and independent caching.

Measure request overhead, transfer size, decode cost, cache invalidation, and maintenance complexity before retaining a sprite pipeline solely for historical performance reasons.

Further reading

Exercises

Check your understanding
Beta
Check your understanding Exercise
Check your understanding Exercise

Which scenario gives a CSS sprite sheet a concrete modern advantage?

Explain how a browser determines what elements match a CSS selector.

Topics
BrowserCSS

TL;DR

Conceptually, selector matching starts from the rightmost compound selector—the candidate element—and checks relationships toward the left. For .card > .title, an element must first match .title, then its parent must match .card. Engines index and optimize selectors internally, so “shorter selectors are always faster” is not a reliable rule. Prefer selectors that communicate intent and profile style recalculation when it is actually significant.


Explain how a browser determines what elements match a CSS selector.

The browser parses selectors into components and tests them against elements while calculating styles. The rightmost compound selector is often called the subject or key selector because it identifies the element that receives the declarations.

Matching relationships

For this selector:

article.featured > h2 a[aria-current='page'] {
font-weight: 700;
}

The browser conceptually checks whether a candidate:

  1. Is an <a> with aria-current="page".
  2. Has an ancestor <h2>.
  3. Has an <h2> ancestor whose direct parent is <article class="featured">, as required by >.

If any condition fails, that candidate does not match. Actual engines maintain indexes, caches, bloom filters, and invalidation data, so this model explains semantics without promising one implementation algorithm.

Dynamic style invalidation

Matching is not only an initial-load operation. Adding a class, changing an attribute, inserting an element, or updating state can make selectors start or stop matching. The engine determines which elements might be affected and recalculates their styles. A selector's cost therefore depends on DOM size, mutation patterns, how broadly its rightmost part selects candidates, and engine optimizations—not just character count.

Write maintainable selectors with deliberate scope and low enough specificity to override safely. Avoid changing markup solely to micro-optimize selector matching without evidence. In a real slowdown, record the interaction in the Performance panel and inspect Recalculate Style duration, affected element count, and repeated DOM mutations.

Further reading

Exercises

Check your understanding
Beta
Check your understanding Exercise
Check your understanding Exercise

For the selector .card > .title, which conceptual matching process is correct?

Have you ever worked with retina graphics?

If so, when and what techniques did you use?
Topics
CSS

TL;DR

“Retina” is an Apple marketing term commonly used for high-pixel-density displays. The practical concern is the device pixel ratio: one CSS pixel can be represented by multiple device pixels. Text, CSS shapes, and SVG usually scale cleanly; raster images and canvas content need enough source pixels without making every user download the largest asset.


Have you ever worked with retina graphics?

CSS pixels and device pixels

window.devicePixelRatio reports the ratio between CSS pixels and physical device pixels for the current display and zoom configuration. A ratio greater than 1 is common but is not guaranteed on every mobile device, and it can change when a window moves between displays.

A raster image displayed at 200 CSS pixels wide may need a 400-pixel-wide source to look sharp at a device pixel ratio of 2. Serving that larger source to every device wastes bandwidth, so the browser should be given candidates.

Images and icons

For an image with a fixed rendered size, density descriptors are concise:

<img
src="/images/logo.png"
srcset="/images/logo.png 1x, /images/logo@2x.png 2x"
width="200"
height="60"
alt="Acme" />

For fluid content images, width descriptors plus sizes are usually more appropriate because the browser can consider both the layout width and pixel density. SVG is often a good choice for logos, icons, and illustrations that can be represented as vectors, but photographs still need raster formats.

CSS background images can offer density candidates with image-set():

.brand-mark {
background-image: image-set(
url('/images/mark.png') 1x,
url('/images/mark@2x.png') 2x
);
}

Canvas rendering

A canvas has separate CSS and bitmap dimensions. To avoid a blurry chart, size its backing store for the current ratio, then scale the drawing context:

const canvas = document.querySelector('canvas');
const size = 240;
const ratio = window.devicePixelRatio || 1;
canvas.style.width = `${size}px`;
canvas.style.height = `${size}px`;
canvas.width = Math.round(size * ratio);
canvas.height = Math.round(size * ratio);
const context = canvas.getContext('2d');
if (!context) {
throw new Error('2D canvas is not available');
}
context.scale(ratio, ratio);

The implementation should also respond if the displayed size or pixel ratio changes. In practice, compare sharpness and transferred bytes in real target devices and browser DevTools rather than assuming the highest-resolution asset is always better.

Further reading

Exercises

Check your understanding
Beta
Check your understanding Exercise
Check your understanding Exercise

Which techniques appropriately support high-pixel-density displays? Select all that apply.

How do you serve your pages for feature-constrained browsers?

What techniques/processes do you use?
Topics
CSS

TL;DR

Define the browsers and capabilities the product must support, provide a semantic and functional baseline, and enhance it when features are available. Use CSS @supports and JavaScript feature detection instead of user-agent sniffing, automate compatible prefixes and transformations from the declared support policy, and test the fallback in real target browsers.


How do you serve your pages for feature-constrained browsers?

Start with a support policy

“Feature-constrained” can mean an older engine, disabled JavaScript, a slow connection, limited memory, an assistive technology, or a missing input capability. First turn product and usage requirements into an explicit support matrix. A tool such as Browserslist can share browser targets with build tools, but analytics should not silently exclude users who cannot load the current application.

Then decide the required baseline. Semantic HTML, ordinary links, native form controls, readable content, and server-side validation often provide useful behavior before optional CSS or JavaScript runs.

Enhance through feature detection

Progressive enhancement adds richer presentation or behavior when the browser supports it. For example, a list can remain readable before Grid is enabled:

.cards {
display: block;
}
.card + .card {
margin-block-start: 1rem;
}
@supports (display: grid) {
.cards {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(16rem, 1fr));
gap: 1rem;
}
.card + .card {
margin-block-start: 0;
}
}

For JavaScript, detect the capability itself:

if ('IntersectionObserver' in window) {
enableVisibilityTracking();
} else {
showAllContent();
}

An @supports result means the browser parses a declaration; it does not prove that an implementation is bug-free, accessible, or fast enough for the use case. Complex interactions still need testing.

Build tools and fallbacks

Autoprefixer can add prefixes required by configured browser targets, and a transpiler can transform some newer JavaScript syntax. Neither automatically polyfills every missing web API or reproduces a newer CSS layout model. Load a focused polyfill only when the feature and fallback requirements justify its cost.

Graceful degradation starts with the enhanced experience and makes sure failure remains usable. It is useful when a true baseline implementation is impractical, but critical actions should not depend on an optional effect or animation. Libraries such as Modernizr can centralize many feature tests in legacy applications; small modern applications often need only direct checks and @supports.

Verify the fallback

Compatibility tables help choose a strategy, but verify the actual workflow in target browsers and devices. Test content access, forms, keyboard use, error handling, slow or failed resources, and unsupported features—not merely whether the first screen resembles the design.

Further reading

Exercises

Check your understanding
Beta
Check your understanding Exercise
Check your understanding Exercise

Which practices support feature-constrained browsers through progressive enhancement? Select all that apply.

How is responsive design different from adaptive design?

Topics
CSS

TL;DR

Responsive design usually means one fluid layout that continuously adapts using flexible sizing, media or container queries, and responsive media. Adaptive design usually means choosing among a smaller set of deliberately different layouts or experiences. The terms are informal and real products often combine both; adaptive design does not inherently require user-agent sniffing.


How is responsive design different from adaptive design?

Conventional distinction

Both approaches aim to make an experience work in different contexts, but they emphasize different forms of adaptation:

DimensionResponsive designAdaptive design
LayoutFluidly grows, shrinks, and reflowsSwitches among predefined arrangements
Common inputsAvailable inline size, aspect ratio, user preferencesBreakpoint ranges, capabilities, or product-defined contexts
Typical implementationFlexible units, Grid, Flexbox, media and container queriesDistinct templates, component variants, or server/client-selected experiences
Main riskA single fluid composition becomes hard to control at extremesVariants drift apart or make unreliable assumptions about users

This is industry terminology, not a strict browser-platform classification. A layout that uses fluid tracks between two major breakpoint-specific compositions could reasonably be described as both.

Practical choices

Responsive techniques are a strong default for content that is fundamentally the same at every size. For example, a product grid can let tracks grow and wrap while responsive images provide suitable resources.

Adaptive variants are useful when a context needs a materially different composition or interaction—for example, a dense editing workspace may expose persistent panels on a wide screen but use an explicit panel switcher in a narrow container. The alternative must preserve access to the same important actions and content rather than hiding them solely because a screen is small.

Avoid device assumptions

Adaptive design does not require detecting an iPhone, tablet, or desktop user agent. CSS media and container queries can select predefined layouts by available space, while feature detection can select behavior by capability. Server-selected variants can be justified for device-specific delivery constraints, but user-agent detection is brittle and caching must account for any request-dependent response.

In either approach, choose breakpoints from content, test text zoom and translations, and avoid equating viewport width with input method. A wide touch device and a narrow desktop window are both normal cases.

Further reading

Exercises

Check your understanding
Beta
Check your understanding Exercise
Check your understanding Exercise

Which design is primarily adaptive rather than fluidly responsive?

How would you approach fixing browser-specific styling issues?

Topics
CSS

TL;DR

Reproduce the issue in the exact browser and version, reduce it to the smallest failing case, and compare computed styles, layout, and feature support with the specification. Prefer a standards-based fallback or an @supports-guarded enhancement. Use configured build tooling for required prefixes, then regression-test real target browsers; user-agent hacks and browser-specific stylesheets are last resorts.


How would you approach fixing browser-specific styling issues?

Diagnose before patching

First confirm that the difference is actually browser-specific. A stale cache, missing font, extension, zoom level, operating-system control style, or invalid markup can look like an engine bug.

  1. Reproduce it in the affected browser version and a known-working browser with the same content and viewport.
  2. Inspect computed styles, the box model, Grid or Flex overlays, loaded resources, console warnings, and the browser's support for the property or value.
  3. Reduce the page to a minimal case that preserves the failure.
  4. Check the current specification, compatibility data, and known engine bugs.
  5. Decide whether the code is invalid, support is missing, or the browser has an implementation defect.

A minimal reproduction also makes an engine bug report useful and prevents an unrelated framework rule from being mistaken for browser behavior.

Prefer capabilities and fallbacks

Give browsers a functional baseline, then opt into a newer feature where it is understood:

.filters {
display: flex;
flex-wrap: wrap;
gap: 0.5rem;
}
@supports (selector(:has(*))) {
.filter-group:has(input:checked) {
outline: 2px solid Highlight;
}
}

Feature queries test parsing support, not the absence of bugs, so the enhanced path still needs browser testing. If a declaration can fail safely, normal cascade fallback can be even simpler: put the widely supported value first and the newer value second.

Keep compatibility policy centralized

Autoprefixer can generate vendor-prefixed declarations from a Browserslist target. A reset or normalization layer can make intentional defaults consistent, but it will not fix an engine bug. Similarly, adopting a UI framework can provide tested components, but importing one solely to mask an unexplained CSS issue increases the debugging surface.

Avoid server-selected browser stylesheets and user-agent-specific selectors unless no capability-based workaround exists. They are hard to cache, easy to become stale, and can misclassify browsers. If a targeted workaround is unavoidable, isolate it, document the affected versions and upstream issue, add a regression case, and define when it can be removed.

Further reading

Exercises

Check your understanding
Beta
Check your understanding Exercise
Check your understanding Exercise

A production card layout breaks only in one supported browser version. Describe the investigation and fix process.

Is there any reason you'd want to use `translate()` instead of `absolute` positioning, or vice-versa? And why?

Topics
CSSPerformance

TL;DR

Use absolute positioning when an element should be taken out of normal flow and anchored to a containing block, such as a badge or popover. Use translate() when the element should keep its layout position but be moved visually, especially for an animation or a percentage-based adjustment. Transforms are often composited efficiently, but they do not guarantee a GPU layer or eliminate painting in every case—measure the actual interaction.


Is there any reason you'd want to use translate() instead of absolute positioning, or vice-versa? And why?

They solve different layout problems

position: absolute removes a box from normal flow and positions it relative to its containing block. Its former position does not reserve space. This is appropriate for an overlay whose location is defined by an anchor:

.button {
position: relative;
}
.button__badge {
position: absolute;
inset-block-start: 0;
inset-inline-end: 0;
transform: translate(50%, -50%);
}

The example intentionally combines the techniques: positioning chooses the logical corner, while translate offsets the badge by half of its own size. Percentage translations are relative to the transformed element's reference box, whereas percentage insets are generally relative to the containing block.

A transform changes where an existing box is drawn without changing the normal-flow space allocated to it. Siblings therefore lay out as if the transformed box were still in its original position, and the transformed box can visually overlap them.

Animation and rendering cost

Animating inset-inline-start, top, or left can require layout and subsequent painting. Animating transform can often be handled in the compositing stage once the content has been painted, which makes it a good candidate for smooth motion. That is not an absolute guarantee: large layers, filters, changing content, memory pressure, and browser heuristics can still cause painting or expensive compositing.

Do not add will-change: transform broadly. It can consume extra memory and should be a temporary, measured optimization. Use the browser's Performance and Layers tooling to check layout, paint, and compositing behavior on representative devices.

Other side effects

A non-none transform creates a stacking context and can establish a containing block for positioned descendants. It can therefore change z-index behavior and the positioning of descendants, even when the visual offset is zero. Hit testing follows the transformed visual position, but surrounding layout does not.

For motion, also provide a reduced-motion treatment when the movement is not essential:

.panel {
transition: transform 200ms ease;
}
@media (prefers-reduced-motion: reduce) {
.panel {
transition: none;
}
}

Further reading

Exercises

Check your understanding
Beta
Check your understanding Exercise
Check your understanding Exercise

A notification badge should be removed from normal flow and anchored to the top-right of its icon. Which mechanism expresses that layout relationship?

What's the difference between "resetting" and "normalizing" CSS?

Which would you choose, and why?
Topics
CSS

TL;DR

A reset deliberately removes selected browser defaults so the application rebuilds them; normalization preserves useful defaults while reducing targeted inconsistencies. Neither is automatically required. Choose a maintained normalization stylesheet when broad native consistency is valuable, or a small documented reset when a design system intentionally owns those styles. Do not erase focus indicators, control affordances, headings, or list semantics without accessible replacements.


What's the difference between "resetting" and "normalizing" CSS?

Resetting and normalizing

ApproachIntentTradeoff
ResetRemove a chosen set of user-agent styles and establish a blank or project-specific baselineThe project must restore useful typography, spacing, controls, focus states, and other affordances
NormalizePreserve useful user-agent behavior while correcting selected inconsistencies and documented browser bugsThe project accepts more native defaults and depends on the normalization stylesheet's scope and version

“Reset” describes a strategy, not one standard file. A reset can range from a destructive * { margin: 0 } rule to a careful design-system baseline. Normalize.css is a particular open-source implementation of the normalization approach; inspect its current rules rather than treating its name as a platform guarantee.

A minimal application reset

Many applications need only a few intentional defaults:

html {
box-sizing: border-box;
}
*,
*::before,
*::after {
box-sizing: inherit;
}
body {
margin: 0;
}
button,
input,
select,
textarea {
font: inherit;
}
img,
video {
max-inline-size: 100%;
block-size: auto;
}

Each declaration should solve an understood project problem. For example, inheriting form fonts improves typographic consistency, but native controls still need testing in each target browser and operating system.

Choosing and maintaining the baseline

Use normalization when content-heavy pages benefit from sensible native typography and the project wants targeted cross-browser corrections. Use a custom reset when a component library defines all relevant tokens and states and the team can maintain that baseline. Some teams combine a small reset with selected normalization rules.

Review the final cascade rather than stacking several resets. Avoid outline: none without an equally visible focus indicator, avoid all: unset on controls unless every lost behavior is rebuilt, and be careful when removing list styles because visual changes can affect how some browser and assistive-technology combinations expose list semantics.

After changing the baseline, test headings, lists, links, forms, validation states, dialogs, keyboard focus, forced colors, zoom, print output, and right-to-left content. The reset is shared infrastructure: a small mistake propagates to every page.

Further reading

Exercises

Check your understanding
Beta
Check your understanding Exercise
Check your understanding Exercise

A content-heavy site wants useful native heading, list, and form defaults while reducing selected cross-browser inconsistencies. Which starting point fits best?

Explain your understanding of the box model and how you would tell the browser in CSS to render your layout in different box models.