Back to interview questions

100+ CSS Interview Questions and Answers

100+ CSS Interview Questions and Answers

Explore 120 CSS interview questions and answers on selectors, cascade, Flexbox, Grid, responsive design, accessibility, performance, and architecture.

Explore 120 CSS interview questions and answers on selectors, cascade, Flexbox, Grid, responsive design, accessibility, performance, and architecture.

By

Team Sherlock.sh

Add Sherlock as a preferred source on Google

CSS interviews reveal whether a candidate can explain why an interface behaves the way it does. A layout that looks correct at one viewport is not enough: recruiters need to assess how candidates reason about the cascade, content sizing, keyboard focus, responsive behavior, and maintainable styling. Asking for the expected result of a small example helps distinguish understanding from memorized property names.

This guide to 100+ CSS Interview Questions and Answers contains 120 questions across beginner, intermediate, and experienced levels. It covers selectors and inheritance, the box model, positioning, Flexbox and Grid, responsive components, typography, animation, accessibility, debugging, and stylesheet architecture. Use the levels to match the role rather than treating every newer feature as a prerequisite.

How can recruiters assess real CSS skills while detecting potential AI cheating, deepfakes, impersonation, and unauthorized assistance?

A CSS interview can ask a candidate to predict which declaration wins, explain a grid overflow, or adapt a component for right-to-left text. Unauthorized assistance can obscure whether the candidate understands those decisions, especially when a polished answer is disconnected from the supplied markup.

Sherlock AI helps recruiters detect potential AI-assisted cheating, deepfakes, candidate impersonation, and unauthorized real-time assistance during CSS interviews.

Pair interview-integrity checks with small, role-relevant changes: add a long label, change the container width, or introduce another cascade layer. Ask the candidate to explain the resulting behavior and its accessibility implications. Review suspicious signals in context rather than treating them as proof of misconduct.

CSS Interview Questions

Select questions for the interface work the role actually requires. Supply the relevant HTML, writing mode, container size, and browser assumptions when a layout result depends on them. Ask for reasoning as well as a property name.

CSS Interview Questions for Beginners

These questions cover CSS syntax, selectors, inheritance, the box model, ordinary layout, basic responsive design, and accessible visual states.

  1. What is CSS, and how is a CSS rule structured?

  2. What are the three main ways to apply CSS to HTML?

  3. How does the CSS cascade resolve competing declarations?

  4. What is selector specificity, and how do you compare it?

  5. How does inheritance work in CSS?

  6. What is the difference between type, class, and ID selectors?

  7. How do selector lists and the universal selector work?

  8. What are attribute selectors, and when are they useful?

  9. What is the difference between descendant, child, and sibling combinators?

  10. What is a pseudo-class?

  11. What is a pseudo-element, and how does it differ from a pseudo-class?

  12. What are the parts of the CSS box model?

  13. How does box-sizing: border-box change element sizing?

  14. What is the difference between margin and padding?

  15. What is margin collapsing?

  16. What is the difference between a border and an outline?

  17. How do width, min-width, and max-width work together?

  18. How do block, inline, and inline-block elements differ?

  19. How do display: none, visibility: hidden, and opacity: 0 differ?

  20. How does the overflow property handle content that does not fit?

  21. What is normal document flow?

  22. What are the basic CSS position values?

  23. What does z-index do?

  24. What is Flexbox, and when should you use it?

  25. How do Flexbox axes and alignment properties work?

  26. What is CSS Grid, and how do you create a simple grid?

  27. What does gap do, and how is it different from margin?

  28. What is the difference between px, em, and rem?

  29. How do percentages and viewport units behave?

  30. What are common ways to specify colors and transparency in CSS?

  31. How do font-family and font-weight affect typography?

  32. Why are line-height and text spacing important?

  33. How do text-align and text-decoration work?

  34. How do background colors and background images work?

  35. How do you make images responsive without distorting them?

  36. What is a media query?

  37. What does mobile-first CSS mean?

  38. How should CSS provide accessible keyboard focus styles?

  39. How are CSS transforms different from normal layout changes?

  40. What are CSS custom properties, and how do you use them?

CSS Interview Questions for Intermediate

These questions examine cascade details, flexible and intrinsic sizing, component queries, animation, typography, and responsive implementation choices.

  1. How do you calculate specificity, and when does a larger score lose?

  2. How do :is(), :where(), :not(), and :has() affect specificity?

  3. How do origin, importance, animations, and transitions affect the cascade?

  4. How do cascade layers change which declaration wins?

  5. When should you use initial, inherit, unset, revert, or revert-layer?

  6. How do custom-property inheritance and var() fallbacks actually work?

  7. What does registering a custom property with @property change?

  8. What is a block formatting context, and how does flow-root help?

  9. When do vertical margins collapse, and how can you prevent it intentionally?

  10. How is the containing block chosen for positioned elements?

  11. Why can a large z-index still appear behind another element?

  12. How do flex-basis, width, and available space determine a flex item's size?

  13. How does flex-shrink distribute negative free space?

  14. Why do flex and grid items sometimes refuse to shrink?

  15. How do alignment, auto margins, and gap interact in flex layouts?

  16. Why does minmax(0, 1fr) behave differently from 1fr in a grid?

  17. What is the difference between auto-fill and auto-fit?

  18. How do explicit tracks, implicit tracks, and grid auto-placement interact?

  19. What problem does subgrid solve compared with a nested grid?

  20. Why does percentage height sometimes behave like auto?

  21. How do min-content, max-content, and fit-content differ?

  22. How do aspect-ratio and object-fit work together?

  23. How should calc(), min(), max(), and clamp() be combined?

  24. When should you use svh, lvh, or dvh instead of vh?

  25. How do you choose useful media queries beyond viewport breakpoints?

  26. How do size container queries differ from media queries?

  27. How do line-height, font metrics, and fallback fonts affect layout?

  28. What trade-offs do font-display values make during font loading?

  29. How do gradients, color stops, and layered backgrounds behave?

  30. How do alpha colors, opacity, and color-mix() differ?

  31. When should you choose a transition instead of a keyframe animation?

  32. Why does transform order matter, and what does transform-origin change?

  33. How do animation delay, direction, and fill mode affect visible states?

  34. How should prefers-reduced-motion change an animated interface?

  35. How do logical properties make layouts work in RTL and vertical writing modes?

  36. What does @supports prove, and how should you structure a fallback?

  37. How does native CSS nesting work, and where can specificity surprise you?

  38. How can :has() express state without adding a parent class?

  39. What is the difference between overflow: hidden, clip, auto, and scroll?

  40. Why does position: sticky sometimes fail to stick?

CSS Interview Questions for Experienced

These questions assess stylesheet architecture, difficult layout failures, performance, internationalization, accessibility, and safe adoption of newer capabilities.

  1. How would you structure cascade layers for a design system used by several product teams?

  2. How would you override third-party CSS without creating a specificity arms race?

  3. A correct-looking declaration is crossed out or has no visible effect. How would you diagnose it?

  4. When would you use revert-layer during a CSS migration?

  5. How would you design CSS tokens for runtime themes without coupling components to a palette?

  6. When would you choose @scope instead of Shadow DOM for component isolation?

  7. Why can a custom property break a declaration without activating its var() fallback?

  8. How do custom-property dependency cycles affect a token system?

  9. When is registering a custom property with @property worth the added contract?

  10. How would you introduce container queries into a reusable card component?

  11. How would you use container style queries without assuming every proposed query is implemented?

  12. What would you investigate before adding CSS containment to a complex widget?

  13. When would content-visibility: auto help a long page, and what are its risks?

  14. How would you investigate a CSS change that makes scrolling or animation slower?

  15. How can a seemingly local CSS change trigger expensive layout across a page?

  16. Why is adding will-change: transform to every animated component a poor optimization?

  17. How would you build fluid typography that remains usable at browser zoom and with enlarged text?

  18. What does a robust migration from left-to-right CSS to bidirectional layouts involve?

  19. How would you audit a component for vertical writing modes rather than only RTL support?

  20. How would you keep keyboard focus visible inside clipped panels and beneath sticky headers?

  21. How would you adapt a custom control for forced-colors mode?

  22. How would you respect reduced-motion preferences without removing important interface feedback?

  23. A sticky sidebar does not stick or stops too early. What would you inspect?

  24. Why might a fixed-position overlay move with a card instead of the viewport?

  25. Why can a modal remain underneath another element despite an enormous z-index?

  26. How would you fix a flex layout where a long title pushes its action buttons outside the container?

  27. How would you diagnose grid columns that overflow despite being sized with fr units?

  28. How would you align repeated cards with subgrid without compromising reading order?

  29. How would you make a dense interface tolerate localization and unpredictable user content?

  30. How would you reduce layout shifts caused by web-font loading?

  31. How would you reserve media space without distorting responsive images or breaking art direction?

  32. What security and privacy concerns arise when an application accepts CSS or loads external CSS assets?

  33. How would you create reliable print styles for a long interactive report?

  34. How would you progressively enhance a tooltip with CSS anchor positioning?

  35. How would you implement a scroll-driven progress indicator without making content depend on animation support?

  36. How would you style view transitions while preserving a usable non-transition experience?

  37. How would you use :has() in a large application without creating fragile or unnecessarily broad selectors?

  38. What would you include in a CSS regression strategy for a shared component library?

  39. How would you retire legacy CSS safely when several teams release independently?

  40. How would you distinguish a valid progressive-enhancement strategy from merely wrapping new CSS in @supports?

CSS Interview Questions and Answers

The answers follow the question bank in order. Examples isolate the behavior being discussed; they are not complete production components. Expected results assume the stated markup, sizing, and cascade conditions. A specification defines intended behavior, while actual feature availability must be checked in the browsers the project supports.

CSS Interview Questions and Answers for Beginners

The beginner answers establish how declarations select elements, produce boxes, and affect everyday interface behavior.

1. What is CSS, and how is a CSS rule structured?

CSS, or Cascading Style Sheets, describes how a document is presented: its colors, typography, spacing, and layout. HTML supplies structure and meaning, while CSS selects elements and assigns presentation values. A style rule consists of a selector followed by a declaration block in braces. Each declaration contains a property, a colon, and a value; semicolons separate declarations.

For example, this rule selects every element with the class notice:

CSS example

.notice {
  color: #17365d;
  background-color: #eef4ff;
  padding: 1rem;
}

.notice {
  color: #17365d;
  background-color: #eef4ff;
  padding: 1rem;
}

.notice {
  color: #17365d;
  background-color: #eef4ff;
  padding: 1rem;
}

The text becomes dark blue, the background becomes pale blue, and space appears inside the element around its content. The rule does not create a notice element or change its semantic role. Multiple declarations can apply to one element, and the cascade resolves conflicts property by property. A misspelled or unsupported declaration is normally ignored rather than making every other valid declaration fail.

2. What are the three main ways to apply CSS to HTML?

CSS can be applied through an external stylesheet linked from HTML, an internal stylesheet inside a style element, or an inline style attribute on an individual element. External stylesheets are a practical default for reusable site styles because several pages can reference the same file. Internal styles suit a document-specific stylesheet. Inline styles attach declarations directly to one element, but become difficult to maintain when repeated.

This HTML connects a stylesheet named styles.css:

HTML example

<head>
  <link rel="stylesheet" href="styles.css">
</head>
<body>
  <p class="notice">Your changes are saved.</p>
</body>

<head>
  <link rel="stylesheet" href="styles.css">
</head>
<body>
  <p class="notice">Your changes are saved.</p>
</body>

<head>
  <link rel="stylesheet" href="styles.css">
</head>
<body>
  <p class="notice">Your changes are saved.</p>
</body>

A .notice rule in that file can then style the paragraph. The URL resolves relative to the HTML document unless another base URL applies, so an incorrect path prevents the stylesheet from loading. An internal stylesheet is not automatically stronger than an external one: ordinary stylesheet rules compete through the cascade. Inline normal declarations usually outrank ordinary author stylesheet declarations, but they are not an unconditional highest-priority override.

3. How does the CSS cascade resolve competing declarations?

The cascade chooses the winning declaration for each property when more than one applicable declaration targets an element. It first considers which rules apply, then precedence factors such as origin and importance, before comparing selector specificity. In a straightforward stylesheet with ordinary rules of equal precedence and specificity, the later declaration wins. This is why saying that CSS always uses the last rule is incomplete.

Here both rules have the same selector and precedence:

CSS example

.notice {
  color: navy;
  padding: 1rem;
}

.notice {
  color: darkgreen;
}

.notice {
  color: navy;
  padding: 1rem;
}

.notice {
  color: darkgreen;
}

.notice {
  color: navy;
  padding: 1rem;
}

.notice {
  color: darkgreen;
}

The notice has dark-green text and retains the earlier padding. CSS does not replace the first rule as a whole; only the conflicting color value is resolved. !important changes a declaration's cascade importance rather than increasing its selector specificity. Before adding it to fix a problem, inspect whether a rule matches, whether its value is valid, and which competing declaration the browser marks as winning.

4. What is selector specificity, and how do you compare it?

Specificity is the relative weight of a selector when the cascade reaches the specificity comparison. For common selectors, compare three counts in order: IDs; classes, attributes, and pseudo-classes; then element names and pseudo-elements. Compare the first unequal count rather than adding the counts as a decimal score. A larger class count cannot compensate for losing the ID comparison.

Assuming ordinary rules with equal cascade precedence:

CSS example

p { color: black; }
.notice { color: navy; }
p.notice { color: darkgreen; }
#summary { color: maroon; }

p { color: black; }
.notice { color: navy; }
p.notice { color: darkgreen; }
#summary { color: maroon; }

p { color: black; }
.notice { color: navy; }
p.notice { color: darkgreen; }
#summary { color: maroon; }

A paragraph with class="notice" and id="summary" receives maroon text. #summary has one ID, whereas the other selectors have none. Without that ID rule, p.notice outweighs .notice because their class counts tie and it adds a type selector. Combinators and the universal selector do not add specificity. Prefer reusable classes and modest selector weights; unnecessarily specific selectors make later component changes harder to express.

5. How does inheritance work in CSS?

Inheritance lets a child use a property's computed value from its parent when that property inherits and no declaration on the child supplies another value. Text properties such as color and font-family commonly inherit. Box properties such as margin, padding, and border usually do not. Inheritance is separate from selector matching: a parent's selector does not become a selector for every descendant.

This example establishes a text color and explicitly lets a link use it:

CSS example

.notice {
  color: #174a37;
  padding: 1rem;
}

.notice a {
  color: inherit;
}

.notice {
  color: #174a37;
  padding: 1rem;
}

.notice a {
  color: inherit;
}

.notice {
  color: #174a37;
  padding: 1rem;
}

.notice a {
  color: inherit;
}

The link takes the notice's computed color, while the notice's padding is not copied onto the link. Any declaration directly setting the child's color participates in that child's cascade before inheritance supplies a fallback. The inherit keyword can request inheritance even for a normally non-inherited property. Use inherited typography to avoid repeating declarations, but inspect form controls and links because browser or author rules may set their values directly.

6. What is the difference between type, class, and ID selectors?

A type selector matches an element name, such as button. A class selector starts with a dot and matches a token in an element's class attribute. An ID selector starts with a hash and matches the element's id. Classes are reusable across many elements; an HTML ID is intended to identify one element within the document.

These rules provide a general button style, a reusable variation, and a specific page hook:

CSS example

button { font: inherit; }
.primary { background-color: navy; color: white; }
#checkout { margin-top: 2rem; }

button { font: inherit; }
.primary { background-color: navy; color: white; }
#checkout { margin-top: 2rem; }

button { font: inherit; }
.primary { background-color: navy; color: white; }
#checkout { margin-top: 2rem; }

A button with class="primary" and id="checkout" can receive all three sets of declarations because they set different properties. An element can have several classes separated by spaces, allowing independent styles to be combined. For ordinary competing declarations of equal cascade precedence, ID selectors outweigh class selectors, and classes outweigh type selectors. Reusable component styling usually benefits from classes rather than unnecessary IDs, which make overrides more specific.

7. How do selector lists and the universal selector work?

A selector list separates selectors with commas so that each matching group receives the same declarations. It is a way to share a rule, not a requirement that one element match every selector. The universal selector, *, matches elements without restricting their element name. It does not automatically select pseudo-elements such as ::before and ::after, so those can be listed separately when needed.

This example shares heading typography and applies a sizing convention:

CSS example

h1,
h2,
.section-title {
  font-family: system-ui, sans-serif;
}

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

h1,
h2,
.section-title {
  font-family: system-ui, sans-serif;
}

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

h1,
h2,
.section-title {
  font-family: system-ui, sans-serif;
}

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

Every matching heading or .section-title receives the font stack. The second rule includes elements and the two named pseudo-elements. By contrast, h1 .section-title without a comma describes a descendant relationship. Broad universal rules should be deliberate because they affect many components. In an ordinary selector list, one invalid selector can invalidate the whole rule, so experimental selectors should not be casually grouped with essential styling.

8. What are attribute selectors, and when are they useful?

Attribute selectors match elements according to the presence or value of an HTML attribute. [required] matches an element that has that attribute, while [type="email"] requires the specified value. Prefix, suffix, and substring operators can match portions of a value, but exact matches are often clearer when an attribute represents a known state.

These rules style required form controls and a status label:

CSS example

input[required] {
  border-left: 0.25rem solid #6b3fa0;
}

[data-status="pending"] {
  background-color: #fff1c2;
}

input[required] {
  border-left: 0.25rem solid #6b3fa0;
}

[data-status="pending"] {
  background-color: #fff1c2;
}

input[required] {
  border-left: 0.25rem solid #6b3fa0;
}

[data-status="pending"] {
  background-color: #fff1c2;
}

A required input receives the accent border, and an element whose data-status is exactly pending receives the background. CSS only responds to the attributes; it does not create validation rules or update application state. The HTML required attribute provides form semantics, whereas the border is presentation. Keep a visible required indicator or other clear explanation because color alone is insufficient. Attribute selectors contribute to specificity in the same category as class selectors.

9. What is the difference between descendant, child, and sibling combinators?

Combinators describe relationships between elements. A space selects descendants at any nesting depth. The child combinator, >, restricts the match to immediate children. The next-sibling combinator, +, matches the immediately following element sibling. The subsequent-sibling combinator, ~, matches later element siblings sharing the same parent, not earlier siblings or arbitrary descendants.

Each rule below targets the element on the right:

CSS example

