Chapter 14 of 14
Top 100 CSS Interview Questions
A rapid-fire reference covering the questions most likely to come up in a real interview - organized by topic and difficulty, with explanations that go beyond a one-line definition and a runnable code example for each one, so the concept sticks. Search or filter to find what you need, and share a direct link to any question.
Showing 100 of 100 questions
The first 15 of 100 questions are free to read. Sign up free to unlock every answer and track your progress.
Unlock all 100CSS (Cascading Style Sheets) describes how HTML content should look - colors, spacing, layout, typography - separating presentation from structure. Without it, browsers would render every page with the same plain default stylesheet, and any visual customization would have to live inline on every element, which quickly becomes unmaintainable at scale.
Example
p {
color: #333;
font-size: 16px;
}Inline styles (the style attribute) apply to a single element and are useful for quick one-offs or dynamically generated styles, but they’re hard to maintain and have the highest specificity. Internal styles (a <style> block in the head) suit a single page with no shared styling needs. External stylesheets (linked via <link>) are preferred for real projects because they’re cacheable, reusable across pages, and keep markup clean.
Example
<!-- external is preferred -->
<link rel="stylesheet" href="styles.css">It refers to the algorithm the browser uses to resolve conflicting rules that target the same element: it weighs origin (browser vs author vs user styles), specificity, and source order, applying later rules over earlier ones when specificity ties. Understanding the cascade is the difference between guessing why a style “isn’t working” and knowing exactly which rule won and why.
Example
p { color: blue; }
p { color: red; } /* wins: same specificity, comes later */Inheritance means some properties automatically pass their computed value from a parent element down to its children unless overridden - mostly text-related properties like color, font-family, and line-height. Box-model properties like margin, padding, border, and width do not inherit, because it would rarely make sense for every nested box to automatically copy its parent’s spacing.
Example
body {
font-family: sans-serif; /* inherited by all text */
color: #222;
}Every element is rendered as a rectangular box made of content, padding, border, and margin, from the inside out. Padding adds space inside the border, margin adds space outside it, and the total rendered size is the sum of all four layers unless box-sizing changes how width and height are calculated. This model underlies virtually every layout calculation in CSS.
Example
.box {
width: 200px;
padding: 10px;
border: 2px solid #000;
margin: 20px;
}Absolute units like px are fixed regardless of context, giving predictable but rigid sizing. Relative units like %, em, rem, vw, and vh scale based on something else - a parent’s size, the root font size, or the viewport - which is what makes responsive, accessible layouts possible, since they respond to user zoom and different screen sizes automatically.
Example
.title {
font-size: 2rem; /* relative to root */
width: 50%; /* relative to parent */
height: 100vh; /* relative to viewport */
}CSS comments are wrapped in /* ... */ and can span multiple lines; there’s no single-line // syntax like in JavaScript. They’re stripped out before rendering and are useful for documenting why a particular hack or override exists, since CSS rules alone rarely explain their own intent.
Example
/* TODO: remove once the new nav ships */
.legacy-nav { display: none; }!important overrides normal cascade and specificity rules, forcing that declaration to win regardless of what else targets the element - short of another !important with higher specificity or a later one at the same specificity. It’s discouraged because it breaks the predictable order of the cascade, making future overrides require yet more !important flags, a pattern that snowballs into unmaintainable stylesheets.
Example
.badge {
color: red !important; /* wins over almost everything else */
}Browsers ship their own default stylesheets (user-agent styles) that differ slightly between browsers - default margins on headings, list bullet styles, form control fonts, and so on. A reset aggressively zeroes out these defaults so you build up from a blank slate, while normalize.css instead makes defaults consistent across browsers without removing sensible ones, which is usually the gentler and more common choice today.
Example
/* minimal reset */
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}User-agent styles are the baseline CSS every browser applies automatically before any author stylesheet loads - things like default margins on <p> and <h1>, bullet points on <ul>, and blue underlined links. They matter because unstyled elements are never truly “unstyled”; they inherit these built-in defaults, which is why a page can look reasonably formatted even before any custom CSS is written.
Example
/* browser default, roughly */
h1 { margin: 0.67em 0; font-size: 2em; }
a { color: blue; text-decoration: underline; }The core types are type selectors (p, div), class selectors (.card), id selectors (#header), attribute selectors ([type="text"]), and pseudo-class/pseudo-element selectors (:hover, ::before). Each has a different specificity weight, and picking the right one is as much about maintainability - avoiding overly tight coupling to markup - as it is about matching the right elements.
Example
p { color: gray; }
.card { border: 1px solid #ddd; }
#logo { width: 120px; }
[type="text"] { padding: 4px; }A descendant combinator (space) matches any nested element at any depth, while a child combinator (>) matches only direct children. An adjacent sibling combinator (+) matches the very next sibling, and a general sibling combinator (~) matches all siblings that follow, regardless of distance. Choosing the tightest combinator that expresses your intent avoids accidentally styling unrelated nested elements.
Example
.card p { } /* any descendant paragraph */
.card > p { } /* direct child paragraph only */
h2 + p { } /* paragraph immediately after an h2 */
h2 ~ p { } /* all paragraphs after an h2, same parent */Specificity is commonly expressed as a tuple of (inline styles, ids, classes/attributes/pseudo-classes, type selectors/pseudo-elements), and a rule with a higher value in an earlier category always outweighs any number of matches in a later one - one id beats a thousand classes. When two selectors have identical specificity, the cascade falls back to source order, so whichever rule appears later in the stylesheet wins.
Example
#nav .item { } /* specificity: 1 id, 1 class */
.nav .item.active { } /* specificity: 0 id, 3 classes - loses to above */Attribute selectors can match presence ([disabled]), exact value ([type="text"]), substring at the start ([href^="https"]), substring at the end ([src$=".png"]), or substring anywhere ([class*="col-"]). This is powerful for targeting elements based on data or state without adding extra classes, though heavy reliance on them can make styles harder to trace back to markup intent.
Example
a[href^="https"] { color: green; }
img[src$=".png"] { border: none; }:not() excludes elements matching its argument, while :is() and :where() let you group multiple selectors into one to avoid repetition. :is() takes on the specificity of its most specific argument, but :where() always contributes zero specificity regardless of what’s inside it, which makes it ideal for writing low-priority base styles or resets that are trivially easy to override later.
Example
:is(h1, h2, h3) { font-weight: 700; }
:where(h1, h2, h3) { margin: 0; } /* contributes 0 specificity */