.article p { line-height: 1.6; }
.menu > li { padding: 0.5rem; }
h2 + p { margin-top: 0; }
h2 ~ p { color: #333; }

.article p { line-height: 1.6; }
.menu > li { padding: 0.5rem; }
h2 + p { margin-top: 0; }
h2 ~ p { color: #333; }

.article p { line-height: 1.6; }
.menu > li { padding: 0.5rem; }
h2 + p { margin-top: 0; }
h2 ~ p { color: #333; }

The first rule reaches paragraphs nested anywhere inside .article. The menu rule avoids matching list items in deeper nested lists unless they are also direct children of a matching menu. The third affects a paragraph immediately after a heading; the fourth can affect several later paragraphs with the same parent. Choosing the narrowest useful relationship prevents accidental styling as markup grows. Combinator symbols themselves do not increase selector specificity.

10. What is a pseudo-class?

A pseudo-class selects an existing element according to a state or structural condition. Examples include :hover for pointer hover, :checked for checked controls, and :first-child for an element that is its parent's first element child. A pseudo-class does not add a real class attribute, and its match can change as the user interacts or the document changes.

This example responds to a native checkbox's checked state:

CSS example

input[type="checkbox"]:checked + label {
  font-weight: 700;
}

.menu > li:first-child {
  margin-top: 0;
}

input[type="checkbox"]:checked + label {
  font-weight: 700;
}

.menu > li:first-child {
  margin-top: 0;
}

input[type="checkbox"]:checked + label {
  font-weight: 700;
}

.menu > li:first-child {
  margin-top: 0;
}

The label becomes bold only when it immediately follows a checked checkbox. The second rule removes the top margin from a menu item that is also its parent's first element child. The checkbox still needs correctly associated label markup and native interaction; CSS supplies only the appearance. Do not make essential information available only on hover, because keyboard and touch users may not encounter that state in the same way.

11. What is a pseudo-element, and how does it differ from a pseudo-class?

A pseudo-element targets a part of an element or a generated box associated with it, rather than selecting the element in a particular state. Common examples are ::first-line, ::before, and ::after. The double-colon notation distinguishes pseudo-elements from pseudo-classes such as :hover. A selector can use both, but they describe different things.

This example adds a decorative rule beneath a heading:

CSS example

.section-title::after {
  content: "";
  display: block;
  width: 3rem;
  height: 0.2rem;
  margin-top: 0.5rem;
  background-color: currentColor;
}

.section-title::after {
  content: "";
  display: block;
  width: 3rem;
  height: 0.2rem;
  margin-top: 0.5rem;
  background-color: currentColor;
}

.section-title::after {
  content: "";
  display: block;
  width: 3rem;
  height: 0.2rem;
  margin-top: 0.5rem;
  background-color: currentColor;
}

The empty content value generates the ::after box, and its dimensions and background make it visible. currentColor uses the heading's text color. This does not insert a real HTML child that can be retrieved as an ordinary DOM element. Generated content is useful for decoration, but essential labels, instructions, and meaningful text should remain in the document rather than depending solely on CSS-generated content and inconsistent accessibility exposure.

12. What are the parts of the CSS box model?

The CSS box model describes content, padding, border, and margin. Content is the area occupied by text or other content. Padding surrounds that content inside the border. The border surrounds the padding, and margin provides space outside the border. Understanding which region a property changes is essential when calculating an element's visible size.

With the standard content-box sizing mode:

CSS example

.card {
  box-sizing: content-box;
  width: 240px;
  padding: 16px;
  border: 2px solid #555;
  margin: 12px;
}

.card {
  box-sizing: content-box;
  width: 240px;
  padding: 16px;
  border: 2px solid #555;
  margin: 12px;
}

.card {
  box-sizing: content-box;
  width: 240px;
  padding: 16px;
  border: 2px solid #555;
  margin: 12px;
}

The card's border-box width is 276 CSS pixels: 240 for content, 32 for horizontal padding, and 4 for borders. The horizontal margins add another 24 pixels of surrounding space, but they are not part of that border box. Background painting normally includes padding, whereas margin is transparent. The calculation illustrates a block box; non-replaced inline elements have additional line-layout rules, so setting width on a plain inline span does not behave like sizing this card.

13. How does box-sizing: border-box change element sizing?

box-sizing: border-box makes a declared width or height include the content, padding, and border. The default content-box interpretation measures only the content area, although browser styles already use border-box for some controls. Border-box often simplifies component sizing because adding padding does not normally increase the declared outer border-box width. Margins remain outside either sizing model.

This card has a total border-box width of 240 CSS pixels:

CSS example

.card {
  box-sizing: border-box;
  width: 240px;
  padding: 16px;
  border: 2px solid #555;
}

.card {
  box-sizing: border-box;
  width: 240px;
  padding: 16px;
  border: 2px solid #555;
}

.card {
  box-sizing: border-box;
  width: 240px;
  padding: 16px;
  border: 2px solid #555;
}

Its content width is 204 pixels after subtracting both padding sides and both borders. Changing the padding to 20 pixels would reduce the content width rather than increase that 240-pixel border box. Border-box is not an overflow cure: long unbreakable content can still extend beyond the available content area. Also, if combined padding and borders exceed a specified size, the content area cannot become negative, so the rendered box can be larger than that size.

14. What is the difference between margin and padding?

Padding creates space between an element's content and its border; margin creates space outside the border between the element and neighboring layout. An element's background normally extends through its padding but not its margin. Padding is therefore useful for the internal breathing room of a card or control, while margin can separate that component from surrounding content.

Here the two forms of spacing serve different purposes:

CSS example

.notice {
  padding: 1rem 1.5rem;
  margin: 0 0 2rem;
  background-color: #eef4ff;
  border: 1px solid #a8bbd8;
}

.notice {
  padding: 1rem 1.5rem;
  margin: 0 0 2rem;
  background-color: #eef4ff;
  border: 1px solid #a8bbd8;
}

.notice {
  padding: 1rem 1.5rem;
  margin: 0 0 2rem;
  background-color: #eef4ff;
  border: 1px solid #a8bbd8;
}

The content has one rem of padding above and below and one-and-a-half rems at each side. The bottom margin separates the notice from following content without extending the blue background. Shorthand values follow top, right, bottom, left order when four are supplied. Padding cannot be negative; margins can, although negative margins require deliberate layout reasoning. Adjacent vertical margins in normal block flow may collapse, so margin spacing is not always a simple sum.

15. What is margin collapsing?

Margin collapsing occurs when certain vertical margins of normal-flow block boxes combine instead of adding together. A common case is the bottom margin of one block meeting the top margin of its following sibling. With two positive margins, the resulting gap is the larger value. This is not how horizontal margins, padding, or gaps between flex and grid items work.

Assuming these are adjacent ordinary block elements with no other spacing:

CSS example

.first { margin-bottom: 24px; }
.second { margin-top: 16px; }

.first { margin-bottom: 24px; }
.second { margin-top: 16px; }

.first { margin-bottom: 24px; }
.second { margin-top: 16px; }

The collapsed margin between them is 24 pixels, not 40. Parent and first or last child margins can also collapse when the relevant separating border, padding, or other conditions are absent. A parent using display: flow-root prevents its margins from collapsing with its children, but does not generally stop sibling margins inside it from collapsing with each other. If consistent additive spacing is the goal, consider a flex or grid container with gap. Negative margins follow additional combination rules rather than simply choosing the largest positive value.

16. What is the difference between a border and an outline?

A border is part of an element's box model, while an outline is drawn around a box without reserving layout space. Border width affects the border-box size or reduces available content space under border-box sizing. Changing an outline normally does not move neighboring elements, which makes it useful for focus indicators. Borders can also be styled side by side; an outline is not a substitute for that level of edge control.

This button keeps a stable border and adds a focus outline:

CSS example

.action {
  border: 1px solid #333;
  padding: 0.6rem 1rem;
}

.action:focus-visible {
  outline: 3px solid #075cca;
  outline-offset: 3px;
}

.action {
  border: 1px solid #333;
  padding: 0.6rem 1rem;
}

.action:focus-visible {
  outline: 3px solid #075cca;
  outline-offset: 3px;
}

.action {
  border: 1px solid #333;
  padding: 0.6rem 1rem;
}

.action:focus-visible {
  outline: 3px solid #075cca;
  outline-offset: 3px;
}

When the button matches :focus-visible, a blue ring appears outside its border with a small offset. The surrounding layout does not need to reserve another three pixels on each side. Do not remove the browser's focus outline unless an effective replacement is present. Check that nearby clipping and background colors do not hide the replacement indicator.

17. How do width, min-width, and max-width work together?

width supplies an element's preferred width, while min-width and max-width constrain it. A maximum is useful when a component should shrink on narrow screens but stop growing on wide screens. A minimum prevents it from becoming smaller than a limit, but an excessive minimum can force horizontal overflow. If the minimum exceeds the maximum, the minimum wins rather than being ignored.

This normal-flow block uses a fluid width with a readable upper limit:

CSS example

.article {
  box-sizing: border-box;
  width: 100%;
  max-width: 48rem;
  padding: 1rem;
  margin-inline: auto;
}

.article {
  box-sizing: border-box;
  width: 100%;
  max-width: 48rem;
  padding: 1rem;
  margin-inline: auto;
}

.article {
  box-sizing: border-box;
  width: 100%;
  max-width: 48rem;
  padding: 1rem;
  margin-inline: auto;
}

The article fills its containing block up to 48 rems, including its padding. When extra horizontal space exists, the auto inline margins distribute it on both sides, centering the block. This is different from text-align: center, which aligns inline content inside a container. Avoid combining a large fixed minimum with this pattern unless the surrounding layout can accommodate it, and check long content that might overflow even when the box itself fits.

18. How do block, inline, and inline-block elements differ?

A block-level box normally starts on a new line in normal flow and, with an automatic width, fills the available inline space. An inline box participates within a line of text; ordinary non-replaced inline elements do not accept width and height in the same way as block boxes. An inline-block is an inline-level box with a block-like interior, so it can have explicit dimensions while sitting alongside text or other inline content.

An inline-block can make a compact badge:

CSS example

.badge {
  display: inline-block;
  min-width: 2rem;
  padding: 0.25rem 0.5rem;
  text-align: center;
  background-color: #e7edf5;
}

.badge {
  display: inline-block;
  min-width: 2rem;
  padding: 0.25rem 0.5rem;
  text-align: center;
  background-color: #e7edf5;
}

.badge {
  display: inline-block;
  min-width: 2rem;
  padding: 0.25rem 0.5rem;
  text-align: center;
  background-color: #e7edf5;
}

The badge remains in the surrounding line, but its minimum width and vertical padding behave as a single rectangular box. Whitespace between inline-block elements can create visible gaps because it is ordinary text whitespace. Changing display changes layout behavior, not the HTML element's meaning: a span styled as a block does not become a semantic heading, and a div styled inline does not become a link.

19. How do display: none, visibility: hidden, and opacity: 0 differ?

display: none removes an element's boxes from layout, so neighboring content can occupy the vacated space. visibility: hidden hides the element while preserving its layout space. opacity: 0 makes the rendered element fully transparent, including its descendants as a composited group, but leaves its layout and potentially its interactions intact.

These classes demonstrate three different outcomes:

CSS example

.removed { display: none; }
.reserved { visibility: hidden; }
.transparent { opacity: 0; }

.removed { display: none; }
.reserved { visibility: hidden; }
.transparent { opacity: 0; }

.removed { display: none; }
.reserved { visibility: hidden; }
.transparent { opacity: 0; }

Apply only one at a time when comparing them. A transparent button can still receive keyboard focus or pointer events, making opacity alone unsuitable for safely hiding an inactive control. A visibility-hidden element cannot receive focus, although a descendant can explicitly restore visibility. Display-none and visibility-hidden content are generally excluded from the accessibility tree, unlike opacity-zero content. Choose based on whether space should remain and whether the content should be available to users; none of these is automatically a screen-reader-only hiding technique.

20. How does the overflow property handle content that does not fit?

The overflow property controls what happens when content extends beyond a box's padding area. visible lets it paint outside. hidden clips it and does not show user scrolling controls, although programmatic scrolling can still occur. auto provides scrolling behavior when needed. scroll requests a scrolling mechanism even without overflow, with the visible scrollbar appearance depending on the platform. clip clips without making the box a scroll container.

This log panel grows only to a defined maximum height:

CSS example

.log {
  max-height: 12rem;
  overflow: auto;
  padding: 1rem;
  border: 1px solid #999;
}

.log {
  max-height: 12rem;
  overflow: auto;
  padding: 1rem;
  border: 1px solid #999;
}

.log {
  max-height: 12rem;
  overflow: auto;
  padding: 1rem;
  border: 1px solid #999;
}

Short content fits naturally; longer content is contained within the panel and can scroll. Overflow usually becomes noticeable when size is constrained, so setting it on an unconstrained box may produce no visible change. Do not hide overflow merely to conceal a layout bug: it can cut off text, menus, or focus rings. Verify that any scrollable region is reachable and usable with a keyboard.

21. What is normal document flow?

Normal document flow is the default way boxes are arranged before techniques such as absolute positioning or floats change their participation. In a typical horizontal writing mode, block boxes stack vertically, while inline content fills lines and wraps when space runs out. Content earlier in the document generally affects where later content is placed.

This layout needs no explicit positioning:

CSS example

.article {
  max-width: 42rem;
  margin-inline: auto;
}

.article p {
  margin-block: 1rem;
}

.article {
  max-width: 42rem;
  margin-inline: auto;
}

.article p {
  margin-block: 1rem;
}

.article {
  max-width: 42rem;
  margin-inline: auto;
}

.article p {
  margin-block: 1rem;
}

Paragraphs remain in flow, so adding text makes a paragraph taller and pushes following content downward. This naturally accommodates translations, user font changes, and unpredictable content length. A flex or grid container can still participate in normal flow even though it uses a different layout model for its own children. By contrast, an absolutely positioned child does not reserve normal-flow space. Start with flow-based layouts and add positioning for specific overlay needs instead of assigning coordinates to every part of a page.

22. What are the basic CSS position values?

position: static is the default and does not use inset offsets. relative preserves an element's normal-flow space while allowing it to be visually offset. absolute removes it from normal flow and positions it against its containing block, commonly established by a positioned ancestor. fixed is usually relative to the viewport, although certain ancestors can establish its containing block. sticky remains in flow but can stick at a specified scroll threshold within its constraints.

A badge can be anchored to a card:

CSS example

.card {
  position: relative;
  padding: 2rem;
}

.card-badge {
  position: absolute;
  top: 0.5rem;
  right: 0.5rem;
}

.card {
  position: relative;
  padding: 2rem;
}

.card-badge {
  position: absolute;
  top: 0.5rem;
  right: 0.5rem;
}

.card {
  position: relative;
  padding: 2rem;
}

.card-badge {
  position: absolute;
  top: 0.5rem;
  right: 0.5rem;
}

The card supplies the badge's positioning reference without being displaced. The badge occupies no normal-flow space, so the card needs enough padding to avoid overlapping its content. Absolute positioning is not automatically relative to the immediate parent. Sticky positioning requires a non-auto inset on the relevant axis and sufficient scrolling room; it does not simply mean fixed to the screen forever.

23. What does z-index do?

z-index controls stacking order for overlapping boxes in the contexts where it applies, including positioned elements and flex or grid items. Within the same stacking context, a higher stack level appears above a lower one. A stacking context groups its descendants into a unit, so a child's large z-index cannot automatically overtake content in a higher ancestor stacking context.

Assume these panels are siblings in the same stacking context:

CSS example

.back-panel {
  position: relative;
  z-index: 1;
}

.front-panel {
  position: relative;
  z-index: 2;
  margin-top: -2rem;
}

.back-panel {
  position: relative;
  z-index: 1;
}

.front-panel {
  position: relative;
  z-index: 2;
  margin-top: -2rem;
}

.back-panel {
  position: relative;
  z-index: 1;
}

.front-panel {
  position: relative;
  z-index: 2;
  margin-top: -2rem;
}

Where the panels overlap, the front panel paints above the back panel. The negative margin creates overlap; z-index by itself does not move either box. If increasing a number has no effect, inspect the elements' ancestors and stacking-context boundaries instead of choosing an arbitrarily huge value. Properties such as transforms and opacity below one can create new contexts, so the visible result depends on more than the number written on the overlapping child.

24. What is Flexbox, and when should you use it?

Flexbox is a layout model designed to arrange items along one main axis, as a row or a column. It is useful for toolbars, navigation groups, button rows, and components whose items should distribute available space. Set display: flex on the parent; its in-flow direct child boxes become flex items. Descendants deeper inside those children do not automatically become items of the same container.

This toolbar allows controls to wrap when necessary:

CSS example

.toolbar {
  display: flex;
  flex-wrap: wrap;
  gap: 0.75rem;
}

.toolbar > .search {
  flex: 1 1 14rem;
  min-width: 0;
}

.toolbar {
  display: flex;
  flex-wrap: wrap;
  gap: 0.75rem;
}

.toolbar > .search {
  flex: 1 1 14rem;
  min-width: 0;
}

.toolbar {
  display: flex;
  flex-wrap: wrap;
  gap: 0.75rem;
}

.toolbar > .search {
  flex: 1 1 14rem;
  min-width: 0;
}

The search item starts from a 14-rem flex basis and may grow or shrink to share space with the other items. min-width: 0 allows it to shrink below its automatic content-based minimum when needed; its content still needs sensible wrapping or sizing. Each wrapped flex line distributes its own space independently. Use Grid instead when aligned rows and columns are the primary requirement, rather than trying to force every flex line into identical tracks.

25. How do Flexbox axes and alignment properties work?

Flexbox alignment is defined relative to the main and cross axes. flex-direction sets the main axis, and the cross axis runs perpendicular to it. justify-content distributes remaining space along the main axis. align-items aligns items along the cross axis, while align-self can override that alignment for one item. The axes are not permanently horizontal and vertical because direction and writing mode matter.

For a row in a horizontal writing mode:

CSS example

.toolbar {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 1rem;
  min-height: 4rem;
}

.toolbar {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 1rem;
  min-height: 4rem;
}

.toolbar {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 1rem;
  min-height: 4rem;
}

Items are centered vertically within the toolbar's cross-axis space, and any remaining horizontal space is distributed between them. The gap remains a baseline separation; space-between can add more space when room is available. If items consume all the main-axis space through their sizes or growth, there may be nothing left for justification to distribute. align-content is different: it distributes flex lines in a multi-line container with spare cross-axis space, not individual items within a line.

26. What is CSS Grid, and how do you create a simple grid?

CSS Grid is a two-dimensional layout model that can coordinate rows and columns. A grid container defines tracks, and its in-flow direct child boxes become grid items that can be placed automatically or explicitly. Grid is well suited to card collections, page regions, and layouts where alignment across several rows matters. Flexbox is often simpler for distributing a single row or column.

This rule creates a two-column card grid:

CSS example

.cards {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 1rem;
}

.cards > * {
  overflow-wrap: anywhere;
}

.cards {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 1rem;
}

.cards > * {
  overflow-wrap: anywhere;
}

.cards {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 1rem;
}

.cards > * {
  overflow-wrap: anywhere;
}

The columns share the available track space equally after the gap is accounted for, and additional cards create new rows. minmax(0, 1fr) allows the tracks to shrink below an automatic content-based minimum; the wrapping rule helps long text fit rather than overflow. fr describes a share of available grid space, not a percentage of the whole viewport. This fixed two-column example still needs an appropriate narrow-screen adaptation if its content becomes cramped.

27. What does gap do, and how is it different from margin?

gap sets gutters between tracks or items in layout systems such as Grid and Flexbox. It is a shorthand for row-gap and column-gap. A single value supplies both; with two values, the first is the row gap and the second is the column gap. Unlike margins applied to every child, a gap does not automatically create spacing around the container's outer edges.

This grid separates cards and also provides intentional outer padding:

CSS example

.cards {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 1.5rem 1rem;
  padding: 1rem;
}

.cards {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 1.5rem 1rem;
  padding: 1rem;
}

.cards {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 1.5rem 1rem;
  padding: 1rem;
}

Rows are separated by one-and-a-half rems and columns by one rem. The padding, not the gap, supplies space between the outer cards and the container boundary. Child margins or distributed alignment can add further visible separation, so the measured distance is not necessarily only the gap value. Prefer gap for a container's consistent internal rhythm, and use margins when an individual item needs separate spacing behavior.

28. What is the difference between px, em, and rem?

px is a CSS reference-pixel unit, not necessarily one physical display pixel. rem is relative to the root element's font size. em is relative to the element's font size for properties such as padding, but when used in font-size itself it refers to the inherited font size. This distinction explains why nested em-based font sizes can compound while rem-based sizes stay tied to one root reference.

This button scales its internal spacing with its text:

CSS example

.button {
  font-size: 1rem;
  padding: 0.6em 1em;
  border: 1px solid currentColor;
}

.button {
  font-size: 1rem;
  padding: 0.6em 1em;
  border: 1px solid currentColor;
}

.button {
  font-size: 1rem;
  padding: 0.6em 1em;
  border: 1px solid currentColor;
}

If the root font size is 16 CSS pixels, the text is 16 pixels and the padding is 9.6 pixels vertically and 16 horizontally. If that root size changes, the rem text and em padding scale together. Do not assume one rem is always 16 pixels. Pixels can be appropriate for fine borders, while relative units help express relationships and accommodate user preferences; accessibility still requires checking zoom, wrapping, and the complete layout.

29. How do percentages and viewport units behave?

A percentage is relative to a reference value determined by the property, not universally to the viewport or immediate parent's height. A normal block's percentage width is based on its containing block's width. Percentage heights need a suitable definite height reference in common block layouts, so height: 100% may not fill the screen when an ancestor's height is automatic. Viewport units instead describe proportions of a viewport dimension.

These declarations use two different references:

CSS example

.panel {
  width: 80%;
  min-height: 50vh;
}

.panel {
  width: 80%;
  min-height: 50vh;
}

.panel {
  width: 80%;
  min-height: 50vh;
}

The panel is 80 percent of its containing block's width, while the minimum height is half the height represented by the vh unit. It can grow taller if its content requires it. Mobile browser controls can change the usable visible area; small, large, and dynamic viewport variants such as svh, lvh, and dvh address different sizing goals. Choose deliberately and check target environments rather than treating every viewport-height unit as interchangeable.

30. What are common ways to specify colors and transparency in CSS?

CSS colors can be expressed with named keywords, hexadecimal notation, and functions such as rgb() and hsl(). RGB describes red, green, and blue channels; HSL describes hue, saturation, and lightness. An alpha channel adds transparency to the particular color. By contrast, the opacity property affects an element and its rendered descendants together, which can unintentionally fade text as well as its background.

Here only the background color is translucent:

CSS example

.notice {
  color: #15253a;
  background-color: rgb(30 100 210 / 15%);
  border: 1px solid currentColor;
}

.notice {
  color: #15253a;
  background-color: rgb(30 100 210 / 15%);
  border: 1px solid currentColor;
}

.notice {
  color: #15253a;
  background-color: rgb(30 100 210 / 15%);
  border: 1px solid currentColor;
}

The text stays opaque, the blue background blends with what is behind it, and the border takes the text color through currentColor. The final appearance of a translucent color depends on that underlying background. Check contrast in the actual composition, not only against an assumed white surface. Color should reinforce meaning rather than carry it alone, so pair status colors with clear text or another recognizable indicator.

31. How do font-family and font-weight affect typography?

font-family supplies a prioritized font stack. The browser uses an available font that can render the relevant text, falling back when a family or glyph is missing. A generic family such as sans-serif gives the browser a final category to choose from. Naming a custom font does not download it; a web font must also be supplied through an appropriate loading mechanism such as @font-face.

This stack favors the user's system interface font:

CSS example

body {
  font-family: system-ui, "Segoe UI", sans-serif;
  font-weight: 400;
}

h1,
h2 {
  font-weight: 700;
}

body {
  font-family: system-ui, "Segoe UI", sans-serif;
  font-weight: 400;
}

h1,
h2 {
  font-weight: 700;
}

body {
  font-family: system-ui, "Segoe UI", sans-serif;
  font-weight: 400;
}

h1,
h2 {
  font-weight: 700;
}

Body text requests normal weight, while headings request a bold weight. The result depends on the available faces and the font's supported weight range; a browser may choose another available weight or synthesize an appearance. A generic family name is not quoted, whereas quoting a multiword named family is clear and safe. Check line breaks and layout using fallback fonts too, because different font metrics can change text width and component height.

32. Why are line-height and text spacing important?

line-height controls the space used for lines of text and strongly influences readability. A unitless value acts as a multiplier of each element's font size and can inherit without freezing the parent's computed pixel line height. letter-spacing changes spacing between characters, while word-spacing changes spacing between words. These are different tools and should not be used interchangeably to force text into a fixed box.

This article style provides generous line spacing:

CSS example

.article {
  font-size: 1rem;
  line-height: 1.6;
}

.article .eyebrow {
  letter-spacing: 0.04em;
}

.article {
  font-size: 1rem;
  line-height: 1.6;
}

.article .eyebrow {
  letter-spacing: 0.04em;
}

.article {
  font-size: 1rem;
  line-height: 1.6;
}

.article .eyebrow {
  letter-spacing: 0.04em;
}

If a paragraph's font size is 16 CSS pixels, its unitless line-height corresponds to 25.6 pixels before other inline content affects the line box. A larger child that inherits the number uses its own font size to calculate the result. The eyebrow receives a modest extra character spacing. Avoid tight fixed heights on text containers, and verify that larger text and user-adjusted spacing do not cause clipping or overlap.

33. How do text-align and text-decoration work?

text-align aligns inline-level content within a block container; it does not move the block itself. center can center a line of text or an inline image inside its container, while start follows the content's writing direction. text-decoration controls decorative lines such as underlines and strikethroughs. Its shorthand can specify the line, style, color, and thickness.

These rules align an introduction and preserve recognizable links:

CSS example

.intro {
  text-align: center;
}

.article a {
  text-decoration: underline;
  text-underline-offset: 0.15em;
}

.intro {
  text-align: center;
}

.article a {
  text-decoration: underline;
  text-underline-offset: 0.15em;
}

.intro {
  text-align: center;
}

.article a {
  text-decoration: underline;
  text-underline-offset: 0.15em;
}

The introduction's text is centered within its existing box, and article links receive an underline slightly separated from their text. To center a width-constrained block itself, use an appropriate layout technique or auto inline margins rather than relying on text-align. Removing every link underline can make links difficult to distinguish from ordinary text, especially when color is the only remaining cue. A child cannot simply cancel a decoration drawn across it by an ancestor using text-decoration: none.

34. How do background colors and background images work?

Background properties paint behind an element's content. A background color provides a base, while a background image can add decoration. background-size, background-position, and background-repeat control the image's presentation. A background image does not determine the element's dimensions, so content, padding, or an explicit sizing rule must provide the visible area.

This banner uses an image with a fallback color:

CSS example

.banner {
  min-height: 12rem;
  background-color: #203447;
  background-image: url("/images/pattern.png");
  background-size: cover;
  background-position: center;
  background-repeat: no-repeat;
}

.banner {
  min-height: 12rem;
  background-color: #203447;
  background-image: url("/images/pattern.png");
  background-size: cover;
  background-position: center;
  background-repeat: no-repeat;
}

.banner {
  min-height: 12rem;
  background-color: #203447;
  background-image: url("/images/pattern.png");
  background-size: cover;
  background-position: center;
  background-repeat: no-repeat;
}

The image scales proportionally to cover the banner and may be cropped; the color remains behind it and can appear if the image cannot load. Background images have no ordinary image alt attribute, so keep meaningful information in HTML and use backgrounds for decoration. Also remember that the background shorthand resets omitted background subproperties, so a later shorthand can unintentionally remove an earlier image or sizing choice.

35. How do you make images responsive without distorting them?

For an ordinary content image, a fluid maximum width and automatic height let it shrink to fit its container while preserving its intrinsic aspect ratio. Use object-fit when the image element has a deliberately constrained box and you need to control how its image content fits within that box. cover preserves proportions but can crop; contain preserves the whole image but can leave unused space.

These rules demonstrate the two use cases:

CSS example

.article-image {
  display: block;
  max-width: 100%;
  height: auto;
}

.avatar {
  width: 4rem;
  height: 4rem;
  object-fit: cover;
  border-radius: 50%;
}

.article-image {
  display: block;
  max-width: 100%;
  height: auto;
}

.avatar {
  width: 4rem;
  height: 4rem;
  object-fit: cover;
  border-radius: 50%;
}

.article-image {
  display: block;
  max-width: 100%;
  height: auto;
}

.avatar {
  width: 4rem;
  height: 4rem;
  object-fit: cover;
  border-radius: 50%;
}

The article image shrinks when its container is narrower than its natural width. The avatar fills a square image box without stretching the image content, then clips into a circle. Neither rule chooses an appropriately sized network resource; HTML features such as srcset and sizes address that separate concern. Supply meaningful alternative text when appropriate, and retain image dimensions in HTML to help reserve space during loading.

36. What is a media query?

A media query conditionally applies CSS according to media characteristics, such as viewport width, print output, or a user preference. It does not add special specificity to the selectors inside it. When the condition matches, those declarations participate in the normal cascade alongside other applicable rules. Several media queries can match at the same time.

This layout changes when the viewport reaches a chosen width:

CSS example

.layout {
  display: grid;
  grid-template-columns: 1fr;
  gap: 1rem;
}

@media (min-width: 48rem) {
  .layout {
    grid-template-columns: 2fr 1fr;
  }
}

.layout {
  display: grid;
  grid-template-columns: 1fr;
  gap: 1rem;
}

@media (min-width: 48rem) {
  .layout {
    grid-template-columns: 2fr 1fr;
  }
}

.layout {
  display: grid;
  grid-template-columns: 1fr;
  gap: 1rem;
}

@media (min-width: 48rem) {
  .layout {
    grid-template-columns: 2fr 1fr;
  }
}

Below the threshold, content uses a single column. At or above it, the main area and sidebar use tracks with a two-to-one flexible ratio, subject to their sizing constraints. The breakpoint should reflect when the content benefits from the extra column, not an assumed model of phone or tablet. Remember to test near the breakpoint as well as far above and below it, and avoid treating viewport width as proof of a user's input method.

37. What does mobile-first CSS mean?

Mobile-first CSS starts with a usable layout for limited space, then adds enhancements as more space becomes available. It usually pairs simple base styles with minimum-width media queries. The goal is not to identify phones; it is to keep the smallest layout complete and let the content determine when a larger arrangement becomes useful. Responsive design also depends on flexible dimensions, media, and readable text, not only breakpoints.

This card list begins as a single column:

CSS example

.products {
  display: grid;
  grid-template-columns: 1fr;
  gap: 1rem;
}

@media (min-width: 40rem) {
  .products {
    grid-template-columns: repeat(2, minmax(0, 1fr));
  }
}

.products {
  display: grid;
  grid-template-columns: 1fr;
  gap: 1rem;
}

@media (min-width: 40rem) {
  .products {
    grid-template-columns: repeat(2, minmax(0, 1fr));
  }
}

.products {
  display: grid;
  grid-template-columns: 1fr;
  gap: 1rem;
}

@media (min-width: 40rem) {
  .products {
    grid-template-columns: repeat(2, minmax(0, 1fr));
  }
}

The default stays readable without needing to undo a desktop-specific layout. Larger viewports receive two columns when appropriate. A responsive HTML document should also use a suitable viewport meta setting, such as width=device-width, initial-scale=1, without disabling user zoom. Keep the DOM's reading order logical and test long content, text enlargement, and intermediate widths rather than checking only a few named device sizes.

38. How should CSS provide accessible keyboard focus styles?

Keyboard users need to see which interactive element currently has focus. CSS should preserve the browser's focus indicator or provide a clearly visible replacement. :focus matches a focused element, while :focus-visible matches when the browser's heuristics indicate that a focus indicator should be shown. It is useful for focus styling, but it is not an exact keyboard-only detector.

This example adds an indicator without removing default outlines elsewhere:

CSS example

a:focus-visible,
button:focus-visible,
input:focus-visible {
  outline: 3px solid #075cca;
  outline-offset: 3px;
}

a:focus-visible,
button:focus-visible,
input:focus-visible {
  outline: 3px solid #075cca;
  outline-offset: 3px;
}

a:focus-visible,
button:focus-visible,
input:focus-visible {
  outline: 3px solid #075cca;
  outline-offset: 3px;
}

The matching control receives a blue outline separated from its edge. Check that the ring contrasts with adjacent backgrounds and is not clipped by an ancestor or covered by a sticky element. A hover effect alone is not a focus indicator. CSS also cannot make arbitrary clickable markup fully accessible: prefer native links and buttons with their keyboard behavior. Review the complete interface by tabbing through it in a logical order and verifying that each active control remains visible.

39. How are CSS transforms different from normal layout changes?

A transform changes how an element is visually rendered through translation, rotation, scaling, or skewing. It does not make surrounding normal-flow content reserve space for the transformed shape. Changing layout properties such as width or margin can affect neighboring positions instead. This makes transforms useful for decorative adjustment, but a poor replacement for a layout that must accommodate content changes.

This badge is tilted without reflowing adjacent text:

CSS example

.badge {
  display: inline-block;
  padding: 0.25rem 0.5rem;
  transform: rotate(-4deg);
}

.badge {
  display: inline-block;
  padding: 0.25rem 0.5rem;
  transform: rotate(-4deg);
}

.badge {
  display: inline-block;
  padding: 0.25rem 0.5rem;
  transform: rotate(-4deg);
}

Its original inline-block box still participates in the line layout, while its painted corners rotate and may overlap nearby content. The inline-block declaration matters because an ordinary non-replaced inline span is not transformable in the same way. A non-none transform also creates a stacking context and can establish a containing block for positioned descendants, so adding one may affect overlays. Do not assume every transform guarantees better performance; choose it for the intended visual behavior and inspect the actual component.

40. What are CSS custom properties, and how do you use them?

CSS custom properties are author-defined values whose names begin with two hyphens. Reference them with var() to share values such as colors and spacing across rules. Ordinary double-dash custom properties participate in the cascade and inherit, so a value can be defined broadly and overridden for a particular component or subtree. This is different from a one-time text substitution performed by a stylesheet preprocessor.

This button uses a shared accent with a local variation:

CSS example

:root {
  --accent: #174a8b;
}

.button {
  background-color: var(--accent, navy);
  color: white;
}

.button.danger {
  --accent: #9f2037;
}

:root {
  --accent: #174a8b;
}

.button {
  background-color: var(--accent, navy);
  color: white;
}

.button.danger {
  --accent: #9f2037;
}

:root {
  --accent: #174a8b;
}

.button {
  background-color: var(--accent, navy);
  color: white;
}

.button.danger {
  --accent: #9f2037;
}

A normal button uses the root accent; a button with the additional danger class uses the local red value. If the referenced custom property is missing or otherwise triggers fallback, navy is used. That fallback does not validate every token value: defining --accent: 10px does not make a valid background color or automatically select navy. Choose meaningful names and keep values appropriate for the properties that consume them.

CSS Interview Questions and Answers for Intermediate

The intermediate answers connect properties to the layout and cascade rules that determine their result, including common edge cases.

41. How do you calculate specificity, and when does a larger score lose?

Specificity compares selectors as three ordered columns: IDs; classes, attributes, and most pseudo-classes; then element names and pseudo-elements. Compare left to right, not as a decimal number. One ID outweighs any number of classes. Combinators and the universal selector contribute nothing.

CSS example

#account .notice { color: navy; } /* 1-1-0 */
main .notice.warning { color: red; } /* 0-2-1 */

#account .notice { color: navy; } /* 1-1-0 */
main .notice.warning { color: red; } /* 0-2-1 */

#account .notice { color: navy; } /* 1-1-0 */
main .notice.warning { color: red; } /* 0-2-1 */

For a matching notice, navy wins if these declarations share the same origin, importance, and layer. However, specificity is not the first cascade decision. An important declaration can beat a normal declaration, and a later normal layer can beat an earlier layer regardless of selector weight. Normal inline styles also outrank normal author stylesheet rules. When specificity ties, scoped rules can be distinguished by scoping proximity before source order is considered. Therefore, adding another class cannot solve a conflict caused by a higher-precedence cascade category.

42. How do :is(), :where(), :not(), and :has() affect specificity?

:is(), :not(), and :has() contribute the specificity of their most specific selector argument, not an extra pseudo-class weight. The highest-weight argument matters even when a different argument caused the match. :where() is different: the function and everything inside it contribute zero specificity.

CSS example

:is(.panel, #featured) a { color: purple; } /* 1-0-1 */
:where(.panel, #featured) a { color: teal; } /* 0-0-1 */
.card:not(.muted) { opacity: 1; } /* 0-2-0 */
.card:has(> img) { padding: 1rem; } /* 0-1-1 */

:is(.panel, #featured) a { color: purple; } /* 1-0-1 */
:where(.panel, #featured) a { color: teal; } /* 0-0-1 */
.card:not(.muted) { opacity: 1; } /* 0-2-0 */
.card:has(> img) { padding: 1rem; } /* 0-1-1 */

:is(.panel, #featured) a { color: purple; } /* 1-0-1 */
:where(.panel, #featured) a { color: teal; } /* 0-0-1 */
.card:not(.muted) { opacity: 1; } /* 0-2-0 */
.card:has(> img) { padding: 1rem; } /* 0-1-1 */

Assuming equal cascade context, the first link rule wins over the second even for a link inside an ordinary .panel. Use :where() for low-weight component defaults that consumers should easily override. Use the other functions for their matching behavior, rather than inserting IDs merely to increase priority. Selector-list factoring can change specificity, so replacing separate rules with :is() is not always a neutral refactor.

43. How do origin, importance, animations, and transitions affect the cascade?

The cascade considers origin and importance before selector specificity. In ascending precedence, the main categories are normal user-agent rules, normal user rules, normal author rules, keyframe animations, important author rules, important user rules, important user-agent rules, and active transitions. Layers and inline styles refine precedence within these categories.

CSS example

.status {
  color: red !important;
  animation: tint 2s infinite;
}
@keyframes tint {
  to { color: blue; }
}

.status {
  color: red !important;
  animation: tint 2s infinite;
}
@keyframes tint {
  to { color: blue; }
}

.status {
  color: red !important;
  animation: tint 2s infinite;
}
@keyframes tint {
  to { color: blue; }
}

The animation cannot override the important red declaration. By contrast, an actively transitioning property's interpolated value occupies the transition category, above important declarations; this does not mean every important change automatically creates a transition. User-important styles intentionally outrank author-important styles so user needs can prevail. Also, !important is not a specificity multiplier, and important declarations inside keyframes are ignored. A high-weight author selector cannot defeat a higher-precedence user-important declaration merely by becoming more specific.

44. How do cascade layers change which declaration wins?

Cascade layers establish precedence before selector specificity. For normal declarations in the same origin, later layers beat earlier layers, and unlayered stylesheet declarations beat layered ones. Layer order is established when layers are first declared; adding rules to an existing layer does not move it.

CSS example

@layer foundation, components;
@layer foundation {
  #checkout .button { color: navy; }
}
@layer components {
  .button { color: teal; }
}

@layer foundation, components;
@layer foundation {
  #checkout .button { color: navy; }
}
@layer components {
  .button { color: teal; }
}

@layer foundation, components;
@layer foundation {
  #checkout .button { color: navy; }
}
@layer components {
  .button { color: teal; }
}

The button is teal despite the earlier ID selector, assuming no higher-precedence rule intervenes. Important declarations reverse the layer order: important declarations in the first layer outrank those in later layers, and layered important declarations outrank unlayered important stylesheet declarations. Important inline styles remain above other important author styles. This reversal is why placing overrides in a last layer and adding !important can produce the opposite result from what was intended. Establish the layer order explicitly rather than relying on stylesheet arrival order.

45. When should you use initial, inherit, unset, revert, or revert-layer?

initial uses the property's specification-defined initial value, not the element's browser stylesheet default. inherit copies the parent's computed value. unset chooses inheritance for inherited properties and the initial value for non-inherited properties. revert rolls back the current origin's contribution; in author CSS it can expose user or user-agent styling. revert-layer instead removes the current layer's contribution for that property.

CSS example

@layer base, theme;
@layer base { .notice { color: navy; } }
@layer theme {
  .notice { color: crimson; }
  .notice.neutral { color: revert-layer; }
}

@layer base, theme;
@layer base { .notice { color: navy; } }
@layer theme {
  .notice { color: crimson; }
  .notice.neutral { color: revert-layer; }
}

@layer base, theme;
@layer base { .notice { color: navy; } }
@layer theme {
  .notice { color: crimson; }
  .notice.neutral { color: revert-layer; }
}

The neutral notice uses navy from base, assuming no competing rule outside these layers. Replacing revert-layer with initial would choose the initial color rather than navy. Similarly, display: initial does not restore a heading's normal block presentation because the initial display value is inline. Select the keyword according to whether the goal is inheritance, a specification default, an origin rollback, or a layer rollback.

46. How do custom-property inheritance and var() fallbacks actually work?

Unregistered custom properties inherit by default and hold token sequences that are substituted when a consuming declaration computes its value. A var() fallback is used when the referenced custom property is unavailable or has the guaranteed-invalid value, not whenever its tokens are unsuitable for the destination property.

CSS example

.notice {
  --accent: 12px;
  color: green;
  color: var(--accent, red);
  border-color: var(--border, blue);
}

.notice {
  --accent: 12px;
  color: green;
  color: var(--accent, red);
  border-color: var(--border, blue);
}

.notice {
  --accent: 12px;
  color: green;
  color: var(--accent, red);
  border-color: var(--border, blue);
}

The border uses blue when --border is undefined. The color does not become red: --accent exists, but 12px is invalid for color. The winning declaration becomes invalid at computed-value time, so color behaves as unset and inherits; the earlier green declaration is not reconsidered. Nested fallbacks such as var(--local, var(--brand, teal)) provide a deliberate token lookup chain. A fallback also cannot teach a browser without custom-property support how to parse var(); that is a separate compatibility concern.

47. What does registering a custom property with @property change?

@property gives a custom property a syntax, an inheritance policy, and usually an initial value. Unlike an unregistered token sequence, a registered value can be validated and animated according to its declared type. Registration does not remove the property from the cascade.

CSS example

@property --angle {
  syntax: "<angle>";
  inherits: false;
  initial-value: 0deg;
}
.dial {
  transform: rotate(var(--angle));
  transition: --angle 300ms ease;
}
.dial.active { --angle: 90deg; }

@property --angle {
  syntax: "<angle>";
  inherits: false;
  initial-value: 0deg;
}
.dial {
  transform: rotate(var(--angle));
  transition: --angle 300ms ease;
}
.dial.active { --angle: 90deg; }

@property --angle {
  syntax: "<angle>";
  inherits: false;
  initial-value: 0deg;
}
.dial {
  transform: rotate(var(--angle));
  transition: --angle 300ms ease;
}
.dial.active { --angle: 90deg; }

With registration support, changing the class interpolates the angle rather than treating an untyped custom property as a discrete token change. Descendants do not inherit this property because inherits is false; absent a value, they receive the registered initial angle. For a typed registration, the initial value must be computationally independent, so a length such as 10px is suitable while a font-dependent 2em is not. Registration is useful when a value's type and inheritance behavior are part of the component's contract.

48. What is a block formatting context, and how does flow-root help?

A block formatting context is a layout boundary governing block boxes and floats. Its contents interact with floats within that context rather than having those floats escape into surrounding layout. A block establishing a new BFC includes its internal floats in its automatic height and prevents parent-child margin collapse across its boundary.

CSS example

.article-summary { display: flow-root; }
.article-summary img {
  float: inline-start;
  inline-size: 6rem;
  margin-inline-end: 1rem;
}

.article-summary { display: flow-root; }
.article-summary img {
  float: inline-start;
  inline-size: 6rem;
  margin-inline-end: 1rem;
}

.article-summary { display: flow-root; }
.article-summary img {
  float: inline-start;
  inline-size: 6rem;
  margin-inline-end: 1rem;
}

The summary can grow to enclose the floated image without adding a clearing element. display: flow-root states the intention directly. Using overflow: hidden can also establish a BFC on a block, but adds clipping and scroll-container behavior that may be unwanted. A BFC is not a stacking context and does not itself establish an absolute-positioning containing block. Also, normal block siblings inside the new context can still collapse margins with each other; the boundary does not globally switch off margin collapsing.

49. When do vertical margins collapse, and how can you prevent it intentionally?

In ordinary block flow with horizontal writing, adjoining vertical margins can collapse between siblings, between a parent and its first or last in-flow child, or through an empty block. Two positive margins collapse to the larger value, not their sum. With positive and negative margins, combine the largest positive with the most negative; an all-negative group uses the most negative margin.

CSS example

.first { margin-block-end: 24px; }
.second { margin-block-start: 16px; }

.first { margin-block-end: 24px; }
.second { margin-block-start: 16px; }

.first { margin-block-end: 24px; }
.second { margin-block-start: 16px; }

Adjacent normal block siblings therefore have a 24-pixel gap, assuming nothing prevents collapse. Parent-child collapse depends on details such as borders, padding, inline content, height, and clearance. display: flow-root on the parent prevents collapse across its boundary. Flex and grid item margins do not collapse, so a column flex layout with gap can provide explicitly additive spacing. Do not add a transparent border merely as a reflex: decide whether the intended model is collapsed document spacing, internal padding, or layout-managed gaps.

50. How is the containing block chosen for positioned elements?

The containing block supplies the reference rectangle for layout calculations, including many percentages and positioned offsets. An absolutely positioned element normally uses the padding box of its nearest ancestor with non-static positioning. A fixed-position element normally uses the viewport in continuous media, but certain ancestor properties, including a non-none transform, can establish its containing block instead.

CSS example

.card { position: relative; padding: 1rem; }
.card .badge {
  position: absolute;
  inset-block-start: 0;
  inset-inline-end: 0;
}

.card { position: relative; padding: 1rem; }
.card .badge {
  position: absolute;
  inset-block-start: 0;
  inset-inline-end: 0;
}

.card { position: relative; padding: 1rem; }
.card .badge {
  position: absolute;
  inset-block-start: 0;
  inset-inline-end: 0;
}

The badge anchors to the card's padding box, not necessarily the viewport or the card's content box. position: relative does not remove the card from normal flow. A containing block is also not synonymous with a DOM parent: unpositioned ancestors may be skipped. Setting z-index alone is not a general way to establish this reference rectangle. Determine the positioning mode and the qualifying ancestor before interpreting a percentage offset or an apparently misplaced fixed element.

51. Why can a large z-index still appear behind another element?

A z-index is compared within the relevant stacking context, not globally across the document. A descendant cannot use a huge number to escape an ancestor stacking context that is painted beneath another context. The ancestor's entire context participates as one unit in its parent's ordering.

CSS example

.sidebar { position: relative; z-index: 1; }
.sidebar .menu { position: absolute; z-index: 9999; }
.content { position: relative; z-index: 2; }

.sidebar { position: relative; z-index: 1; }
.sidebar .menu { position: absolute; z-index: 9999; }
.content { position: relative; z-index: 2; }

.sidebar { position: relative; z-index: 1; }
.sidebar .menu { position: absolute; z-index: 9999; }
.content { position: relative; z-index: 2; }

If sidebar and content are sibling stacking contexts in the same parent, overlapping content paints above the menu. Increasing the menu's number does not change that relationship. Positioned elements with non-auto z-index, transforms, opacity below one, and isolation: isolate are common context creators; fixed and sticky elements also create contexts. Conversely, merely setting position: relative with z-index: auto does not create one. Correct the ordering of the relevant ancestor contexts or change the element's placement, rather than treating every layering issue as an insufficient integer.

52. How do flex-basis, width, and available space determine a flex item's size?

flex-basis establishes the starting main-axis size used by flex sizing. A definite non-auto basis takes precedence over the main-size property for that starting point. With flex-basis: auto, a definite main-size property can supply the basis; otherwise content sizing is involved. The final size can still change through growing, shrinking, and minimum or maximum constraints.

CSS example

.row { display: flex; inline-size: 600px; }
.item {
  width: 200px;
  flex: 1 1 100px;
  min-width: 0;
}

.row { display: flex; inline-size: 600px; }
.item {
  width: 200px;
  flex: 1 1 100px;
  min-width: 0;
}

.row { display: flex; inline-size: 600px; }
.item {
  width: 200px;
  flex: 1 1 100px;
  min-width: 0;
}

For three identical items in a horizontal row, ignoring padding, borders, margins, and gaps, each starts from 100 pixels. The remaining 300 pixels are distributed equally, producing 200-pixel items. That final match with width is coincidental, not evidence that width supplied the basis. Percentages in flex-basis refer to the container's inner main size; when that size is indefinite, the percentage basis uses content sizing rather than resolving against an arbitrary ancestor.

53. How does flex-shrink distribute negative free space?

Flex shrink distributes negative free space using each item's scaled shrink factor: its flex-shrink value multiplied by its inner flex base size. Equal shrink factors therefore do not mean equal pixel reductions. Larger bases normally surrender proportionally more space, subject to minimum and maximum constraints and the algorithm's freezing steps.

CSS example

.row { display: flex; width: 300px; }
.small { flex: 0 1 100px; min-width: 0; }
.large { flex: 0 1 300px; min-width: 0; }

.row { display: flex; width: 300px; }
.small { flex: 0 1 100px; min-width: 0; }
.large { flex: 0 1 300px; min-width: 0; }

.row { display: flex; width: 300px; }
.small { flex: 0 1 100px; min-width: 0; }
.large { flex: 0 1 300px; min-width: 0; }

With only these two items and no gaps, margins, padding, or borders, their bases total 400 pixels. The 100-pixel deficit is divided in a 1:3 ratio: the small item loses 25 pixels and the large item loses 75, ending at 75 and 225 pixels. If a minimum prevents an item from shrinking that far, remaining deficit is redistributed. flex-shrink: 0 opts an item out of shrinking, but cannot guarantee the container itself will avoid overflow.

54. Why do flex and grid items sometimes refuse to shrink?

A flex or grid item's automatic minimum size can preserve a content-based minimum rather than allowing the item to become arbitrarily small. A long unbreakable string, wide image, or preformatted block can therefore overflow a layout even when flexible sizing appears correct. The exact automatic-minimum rules differ between flex and grid and include exceptions for scroll containers and some grid spans.

CSS example

.row { display: flex; }
.row .title {
  flex: 1 1 0;
  min-inline-size: 0;
  overflow: hidden;
  white-space: nowrap;
  text-overflow: ellipsis;
}

.row { display: flex; }
.row .title {
  flex: 1 1 0;
  min-inline-size: 0;
  overflow: hidden;
  white-space: nowrap;
  text-overflow: ellipsis;
}

.row { display: flex; }
.row .title {
  flex: 1 1 0;
  min-inline-size: 0;
  overflow: hidden;
  white-space: nowrap;
  text-overflow: ellipsis;
}

In a horizontal writing mode, the explicit zero minimum permits the title box to shrink. The remaining declarations then provide single-line truncation instead of merely forcing the box smaller while its text paints outside. For a vertically constrained column flex layout, the analogous adjustment is often min-block-size: 0. Set the minimum on the actual flex or grid item involved, not automatically on a deeper child that does not participate in that layout.

55. How do alignment, auto margins, and gap interact in flex layouts?

Flexbox alignment follows its main and cross axes, which are determined by writing mode and flex-direction. justify-content distributes remaining main-axis space; align-items and align-self align items on the cross axis. Auto margins absorb positive free space before alignment distribution, while gap supplies explicit gutters between items or lines.

CSS example

.toolbar {
  display: flex;
  align-items: center;
  gap: 0.75rem;
}
.toolbar .account { margin-inline-start: auto; }

.toolbar {
  display: flex;
  align-items: center;
  gap: 0.75rem;
}
.toolbar .account { margin-inline-start: auto; }

.toolbar {
  display: flex;
  align-items: center;
  gap: 0.75rem;
}
.toolbar .account { margin-inline-start: auto; }

For a normal row, the account item is pushed toward inline-end when free space exists, leaving the earlier controls grouped together. This avoids adding a spacer element. justify-self does not position individual flex items along the main axis. align-content distributes flex lines in a multiline container with cross-axis space; it is not a substitute for align-items on a single-line container. Also, the visible distance between items can exceed gap when margins or distributed alignment add more space.

56. Why does minmax(0, 1fr) behave differently from 1fr in a grid?

A bare flexible grid track such as 1fr has an automatic minimum, effectively behaving as minmax(auto, 1fr). That minimum can be influenced by the items' content-based minimum sizes. Consequently, equal fractional tracks are not a promise that long content will never make the grid wider than its container.

CSS example

.layout {
  display: grid;
  grid-template-columns: 16rem minmax(0, 1fr);
  gap: 1rem;
}
.layout pre { overflow: auto; }

.layout {
  display: grid;
  grid-template-columns: 16rem minmax(0, 1fr);
  gap: 1rem;
}
.layout pre { overflow: auto; }

.layout {
  display: grid;
  grid-template-columns: 16rem minmax(0, 1fr);
  gap: 1rem;
}
.layout pre { overflow: auto; }

The second track may shrink below its content's intrinsic minimum because its explicit minimum is zero. The preformatted content can then scroll instead of widening the track. This changes track sizing, not the content's wrapping rules; without an overflow strategy, text may still paint outside its box. Fractional units divide the remaining track space after fixed tracks and gaps are accounted for. They are not simple percentages of the container, and fr is valid as a minmax() maximum, not its minimum.

57. What is the difference between auto-fill and auto-fit?

Both auto-fill and auto-fit calculate how many repetitions fit in the grid's available size, accounting for the track sizing rules and gaps. Their distinction appears after placement: auto-fill preserves empty repeated tracks, whereas auto-fit collapses empty repeated tracks and their associated gutters. A track is not empty if an in-flow item occupies or spans it.

CSS example

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

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

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

When there are fewer cards than available columns, the occupied tracks can expand into the space released by collapsed tracks. Switching to auto-fill keeps room for absent cards, which can be useful for consistent column slots. The nested min() prevents the nominal minimum from exceeding a very narrow container. If every repeated track is occupied, the two keywords can produce the same layout. Neither keyword rearranges the DOM or chooses a different semantic reading order.

58. How do explicit tracks, implicit tracks, and grid auto-placement interact?

Tracks declared by grid-template-columns, grid-template-rows, or template areas form the explicit grid. Placement outside that grid, or additional auto-placed items, can create implicit tracks. grid-auto-columns and grid-auto-rows size those extra tracks; they do not replace the explicit track definitions.

CSS example

.gallery {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  grid-auto-rows: minmax(8rem, auto);
  gap: 1rem;
}
.gallery .wide { grid-column: span 2; }

.gallery {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  grid-auto-rows: minmax(8rem, auto);
  gap: 1rem;
}
.gallery .wide { grid-column: span 2; }

.gallery {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  grid-auto-rows: minmax(8rem, auto);
  gap: 1rem;
}
.gallery .wide { grid-column: span 2; }

The gallery creates additional rows as needed, with each implicit row at least eight rem tall and able to grow for content. Default row auto-placement moves forward rather than repeatedly filling earlier holes. grid-auto-flow: row dense permits backfilling, so a later small item can appear visually before an earlier large item. That does not reorder the DOM or ordinary keyboard navigation. Use dense packing only when this visual-versus-logical distinction is acceptable, rather than treating it as a general solution for ordered content.

59. What problem does subgrid solve compared with a nested grid?

A normal nested grid sizes its tracks independently from its parent. Subgrid lets a grid item reuse the parent tracks it spans in one or both axes, allowing nested content to align with content in neighboring items without flattening the document structure. Descendant content can also contribute to the shared track sizing.

CSS example

.results {
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto;
  gap: 0.75rem 2rem;
}
.result {
  grid-column: 1 / -1;
  display: grid;
  grid-template-columns: subgrid;
}

.results {
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto;
  gap: 0.75rem 2rem;
}
.result {
  grid-column: 1 / -1;
  display: grid;
  grid-template-columns: subgrid;
}

.results {
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto;
  gap: 0.75rem 2rem;
}
.result {
  grid-column: 1 / -1;
  display: grid;
  grid-template-columns: subgrid;
}

With a title and action inside each result, titles share the first column and actions share the second. The result remains a real wrapper. Its subgridded axis uses the tracks covered by its span rather than creating an independent set or implicit tracks in that axis. The other axis can still use ordinary grid sizing. Parent gaps and line names are available to the subgrid, although gaps can be overridden. Use subgrid for shared alignment, not simply because a grid happens to be nested.

60. Why does percentage height sometimes behave like auto?

A percentage height needs an appropriate reference height. In ordinary block layout, an in-flow child's percentage height generally behaves as auto when its containing block's height is content-dependent. The fact that the parent eventually has a measurable rendered height does not necessarily make that height definite for percentage resolution.

CSS example

.parent { height: 400px; }
.child { height: 50%; }

.parent { height: 400px; }
.child { height: 50%; }

.parent { height: 400px; }
.child { height: 50%; }

Here the child's content-box height is 200 pixels under ordinary content-box sizing. Replacing the parent's height with auto makes the relationship content-dependent, so the same percentage does not simply mean half the eventual measured parent. min-height alone is not a universal replacement for a definite height. Absolute positioning and flex or grid sizing have additional rules that can provide definite references, so “percentage height always needs a pixel-height parent” is also incorrect. Choose explicit sizing, layout stretch, or content-driven sizing according to the intended behavior instead of adding height: 100% through every ancestor.

61. How do min-content, max-content, and fit-content differ?

min-content is the smallest intrinsic size that avoids overflow when all permitted soft wrapping opportunities are taken. For ordinary text, a long unbreakable segment often determines that width. max-content is the intrinsic width without soft wrapping, so a paragraph can become very wide. The fit-content keyword fits the available space while remaining bounded by those intrinsic limits.

CSS example

.tag {
  inline-size: fit-content;
  max-inline-size: 100%;
  overflow-wrap: anywhere;
}

.tag {
  inline-size: fit-content;
  max-inline-size: 100%;
  overflow-wrap: anywhere;
}

.tag {
  inline-size: fit-content;
  max-inline-size: 100%;
  overflow-wrap: anywhere;
}

A short tag stays compact, while a long label can wrap within its container. Without suitable wrapping opportunities or an explicit maximum, the intrinsic minimum can still exceed the available space; fit-content is not an absolute no-overflow guarantee. Its sizing formula is min(max-content, max(min-content, stretch)). The fit-content() function used in grid track sizing is related but accepts an explicit limit and follows grid-specific sizing rules. Distinguish a content-sized box from a capped grid track when selecting the syntax.

62. How do aspect-ratio and object-fit work together?

aspect-ratio participates in sizing the element's box, while object-fit controls how replaced content, such as an image, fits inside that box. The ratio supplies a preferred width-to-height relationship when at least one dimension is automatic; it does not override two independently definite dimensions or all minimum-size constraints.

CSS example

.thumbnail {
  display: block;
  width: 100%;
  height: auto;
  aspect-ratio: 16 / 9;
  object-fit: cover;
  object-position: center;
}

.thumbnail {
  display: block;
  width: 100%;
  height: auto;
  aspect-ratio: 16 / 9;
  object-fit: cover;
  object-position: center;
}

.thumbnail {
  display: block;
  width: 100%;
  height: auto;
  aspect-ratio: 16 / 9;
  object-fit: cover;
  object-position: center;
}

With a definite available width and no conflicting sizing constraints, the image box is 16:9. cover preserves the source image's own proportions while filling the box, cropping excess content. contain instead shows the entire image and may leave unused space. Neither changes the source file. For replaced elements, aspect-ratio: auto 16 / 9 has a different meaning: use the supplied ratio until a natural ratio is available, then use the natural one. Omit auto when the design deliberately requires a fixed crop ratio.

63. How should calc(), min(), max(), and clamp() be combined?

calc() expresses arithmetic within a property value; min() chooses the smallest candidate, max() the largest, and clamp() bounds a preferred value between a minimum and maximum. Inputs must resolve to a type the destination property accepts, so adding a length to an unrelated angle is invalid.

CSS example

.article {
  inline-size: min(65ch, calc(100% - 2rem));
  margin-inline: auto;
}
h1 { font-size: clamp(1.75rem, 1rem + 3vw, 3.5rem); }

.article {
  inline-size: min(65ch, calc(100% - 2rem));
  margin-inline: auto;
}
h1 { font-size: clamp(1.75rem, 1rem + 3vw, 3.5rem); }

.article {
  inline-size: min(65ch, calc(100% - 2rem));
  margin-inline: auto;
}
h1 { font-size: clamp(1.75rem, 1rem + 3vw, 3.5rem); }

The article is capped at a readable measure while leaving two rem of total inline room. The heading grows with the viewport but stays within its bounds. Arithmetic is allowed directly inside comparison functions; another calc() is not required around the preferred expression. Spaces around binary plus and minus operators are significant for parsing. clamp() does not automatically validate accessibility or text zoom behavior, so a fluid formula should include font-relative sizing and be checked with enlarged text. If minimum exceeds maximum, the minimum wins.

64. When should you use svh, lvh, or dvh instead of vh?

The small viewport units, such as svh, use the viewport size with retractable browser interfaces expanded. Large units such as lvh use the size with those interfaces retracted. Dynamic units such as dvh respond to the changing dynamic viewport. The default vh is tied to the large viewport model, not a guarantee that every pixel remains visible behind browser controls.

CSS example

.hero { min-block-size: 100svh; }
.app-panel { block-size: 100dvh; overflow: auto; }

.hero { min-block-size: 100svh; }
.app-panel { block-size: 100dvh; overflow: auto; }

.hero { min-block-size: 100svh; }
.app-panel { block-size: 100dvh; overflow: auto; }

The hero gets a stable minimum that accommodates expanded controls; it may leave extra room after controls retract. The app panel tracks the available dynamic height more closely but can resize during interaction. Choose stability versus adaptation intentionally. Using a minimum rather than a fixed height also lets content grow where appropriate. These units are not a universal solution for on-screen keyboards or display cutouts; keyboard viewport behavior and safe-area insets are separate concerns. Avoid equating viewport height with physical screen height.

65. How do you choose useful media queries beyond viewport breakpoints?

Media queries can respond to viewport dimensions, input capabilities, output media, and user preferences. Choose a query that describes the actual requirement instead of treating a width breakpoint as a device detector. A narrow window does not prove touch input, and a wide window does not prove a mouse is available.

CSS example

@media (hover: hover) and (pointer: fine) {
  .card:hover { outline: 2px solid currentColor; }
}
.card:focus-visible { outline: 2px solid currentColor; }
@media print {
  .site-navigation { display: none; }
}

@media (hover: hover) and (pointer: fine) {
  .card:hover { outline: 2px solid currentColor; }
}
.card:focus-visible { outline: 2px solid currentColor; }
@media print {
  .site-navigation { display: none; }
}

@media (hover: hover) and (pointer: fine) {
  .card:hover { outline: 2px solid currentColor; }
}
.card:focus-visible { outline: 2px solid currentColor; }
@media print {
  .site-navigation { display: none; }
}

The hover enhancement targets a primary pointer that can hover accurately, while keyboard focus still has its own treatment. any-pointer and any-hover ask about any available input device rather than only the primary one, which matters on hybrid systems. For layout changes, choose breakpoints where content needs a new arrangement. Media query matching does not add selector specificity: declarations inside a matching query still compete through the ordinary cascade.

66. How do size container queries differ from media queries?

A media query usually evaluates the viewport or broader environment. A size container query evaluates an eligible ancestor's dimensions, allowing the same component to adapt differently in a sidebar and a main column at the same viewport width. The ancestor must establish the appropriate size-query context.

CSS example

.card-slot { container: card / inline-size; }
.card { display: grid; gap: 1rem; }
@container card (min-width: 32rem) {
  .card { grid-template-columns: 10rem minmax(0, 1fr); }
}

.card-slot { container: card / inline-size; }
.card { display: grid; gap: 1rem; }
@container card (min-width: 32rem) {
  .card { grid-template-columns: 10rem minmax(0, 1fr); }
}

.card-slot { container: card / inline-size; }
.card { display: grid; gap: 1rem; }
@container card (min-width: 32rem) {
  .card { grid-template-columns: 10rem minmax(0, 1fr); }
}

In horizontal writing, cards inside sufficiently wide named slots use two columns, while narrow slots retain the single-column arrangement. inline-size enables queries in the inline dimension without applying full size containment in both axes. The rule selects an eligible ancestor named card; it does not let the slot query its own size to style itself. Container naming helps distinguish nested contexts. This is a component-layout decision, not a replacement for media queries about print, input capabilities, or user preferences.

67. How do line-height, font metrics, and fallback fonts affect layout?

line-height controls line layout, not the visible height of individual letterforms. Font metrics, fallback choices, inline images, and mixed-size text all affect the resulting appearance. A unitless line-height inherits as a multiplier, while an em or percentage value computes to a length before inheritance, which can surprise larger descendants.

CSS example

.article { font-size: 16px; line-height: 1.5; }
.article h2 { font-size: 32px; }

.article { font-size: 16px; line-height: 1.5; }
.article h2 { font-size: 32px; }

.article { font-size: 16px; line-height: 1.5; }
.article h2 { font-size: 32px; }

The heading inherits 1.5 and therefore uses a 48-pixel line-height. If the article instead specified line-height: 1.5em, its computed 24-pixel length would be inherited by the heading, potentially making multiline headings too tight. normal depends on the font and user agent, so it is not a fixed universal multiplier. A fallback font can change glyph widths and wrapping even with an explicit line-height. Avoid fixed-height text containers that assume identical metrics across fonts, languages, or user text-size settings.

68. What trade-offs do font-display values make during font loading?

font-display determines how a downloadable face participates in the font loading timeline. block permits a short invisible-text block period followed by an unlimited swap period. swap uses an extremely short block period and permits a later swap. fallback limits the swap window, while optional has an extremely short block period and no swap period. Exact timing is user-agent-defined.

CSS example

@font-face {
  font-family: "Interface";
  src: url("/fonts/interface.woff2") format("woff2");
  font-display: swap;
}
body { font-family: "Interface", system-ui, sans-serif; }

@font-face {
  font-family: "Interface";
  src: url("/fonts/interface.woff2") format("woff2");
  font-display: swap;
}
body { font-family: "Interface", system-ui, sans-serif; }

@font-face {
  font-family: "Interface";
  src: url("/fonts/interface.woff2") format("woff2");
  font-display: swap;
}
body { font-family: "Interface", system-ui, sans-serif; }

If the face is not ready after the short block period, the fallback remains readable and can later be replaced when the face loads. A swap may alter line breaks when metrics differ, so swap is not a guarantee of visual stability. optional can favor retaining the fallback for that navigation. These descriptors control display policy, not whether the font URL is valid, accessible, correctly licensed, or a good metric match.

69. How do gradients, color stops, and layered backgrounds behave?

A gradient is a generated image, not a color value, so it belongs in an image-capable property such as background-image. Its stops define positions along a gradient line. Adjacent colors at the same stop create a hard transition; separated stops interpolate. Multiple backgrounds are painted with the first listed image on top.

CSS example

.banner {
  background:
    linear-gradient(rgb(255 255 255 / 15%),
                    rgb(255 255 255 / 15%)),
    linear-gradient(90deg, navy 0 50%, teal 50% 100%);
}

.banner {
  background:
    linear-gradient(rgb(255 255 255 / 15%),
                    rgb(255 255 255 / 15%)),
    linear-gradient(90deg, navy 0 50%, teal 50% 100%);
}

.banner {
  background:
    linear-gradient(rgb(255 255 255 / 15%),
                    rgb(255 255 255 / 15%)),
    linear-gradient(90deg, navy 0 50%, teal 50% 100%);
}

The lower layer produces navy and teal vertical halves with a sharp midpoint; the upper translucent white layer lightens both. 90deg runs toward the right, while the default linear-gradient direction is toward the bottom. A gradient has no intrinsic dimensions, so its rendered size depends on the background positioning area and background sizing. background-color is painted beneath all image layers. Explicit color-space interpolation can change the colors between stops, but it does not replace choosing readable foreground contrast.

70. How do alpha colors, opacity, and color-mix() differ?

An alpha channel makes a particular color translucent. opacity applies to the composited element and its descendants as a group, so a child cannot restore full visual opacity by setting opacity: 1. A value below one also creates a stacking context. color-mix() instead computes a new color by interpolating specified colors in a chosen color space.

CSS example

.panel {
  background-color: rgb(0 0 0 / 15%);
  color: black;
}
.badge {
  background-color: color-mix(in oklab, teal 80%, white);
}

.panel {
  background-color: rgb(0 0 0 / 15%);
  color: black;
}
.badge {
  background-color: color-mix(in oklab, teal 80%, white);
}

.panel {
  background-color: rgb(0 0 0 / 15%);
  color: black;
}
.badge {
  background-color: color-mix(in oklab, teal 80%, white);
}

The panel's background is translucent while its text remains opaque. The badge receives an opaque mixture with an implied 20-percent white contribution, rather than making the entire badge transparent. Mixing with white is also not the same as mixing with transparent, because transparency exposes the eventual backdrop. Different interpolation spaces can produce different intermediate colors. Neither using a perceptual space nor generating a lighter tint guarantees sufficient contrast; evaluate the resulting foreground-background pair.

71. When should you choose a transition instead of a keyframe animation?

Choose a transition when a property should move smoothly between changing computed states, such as a button's resting and focused colors. Choose keyframe animation when the effect needs an explicit timeline, multiple intermediate stages, repetition, or independent playback controls. A transition requires a relevant value change; merely declaring its duration does not start movement.

CSS example

.button {
  background-color: navy;
  transition: background-color 180ms ease;
}
.button:is(:hover, :focus-visible) {
  background-color: teal;
}

.button {
  background-color: navy;
  transition: background-color 180ms ease;
}
.button:is(:hover, :focus-visible) {
  background-color: teal;
}

.button {
  background-color: navy;
  transition: background-color 180ms ease;
}
.button:is(:hover, :focus-visible) {
  background-color: teal;
}

Placing the transition on the base rule allows both entering and leaving the highlighted state to animate. A keyframe sequence could instead move through navy, teal, and purple on a repeating schedule. Neither mechanism makes every property or value pair continuously interpolable; animation behavior depends on the property's animation type and supported features. List the intended transition properties rather than using all indiscriminately, so unrelated layout or color changes do not unexpectedly acquire motion. Preserve usable focus and state indicators independently of animation.

72. Why does transform order matter, and what does transform-origin change?

Transforms compose mathematically, so changing their order can change the result. Keeping the original coordinate system fixed, a transform list can be understood as applying its rightmost function first. Alternatively, read left to right as each function establishes the coordinate system for the next. These are equivalent interpretations, not contradictory rules.

CSS example

.a, .b { transform-origin: 0 0; }
.a { transform: scale(2) translateX(10px); }
.b { transform: translateX(10px) scale(2); }

.a, .b { transform-origin: 0 0; }
.a { transform: scale(2) translateX(10px); }
.b { transform: translateX(10px) scale(2); }

.a, .b { transform-origin: 0 0; }
.a { transform: scale(2) translateX(10px); }
.b { transform: translateX(10px) scale(2); }

Both boxes double in size. The first box's translation is scaled, placing its transformed origin 20 pixels rightward; the second origin moves 10 pixels. transform-origin sets the pivot for operations such as rotation and scaling, so changing it can move the visible result without altering the transform functions. Transforms affect visual coordinates without making surrounding normal-flow boxes reflow around the transformed size. A non-none transform also creates a stacking context and can establish a containing block for positioned descendants.

73. How do animation delay, direction, and fill mode affect visible states?

Animation delay determines when active playback begins; a negative delay starts as though part of the timeline has already elapsed. Direction changes which keyframes are encountered first and last. Fill mode controls whether animation values apply outside the active interval: backwards during the delay, forwards afterward, both for both periods, and none for neither.

CSS example

.intro {
  opacity: 0.6;
  animation: reveal 2s linear 1s both;
}
@keyframes reveal {
  from { opacity: 0.2; }
  to { opacity: 1; }
}

.intro {
  opacity: 0.6;
  animation: reveal 2s linear 1s both;
}
@keyframes reveal {
  from { opacity: 0.2; }
  to { opacity: 1; }
}

.intro {
  opacity: 0.6;
  animation: reveal 2s linear 1s both;
}
@keyframes reveal {
  from { opacity: 0.2; }
  to { opacity: 1; }
}

With the default normal direction and one iteration, the intro is at 0.2 during the delay, animates to one, then retains one while the animation remains applied. Removing the animation exposes the underlying 0.6 again; fill mode does not rewrite that declaration. With reverse direction, the retained endpoint would be the from value instead. Alternating animations additionally depend on iteration count, so “forwards always means the 100-percent keyframe” is incorrect.

74. How should prefers-reduced-motion change an animated interface?

prefers-reduced-motion communicates a user preference to reduce non-essential motion, not an instruction to remove all feedback. Replace large movement, zooming, or continuous decorative effects with a stable state or less motion-intensive indication while preserving the information the animation conveyed. A useful pattern starts with the complete static interface and enables optional motion only when no reduction preference is expressed.

CSS example

.toast { opacity: 1; transform: none; }
@media (prefers-reduced-motion: no-preference) {
  .toast.entering { animation: arrive 180ms ease-out; }
}
@keyframes arrive {
  from { opacity: 0; transform: translateY(8px); }
  to { opacity: 1; transform: none; }
}

.toast { opacity: 1; transform: none; }
@media (prefers-reduced-motion: no-preference) {
  .toast.entering { animation: arrive 180ms ease-out; }
}
@keyframes arrive {
  from { opacity: 0; transform: translateY(8px); }
  to { opacity: 1; transform: none; }
}

.toast { opacity: 1; transform: none; }
@media (prefers-reduced-motion: no-preference) {
  .toast.entering { animation: arrive 180ms ease-out; }
}
@keyframes arrive {
  from { opacity: 0; transform: translateY(8px); }
  to { opacity: 1; transform: none; }
}

The toast remains visible without the animation, including when the media query does not match. Avoid making essential application state depend solely on an animation-end event, since the reduced-motion path may never start that animation. Extremely short blanket durations can also disrupt logic or retain distracting effects. Choose the alternative per interaction and retain clear textual or visual status.

75. How do logical properties make layouts work in RTL and vertical writing modes?

Logical properties describe layout relative to text flow rather than fixed physical edges. Inline runs along a line of text; block runs across successive lines. In horizontal left-to-right writing, inline-start is left, but it becomes right in RTL. Vertical writing changes the relationship between inline/block dimensions and physical width/height.

CSS example

.notice {
  padding-inline: 1rem;
  border-inline-start: 4px solid teal;
  margin-block: 1rem;
  max-inline-size: 45ch;
  text-align: start;
}

.notice {
  padding-inline: 1rem;
  border-inline-start: 4px solid teal;
  margin-block: 1rem;
  max-inline-size: 45ch;
  text-align: start;
}

.notice {
  padding-inline: 1rem;
  border-inline-start: 4px solid teal;
  margin-block: 1rem;
  max-inline-size: 45ch;
  text-align: start;
}

The accent border follows the beginning of the text flow without a separate RTL override. Use appropriate language and dir attributes in HTML to express document direction; logical CSS then follows that context. Replacing margin-left with margin-inline-start is appropriate only when the intended meaning is start-side spacing. A physical edge can still be correct for a map or fixed spatial diagram. Logical properties do not automatically mirror images, transform translations, or every directional icon, so those choices require separate consideration.

76. What does @supports prove, and how should you structure a fallback?

@supports is a feature query that checks whether the browser accepts a CSS declaration or supported selector syntax. It does not demonstrate that a complete design renders correctly, that every edge case is implemented, or that the feature is appropriate for the user. Keep a usable baseline outside the condition and scope the enhancement to the tested capability.

CSS example

.accent { background-color: teal; }
@supports (background-color: color-mix(in oklab, teal, white)) {
  .accent {
    background-color: color-mix(in oklab, teal 80%, white);
  }
}

.accent { background-color: teal; }
@supports (background-color: color-mix(in oklab, teal, white)) {
  .accent {
    background-color: color-mix(in oklab, teal 80%, white);
  }
}

.accent { background-color: teal; }
@supports (background-color: color-mix(in oklab, teal, white)) {
  .accent {
    background-color: color-mix(in oklab, teal 80%, white);
  }
}

The baseline remains if the color expression is unsupported. @supports selector(:has(*)) can query selector support rather than a declaration. Conditions can be combined using and, or, and not, with parentheses making the intended grouping explicit. Testing an arbitrary unregistered custom-property value is not a substitute for testing the eventual consuming feature, because custom properties accept broad token sequences. A matching feature query also does not increase the specificity of rules inside it.

77. How does native CSS nesting work, and where can specificity surprise you?

Native CSS nesting lets a style rule contain related rules that refer to the outer selector with &. An attached pseudo-class such as &:hover targets the outer element in that state; & .title targets a descendant. Native nesting is interpreted by the browser, not automatically expanded as a preprocessor's string substitution.

CSS example

.card, #featured {
  padding: 1rem;
  &:hover { background-color: ivory; }
  & .title { font-weight: 700; }
}

.card, #featured {
  padding: 1rem;
  &:hover { background-color: ivory; }
  & .title { font-weight: 700; }
}

.card, #featured {
  padding: 1rem;
  &:hover { background-color: ivory; }
  & .title { font-weight: 700; }
}

The nesting selector takes the highest specificity from the outer selector list, similar to :is(). Therefore the nested title rule has 1-1-0 specificity even when matching a title inside an ordinary .card. Splitting unlike-specificity parents can avoid that surprise. Unlike Sass-style concatenation, &__title cannot be used to construct a class name such as .card__title; write that selector explicitly. Nesting also does not create a shadow boundary or prevent outside rules from matching descendants. Keep nesting shallow enough that the effective selectors remain understandable.

78. How can :has() express state without adding a parent class?

:has() matches an element when a relative selector, evaluated from that element, finds a matching relationship. It can express more than parent selection: child, descendant, and following-sibling relationships can influence whether the subject matches. This lets visual state follow existing markup or native control state without duplicating it in a parent class.

CSS example

.choice:has(> input:checked) {
  outline: 2px solid teal;
}
.step:has(+ .step.current) {
  font-weight: 700;
}

.choice:has(> input:checked) {
  outline: 2px solid teal;
}
.step:has(+ .step.current) {
  font-weight: 700;
}

.choice:has(> input:checked) {
  outline: 2px solid teal;
}
.step:has(+ .step.current) {
  font-weight: 700;
}

The first rule highlights a choice whose direct input child is checked. The second highlights a step immediately before the current step; it does not select the current step itself. Comma-separated arguments express alternatives, while chaining two :has() functions requires both conditions. A :has() cannot be nested inside another :has(), and pseudo-elements generally cannot be queried with it. The selector changes styling, not form validity, document semantics, or application state. Keep the selector anchored to the relevant component so its intended relationship is clear.

79. What is the difference between overflow: hidden, clip, auto, and scroll?

overflow: hidden clips overflow without presenting ordinary scrollbars, but can still permit programmatic scrolling or scrolling caused by focus navigation. clip clips at the overflow clip edge and does not create a scroll container or allow programmatic scrolling. auto provides scrolling when needed; scroll requests scrolling behavior even when content fits, with scrollbar appearance also influenced by platform settings.

CSS example

.log {
  max-block-size: 18rem;
  overflow: auto;
}
.decorative-crop {
  overflow: clip;
  display: flow-root;
}

.log {
  max-block-size: 18rem;
  overflow: auto;
}
.decorative-crop {
  overflow: clip;
  display: flow-root;
}

.log {
  max-block-size: 18rem;
  overflow: auto;
}
.decorative-crop {
  overflow: clip;
  display: flow-root;
}

The log can grow until its maximum and then scroll. The decorative crop clips without gaining a scrolling mechanism; flow-root independently establishes a block formatting context, because clip alone does not. Overflow axes also interact: when one axis is neither visible nor clip, a visible value on the other computes to auto, and clip computes to hidden. This is why overflow-x: hidden can unexpectedly affect vertical scrolling and sticky positioning. Do not hide meaningful content without a way to access it.

80. Why does position: sticky sometimes fail to stick?

Sticky positioning keeps an element in normal flow, then adjusts its position relative to the nearest relevant scrollport while respecting its containing block. It needs a non-auto inset on the axis where sticking is intended, sufficient scrolling, and room for the sticky box to move within its containing block.

CSS example

.scroller { block-size: 20rem; overflow: auto; }
.section-title {
  position: sticky;
  inset-block-start: 0;
  background: white;
}

.scroller { block-size: 20rem; overflow: auto; }
.section-title {
  position: sticky;
  inset-block-start: 0;
  background: white;
}

.scroller { block-size: 20rem; overflow: auto; }
.section-title {
  position: sticky;
  inset-block-start: 0;
  background: white;
}

When the title is inside this scroller and enough content overflows, it sticks at the scroller's block-start edge until its containing block constrains further movement. An intervening ancestor with overflow: hidden or auto can change the relevant scrolling ancestor, even if that ancestor is not visibly scrolling. A short wrapper or a stretched flex/grid item can leave no useful travel distance. Sticky is therefore not simply fixed positioning that starts later: fixed elements are removed from flow, whereas sticky elements retain their original layout space and remain bounded by their containing block.

CSS Interview Questions and Answers for Experienced

The experienced answers emphasize diagnosis, trade-offs, and evidence. They distinguish a promising CSS feature from a verified improvement in a real interface.

81. How would you structure cascade layers for a design system used by several product teams?

I would declare the layer order centrally before team styles load, then give each layer a documented responsibility. A practical order is reset, vendor, foundations, components, utilities, and product overrides. For normal author declarations, later layers beat earlier layers before selector specificity is compared.

CSS example

@layer reset, vendor, foundations, components, utilities, product;
@layer components {
  .button { padding-inline: var(--button-padding, 1rem); }
}
@layer product {
  .button { --button-padding: 1.25rem; }
}

@layer reset, vendor, foundations, components, utilities, product;
@layer components {
  .button { padding-inline: var(--button-padding, 1rem); }
}
@layer product {
  .button { --button-padding: 1.25rem; }
}

@layer reset, vendor, foundations, components, utilities, product;
@layer components {
  .button { padding-inline: var(--button-padding, 1rem); }
}
@layer product {
  .button { --button-padding: 1.25rem; }
}

I would prohibit accidental unlayered application rules because normal unlayered declarations outrank every named layer. Component APIs should primarily expose semantic custom properties and variants, rather than require consumers to know internal selectors. Layer names and their order become versioned architectural contracts. I would also document that important declarations reverse layer precedence, so layering is not permission to scatter !important everywhere. Bundle-order changes should not silently redefine the intended ownership of an override.

82. How would you override third-party CSS without creating a specificity arms race?

I would first use the library's supported tokens, variants, or theming interface. If stylesheet control is necessary, I would place its CSS in a dedicated layer and put normal application overrides in a later layer. This lets a simple application selector beat a more specific vendor selector without duplicating its DOM structure.

CSS example

@layer important-fixes, vendor, application;
@import url("vendor.css") layer(vendor);
@layer application {
  .vendor-button { border-radius: 0.25rem; }
}

@layer important-fixes, vendor, application;
@import url("vendor.css") layer(vendor);
@layer application {
  .vendor-button { border-radius: 0.25rem; }
}

@layer important-fixes, vendor, application;
@import url("vendor.css") layer(vendor);
@layer application {
  .vendor-button { border-radius: 0.25rem; }
}

Existing vendor !important rules require separate analysis: important layer precedence runs in reverse, so a later normal override cannot defeat them. A narrowly scoped, documented important fix in the earliest layer can be appropriate when no supported API exists. Normal inline styles also outrank normal stylesheet declarations, making library configuration preferable. I would record the vendor version and reason for every workaround, then remove fixes when upgrades provide a durable extension point.

83. A correct-looking declaration is crossed out or has no visible effect. How would you diagnose it?

I would separate cascade failure from layout or rendering failure. First I would confirm the stylesheet loaded, the selector matches the intended element, and relevant media, support, and container conditions match. Next I would inspect the computed property and trace its winning declaration, including origin, importance, layer, specificity, scoping proximity, and source order. Specificity is not the first step in the cascade.

If the declaration wins, I would check whether the property applies to that box and whether its value is invalid after custom-property substitution. A winning width may still interact with minimum sizes, box sizing, flex shrinking, or grid track constraints. Shorthands can reset a longhand unexpectedly; animations or transitions may also control the observed value. Finally, I would reduce the surrounding rules while preserving the failure. The useful outcome is a causal explanation of the computed and used values, not merely another stronger selector.

84. When would you use revert-layer during a CSS migration?

I would use revert-layer when a later layer should withdraw its own decision and let an earlier layer supply a property. This is useful during a staged redesign when most components adopt a new treatment but one variant must retain the established base behavior.

CSS example

@layer base, redesign;
@layer base {
  .notice { border-radius: 0.25rem; }
}
@layer redesign {
  .notice { border-radius: 1rem; }
  .notice--compact { border-radius: revert-layer; }
}

@layer base, redesign;
@layer base {
  .notice { border-radius: 0.25rem; }
}
@layer redesign {
  .notice { border-radius: 1rem; }
  .notice--compact { border-radius: revert-layer; }
}

@layer base, redesign;
@layer base {
  .notice { border-radius: 0.25rem; }
}
@layer redesign {
  .notice { border-radius: 1rem; }
  .notice--compact { border-radius: revert-layer; }
}

Here the compact notice receives the base layer's quarter-rem radius, assuming no higher-priority declaration intervenes. revert-layer does not mean “use the preceding declaration in this file”; it removes the current layer's contribution for that property. initial would instead select the property's initial value, while unset chooses inheritance or initialization. I would keep rollbacks property-specific: broad all: revert-layer rules can unintentionally remove typography, layout, and accessibility treatments introduced by the same layer.

85. How would you design CSS tokens for runtime themes without coupling components to a palette?

I would separate primitive palette values from semantic roles and component-level hooks. A component should consume --surface-raised or --text-muted, not assume that “gray-100” always represents its background. Theme boundaries should define coherent foreground/background pairs, including interaction, error, and disabled states.

CSS example

:root {
  --surface-raised: #fff;
  --text-primary: #202124;
}
[data-theme="dark"] {
  --surface-raised: #202124;
  --text-primary: #f4f4f5;
}
.panel {
  background: var(--surface-raised);
  color: var(--text-primary);
}

:root {
  --surface-raised: #fff;
  --text-primary: #202124;
}
[data-theme="dark"] {
  --surface-raised: #202124;
  --text-primary: #f4f4f5;
}
.panel {
  background: var(--surface-raised);
  color: var(--text-primary);
}

:root {
  --surface-raised: #fff;
  --text-primary: #202124;
}
[data-theme="dark"] {
  --surface-raised: #202124;
  --text-primary: #f4f4f5;
}
.panel {
  background: var(--surface-raised);
  color: var(--text-primary);
}

I would apply aliases at the scope where their dependencies are intended to resolve. An alias computed on an ancestor does not become a live expression reevaluated against different descendant tokens. Component defaults should allow supported overrides rather than redefine global semantic tokens locally. I would document inheritance expectations, assess contrast for each state, and coordinate color-scheme with native controls without treating it as a replacement for the application's actual theme colors.

86. When would you choose @scope instead of Shadow DOM for component isolation?

I would choose @scope when I need local selector targeting while retaining ordinary light-DOM integration. A scope root can bound a component's styles, and a scope limit can exclude a nested subtree from direct selector matching.

CSS example

@scope (.article) to (.embedded-widget) {
  h2 { margin-block-start: 2rem; }
}

@scope (.article) to (.embedded-widget) {
  h2 { margin-block-start: 2rem; }
}

@scope (.article) to (.embedded-widget) {
  h2 { margin-block-start: 2rem; }
}

This does not create a sealed style boundary: outside selectors can still match the article's descendants, and inherited properties can cross the limit. The root selector does not automatically add its specificity to every scoped selector; scoping proximity resolves certain otherwise-tied declarations before source order. Shadow DOM offers stronger selector encapsulation and explicit styling surfaces such as custom properties and parts, but introduces a different component integration contract. It is not a security sandbox either. I would select based on the required isolation boundary and verify actual target-browser behavior, especially when combining scoping with nesting or shadow-tree features.

87. Why can a custom property break a declaration without activating its var() fallback?

A var() fallback is not a general-purpose type validator. An unregistered custom property can hold a token sequence that exists but is unsuitable for the property consuming it. In that case substitution succeeds, then the resulting declaration becomes invalid at computed-value time.

CSS example

.notice {
  --accent: 12px;
  color: blue;
  color: var(--accent, red);
}

.notice {
  --accent: 12px;
  color: blue;
  color: var(--accent, red);
}

.notice {
  --accent: 12px;
  color: blue;
  color: var(--accent, red);
}

The result is not red, because --accent is present, and it does not fall back to the earlier blue declaration after the cascade has already selected the variable-based declaration. Since color inherits, the invalid result normally produces the inherited color; non-inherited properties instead use their initial value. I would inspect both the custom property's computed value and the consuming property. Fixes include enforcing a token contract, validating externally supplied theme values, or registering an appropriately typed custom property. Merely adding another fallback argument does not repair a value that is present but of the wrong type.

88. How do custom-property dependency cycles affect a token system?

Custom-property dependencies form a graph for each element. If properties on the same element refer back to one another, every property in that cycle becomes invalid at computed-value time. Dependencies inside fallback arguments also participate, so a fallback is not a reliable way to conceal a circular token design.

CSS example

.panel {
  --space-a: calc(var(--space-b) + 1rem);
  --space-b: var(--space-a);
  padding: var(--space-a, 1rem);
}

.panel {
  --space-a: calc(var(--space-b) + 1rem);
  --space-b: var(--space-a);
  padding: var(--space-a, 1rem);
}

.panel {
  --space-a: calc(var(--space-b) + 1rem);
  --space-b: var(--space-a);
  padding: var(--space-a, 1rem);
}

For these unregistered properties, the cycle makes --space-a unusable, and the consuming padding declaration can use its one-rem fallback. I would repair the dependency direction instead of keeping the fallback as the permanent solution: primitives should feed semantic tokens, which feed component tokens, without references back upstream. Inheritance matters because variables are resolved before their values are inherited; identically named tokens on different ancestors do not automatically constitute a cycle. Reviewing the graph at each theme boundary helps distinguish a real cycle from ordinary inherited overrides.

89. When is registering a custom property with @property worth the added contract?

Registration is valuable when a custom property needs a stable type, explicit inheritance behavior, or interpolation as a typed value. An angle controlling a decorative dial is a good example; an arbitrary token bundle is not.

CSS example

@property --dial-angle {
  syntax: "<angle>";
  inherits: false;
  initial-value: 0deg;
}
.dial {
  transform: rotate(var(--dial-angle));
  transition: --dial-angle 200ms linear;
}
.dial.is-active { --dial-angle: 90deg; }

@property --dial-angle {
  syntax: "<angle>";
  inherits: false;
  initial-value: 0deg;
}
.dial {
  transform: rotate(var(--dial-angle));
  transition: --dial-angle 200ms linear;
}
.dial.is-active { --dial-angle: 90deg; }

@property --dial-angle {
  syntax: "<angle>";
  inherits: false;
  initial-value: 0deg;
}
.dial {
  transform: rotate(var(--dial-angle));
  transition: --dial-angle 200ms linear;
}
.dial.is-active { --dial-angle: 90deg; }

Where registration is supported, the custom angle can interpolate instead of switching as an unregistered token sequence. inherits: false also prevents an ancestor's angle from leaking into nested dials. Typed registrations require a valid, computationally independent initial value, except for the universal syntax case. Invalid registered values are handled at computed-value time; they do not necessarily expose an earlier declaration as a fallback. I would treat property names and registrations as shared API contracts, namespace them, and keep the final component state usable when registration or animation is unavailable.

90. How would you introduce container queries into a reusable card component?

I would give the embedding layout responsibility for the query container and let the card adapt to the space that container provides. A wrapper avoids trying to make an element query its own size, which would create circular layout decisions.

CSS example

.card-slot { container: card / inline-size; }
.card { display: grid; gap: 1rem; }
@container card (inline-size > 36rem) {
  .card { grid-template-columns: 12rem minmax(0, 1fr); }
}

.card-slot { container: card / inline-size; }
.card { display: grid; gap: 1rem; }
@container card (inline-size > 36rem) {
  .card { grid-template-columns: 12rem minmax(0, 1fr); }
}

.card-slot { container: card / inline-size; }
.card { display: grid; gap: 1rem; }
@container card (inline-size > 36rem) {
  .card { grid-template-columns: 12rem minmax(0, 1fr); }
}

The default remains a usable single-column card; the wider arrangement is an enhancement. I would use a meaningful container name to prevent an unrelated nearer container from changing the query's target. Inline-size containment changes how intrinsic inline sizing works, so the wrapper needs sensible sizing from its parent rather than relying solely on its children. Verification should include the card in a sidebar, full-width region, nested grid, and enlarged-text setting. Breakpoints should follow content pressure, not recreate viewport device categories under a different syntax.

91. How would you use container style queries without assuming every proposed query is implemented?

I would use a style query to let descendants respond to an explicit component-context token, while keeping a normal usable presentation outside the query. I would not assume that because the specification describes arbitrary property queries, every browser can query a computed color or display value.

CSS example

.toolbar { --density: comfortable; }
.toolbar[data-density="compact"] { --density: compact; }
.tool { padding: 0.75rem; }
@container style(--density: compact) {
  .tool { padding: 0.5rem; }
}

.toolbar { --density: comfortable; }
.toolbar[data-density="compact"] { --density: compact; }
.tool { padding: 0.75rem; }
@container style(--density: compact) {
  .tool { padding: 0.5rem; }
}

.toolbar { --density: comfortable; }
.toolbar[data-density="compact"] { --density: compact; }
.tool { padding: 0.75rem; }
@container style(--density: compact) {
  .tool { padding: 0.5rem; }
}

Custom-property style queries are the practical supported category to assess; individual query features still need compatibility review for the project's browser matrix. A style-only query does not require size containment. The queried value belongs to an ancestor container, not the element being restyled by its own condition. I would document the token's allowed values and avoid deriving essential functionality from the query. A browser that ignores it should retain comfortable, operable controls rather than an incomplete layout.

92. What would you investigate before adding CSS containment to a complex widget?

I would identify which dependency I actually want to isolate before choosing a containment value. Layout containment limits layout interactions, paint containment clips descendant painting, and size containment makes the element's own sizing independent of its contents. They are different promises, not interchangeable performance switches.

For a widget with escaping menus, contain: paint may clip the menu or focus treatment. Layout or paint containment can establish containing blocks and stacking contexts, changing absolute or fixed descendants. Size containment can collapse an otherwise content-sized box unless the layout provides dimensions or an intrinsic-size substitute. contain: strict combines strong restrictions and is therefore rarely a harmless blanket addition.

I would inspect the widget's sizing contract, overlays, sticky behavior, counters, and overflow before selecting the narrowest useful containment. Then I would compare rendering traces and functional states against the unchanged widget. A containment change is justified when it measurably limits work without violating those relationships, not merely because it sounds like isolation.

93. When would content-visibility: auto help a long page, and what are its risks?

content-visibility: auto can help pages with many substantial off-screen sections by allowing the browser to skip their rendering work until needed. It is not DOM virtualization: the content still exists, and off-screen auto content remains available to accessibility features and document navigation.

CSS example

.report-section {
  content-visibility: auto;
  contain-intrinsic-size: auto 40rem;
}

.report-section {
  content-visibility: auto;
  contain-intrinsic-size: auto 40rem;
}

.report-section {
  content-visibility: auto;
  contain-intrinsic-size: auto 40rem;
}

The intrinsic-size estimate reserves space before rendering; the auto form can reuse a previously rendered size. A poor estimate can still cause scroll-position changes as real dimensions become known. I would apply this to meaningful sections, not indiscriminately to every small element, and check in-page links, find-in-page, focus navigation, printing, and accessibility-tree behavior. Geometry measurements inside skipped content can undermine the optimization by requiring rendering. The page must remain complete if the property is ignored. I would measure actual savings and verify that containment does not clip overlays or change positioned descendants unexpectedly.

94. How would you investigate a CSS change that makes scrolling or animation slower?

I would reproduce the slowdown with a fixed page state and compare performance traces before and after the CSS change. The first question is which stage became expensive: style recalculation, layout, paint, rasterization, or compositing. A frame-rate drop alone does not identify the responsible declaration.

For layout-heavy traces, I would inspect animated geometry, intrinsic sizing dependencies, and the affected subtree. For paint-heavy traces, I would inspect large shadows, filters, blending, repeated repaint areas, and oversized surfaces. If compositing or memory grew, I would inspect layer count and dimensions rather than assume more layers are better. Browser rendering tools can show paint regions and layer boundaries, but I would correlate those views with recorded timing.

I would change one suspected cause at a time and repeat the same interaction on representative devices. Replacing width animation with transform is useful only when the intended layout semantics allow it; optimization should preserve content, hit targets, and accessibility.

95. How can a seemingly local CSS change trigger expensive layout across a page?

Layout cost follows dependencies, not just the size of the selector's target. Changing one label's font or padding can alter its intrinsic width, resize a grid track, rewrap neighboring content, and change the heights of later sections. A local declaration can therefore invalidate geometry far outside the visible component.

I would inspect whether the component participates in content-sized tracks, shared table columns, percentage sizing, or parent dimensions derived from descendants. A fixed-size decorative translation usually avoids those layout dependencies, whereas animating height intentionally changes document flow. The right repair might be a better sizing contract, a narrower containment boundary, or avoiding repeated geometry changes—not automatically absolute positioning.

I would also distinguish layout invalidation from forced synchronous layout. CSS changes can mark layout dirty; a subsequent geometry read may force pending work to run immediately. Performance traces reveal whether the problem is one necessary large layout or repeated read/write cycles. Those require different remedies and should not be conflated.

96. Why is adding will-change: transform to every animated component a poor optimization?

will-change is a hint that can reserve rendering resources before a change, not a guarantee of GPU acceleration or smoother animation. Applying it everywhere can increase memory use and compositing overhead, especially when large surfaces become separate layers. It can also create stacking contexts or containing blocks early, changing overlay behavior before an animation begins.

I would first confirm a specific rendering problem and check whether the browser already promotes the animated element appropriately. If a hint helps, I would apply it to the smallest relevant surface shortly before the interaction and remove it when the optimization is no longer needed. A permanently active animation may justify a longer-lived hint, but that still needs measurement.

I would compare traces, layer dimensions, memory pressure, and visual behavior on constrained devices. transform and opacity are often favorable animation targets, but neither they nor will-change eliminate the cost of large textures, painting, filters, or unnecessary content.

97. How would you build fluid typography that remains usable at browser zoom and with enlarged text?

I would combine a readable relative-unit floor, a fluid preferred value, and a relative-unit ceiling, then test actual enlargement rather than assuming clamp() guarantees accessibility.

CSS example

.article-title {
  font-size: clamp(1.75rem, 1.25rem + 2vw, 3rem);
  line-height: 1.15;
  overflow-wrap: anywhere;
}

.article-title {
  font-size: clamp(1.75rem, 1.25rem + 2vw, 3rem);
  line-height: 1.15;
  overflow-wrap: anywhere;
}

.article-title {
  font-size: clamp(1.75rem, 1.25rem + 2vw, 3rem);
  line-height: 1.15;
  overflow-wrap: anywhere;
}

The rem terms respond to the root font size; a viewport-only font size can counteract expected enlargement when zoom reduces the CSS viewport. Even mixed-unit formulas can produce insufficient growth in some ranges, so I would check browser zoom, text-only enlargement where available, and user font-size preferences. The surrounding layout must allow wrapping and increased block size rather than impose a fixed-height heading box.

I would also inspect navigation, buttons, sticky regions, and long localized headings at narrow effective viewport widths. Typography is successful only when the whole interface reflows and retains information and controls. A mathematically smooth scale is secondary to readable text and usable content.

98. What does a robust migration from left-to-right CSS to bidirectional layouts involve?

I would start with semantic direction in the document, normally the HTML dir attribute, and replace layout assumptions with logical properties. Leading space should use margin-inline-start; inline alignment should use start or end; leading borders should use their logical equivalents.

CSS example

.message {
  border-inline-start: 0.25rem solid currentColor;
  padding-inline-start: 1rem;
  text-align: start;
}

.message {
  border-inline-start: 0.25rem solid currentColor;
  padding-inline-start: 1rem;
  text-align: start;
}

.message {
  border-inline-start: 0.25rem solid currentColor;
  padding-inline-start: 1rem;
  text-align: start;
}

Then I would audit exceptions. A physical map direction, brand mark, or media-play symbol may not be something to mirror, while a navigation chevron may need a direction-specific transform. Mixed-script user content needs appropriate bidi isolation or direction markup, not blanket reversal of flex order. I would avoid keeping competing physical and logical declarations without understanding their cascade order.

Verification should cover nested opposite-direction regions, numbers, punctuation, input text, overflow, and keyboard sequence. RTL readiness is not simply replacing every occurrence of “left” with “right”; it requires preserving the meaning and reading order of each component.

99. How would you audit a component for vertical writing modes rather than only RTL support?

I would change the component's writing mode and inspect which assumptions are truly flow-relative. RTL changes the inline direction of horizontal text, whereas a vertical writing mode also changes the physical orientation of inline and block axes.

CSS example

.reading-panel {
  writing-mode: vertical-rl;
  inline-size: 24rem;
  padding-block: 1rem;
  padding-inline: 0.75rem;
}

.reading-panel {
  writing-mode: vertical-rl;
  inline-size: 24rem;
  padding-block: 1rem;
  padding-inline: 0.75rem;
}

.reading-panel {
  writing-mode: vertical-rl;
  inline-size: 24rem;
  padding-block: 1rem;
  padding-inline: 0.75rem;
}

Here inline-size constrains the vertical inline dimension; substituting a fixed physical width would constrain a different axis. I would inspect flex rows, grid line placement, logical alignment, scrolling, borders, and positioned labels. Typography needs review too: text-orientation, punctuation, mixed Latin text, and available fonts can affect legibility independently of box layout.

I would not rotate the entire component with transform as a substitute, because rotation does not create equivalent flow or sizing behavior. Native form controls and newer writing-mode values deserve their own target-browser checks. The success criterion is a coherent vertical reading experience, not merely rotated text.

100. How would you keep keyboard focus visible inside clipped panels and beneath sticky headers?

I would treat visibility of the focused element and visibility of its indicator as separate requirements. An outline can be clipped by an ancestor's overflow even when the control itself is visible. I would provide enough internal space, reconsider unnecessary clipping, or use a carefully tested inset treatment rather than remove the indicator.

CSS example

html { scroll-padding-block-start: 5rem; }
.control:focus-visible {
  outline: 2px solid currentColor;
  outline-offset: 3px;
}

html { scroll-padding-block-start: 5rem; }
.control:focus-visible {
  outline: 2px solid currentColor;
  outline-offset: 3px;
}

html { scroll-padding-block-start: 5rem; }
.control:focus-visible {
  outline: 2px solid currentColor;
  outline-offset: 3px;
}

Scroll padding can reserve a comfortable visible area beneath a persistent header; target-level scroll margins can provide additional separation. These declarations are not proof that every keyboard-navigation path is correct. I would traverse forwards and backwards, test nested scrollers, and inspect enlarged text and short viewports. If a sticky header occupies too much space, I would make it non-sticky at constrained sizes. Raising the focus indicator's z-index cannot fix ancestor clipping or an element scrolled behind an unrelated fixed region.

101. How would you adapt a custom control for forced-colors mode?

I would let the user's forced palette do most of the work, then repair affordances that disappear. A control drawn only with a shadow or gradient can lose its visible boundary because those effects may be suppressed. Real borders, system colors, and non-color state cues make the control more resilient.

CSS example

@media (forced-colors: active) {
  .toggle {
    border: 3px solid ButtonText;
    background: ButtonFace;
    color: ButtonText;
  }
  .toggle[aria-pressed="true"] {
    border-style: double;
  }
}

@media (forced-colors: active) {
  .toggle {
    border: 3px solid ButtonText;
    background: ButtonFace;
    color: ButtonText;
  }
  .toggle[aria-pressed="true"] {
    border-style: double;
  }
}

@media (forced-colors: active) {
  .toggle {
    border: 3px solid ButtonText;
    background: ButtonFace;
    color: ButtonText;
  }
  .toggle[aria-pressed="true"] {
    border-style: double;
  }
}

I would also verify focus, selected items, disabled states, and icons that use background images or masks. Native semantics matter: assigning an ARIA role does not necessarily give a generic element the browser's native-control color treatment. forced-color-adjust: none should be a narrowly justified exception, such as a genuinely color-dependent swatch, not a blanket way to restore the brand palette. Testing should use the actual forced-colors presentation, rather than assuming a normal dark theme exercises the same behavior.

102. How would you respect reduced-motion preferences without removing important interface feedback?

I would identify the purpose of each animation and preserve that purpose with less movement. Large spatial transitions, parallax, repeated scaling, and smooth scrolling are strong candidates for removal or substitution. A state change still needs to remain understandable through text, icons, color, or an immediate final layout.

CSS example

.panel { transition: transform 180ms ease; }
@media (prefers-reduced-motion: reduce) {
  .panel { transition: none; }
  html { scroll-behavior: auto; }
}

.panel { transition: transform 180ms ease; }
@media (prefers-reduced-motion: reduce) {
  .panel { transition: none; }
  html { scroll-behavior: auto; }
}

.panel { transition: transform 180ms ease; }
@media (prefers-reduced-motion: reduce) {
  .panel { transition: none; }
  html { scroll-behavior: auto; }
}

The base styles and state selectors must already define the final visible state; essential content should not depend on an animation completing. I would avoid a universal rule that sets every animation duration to an arbitrary tiny value, since components may rely on distinct behaviors and iteration counts. Some users may tolerate subtle fades, but reduced motion is not permission to replace every movement with another effect automatically. I would check loading, validation, expand/collapse, and route changes separately, including any motion inherited from third-party components.

103. A sticky sidebar does not stick or stops too early. What would you inspect?

I would inspect the sticky element's scroll container, containing block, inset, and available travel distance. A non-auto inset is required on the axis being used. An ancestor with overflow: auto, scroll, or hidden can become the relevant scrolling ancestor even when the user is actually scrolling a different region.

CSS example

.sidebar {
  position: sticky;
  inset-block-start: 1rem;
  align-self: start;
}

.sidebar {
  position: sticky;
  inset-block-start: 1rem;
  align-self: start;
}

.sidebar {
  position: sticky;
  inset-block-start: 1rem;
  align-self: start;
}

In a stretched grid or flex layout, the sidebar may already occupy its parent's full height, leaving no useful distance to travel; align-self: start can address that specific cause. A short containing block also limits how far sticky positioning can continue, so stopping at its bottom is expected behavior rather than necessarily a bug. I would inspect computed overflow on both axes, including values affected by their interaction, and compare sidebar height with the scrollport. I would not replace sticky with fixed positioning until confirming that losing normal-flow and container-boundary behavior is actually desired.

104. Why might a fixed-position overlay move with a card instead of the viewport?

I would inspect the overlay's ancestors for properties that establish a fixed-position containing block. A transform on a card, even one used as a performance workaround, can make a fixed descendant position relative to that card rather than the viewport.

CSS example

.card { transform: translateZ(0); }
.card .overlay { position: fixed; inset: 0; }

.card { transform: translateZ(0); }
.card .overlay { position: fixed; inset: 0; }

.card { transform: translateZ(0); }
.card .overlay { position: fixed; inset: 0; }

In this example, the overlay's positioning reference is altered by the transformed ancestor. Relevant containment, filters, and will-change settings can produce similar surprises, so I would inspect computed values rather than search only for position. Increasing z-index does not change the containing block.

The repair depends on ownership: remove an unnecessary transform, place a viewport-level overlay outside the affected subtree, or use an appropriate browser top-layer mechanism for a modal or popover. I would then recheck scrolling, clipping, focus behavior, and theme inheritance. Moving an overlay can solve its geometry while accidentally dropping inherited custom properties or component-specific styles, so the styling contract must move with it.

105. Why can a modal remain underneath another element despite an enormous z-index?

I would trace the modal's stacking-context ancestors rather than compare its z-index directly with unrelated elements. A descendant cannot escape a lower ancestor stacking context merely by choosing a larger number. Transforms, opacity, isolation, containment, and positioned elements are common sources of these boundaries.

For a genuine modal, I would consider a native dialog opened modally, which enters the browser's top layer. Setting a dialog's open attribute or assigning display: block is not equivalent to opening it modally. Ordinary author z-index cannot place a page element above the top layer.

CSS example

dialog {
  max-inline-size: min(40rem, 90vw);
  max-block-size: 80dvh;
  overflow: auto;
}
dialog::backdrop { background: rgb(0 0 0 / 0.5); }

dialog {
  max-inline-size: min(40rem, 90vw);
  max-block-size: 80dvh;
  overflow: auto;
}
dialog::backdrop { background: rgb(0 0 0 / 0.5); }

dialog {
  max-inline-size: min(40rem, 90vw);
  max-block-size: 80dvh;
  overflow: auto;
}
dialog::backdrop { background: rgb(0 0 0 / 0.5); }

I would still review competing top-layer elements, backdrop styling, overflow, and small-view-height usability. Top-layer placement resolves a painting boundary; it does not by itself supply the application's accessible labeling, appropriate focus restoration, or complete interaction design.

106. How would you fix a flex layout where a long title pushes its action buttons outside the container?

I would inspect the flex item's automatic minimum size before changing its shrink factor. The text region may refuse to become narrower than its content-based minimum, especially with an unbroken identifier or a no-wrap rule. flex-shrink: 1 does not necessarily override that minimum.

CSS example

.row { display: flex; gap: 0.75rem; }
.row__title {
  flex: 1 1 auto;
  min-inline-size: 0;
  overflow-wrap: anywhere;
}
.row__actions { flex: 0 0 auto; }

.row { display: flex; gap: 0.75rem; }
.row__title {
  flex: 1 1 auto;
  min-inline-size: 0;
  overflow-wrap: anywhere;
}
.row__actions { flex: 0 0 auto; }

.row { display: flex; gap: 0.75rem; }
.row__title {
  flex: 1 1 auto;
  min-inline-size: 0;
  overflow-wrap: anywhere;
}
.row__actions { flex: 0 0 auto; }

This permits the title region to shrink and break a long token while keeping the action group intact. I would confirm that the minimum is applied to the actual flex item, not only a nested text node's element; nested flex ancestors may have their own constraints. Truncation is a separate product decision, not an automatic fix, and must leave essential content discoverable. If the buttons alone exceed the available width, the layout needs wrapping or a stacked arrangement rather than further shrinking the title.

107. How would you diagnose grid columns that overflow despite being sized with fr units?

I would check the track's minimum, the grid item's minimum, and the overflowing descendant separately. A bare 1fr track has an automatic minimum; a long code sample or wide image can therefore make the track wider than the available space even though its maximum is flexible.

CSS example

.workspace {
  display: grid;
  grid-template-columns: 16rem minmax(0, 1fr);
  gap: 1rem;
}
.workspace__main { min-inline-size: 0; }
.workspace__main pre { overflow: auto; }

.workspace {
  display: grid;
  grid-template-columns: 16rem minmax(0, 1fr);
  gap: 1rem;
}
.workspace__main { min-inline-size: 0; }
.workspace__main pre { overflow: auto; }

.workspace {
  display: grid;
  grid-template-columns: 16rem minmax(0, 1fr);
  gap: 1rem;
}
.workspace__main { min-inline-size: 0; }
.workspace__main pre { overflow: auto; }

The zero minimum lets the main track fit its allocation, while the code block handles its own excess content through scrolling. It does not magically shrink every descendant or make content safe to clip. I would also account for gaps, fixed sidebars, explicit widths, and percentage tracks whose totals leave no room for gutters. At narrow sizes, the fixed sidebar may itself be the problem and require a one-column layout. The diagnosis should identify which box overflows and why, not stop at replacing every fraction with minmax().

108. How would you align repeated cards with subgrid without compromising reading order?

I would keep each card's title, body, and action in meaningful DOM order, then let its rows participate in the parent grid's track sizing. Subgrid can align those regions across neighboring cards without measuring heights or flattening the semantic card wrappers.

CSS example

.cards {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: 1rem;
}
.card { display: grid; gap: 1rem; }
@supports (grid-template-rows: subgrid) {
  .card {
    grid-row: span 3;
    grid-template-rows: subgrid;
  }
}

.cards {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: 1rem;
}
.card { display: grid; gap: 1rem; }
@supports (grid-template-rows: subgrid) {
  .card {
    grid-row: span 3;
    grid-template-rows: subgrid;
  }
}

.cards {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: 1rem;
}
.card { display: grid; gap: 1rem; }
@supports (grid-template-rows: subgrid) {
  .card {
    grid-row: span 3;
    grid-template-rows: subgrid;
  }
}

This example assumes three consecutive content regions per card. Without subgrid support, each card remains an independent grid; the cross-card row alignment is the enhancement. I would test optional regions, longer translations, and responsive column-count changes because they affect the shared track model. Explicit placement, order, and dense packing should not create a visual sequence that contradicts keyboard or reading order. Alignment is useful only when the underlying content relationships remain understandable without the visual arrangement.

109. How would you make a dense interface tolerate localization and unpredictable user content?

I would treat content variability as a layout input rather than an exceptional state. Fixed-height labels, tightly packed action rows, and assumptions about word length are common failure points. Components should allow text wrapping, growing block sizes, and layout changes when their available inline space becomes insufficient.

CSS example

.summary__value {
  min-inline-size: 0;
  overflow-wrap: anywhere;
}
.summary__actions {
  display: flex;
  flex-wrap: wrap;
  gap: 0.5rem;
}

.summary__value {
  min-inline-size: 0;
  overflow-wrap: anywhere;
}
.summary__actions {
  display: flex;
  flex-wrap: wrap;
  gap: 0.5rem;
}

.summary__value {
  min-inline-size: 0;
  overflow-wrap: anywhere;
}
.summary__actions {
  display: flex;
  flex-wrap: wrap;
  gap: 0.5rem;
}

I would use aggressive breaking selectively for identifiers and user-supplied strings, not indiscriminately across all natural-language text. Hyphenation depends on language metadata and available browser dictionaries; it is not guaranteed for every locale. Tables and code may legitimately need their own scrolling region rather than unreadable compression.

The review set should contain long translations, mixed scripts, empty values, large numbers, emoji, and unbroken tokens. I would distinguish optional visual truncation from loss of essential information and avoid hover-only mechanisms for recovering full text on touch or keyboard interfaces.

110. How would you reduce layout shifts caused by web-font loading?

I would distinguish loading behavior from metric compatibility. font-display: swap makes fallback text available quickly, but a later font swap can still change glyph widths, line breaks, and block heights. It is not a standalone layout-shift fix.

I would choose a fallback with similar metrics and, where supported, define a dedicated fallback face using measured size-adjust, ascent-override, descent-override, and line-gap-override values. Those values must come from the actual font pairing; copying another site's percentages can make the mismatch worse. Reducing unnecessary font variants and loading the critical face promptly can shorten the period of instability. An optional display policy may avoid a late swap, but accepts that some visits will retain the fallback.

I would compare initial and final line wrapping across weights, scripts, and viewport sizes, including failed or blocked font requests. Reserving a guessed fixed height for every text block is fragile. The goal is readable fallback text and stable layout, not merely hiding the text until the preferred font arrives.

111. How would you reserve media space without distorting responsive images or breaking art direction?

I would reserve the intended aspect ratio before the media loads, using accurate image dimensions in markup or an explicit CSS ratio for a deliberately cropped presentation. Responsive width and automatic height preserve the natural ratio when cropping is not required.

CSS example

.editorial-image {
  display: block;
  max-inline-size: 100%;
  block-size: auto;
}
.thumbnail {
  inline-size: 100%;
  aspect-ratio: 4 / 3;
  object-fit: cover;
}

.editorial-image {
  display: block;
  max-inline-size: 100%;
  block-size: auto;
}
.thumbnail {
  inline-size: 100%;
  aspect-ratio: 4 / 3;
  object-fit: cover;
}

.editorial-image {
  display: block;
  max-inline-size: 100%;
  block-size: auto;
}
.thumbnail {
  inline-size: 100%;
  aspect-ratio: 4 / 3;
  object-fit: cover;
}

For the thumbnail, I would ensure another rule or markup sizing does not impose a conflicting definite height. object-fit: cover preserves the image's proportions while cropping within the reserved box; it does not guarantee that an important subject remains visible. object-position may need an editorial focal point.

Art-directed sources can have different ratios at different breakpoints, so the reserved dimensions must match the selected composition rather than assume every source is interchangeable. I would review loading and failure states, narrow containers, and slow-network rendering. Lazy loading postpones fetching but does not itself reserve space or prevent layout shifts.

112. What security and privacy concerns arise when an application accepts CSS or loads external CSS assets?

CSS is not harmless merely because it is not JavaScript. Untrusted rules can obscure controls, imitate interface states, and initiate requests through images, fonts, or imports. External requests can expose connection metadata, and malicious selectors combined with resource loading can create information-disclosure risks in some contexts.

I would prefer a constrained theming API with allowlisted properties and validated values over accepting arbitrary stylesheets. URL-bearing values need separate URL validation and an approved asset-origin policy. A Content Security Policy should restrict stylesheet, font, and image sources appropriately; restricting style-src alone does not define every resource policy. Reviewed, versioned assets and integrity checking where applicable reduce third-party supply-chain exposure.

I would not claim that @scope, CSS Modules, or Shadow DOM provides a security sandbox. Nor would I treat display: none as protection for confidential data already delivered to the browser. The boundary should be enforced by trusted data handling and isolation, with CSS restrictions as one defensive layer.

113. How would you create reliable print styles for a long interactive report?

I would create a print presentation that exposes the report's essential information without depending on scrolling, hover, collapsed panels, or color backgrounds. Navigation and purely interactive controls can be removed, while constrained content regions should return to normal document flow.

CSS example

@media print {
  .site-nav, .report-actions { display: none; }
  .report-body {
    overflow: visible;
    max-block-size: none;
  }
  .sticky-heading { position: static; }
  figure { break-inside: avoid; }
  h2 { break-after: avoid; }
}

@media print {
  .site-nav, .report-actions { display: none; }
  .report-body {
    overflow: visible;
    max-block-size: none;
  }
  .sticky-heading { position: static; }
  figure { break-inside: avoid; }
  h2 { break-after: avoid; }
}

@media print {
  .site-nav, .report-actions { display: none; }
  .report-body {
    overflow: visible;
    max-block-size: none;
  }
  .sticky-heading { position: static; }
  figure { break-inside: avoid; }
  h2 { break-after: avoid; }
}

Break avoidance is a preference, not a guarantee: an element taller than a page must still fragment or overflow. I would inspect long tables, repeated headings, footnotes, page margins, and content made conditional for the screen. Screen-only rendering optimizations may need resetting for print. Browser print settings can suppress backgrounds or alter pagination, so color cannot carry the only meaning. Finally, CSS hiding is not redaction; sensitive information should not be included in the print document merely because a rule is expected to hide it.

114. How would you progressively enhance a tooltip with CSS anchor positioning?

I would first provide an operable help disclosure with adjacent content, then enhance only its positioning. The tooltip's accessible relationship, keyboard behavior, and dismissal must not depend on anchor positioning.

CSS example

@supports (anchor-name: --help) and (position-anchor: --help)
  and (top: anchor(bottom)) {
  .help-trigger { anchor-name: --help; }
  .help-panel {
    position: fixed;
    position-anchor: --help;
    inset: auto;
    top: anchor(bottom);
    left: anchor(left);
  }
}

@supports (anchor-name: --help) and (position-anchor: --help)
  and (top: anchor(bottom)) {
  .help-trigger { anchor-name: --help; }
  .help-panel {
    position: fixed;
    position-anchor: --help;
    inset: auto;
    top: anchor(bottom);
    left: anchor(left);
  }
}

@supports (anchor-name: --help) and (position-anchor: --help)
  and (top: anchor(bottom)) {
  .help-trigger { anchor-name: --help; }
  .help-panel {
    position: fixed;
    position-anchor: --help;
    inset: auto;
    top: anchor(bottom);
    left: anchor(left);
  }
}

This positions the panel below a uniquely named trigger in supporting implementations; it is a positioning excerpt, not a complete tooltip. Repeated components need deliberate anchor naming or supported anchor scoping so one instance does not attach to another's trigger. I would evaluate overflow fallback features separately rather than infer their support from anchor-name. Viewport edges, zoom, nested scrolling, hidden anchors, and top-layer combinations deserve explicit checks. A successful feature query confirms accepted syntax, not correct behavior for every combination; unsupported browsers should retain the usable baseline presentation.

115. How would you implement a scroll-driven progress indicator without making content depend on animation support?

I would make the indicator decorative and keep article content fully visible without it. For an already positioned, styled progress bar, the animation can be gated on both timeline support and the motion preference.

CSS example

.progress { display: none; }
@supports (animation-timeline: scroll(root block)) {
  @media (prefers-reduced-motion: no-preference) {
    .progress {
      display: block;
      transform-origin: left;
      transform: scaleX(0);
      animation: reading-progress 1s linear both;
      animation-timeline: scroll(root block);
    }
  }
}
@keyframes reading-progress {
  from { transform: scaleX(0); }
  to { transform: scaleX(1); }
}

.progress { display: none; }
@supports (animation-timeline: scroll(root block)) {
  @media (prefers-reduced-motion: no-preference) {
    .progress {
      display: block;
      transform-origin: left;
      transform: scaleX(0);
      animation: reading-progress 1s linear both;
      animation-timeline: scroll(root block);
    }
  }
}
@keyframes reading-progress {
  from { transform: scaleX(0); }
  to { transform: scaleX(1); }
}

.progress { display: none; }
@supports (animation-timeline: scroll(root block)) {
  @media (prefers-reduced-motion: no-preference) {
    .progress {
      display: block;
      transform-origin: left;
      transform: scaleX(0);
      animation: reading-progress 1s linear both;
      animation-timeline: scroll(root block);
    }
  }
}
@keyframes reading-progress {
  from { transform: scaleX(0); }
  to { transform: scaleX(1); }
}

The scroll position, not elapsed seconds, drives the supported timeline. animation-timeline follows the shorthand because animation resets it. I would choose the intended scroller explicitly and check short pages, nested scrollers, changing content height, and directionality. Support for one timeline feature does not imply every range or timeline-scoping feature works. I would avoid exposing this CSS-only decoration as an accessible numeric progress control, since it does not provide a synchronized semantic value. Failure to animate must never leave reading content transparent or transformed off-screen.

116. How would you style view transitions while preserving a usable non-transition experience?

I would keep navigation and state updates independent of their animation, then style the transition snapshots as an optional presentation layer. Same-document transitions require the corresponding API flow; CSS names alone do not trigger them. Cross-document transitions can be opted into with CSS for eligible same-origin navigation, with both documents participating.

CSS example

@media (prefers-reduced-motion: no-preference) {
  @view-transition { navigation: auto; }
  ::view-transition-old(root),
  ::view-transition-new(root) {
    animation-duration: 150ms;
  }
}

@media (prefers-reduced-motion: no-preference) {
  @view-transition { navigation: auto; }
  ::view-transition-old(root),
  ::view-transition-new(root) {
    animation-duration: 150ms;
  }
}

@media (prefers-reduced-motion: no-preference) {
  @view-transition { navigation: auto; }
  ::view-transition-old(root),
  ::view-transition-new(root) {
    animation-duration: 150ms;
  }
}

This opt-in is intentionally omitted for reduced-motion users. I would not infer cross-document support from support for a same-document API or one pseudo-element. Named elements need unique names among simultaneously rendered participants; collisions can cause the transition to be skipped. The old snapshot is visual state, not a replacement for the new page's accessible content. I would review interrupted navigation, back/forward behavior, large snapshots, and delayed assets while ensuring that an unsupported or skipped transition still produces the correct destination immediately.

117. How would you use :has() in a large application without creating fragile or unnecessarily broad selectors?

I would use :has() when a component's presentation genuinely depends on a descendant or sibling relationship, and keep the relationship as narrow as practical. It should express a stable component contract rather than search an entire application for incidental markup.

CSS example

.field:has(> input:invalid) {
  border-inline-start: 0.25rem solid var(--error-color);
}

.field:has(> input:invalid) {
  border-inline-start: 0.25rem solid var(--error-color);
}

.field:has(> input:invalid) {
  border-inline-start: 0.25rem solid var(--error-color);
}

The direct-child relationship makes the condition more explicit than a broad selector such as body:has(.error). Dynamic descendant changes can require reevaluating relational selectors, so broad anchors and unconstrained descendant searches deserve profiling in large, frequently changing trees. I would not claim that every :has() selector is slow; actual invalidation patterns matter.

I would also account for specificity: :has() takes the specificity of its most specific argument, which can make an innocent-looking rule harder to override. A validation indicator still needs meaningful text or semantics and an appropriate timing policy; styling :invalid alone can expose errors before a user has interacted.

118. What would you include in a CSS regression strategy for a shared component library?

I would build the regression set around component contracts and known failure modes, not only a gallery of ideal desktop screenshots. Each shared component should cover its supported variants and states, long and missing content, narrow containers, themes, RTL, enlarged text, forced colors, and reduced motion where relevant.

Visual comparisons can catch spacing, clipping, and alignment changes, while focused checks of computed styles or box geometry can protect critical constraints such as a non-overlapping action area. Neither proves usability: keyboard traversal, focus visibility, scrolling, and reading order still need deliberate review. Browser and font differences should be controlled where possible so noisy baselines do not train reviewers to ignore failures.

I would add a small reproducible case whenever a cascade or intrinsic-sizing bug is fixed. Lint rules can enforce layer ownership, token conventions, and forbidden specificity patterns, but cannot establish that a layout works. A baseline update should explain the intended design change and affected consumers rather than merely approve every new image.

119. How would you retire legacy CSS safely when several teams release independently?

I would inventory legacy selectors, their consuming components, global side effects, and ownership before deleting anything. Runtime coverage is evidence about exercised pages and states, not proof that a rule is unused across the product. Rare dialogs, print styles, seasonal routes, and dynamically selected classes are easy to miss.

I would migrate a bounded component family behind explicit contracts: new token names, supported variants, documented overrides, and a compatibility period. Introducing layers can help isolate old and new styling, but moving previously unlayered rules into a layer changes precedence and therefore requires review as a behavioral change. I would avoid a giant reset that hides migration conflicts by accident.

Removal should depend on known consumers upgrading and representative states remaining correct. Deprecated hooks need an owner, replacement, and removal condition, with a rollback path for each release. The useful measure of progress is fewer undocumented dependencies and safer changes—not simply a smaller stylesheet after an aggressive purge.

120. How would you distinguish a valid progressive-enhancement strategy from merely wrapping new CSS in @supports?

A valid strategy starts with a complete baseline and identifies exactly what the enhancement changes. @supports is one guard, not the strategy itself. It generally tests whether a browser accepts a declaration or supported selector syntax; it does not certify bug-free layout, accessibility, performance, or every related feature in a module.

I would define failure cases explicitly: unsupported syntax, partial implementation, missing assets, reduced motion, and constrained space. Essential content and controls should remain available in each case. Fallback rules should stay outside unsupported at-rules, and unfamiliar selectors should not accidentally invalidate a selector list containing the baseline rule. Testing a custom property assignment is not proof that the browser understands a future value stored inside it.

I would then compare baseline and enhanced behavior on the actual browser matrix, including combinations such as containment plus positioning. Release criteria should specify preserved outcomes, not just a passing feature query. An enhancement is successful when removing it changes polish or efficiency without removing the user's ability to complete the task.

CSS Documentation and Further Reading

CSS specifications and browser implementations evolve independently. Check current compatibility data and the target browser versions before relying on newer syntax, query types, positioning features, or animation APIs. Examples describe intended behavior under their stated conditions; static parsing and synthetic calculations do not establish visual correctness, accessibility conformance, cross-browser consistency, or performance.

Use these curated W3C specifications and MDN references to check normative rules, practical examples, and browser compatibility. A Working Draft or Candidate Recommendation is not a promise that every feature is implemented in every browser.

Team Sherlock.sh

Building fraud intelligence for live high-stake video interactions (financial discussions, hiring interviews, board meeting etc).

Catch candidate fraud before it costs you.

Sherlock AI helps you detect AI-assisted, proxy, and deepfake candidates, so every hiring decision is backed by evidence.

Ask AI about Sherlock.sh

Similar Posts