
These are the HTML and CSS interview questions I’d study before a frontend loop in 2026: 146 of them, from document structure and forms to the cascade, grid, container queries, and hands-on layout tasks. Every question has a direct answer, and the tricky CSS interview questions include the rule an interviewer expects you to name.
HTML and CSS rounds rarely stop at definitions. Interviewers ask why a z-index does nothing, why a flex item overflows, or how a screen reader sees your markup, and then they ask you to build a layout live. This list is written for that format, and the browser support notes follow MDN’s Baseline status as of late 2026.
What to Expect
- HTML interview questions first (Q1-58), then CSS (Q59-134), then hands-on coding (Q135-146).
- Direct answers, then code, comparison tables, and the pitfalls that come up in follow-ups.
- HTML5 interview questions on loading, media, and modern elements like
<dialog>and popover. - HTML and CSS interview questions for experienced developers: stacking contexts, cascade layers, performance, and accessibility.
For JavaScript, browser, and framework questions in the same loop, use the broader frontend interview questions list alongside this one.
Jump to a section
- HTML Fundamentals and Document Structure (Q1-14)
- HTML Forms and Validation (Q15-24)
- Accessibility and ARIA Basics (Q25-34)
- Media, Script Loading, and SEO (Q35-48)
- Dialog, Details, Popover, and Web Components (Q49-58)
- Cascade, Specificity, and Layers (Q59-70)
- Box Model, Positioning, and Stacking Context (Q71-84)
- Flexbox and CSS Grid (Q85-100)
- Responsive Design, Units, and Container Queries (Q101-112)
- Modern CSS Features (Q113-124)
- Transitions, Animations, and Performance (Q125-134)
- Hands-On HTML and CSS Coding Questions (Q135-146)
- How to Use This List
HTML Interview Questions: Fundamentals and Document Structure

Find in-depth explanations and practice here: HTML interview questions in the handbook ->
HTML interview questions look easy, which is exactly why weak answers stand out. Interviewers check whether you know what each element means to the browser, assistive technology, and search engines, not just what it looks like.
1. What does <!DOCTYPE html> do?
It tells the browser to render the page in standards mode. Without it, browsers fall back to quirks mode, which emulates old rendering bugs: the box model, table sizing, and some inline layout behave differently. The HTML5 doctype is short on purpose, because it no longer references a DTD; its only job is to switch modes.
2. What is the minimum structure of a valid HTML document?
A doctype, an <html> element with a lang attribute, a <head> with a character encoding and a <title>, and a <body>. Add the viewport meta tag for any page that should work on phones.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>Orders - Acme</title>
</head>
<body>
<main>...</main>
</body>
</html>
3. Why does the lang attribute on <html> matter?
Screen readers use it to pick the right pronunciation rules, browsers use it for hyphenation (hyphens: auto), spell checking, and translation prompts, and search engines use it as a language hint. You can also set lang on any element inside the page when a quote or phrase is in another language.
4. What is semantic HTML, and why does it matter?
Semantic HTML means picking elements for their meaning, such as <nav>, <button>, <article>, and <h2>, instead of styling generic <div>s to look the part. The browser exposes that meaning in the accessibility tree, so screen reader users can jump between landmarks and headings. You also get free behavior: a <button> is focusable and fires click on Enter and Space, while a clickable <div> does neither.
Deep dive: What is semantic markup in frontend?
5. What are landmark elements, and which ones should every page have?
Landmarks are regions that assistive technology can list and jump to. Most pages need one <main>, a <header> and <footer> at the top level (exposed as banner and contentinfo), and <nav> for major navigation blocks. Use <aside> for complementary content and <search> to wrap a search form.
| Element | Implicit role | Note |
|---|---|---|
<main> |
main | Only one visible per page |
<header> |
banner | Only when not inside article, section, aside, main, or nav |
<footer> |
contentinfo | Same scoping rule as header |
<nav> |
navigation | Label it when there are several |
<aside> |
complementary | Sidebars, related links |
6. What is the difference between <section>, <article>, and <div>?
<article> is self-contained content that still makes sense on its own, like a blog post, a comment, or a product card. <section> groups a thematic part of a document and should normally start with a heading. <div> has no meaning and is the right choice when you only need a styling or layout hook.
A quick test: if you could syndicate it in a feed, it’s an article. If it needs a heading to explain what it is, it’s a section. Otherwise, it’s a div.
7. How should you structure headings on a page?
Use one <h1> that describes the page, then nest <h2> to <h6> in order without skipping levels for visual size. Many screen reader users navigate by headings, so the outline is effectively the page’s table of contents. Pick the level for structure and set the size with CSS.
The old “document outline algorithm”, where every <section> restarts at <h1>, was never implemented by browsers or screen readers and was removed from the HTML spec, so don’t rely on it.
8. What is the difference between block and inline elements?
Block-level elements like <div>, <p>, and <section> start on a new line and fill the available width. Inline elements like <span>, <a>, and <em> flow within a line of text, and vertical margins and width/height don’t affect their layout. Strictly speaking this is a CSS display default, not an HTML property, so you can change it.
Deep dive: Block vs inline elements
9. What is the difference between an attribute and a property?
Attributes live in the HTML source; properties live on the DOM object. The browser initializes properties from attributes, but they can diverge: for an input, getAttribute('value') returns the initial value, while input.value returns what the user typed. Boolean attributes like disabled are true whenever they’re present, even as disabled="false".
<input id="q" value="hello" />
<!-- user types "world" -->
<!-- q.getAttribute('value') -> "hello"
q.value -> "world" -->
Deep dive: Attribute vs property and HTML tag attributes
10. What are data-* attributes, and when should you use them?
Custom data-* attributes store extra information on an element without inventing non-standard attributes. JavaScript reads them through element.dataset (data-user-id becomes dataset.userId), and CSS can select them with [data-state="open"]. They’re good for state hooks and test selectors, but don’t store secrets or large data in them: everything in the DOM is visible to the user.
11. What is the difference between <b> and <strong>, and <i> and <em>?
<strong> marks importance and <em> marks stress emphasis that changes the meaning of a sentence. <b> and <i> are no longer “presentational” in HTML5: <b> draws attention without extra importance (a product name in a review), and <i> is for alternate voice, such as a technical term or a phrase in another language. If you only want bold text, use CSS.
12. What are void elements?
Void elements can’t have content or a closing tag: <img>, <input>, <br>, <hr>, <meta>, <link>, <source>, and a few others. In HTML the trailing slash in <br /> is allowed but ignored. Writing <div /> doesn’t self-close a div: the parser treats it as an open tag, which is a common bug when moving JSX habits into plain HTML.
13. How does the browser handle invalid HTML?
The HTML parsing algorithm is fully specified for errors, so every browser recovers the same way instead of failing. For example, it closes an open <p> when it meets a <div>, and it moves stray content out of tables. The result is that the DOM you inspect can differ from the source you wrote, which matters when JavaScript or CSS selectors expect a structure.
<p>Intro <div>Block</div></p>
<!-- Parsed DOM: <p>Intro </p><div>Block</div><p></p> -->
14. What happens between typing a URL and seeing the rendered HTML?
The browser resolves DNS, opens a connection (TCP plus TLS, or QUIC for HTTP/3), sends the request, and starts parsing the HTML as bytes stream in. Parsing builds the DOM, CSS builds the CSSOM, and together they produce the render tree, then layout, paint, and compositing. Synchronous scripts pause the parser, and CSS blocks rendering, which is why the next sections care about where you put them.
Deep dive: Web request lifecycle and Critical rendering path
HTML Forms and Validation

Find in-depth explanations and practice here: Support Ticket Form coding question ->
Forms are where HTML knowledge pays off in UI coding rounds. A candidate who uses real labels, the right input types, and native validation finishes faster and builds a more accessible form than one who rebuilds everything in JavaScript.
15. How do you correctly label a form input?
Use a <label> whose for matches the input’s id, or wrap the input inside the label. Both give the input an accessible name and make the label clickable, which enlarges the tap target. A placeholder isn’t a label: it disappears on input, often has low contrast, and isn’t reliably announced.
<label for="email">Work email</label>
<input id="email" name="email" type="email" autocomplete="email" required />
16. Why use specific input types like email, tel, number, and date?
The type gives you a matching mobile keyboard, built-in validation (for email and url), and native pickers for dates and colors, all without JavaScript. Use type="tel" for phone numbers and inputmode="numeric" for codes like ZIPs or card numbers. type="number" is meant for real quantities: it adds spinners, allows e, and changes the value on scroll in some browsers.
17. What built-in validation does HTML provide?
Attributes like required, minlength, maxlength, min, max, step, pattern, and typed inputs block submission and show a native message when they fail. The Constraint Validation API exposes the same rules to JavaScript through input.validity, checkValidity(), reportValidity(), and setCustomValidity().
<input name="code" required pattern="[A-Z]{3}-[0-9]{4}" title="Format: ABC-1234" />
<!-- "abc-12" fails: validity.patternMismatch === true -->
Client-side validation is a UX feature, not security. The server must validate everything again.
18. What is the difference between :invalid and :user-invalid?
:invalid matches as soon as a field breaks a rule, so a required field shows an error before the user has touched it. :user-invalid only matches after the user has interacted with the field (or tried to submit), which is usually the behavior designers want. It’s a newer selector, so check MDN’s compatibility table before relying on it without a fallback.
input:user-invalid { border-color: crimson; }
19. What does the autocomplete attribute do?
It tells the browser and password managers what a field contains, using tokens like email, name, street-address, one-time-code, current-password, and new-password. Correct tokens make autofill reliable, which speeds up checkout and sign-up forms. It also helps accessibility: WCAG’s “Identify Input Purpose” criterion relies on these tokens.
20. What is the difference between GET and POST form submission?
With method="get", field values go into the URL query string, so the result is bookmarkable and cacheable; it suits search and filters. method="post" sends values in the request body and suits anything that changes data. Neither is encrypted on its own; HTTPS does that. File uploads need method="post" with enctype="multipart/form-data".
Deep dive: HTTP GET vs POST
21. How do you group related form controls?
Wrap them in a <fieldset> with a <legend>. Screen readers announce the legend with each control, which matters for radio groups where the individual labels (“Yes”, “No”) mean nothing alone. A disabled fieldset also disables every control inside it.
<fieldset>
<legend>Shipping speed</legend>
<label><input type="radio" name="speed" value="std" checked /> Standard</label>
<label><input type="radio" name="speed" value="exp" /> Express</label>
</fieldset>
22. What is the difference between disabled and readonly?
A readonly field can be focused and selected, and its value is submitted with the form. A disabled field is skipped by keyboard focus, isn’t submitted, and is often skipped by validation. Use readonly for values the user should see and send but not edit, such as a generated ID.
23. Why should a button inside a form have an explicit type?
A <button> inside a form defaults to type="submit", so a “Show password” or “Add row” button submits the form by accident. Set type="button" for anything that isn’t a submit action. Also keep a real submit button: pressing Enter in a text field submits the form through it, which users expect.
24. How do you show accessible error messages?
Put the message in the DOM next to the field, link it with aria-describedby, and set aria-invalid="true" on the field when it fails. On submit, move focus to the first invalid field or to an error summary at the top. For errors that appear without a focus change, announce them through a live region.
<label for="pw">Password</label>
<input id="pw" type="password" aria-invalid="true" aria-describedby="pw-err" />
<p id="pw-err">Use at least 12 characters.</p>
Practice: Signup Form
Accessibility and ARIA Basics

Find in-depth explanations and practice here: Accessibility interview questions ->
Most HTML and CSS interview questions for experienced developers come back to accessibility. Interviewers want to hear that native elements come first and ARIA fills the gaps.
25. What is the accessibility tree?
It’s a parallel tree the browser builds from the DOM, exposing each meaningful node’s role, accessible name, state, and value to assistive technology through platform APIs. Semantic HTML fills it in automatically; a <div> with a click handler appears as plain text. You can inspect it in the Accessibility panel of Chrome or Firefox DevTools.
26. What is ARIA, and what is the first rule of ARIA?
ARIA (Accessible Rich Internet Applications) is a set of attributes that add roles, states, and properties to the accessibility tree. The first rule is: if a native HTML element already has the semantics and behavior you need, use it instead of adding ARIA. ARIA changes only what assistive technology hears; it adds no keyboard behavior, focus, or styling.
<!-- Avoid -->
<div role="button" tabindex="0">Save</div> <!-- still needs Enter/Space handlers -->
<!-- Prefer -->
<button type="button">Save</button>
Deep dive: ARIA roles and attributes
27. What is the difference between aria-label, aria-labelledby, and aria-describedby?
| Attribute | Provides | Use when |
|---|---|---|
aria-label |
Name as a string | No visible text, like an icon button |
aria-labelledby |
Name from other elements’ text | A visible heading already names a region or dialog |
aria-describedby |
Extra description | Hints and error messages |
aria-labelledby wins over aria-label, and both override the element’s own text content. Prefer visible text where possible, because aria-label strings are often missed by translation tools.
28. How do you write good alt text for images?
Describe what the image communicates in context, not what it is: “Revenue grew 40% in Q3” beats “bar chart”. Decorative images get an empty alt="" so screen readers skip them. If an image is inside a link and is its only content, the alt text should describe the link’s destination.
29. How do you hide content visually but keep it for screen readers?
Use a “visually hidden” utility class. display: none and visibility: hidden remove content from the accessibility tree, so they hide it from everyone. Do the opposite with aria-hidden="true", which hides content from assistive technology but leaves it on screen.
.visually-hidden {
position: absolute;
width: 1px;
height: 1px;
margin: -1px;
padding: 0;
overflow: hidden;
clip-path: inset(50%);
white-space: nowrap;
border: 0;
}
30. What does tabindex do, and which values should you use?
tabindex="0" adds an element to the natural tab order, and tabindex="-1" makes it focusable from script (el.focus()) but not by Tab. Positive values force a custom order that’s almost always confusing and hard to maintain, so avoid them. Interactive native elements are already focusable and don’t need tabindex at all.
31. What is the difference between :focus and :focus-visible?
:focus matches any focused element, including a button clicked with a mouse. :focus-visible matches only when the browser decides a focus indicator is helpful, such as keyboard navigation, so you can style focus rings for keyboard users without showing them on every click. Never remove outlines without a visible replacement.
button:focus-visible { outline: 3px solid #22d3ee; outline-offset: 2px; }
32. What is a live region?
A live region is an element whose content changes are announced by screen readers without moving focus. Use aria-live="polite" (or role="status") for updates like “3 results found”, and role="alert" for urgent errors. The region must exist in the DOM before its text changes, or many screen readers won’t announce it.
33. What does the inert attribute do?
inert makes an element and its subtree non-interactive: it can’t be focused or clicked, and it’s removed from the accessibility tree. It’s the clean way to disable the page behind a custom drawer or an off-screen panel. A modal <dialog> opened with showModal() already makes the rest of the page inert for you. inert is Baseline widely available.
34. How do you support users who prefer reduced motion or a dark color scheme?
Use the user-preference media features. prefers-reduced-motion: reduce lets you remove or shorten large animations, and prefers-color-scheme: dark lets you switch tokens. Combined with custom properties, both are a few lines of CSS.
@media (prefers-reduced-motion: reduce) {
*, *::before, *::after { animation-duration: 0.01ms !important; transition-duration: 0.01ms !important; }
}
:root { color-scheme: light dark; }
Deep dive: Web accessibility
HTML5 Interview Questions: Media, Script Loading, and SEO

Find in-depth explanations and practice here: Image optimization strategies ->
These HTML5 interview questions are about loading: what blocks the parser, how the browser picks an image, and which tags search engines read. They connect HTML to Core Web Vitals, so expect follow-ups about LCP and CLS.
35. What is the difference between <script>, <script async>, and <script defer>?
A classic script blocks HTML parsing while it downloads and runs. async downloads in parallel and runs as soon as it arrives, in any order, which suits independent scripts like analytics. defer downloads in parallel and runs in document order after parsing finishes, just before DOMContentLoaded, which suits app code that needs the DOM.
| Script | Blocks parsing | Execution order | Runs |
|---|---|---|---|
| Classic | Yes | Document order | Immediately |
async |
Only while running | Whichever loads first | When downloaded |
defer |
No | Document order | After parsing |
type="module" |
No | Document order | Deferred by default |
36. Why put CSS in the <head>?
Stylesheets are render-blocking: the browser won’t paint until it has the CSSOM, to avoid a flash of unstyled content. Putting <link rel="stylesheet"> in the head lets the browser discover and download CSS as early as possible. A stylesheet also blocks later scripts from running, because those scripts might read computed styles.
Deep dive: Critical rendering path
37. What do preload, preconnect, dns-prefetch, and prefetch do?
| Hint | Does | Use for |
|---|---|---|
preload |
Fetches a resource for the current page early | Hero image, critical font |
preconnect |
Opens DNS, TCP, and TLS to an origin early | Your CDN or API origin |
dns-prefetch |
Resolves DNS only | Lower-priority third parties |
prefetch |
Fetches at low priority for a future navigation | The likely next page |
Preloading everything makes everything compete, so limit it to resources the browser would find late. Fonts need crossorigin on the preload or they’re fetched twice.
38. How do srcset and sizes work?
srcset lists image candidates with their widths (w) or pixel densities (x), and sizes tells the browser how wide the image will display at each breakpoint. The browser combines that with the device pixel ratio to download the smallest file that looks sharp. Without sizes, a w-based srcset assumes the image is 100vw wide.
<img
src="card-800.jpg"
srcset="card-400.jpg 400w, card-800.jpg 800w, card-1600.jpg 1600w"
sizes="(min-width: 960px) 33vw, 100vw"
width="800" height="450"
alt="Team planning board" />
39. When do you use <picture> instead of srcset?
Use <picture> when you need control, not just resolution switching: serving AVIF or WebP with a JPEG fallback, or swapping to a differently cropped image on small screens (art direction). The browser uses the first <source> whose type and media match, and the inner <img> carries the alt text and the fallback.
<picture>
<source type="image/avif" srcset="hero.avif" />
<source media="(max-width: 600px)" srcset="hero-square.jpg" />
<img src="hero.jpg" alt="Dashboard overview" width="1200" height="600" />
</picture>
40. Why set width and height on images?
Browsers use the two attributes to compute an aspect ratio before the image loads, so they reserve the right space and the layout doesn’t jump. That prevents Cumulative Layout Shift (CLS). In CSS, keep height: auto alongside max-width: 100% so the image still scales responsively.
Deep dive: Core Web Vitals
41. What do loading="lazy" and fetchpriority="high" do?
loading="lazy" defers off-screen images and iframes until the user scrolls near them; it’s Baseline widely available for images. fetchpriority="high" asks the browser to download a resource earlier than it otherwise would. Use them in opposite places: lazy-load below-the-fold images, and never lazy-load the LCP hero image, which should get fetchpriority="high" instead.
42. How do <video> and <audio> work, and what should you include?
Both accept multiple <source> children so the browser picks a format it supports. Add controls unless you build your own, a poster for video, and <track kind="captions"> for captions. Browsers block autoplay with sound, so autoplaying video also needs muted and usually playsinline for iOS.
43. Which HTML elements and tags matter for SEO?
A unique, descriptive <title>, a <meta name="description"> (used for snippets, not ranking), one clear <h1> with a logical heading structure, descriptive link text, and alt text on images. Add <link rel="canonical"> to consolidate duplicate URLs and keep content in the HTML rather than only rendered by client-side JavaScript.
44. What is the viewport meta tag, and what happens without it?
<meta name="viewport" content="width=device-width, initial-scale=1"> tells mobile browsers to lay out the page at the device’s width. Without it, mobile browsers render at a wide virtual viewport (typically 980px) and zoom out, so your media queries never trigger. Don’t add user-scalable=no or maximum-scale=1: blocking zoom fails users with low vision.
45. What are Open Graph and structured data?
Open Graph tags like og:title, og:description, and og:image control how a link looks when it’s shared in chat apps and social networks. Structured data, usually JSON-LD with schema.org types like Article or Product, describes the page to search engines and can make it eligible for rich results. Neither replaces good content in the HTML.
46. What is the difference between <link rel="stylesheet"> and @import?
A <link> lets the preload scanner discover the stylesheet while parsing HTML, so several stylesheets download in parallel. An @import inside CSS is only found after the parent stylesheet downloads and parses, creating a request chain that delays rendering. In production, bundle CSS or use multiple <link> tags instead.
47. How do you prevent XSS when rendering user content into HTML?
Treat user input as text, not markup: set textContent instead of innerHTML, and let frameworks escape output by default. When you must render HTML (rich text, Markdown), sanitize it with an allowlist of tags and attributes, and drop on* handlers and javascript: URLs. A Content Security Policy adds a second layer by blocking inline scripts.
Practice: Allowlist HTML Sanitizer
48. What does rel="noopener" do on links with target="_blank"?
It stops the new page from getting a reference to your page through window.opener, which a malicious page could use to redirect your tab (tabnabbing). Modern browsers apply noopener to target="_blank" links by default, but adding it explicitly documents intent. noreferrer also omits the Referer header and implies noopener.
Modern HTML: Dialog, Details, Popover, and Web Components

Find in-depth explanations and practice here: Design an accessible dialog ->
The four cards above stand for the widgets this section covers: a modal <dialog>, a <details> disclosure, a popover anchored to a button, and a custom element. Modern HTML now ships widgets that used to need a library. Interviewers ask about them to see whether your knowledge is current and whether you know what the browser gives you for free.
49. What does the <dialog> element give you?
Calling showModal() opens the dialog in the top layer, above every z-index, with a ::backdrop you can style. It makes the rest of the page inert, moves focus into the dialog, closes on Escape, and returns focus to the trigger when it closes. show() opens a non-modal dialog without those behaviors. <dialog> is Baseline widely available.
<button type="button" onclick="confirmDlg.showModal()">Delete</button>
<dialog id="confirmDlg" aria-labelledby="dlg-title">
<h2 id="dlg-title">Delete this repo?</h2>
<form method="dialog">
<button value="cancel">Cancel</button>
<button value="confirm">Delete</button>
</form>
</dialog>
<!-- confirmDlg.returnValue -> "confirm" or "cancel" after close -->
Practice: Focus-Trapped Dialog
50. What is the popover attribute, and how is it different from a dialog?
The popover attribute turns any element into a top-layer popup that a button opens with popovertarget, with no JavaScript. popover="auto" gets light dismiss (click outside or Escape) and closes other auto popovers. Unlike a modal dialog, a popover doesn’t make the page inert or trap focus, so it suits menus, toasts, and tooltips rather than blocking confirmations. Popover is Baseline newly available since January 2025.
<button popovertarget="menu">Options</button>
<div id="menu" popover>
<a href="/settings">Settings</a>
</div>
51. How do <details> and <summary> work?
<details> is a native disclosure widget: clicking the <summary> toggles the open attribute and fires a toggle event. It’s keyboard accessible and works without JavaScript. Giving several details the same name makes them an exclusive accordion where only one opens at a time; that’s Baseline newly available since 2024.
<details name="faq"><summary>Shipping</summary>Ships in 2 days.</details>
<details name="faq"><summary>Returns</summary>30-day returns.</details>
52. What are Web Components?
Web Components are three browser standards for building reusable elements without a framework. Custom elements let you define a tag such as <user-card> with a JavaScript class. Shadow DOM gives the element an encapsulated DOM and styles. <template> and <slot> hold inert markup and let users pass content in. They suit design systems shared across React, Vue, and plain pages.
<user-card><span slot="name">Ada</span></user-card>
<script type="module">
customElements.define('user-card', class extends HTMLElement {
connectedCallback() {
this.attachShadow({ mode: 'open' }).innerHTML =
'<p>Hello, <slot name="name"></slot></p>';
}
});
</script>
53. How does styling work across the shadow DOM boundary?
Page selectors don’t reach into a shadow root, and the component’s styles don’t leak out. Inherited properties like color and font still flow in, and so do CSS custom properties, which makes them the main theming API. Components can also expose parts with part="label" for outside styling through ::part(label), and style themselves with :host.
54. What is the <template> element?
<template> holds markup that the parser builds but doesn’t render: images don’t load and scripts don’t run. You clone its content (a DocumentFragment) with importNode or cloneNode(true) and insert it, which is faster and safer than building strings for innerHTML. It’s also how declarative shadow DOM works, with <template shadowrootmode="open"> for server-rendered components.
55. What is the difference between localStorage, sessionStorage, and cookies?
| localStorage | sessionStorage | Cookies | |
|---|---|---|---|
| Lifetime | Until cleared | Until the tab closes | Expiry date or session |
| Sent to server | No | No | With every matching request |
| Scope | Origin | Origin plus tab | Domain and path |
| JS access | Yes | Yes | Not with HttpOnly |
Web Storage is synchronous and stores strings only, so keep it small and don’t put auth tokens there, since any XSS can read it. For structured or large data, use IndexedDB.
Deep dive: IndexedDB. Practice: Expiring Storage
56. What are <iframe> security attributes you should know?
sandbox starts an iframe with almost everything disabled (scripts, forms, popups, same-origin access), and you re-enable only what you need with tokens like allow-scripts. allow controls powerful features like the camera or fullscreen. On your own pages, the frame-ancestors CSP directive (or the older X-Frame-Options header) prevents other sites from framing you for clickjacking.
57. What is progressive enhancement?
Progressive enhancement starts with working HTML, then adds CSS and JavaScript as layers that improve the experience when they’re available. A search form that submits with GET still works if the JavaScript bundle fails; the script just upgrades it to instant results. Graceful degradation goes the other way, building the full experience first and patching fallbacks.
Deep dive: Progressive enhancement vs graceful degradation
58. What is the difference between DOMContentLoaded and load?
DOMContentLoaded fires when the HTML is fully parsed and deferred scripts have run, without waiting for images or iframes. load fires on window after every subresource, including images and stylesheets, has finished. Most initialization code should run at DOMContentLoaded, or simply in a deferred or module script.
CSS Interview Questions: Cascade, Specificity, and Layers

Find in-depth explanations and practice here: CSS interview questions in the handbook ->
Cascade questions are the most common CSS interview questions because every “why isn’t my style applying” bug comes back to them. Expect to calculate specificity on the spot and explain where @layer fits.
59. How does the cascade decide which declaration wins?
The browser compares competing declarations for the same property in this order, and the first difference decides:
- Origin and importance: user-agent, user, and author styles, with
!importantreversing that order. - Context: scoped styles, such as shadow DOM versus the page.
- Element-attached styles: the inline
styleattribute. - Cascade layers: later layers beat earlier ones, and unlayered styles beat all layers.
- Specificity.
- Order of appearance: the last one wins.
Deep dive: MDN: Introducing the CSS cascade
60. How is specificity calculated?
Specificity is three numbers compared left to right: ID selectors, then classes, attributes, and pseudo-classes, then type selectors and pseudo-elements. A higher first number always wins, no matter how big the later numbers are, so eleven classes never beat one ID. The universal selector * and combinators add nothing.
| Selector | Specificity |
|---|---|
li |
0, 0, 1 |
ul li::marker |
0, 0, 3 |
.nav a:hover |
0, 2, 1 |
input[type="email"] |
0, 1, 1 |
#app .btn |
1, 1, 0 |
61. How do :is(), :where(), and :not() affect specificity?
:is() and :not() take the specificity of their most specific argument, so :is(#hero, .card) h2 counts as (1, 0, 1) even when it matches through .card. :where() always has zero specificity, which makes it ideal for library and reset styles that users should override easily. All three are Baseline widely available.
:where(.btn) { padding: 8px 12px; } /* 0,0,0: any class overrides it */
:is(header, footer) a { color: inherit; } /* 0,0,2 */
62. Why is !important discouraged, and when is it acceptable?
It jumps the normal specificity contest, and the only way to beat it is another !important rule, which starts an escalation war. It’s reasonable in utility classes that must always win (.hidden { display: none !important; }), user-preference overrides like reduced motion, and to override third-party inline styles you can’t change. Cascade layers are usually the better fix for ordering problems.
63. What are cascade layers (@layer)?
Cascade layers let you group styles into named layers whose order you declare once. A rule in a later layer beats a rule in an earlier layer regardless of specificity, so a simple class in components overrides a heavy selector in reset. Unlayered styles beat every layer. Layers are Baseline widely available.
@layer reset, base, components, utilities;
@layer reset { #app ul li a { color: black; } }
@layer components { .link { color: teal; } } /* wins: later layer */
For !important declarations, layer order reverses: an important rule in an earlier layer beats an important rule in a later one. That lets a reset layer protect critical rules.
64. What is inheritance in CSS, and which properties inherit?
Some properties pass their computed value from parent to child by default, mostly text-related ones: color, font-*, line-height, letter-spacing, text-align, visibility, and cursor. Box properties like margin, padding, border, width, and background don’t. Inheritance has no specificity, so any rule that targets the child directly wins over an inherited value.
65. What do inherit, initial, unset, revert, and revert-layer mean?
| Keyword | Sets the property to |
|---|---|
inherit |
The parent’s computed value |
initial |
The spec’s initial value (display: inline, even on a div) |
unset |
inherit for inherited properties, otherwise initial |
revert |
What the previous origin would give, usually the browser default |
revert-layer |
The value from the previous cascade layer |
The common trap is initial: div { display: initial } makes the div inline, while revert brings back block.
66. What is the difference between pseudo-classes and pseudo-elements?
Pseudo-classes (one colon) select an existing element in a particular state or position: :hover, :focus-visible, :checked, :nth-child(2n). Pseudo-elements (two colons) style a part of an element or generate one: ::before, ::after, ::placeholder, ::marker, ::selection. ::before and ::after need a content value to render, and they don’t work on replaced elements like <img> or <input>.
67. What is the difference between :nth-child() and :nth-of-type()?
:nth-child(2) matches an element that is the second child of its parent, counting all siblings, and only if it also matches the rest of the selector. :nth-of-type(2) counts only siblings of the same tag. Modern browsers also support :nth-child(2 of .item), which counts only siblings matching .item.
<div><h2>Title</h2><p>A</p><p>B</p></div>
<!-- p:nth-child(2) -> A (2nd child overall)
p:nth-of-type(2) -> B (2nd p) -->
68. Which CSS combinators should you know?
Descendant (A B), child (A > B), next sibling (A + B), and subsequent sibling (A ~ B). Sibling combinators only look forward in the DOM, which is why styling an earlier sibling or a parent used to be impossible without :has(). Browsers match selectors right to left, so the rightmost part (the key selector) matters most for matching cost.
69. What is the difference between resetting and normalizing CSS?
A reset strips most browser defaults (margins, heading sizes, list styles) so you start from zero. Normalize keeps useful defaults and only fixes inconsistencies between browsers. Most teams now use a small modern reset: box-sizing: border-box, zero body margin, images as display: block; max-width: 100%, and inherited fonts on form controls.
Deep dive: Resetting vs normalizing CSS
70. How do you organize CSS in a large codebase?
Pick a scoping strategy and enforce it. BEM uses naming conventions (.card__title--active), CSS Modules hash class names per file at build time, utility-first frameworks like Tailwind compose small single-purpose classes, and CSS-in-JS colocates styles with components. Each trades off differently, so a senior answer names the trade-off, not a favorite.
| Approach | Scoping | Runtime cost | Main trade-off |
|---|---|---|---|
| BEM | Convention | None | Relies on discipline |
| CSS Modules | Build-time hashing | None | Theming needs custom properties |
| Utility-first | No global selectors | None | Long class lists in markup |
| Runtime CSS-in-JS | Generated classes | Style work at render | Poor fit for server components |
Deep dive: SASS and LESS preprocessors. Practice: Variant Class Builder
Box Model, Display, Positioning, and Stacking Context

Find in-depth explanations and practice here: CSS questions that trip up senior engineers ->
This is where CSS interview questions for experienced developers get tricky: collapsing margins, containing blocks, and z-index values that seem to do nothing. Answer with the rule, then the fix.
71. What is the CSS box model?
Every element generates a box made of content, padding, border, and margin, from the inside out. With the default box-sizing: content-box, width sets only the content, so padding and border make the box bigger. With box-sizing: border-box, width includes padding and border, which is easier to reason about and is why most resets apply it globally.
.a { box-sizing: content-box; width: 200px; padding: 20px; border: 5px solid; } /* 250px wide */
.b { box-sizing: border-box; width: 200px; padding: 20px; border: 5px solid; } /* 200px wide */
72. What is margin collapsing?
Vertical margins between adjacent block boxes in normal flow combine into one margin equal to the larger of the two, not their sum. It also happens between a parent and its first or last child when nothing (padding, border, or a new formatting context) separates them, which is why a child’s top margin can push the parent down. Margins never collapse in flex or grid containers, or horizontally.
h2 { margin-bottom: 24px; }
p { margin-top: 16px; } /* gap between them: 24px, not 40px */
73. What is a block formatting context (BFC)?
A BFC is an independent layout region where floats are contained and margins don’t collapse with the outside. You create one with display: flow-root (the explicit way), overflow other than visible, floats, absolute positioning, or flex and grid items. It’s the modern fix for a parent that collapses to zero height around floated children.
Deep dive: Block formatting context
74. What is the difference between display: none, visibility: hidden, and opacity: 0?
| Takes up space | Clickable | Screen readers | Can transition | |
|---|---|---|---|---|
display: none |
No | No | Hidden | Not by default |
visibility: hidden |
Yes | No | Hidden | Yes (discrete) |
opacity: 0 |
Yes | Yes | Still read | Yes |
The opacity: 0 trap: the invisible element still catches clicks and is still focusable, so add pointer-events: none or switch visibility after the fade.
75. What is the difference between inline, inline-block, and block?
block boxes start on a new line and respect width, height, and all margins. inline boxes flow within text and ignore width, height, and vertical margins for layout. inline-block flows inline like text but behaves like a block inside, so width, height, and vertical padding all work. Whitespace between inline-block elements renders as a gap, which is a classic layout bug.
76. What are the values of position?
| Value | In normal flow? | Offsets relative to |
|---|---|---|
static |
Yes | Offsets ignored |
relative |
Yes (space kept) | Its own normal position |
absolute |
No | Nearest positioned ancestor |
fixed |
No | The viewport (usually) |
sticky |
Yes | Its scroll container, once a threshold is crossed |
77. What is a containing block?
The containing block is the box that percentages and offsets resolve against. For absolute, it’s the padding box of the nearest ancestor with a position other than static. For fixed, it’s the viewport, unless an ancestor has transform, filter, perspective, contain: paint, or similar, in which case that ancestor becomes the containing block. That exception explains most “my fixed header scrolls with the page” bugs.
78. Why isn’t my position: sticky element sticking?
Check four things, in this order:
- It needs a threshold, like
top: 0. - An ancestor with
overflow: hidden,auto, orscrollbecomes the scroll container, and if that ancestor doesn’t scroll, nothing sticks. - It only sticks within its parent, so a parent no taller than the element gives it nowhere to travel.
- In a flex or grid container,
align-items: stretchcan make it as tall as the container; setalign-self: start.
79. What is a stacking context?
A stacking context is a group of elements painted together as one layer, with z-index values that only compete inside the group. The root element creates one, and so does any positioned element with a z-index other than auto, flex and grid items with a z-index, and elements with opacity below 1, transform, filter, isolation: isolate, will-change on those properties, or position: fixed or sticky.
Deep dive: MDN: Stacking context
80. Why doesn’t z-index: 9999 put my element on top?
Usually because the element is trapped in a parent’s stacking context: if the parent has z-index: 1 and a sibling section has z-index: 2, nothing inside the parent can rise above that sibling. Other causes are a z-index on a non-positioned element outside flex or grid (it’s ignored), or an ancestor with transform or opacity creating a context you didn’t notice. The modern fix for modals and menus is the top layer: <dialog> or popover.
.page { position: relative; z-index: 1; }
.page .modal { position: fixed; z-index: 9999; } /* still under .toolbar */
.toolbar { position: relative; z-index: 2; }
81. What does isolation: isolate do?
It creates a new stacking context without any side effects like positioning or opacity. Put it on a component’s root to keep its internal z-index values from leaking out and competing with the rest of the page. It also limits mix-blend-mode to the component.
82. How do floats work, and are they still used?
A floated element is taken out of normal flow and shifted left or right, and inline content such as text wraps around it. Floats were the main layout tool before flexbox, which is why legacy code has clearfix hacks. Today, use them for their original purpose: wrapping text around an image, optionally with shape-outside.
83. What do the overflow values do?
visible lets content spill out, hidden clips it but still allows programmatic scrolling, clip clips without creating a scroll container, scroll always shows scrollbars, and auto shows them only when needed. Any value besides visible and clip creates a scroll container, which is why overflow: hidden can break position: sticky on descendants. Use overflow: clip when you only want to hide overflow.
84. What does display: contents do?
It removes the element’s own box so its children behave as if they were children of its parent, which is useful for letting wrapped items join a parent grid or flex layout. Historically, browsers also dropped the element’s semantics from the accessibility tree, so using it on buttons, lists, or tables caused accessibility bugs. Prefer subgrid when you can.
Flexbox and CSS Grid

Find in-depth explanations and practice here: CSS fundamentals ->
Layout questions are often live: the interviewer shares a mockup and asks you to build it. Know the defaults, because most flexbox bugs come from forgetting them.
85. When do you use flexbox and when do you use grid?
Flexbox lays out items along one axis and lets content size drive the layout, so it suits navbars, toolbars, button groups, and centering. Grid lays out rows and columns together and lets the container define the tracks, so it suits page layouts, card galleries, and anything that must align in two directions. They combine well: a grid page with flex components inside.
| Flexbox | Grid | |
|---|---|---|
| Dimensions | One (row or column) | Two (rows and columns) |
| Sizing driven by | Content | Container tracks |
| Overlapping items | Awkward | Easy (same cell or area) |
| Typical use | Components | Page and gallery layouts |
86. What are the flexbox defaults?
A flex container defaults to flex-direction: row, flex-wrap: nowrap, justify-content: flex-start, and align-items: stretch. Items default to flex: 0 1 auto: they don’t grow, they can shrink, and their base size comes from their width or content. Most surprises, like stretched items or cramped rows, come from stretch and nowrap.
87. What do justify-content and align-items do?
justify-content distributes items along the main axis (horizontal in a row), and align-items aligns them on the cross axis. When you switch to flex-direction: column, the axes swap, so justify-content becomes vertical. align-content only matters when items wrap onto several lines, and align-self overrides alignment for one item.
88. What do flex-grow, flex-shrink, and flex-basis mean?
flex-basis is the item’s starting size before free space is shared. flex-grow is how many shares of the leftover space it takes, and flex-shrink is how much it gives up when space runs out (weighted by its basis). Shorthands to remember: flex: 1 is 1 1 0%, flex: auto is 1 1 auto, and flex: none is 0 0 auto.
.row { display: flex; width: 600px; }
.a { flex: 1; } /* 200px */
.b { flex: 2; } /* 400px: same basis (0), twice the share */
89. Why does my flex item overflow instead of shrinking?
Flex items have min-width: auto by default, so they refuse to shrink below their content’s minimum size: a long URL, a wide table, or a <pre> block. Set min-width: 0 on the item (or min-height: 0 in a column) to let it shrink, then handle the overflow with overflow or text truncation. The same rule causes the classic “grid column blows out” bug, fixed with minmax(0, 1fr).
.sidebar { flex: 0 0 240px; }
.content { flex: 1; min-width: 0; } /* now long content can't push the layout wider */
90. What does gap do, and why use it instead of margins?
gap sets the space between flex items or grid tracks, but never on the outer edges. That removes the “margin on every item except the last” hacks and negative-margin wrappers, and it works with wrapping. Flexbox gap is Baseline widely available.
91. How do you push one flex item to the far end?
Give it margin-left: auto (or margin-inline-start: auto). Auto margins absorb all the free space on that side, so a logout button moves to the right of a navbar while the other links stay left. The same trick with margin: auto centers a single item on both axes.
.nav { display: flex; gap: 16px; }
.nav .logout { margin-left: auto; }
Practice: Navigation
92. What does the fr unit mean in grid?
fr is a fraction of the free space left after fixed and content-sized tracks are placed. grid-template-columns: 200px 1fr 2fr gives the first column 200px, then splits the rest one-third and two-thirds. Note that 1fr has a minimum of auto, so long content can still stretch the track; use minmax(0, 1fr) for strictly equal columns.
93. What is the difference between auto-fill and auto-fit?
Both create as many columns as fit, which gives you a responsive card grid without media queries. auto-fill keeps empty tracks when there are few items, so cards stay their minimum size. auto-fit collapses empty tracks, so the existing cards stretch to fill the row.
.cards {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(min(240px, 100%), 1fr));
gap: 16px;
}
The min(240px, 100%) stops the grid from overflowing on screens narrower than 240px.
94. What are grid template areas?
grid-template-areas lets you draw the layout as named strings, and each child joins an area with grid-area. The layout is readable at a glance, and changing it at a breakpoint means rewriting only the strings. Every area must be a rectangle, and a dot (.) marks an empty cell.
.page {
display: grid;
grid-template-columns: 220px 1fr;
grid-template-areas:
"header header"
"nav main"
"footer footer";
}
.page > header { grid-area: header; }
95. What is the difference between explicit and implicit grid tracks?
Explicit tracks are the ones you define with grid-template-rows and grid-template-columns. When items are placed outside them, the grid creates implicit tracks, sized by grid-auto-rows and grid-auto-columns (default auto). Setting grid-auto-rows: minmax(120px, auto) is the usual way to give every generated row a sensible minimum.
96. How do you place and span items in a grid?
Use line numbers or spans: grid-column: 1 / 3 runs from line 1 to line 3, grid-column: span 2 covers two columns, and grid-column: 1 / -1 stretches across the whole explicit grid. Two items placed in the same cell overlap, which makes grid the simplest way to stack an image and a caption.
97. What does grid-auto-flow: dense do?
It lets the auto-placement algorithm backfill earlier gaps with smaller items that come later in the source. It’s useful for galleries with mixed spans, but it changes visual order without changing DOM order, so keyboard focus can jump around. Avoid it for content where reading order matters.
98. What is subgrid?
grid-template-columns: subgrid (or rows) makes a nested grid reuse its parent’s tracks instead of defining its own. The classic use is a row of cards where every title, body, and footer lines up across cards even when content lengths differ. Subgrid is Baseline widely available.
.cards { display: grid; grid-template-columns: repeat(3, 1fr); }
.card { display: grid; grid-row: span 3; grid-template-rows: subgrid; }
99. How do you build a masonry layout today?
Native CSS masonry is still being specified and isn’t Baseline, so production code uses CSS multi-column layout (columns: 3, which orders items top to bottom per column) or JavaScript that places items in the shortest column. In an interview, explain the reading order trade-off of the columns approach, then build the JavaScript version if asked.
Practice: Masonry Grid
100. What does the order property do, and what’s the catch?
order changes the visual position of flex and grid items without touching the DOM. The catch is accessibility: keyboard focus and screen readers still follow DOM order, so a visually reordered button sequence becomes confusing. Change the source order when the meaning changes, and use order only for purely visual tweaks.
Responsive Design, Units, and Container Queries

Find in-depth explanations and practice here: useMatchMedia coding question ->
Responsive design questions check whether you build layouts that adapt by default instead of patching them at every breakpoint. Modern answers lean on intrinsic sizing, clamp(), and container queries.
101. What does mobile-first mean in CSS?
You write the base styles for small screens and add min-width media queries to enhance the layout as space grows. Small screens download and apply only the simple rules, and the code tends to stay smaller because wide layouts add features rather than undo them. Desktop-first with max-width queries usually ends up overriding more.
.grid { display: grid; gap: 16px; }
@media (min-width: 48em) { .grid { grid-template-columns: 1fr 1fr; } }
102. What is the difference between px, em, rem, and %?
| Unit | Relative to | Good for |
|---|---|---|
px |
A CSS pixel (fixed) | Borders, shadows |
rem |
The root font size | Font sizes, spacing scales |
em |
The element’s own font size (parent’s, for font-size) |
Padding that scales with a component’s text |
% |
Depends on the property, often the containing block | Fluid widths |
Use rem for font sizes so text respects the user’s browser font setting. Nested em font sizes compound: 1.2em inside 1.2em is 1.44 times the base.
103. What are vw, vh, dvh, svh, and lvh?
vw and vh are 1% of the viewport’s width and height. On mobile, 100vh is the height with the browser toolbar retracted, so a full-height section gets cut off when the toolbar shows. svh (small), lvh (large), and dvh (dynamic, tracks the toolbar) fix that, and these viewport unit variants are Baseline widely available.
.hero { min-height: 100svh; }
104. What do min(), max(), and clamp() do?
They pick a value at computation time. min() uses the smallest argument, max() the largest, and clamp(MIN, PREFERRED, MAX) keeps a fluid value between two bounds. They replace many media queries for widths and font sizes.
h1 { font-size: clamp(1.75rem, 1.2rem + 2.5vw, 3rem); } /* fluid, never below 28px or above 48px */
.container { width: min(100% - 2rem, 70rem); margin-inline: auto; }
Keep a rem component in the preferred value of fluid type, as above, so browser zoom still scales the text.
105. What are container queries?
Container queries style an element based on the size of an ancestor container instead of the viewport. A card can switch to a horizontal layout when its column is wide, no matter where it’s placed on the page, which makes components truly reusable. You mark the ancestor with container-type, then use @container. Container size queries are Baseline widely available.
.card-slot { container-type: inline-size; }
@container (min-width: 400px) {
.card { display: grid; grid-template-columns: 120px 1fr; }
}
106. What are container query units?
cqi, cqb, cqw, and cqh are 1% of the query container’s inline size, block size, width, and height. They let typography and spacing inside a component scale with the component, for example font-size: clamp(1rem, 4cqi, 1.5rem) on a card title. Without a declared container, they fall back to the small viewport units.
107. Should media queries use px or em?
em in media queries is based on the browser’s default font size, not the root element’s CSS, so breakpoints move when a user increases their default text size. That keeps the layout from cramming large text into a narrow layout. Modern browsers zoom consistently with pixel breakpoints too, so the main argument for em is the font-size setting.
108. What are media query range syntax and other useful media features?
Range syntax lets you write @media (width >= 768px) or (400px <= width <= 700px) instead of min-width and max-width. Beyond width, useful features include hover and pointer (to detect touch versus mouse), prefers-reduced-motion, prefers-color-scheme, prefers-contrast, and orientation.
@media (hover: hover) and (pointer: fine) {
.row-actions { opacity: 0; }
.row:hover .row-actions { opacity: 1; }
}
109. How do you make text responsive and readable?
Use fluid font sizes with clamp(), limit line length to roughly 60 to 75 characters with max-width: 65ch, and set a unitless line-height (like 1.5) so it scales with each element’s font size. text-wrap: balance evens out heading lines and text-wrap: pretty avoids single-word last lines; balance is Baseline newly available since 2024.
110. What is the difference between logical and physical properties?
Physical properties like margin-left and width are tied to screen directions. Logical properties like margin-inline-start, padding-block, and inline-size follow the writing mode and direction, so a right-to-left layout (dir="rtl") flips automatically. Use them by default in products that ship in Arabic or Hebrew.
111. What do min-content, max-content, and fit-content mean?
min-content is the narrowest a box can get without overflowing (the longest word), max-content is its width with no wrapping at all, and fit-content uses the available space but never more than max-content. width: fit-content is a clean way to make a block element shrink-wrap its content, for a tag or a centered button.
112. How do you test responsive layouts?
Use DevTools device mode for quick checks, then test on real devices for touch, the mobile toolbar, and performance. Drag the viewport slowly through every width instead of jumping between presets, because layouts usually break between breakpoints. Also test at 200% browser zoom and with a larger default font size, which catches fixed heights and px font sizes.
Practice: useViewportSize
Modern CSS: Custom Properties, :has(), Nesting, and New Features

Find in-depth explanations and practice here: Frontend interview questions ->
Modern CSS is how interviewers tell whether your knowledge stopped in 2019. You don’t need every new feature, but you should know the widely supported ones and say honestly which ones aren’t Baseline yet.
113. What are CSS custom properties?
Custom properties (CSS variables) are properties starting with -- that you read with var(). Unlike Sass variables, which are replaced at build time, they’re live at runtime: they cascade, inherit, can change inside media queries or on hover, and can be updated from JavaScript. That makes them the standard tool for theming and design tokens.
:root { --brand: #22d3ee; --radius: 8px; }
.dark { --brand: #a78bfa; }
.btn { background: var(--brand); border-radius: var(--radius, 4px); } /* 4px is a fallback */
114. How do you change a custom property from JavaScript?
Call element.style.setProperty('--x', value) and read it with getComputedStyle(element).getPropertyValue('--x'). This pattern keeps JavaScript out of styling decisions: the script only updates a number, such as pointer position or scroll progress, and CSS decides how to use it.
.spotlight { background: radial-gradient(circle at var(--mx, 50%) var(--my, 50%), #fff3, transparent 40%); }
/* JS: el.style.setProperty('--mx', e.offsetX + 'px') */
115. What does @property add to custom properties?
@property registers a custom property with a type (syntax), an initial value, and whether it inherits. Because the browser then knows it’s, say, a <color> or <angle>, it can animate it, which unregistered custom properties can’t do. A common use is animating a gradient angle. Registered custom properties are Baseline newly available since 2024.
@property --angle { syntax: "<angle>"; initial-value: 0deg; inherits: false; }
.ring { background: conic-gradient(from var(--angle), cyan, violet, cyan); animation: spin 3s linear infinite; }
@keyframes spin { to { --angle: 360deg; } }
116. What is the :has() selector?
:has() matches an element if any of the relative selectors inside it match, which finally gives CSS a parent selector and a previous-sibling selector. You can style a card that contains an image, a form field whose input is invalid, or a page when a modal is open. :has() is Baseline widely available.
.card:has(img) { grid-template-rows: 200px auto; }
.field:has(input:user-invalid) label { color: crimson; }
body:has(dialog[open]) { overflow: hidden; }
h2:has(+ p) { margin-bottom: 0.5rem; } /* h2 directly followed by p */
117. How does native CSS nesting work?
You can now nest rules inside a parent rule in plain CSS, like in Sass. & refers to the parent selector, and nested media queries apply to the parent. Nesting is Baseline widely available. The key difference from Sass: you can’t concatenate selector strings, so &__title doesn’t create a BEM class, and the nested selector is wrapped in :is() semantics for specificity.
.card {
padding: 1rem;
&:hover { box-shadow: 0 4px 12px #0003; }
.title { font-weight: 700; } /* .card .title */
@media (min-width: 40em) { padding: 2rem; }
}
118. What does @scope do?
@scope (.card) to (.content) { ... } limits rules to elements inside .card, stopping at a lower boundary, which is useful for components that contain other components. When two scoped rules conflict, the one with the nearer scope root wins (scoping proximity). @scope became Baseline newly available in 2026, so check your browser support targets before relying on it.
119. What does aspect-ratio do?
It sets a preferred width-to-height ratio, so a box with only a width gets its height automatically. It replaced the old padding-top percentage hack for video embeds, thumbnails, and skeleton placeholders. Pair it with object-fit: cover to crop images into the box without distortion. It’s Baseline widely available.
.thumb { aspect-ratio: 16 / 9; width: 100%; object-fit: cover; }
120. What do object-fit and object-position do?
They control how replaced content like an <img> or <video> fills its box. cover fills the box and crops, contain fits the whole image and letterboxes, fill stretches (the default), and none keeps the natural size. object-position picks which part stays visible when cropping, such as top for portraits.
121. What are color-mix() and light-dark()?
color-mix(in oklch, var(--brand) 70%, white) mixes two colors in a chosen color space, so you can derive hover and disabled shades from one token. light-dark(#111, #eee) returns the first value in light mode and the second in dark mode, based on color-scheme. color-mix() is Baseline widely available and light-dark() is Baseline newly available since 2024.
122. What are the oklch() and other modern color functions good for?
oklch() describes colors by lightness, chroma, and hue in a perceptually uniform space, so changing only lightness gives a predictable palette with even contrast steps. It can also express colors outside sRGB, on wide-gamut displays. That makes it a good format for design tokens, where you generate scales by stepping lightness.
123. How do you style form controls with modern CSS?
accent-color recolors checkboxes, radios, range sliders, and progress bars in one line. appearance: none removes native styling when you need a fully custom control, which then needs your own focus and checked states. Always keep the native element underneath, so keyboard and screen reader behavior stay correct.
input[type="checkbox"], input[type="radio"] { accent-color: #7c3aed; }
124. What is CSS anchor positioning?
Anchor positioning lets you place an element, like a tooltip or menu, relative to another element with anchor-name and position-anchor, and flip it with fallback positions when it would overflow the viewport. It replaces JavaScript positioning libraries for many cases. It isn’t Baseline yet, so treat it as progressive enhancement and keep a fallback.
.trigger { anchor-name: --menu-btn; }
.menu { position: absolute; position-anchor: --menu-btn; top: anchor(bottom); left: anchor(left); }
Transitions, Animations, and CSS Performance

Find in-depth explanations and practice here: Reflow vs repaint ->
Performance questions separate senior candidates quickly. A strong answer names the rendering stage a property triggers and explains how to measure it before optimizing.
125. What is the difference between CSS transitions and animations?
A transition animates between two states when a property value changes, for example on :hover or when a class toggles. An animation uses @keyframes to run multi-step sequences that can start on their own, loop, alternate, and pause. Use transitions for state changes and animations for loaders, attention cues, and entrances.
.btn { transition: transform 150ms ease-out; }
.btn:hover { transform: translateY(-2px); }
@keyframes pulse { 50% { opacity: 0.5; } }
.skeleton { animation: pulse 1.5s ease-in-out infinite; }
126. What is the difference between reflow and repaint?
Reflow (layout) recalculates the size and position of elements, and it can cascade to parents, children, and siblings. Repaint redraws pixels for changes that don’t affect geometry, like color or background. Layout is usually the most expensive stage, so changing width, top, or font-size on many elements per frame causes jank.
| Change | Layout | Paint | Composite |
|---|---|---|---|
width, top, margin |
Yes | Yes | Yes |
color, background, box-shadow |
No | Yes | Yes |
transform, opacity |
No | Usually no | Yes |
127. Why animate transform and opacity?
Browsers can usually run transform and opacity animations on the compositor, often on the GPU, without layout or paint on each frame. They can even keep running smoothly while the main thread is busy with JavaScript. To move something, use transform: translate() instead of left; to show or hide it, fade opacity.
Practice: Animate Element To The Right
128. What does will-change do, and when should you avoid it?
will-change: transform hints that a property is about to change, so the browser can promote the element to its own layer ahead of time and avoid a stutter on the first frame. Each layer costs memory, so applying it to many elements or leaving it on permanently can make performance worse. Add it just before an animation, or keep it for elements that animate constantly, and remove it otherwise.
129. What is layout thrashing, and how do you avoid it?
Layout thrashing happens when JavaScript alternates between writing styles and reading layout values (offsetHeight, getBoundingClientRect()) in a loop, forcing a synchronous layout on every read. Fix it by batching all reads first and all writes after, or by scheduling writes in requestAnimationFrame. Performance panels flag this as “forced reflow”.
130. What does the contain property do?
contain tells the browser that an element’s internals are independent of the rest of the page, so it can limit layout, style, and paint work to that subtree. contain: layout paint on a widget means changes inside it can’t trigger layout outside it. contain: content is a safe shorthand for layout, paint, and style containment. It’s Baseline widely available.
131. What does content-visibility: auto do?
It skips layout and paint for off-screen sections until they approach the viewport, which can cut initial rendering time on long pages. Pair it with contain-intrinsic-size so the skipped section keeps an estimated height and the scrollbar doesn’t jump. It’s Baseline newly available since 2025.
.feed-section { content-visibility: auto; contain-intrinsic-size: auto 800px; }
132. Which CSS properties are expensive to paint?
Large or frequently changing box-shadow, filter: blur(), backdrop-filter, and big gradients over large areas take the most paint work. They’re fine on static elements, but animating them every frame on a large area can drop frames on mid-range phones. Measure with the Performance panel and the paint flashing overlay before you swap them out.
133. How do you animate elements in and out of display: none?
Two newer features make it possible in CSS. transition-behavior: allow-discrete lets display and overlay switch at the right moment of a transition, and @starting-style defines the “from” values for an element’s first style update, so entry animations run. Both are Baseline newly available since 2024, which is why they’re often used to animate dialogs and popovers.
[popover] { opacity: 0; transition: opacity 200ms, display 200ms allow-discrete, overlay 200ms allow-discrete; }
[popover]:popover-open { opacity: 1; }
@starting-style { [popover]:popover-open { opacity: 0; } }
134. How do you load web fonts without hurting performance?
Self-host WOFF2 files, subset them to the characters you need, and preload only the one or two fonts used above the fold. font-display: swap shows fallback text right away instead of invisible text. To reduce the layout shift when the font swaps in, tune the fallback with size-adjust and ascent-override in an @font-face rule so its metrics match.
Deep dive: Frontend performance optimizations
Hands-On HTML and CSS Coding Interview Questions

Find in-depth explanations and practice here: Masonry Grid coding question ->
These are the build-it questions from UI coding rounds and take-homes. For each one, say the approach first, write the smallest correct CSS, and mention the edge case you’d test.
135. How do you center an element horizontally and vertically?
The shortest modern answer is a grid container with place-items: center. Flexbox with justify-content and align-items works the same way. For an overlay over a positioned parent, use absolute positioning with inset: 0 and margin: auto on an element with a set size.
/* 1. Grid */
.center { display: grid; place-items: center; min-height: 100svh; }
/* 2. Flexbox */
.center { display: flex; justify-content: center; align-items: center; }
/* 3. Absolute, for a sized box */
.modal { position: absolute; inset: 0; margin: auto; width: 320px; height: fit-content; }
Horizontal centering of a block only needs margin-inline: auto and a width; text needs text-align: center.
136. Build the holy grail layout.
The holy grail is a header, a footer, and three columns (nav, main, aside) where the footer stays at the bottom on short pages and the columns stack on mobile. Grid template areas make it a few lines, and the sticky footer comes from grid-template-rows: auto 1fr auto with a full-height container.
<body class="layout">
<header>Header</header>
<nav>Nav</nav>
<main>Main</main>
<aside>Aside</aside>
<footer>Footer</footer>
</body>
.layout {
display: grid;
min-height: 100svh;
grid-template-rows: auto 1fr auto;
grid-template-areas: "header" "main" "nav" "aside" "footer";
}
@media (min-width: 48em) {
.layout {
grid-template-columns: 200px minmax(0, 1fr) 200px;
grid-template-rows: auto 1fr auto;
grid-template-areas:
"header header header"
"nav main aside"
"footer footer footer";
}
}
header { grid-area: header; } nav { grid-area: nav; } main { grid-area: main; }
aside { grid-area: aside; } footer { grid-area: footer; }
On mobile there are five rows, so the 1fr row trick needs adjusting if a sticky footer matters there too. Mentioning that unprompted shows the interviewer you test both layouts.
137. Truncate text to one line with an ellipsis.
It takes three properties together, and the element needs a constrained width. Inside a flex item, also add min-width: 0, or the item grows to fit the text and the ellipsis never appears.
.truncate {
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
}
Add a title attribute or a tooltip with the full text, because truncated text is otherwise unreadable to sighted users.
138. Truncate text to three lines.
Use line-clamp through its widely supported prefixed form, which needs the legacy display: -webkit-box setup. The unprefixed line-clamp property isn’t Baseline yet, so keep the prefixed version in production.
.clamp-3 {
display: -webkit-box;
-webkit-box-orient: vertical;
-webkit-line-clamp: 3;
overflow: hidden;
}
139. Make a responsive 16:9 video or image box.
Use aspect-ratio on the wrapper or the media element itself, with width: 100% so it scales with its container. For images, add object-fit: cover to crop rather than stretch.
.video { width: 100%; aspect-ratio: 16 / 9; }
.video > iframe, .video > video { width: 100%; height: 100%; border: 0; }
The legacy answer, still worth recognizing in old code, is a wrapper with padding-top: 56.25% and an absolutely positioned child, because vertical padding percentages resolve against the width.
140. Build a sticky header that doesn’t cover anchor targets.
Make the header position: sticky with top: 0 and a background, so content doesn’t show through. Then set scroll-padding-top on the scroll container, so in-page links like #pricing don’t land under the header.
.site-header {
position: sticky;
top: 0;
z-index: 10;
background: white;
}
html { scroll-padding-top: 72px; } /* header height */
If the header doesn’t stick, check for an ancestor with overflow: hidden (Q78).
141. Build a CSS-only toggle or accordion.
Prefer <details> and <summary>: it’s accessible, keyboard-friendly, and needs no CSS hacks. If the interviewer wants a switch, style a real checkbox and use :checked with a sibling selector or :has(). Avoid the old “:target” or hidden-checkbox-label hacks for menus, because they don’t expose expanded state to screen readers.
<label class="switch">
<input type="checkbox" role="switch" /> Dark mode
</label>
.switch input { appearance: none; width: 40px; height: 22px; border-radius: 999px; background: #94a3b8; position: relative; }
.switch input::before { content: ""; position: absolute; top: 3px; left: 3px; width: 16px; height: 16px; border-radius: 50%; background: white; transition: translate 150ms; }
.switch input:checked { background: #7c3aed; }
.switch input:checked::before { translate: 18px 0; }
.switch input:focus-visible { outline: 3px solid #22d3ee; outline-offset: 2px; }
Once appearance: none is set, browsers render ::before on checkboxes, which is why this works even though inputs are replaced-like elements.
142. Build a responsive card grid without media queries.
Combine auto-fit, minmax(), and min() so the grid adds columns as space allows and never overflows a narrow screen. Use subgrid on the cards if titles and footers must line up.
.cards {
display: grid;
gap: 1rem;
grid-template-columns: repeat(auto-fit, minmax(min(16rem, 100%), 1fr));
}
143. Create a triangle or a tooltip arrow with CSS.
The modern way is clip-path: polygon() on a small square, which is easy to read and to resize. The classic way, still common in interviews, uses borders: a zero-size box with one colored border and transparent neighbors.
.arrow-modern { width: 12px; height: 8px; background: #111; clip-path: polygon(50% 0, 100% 100%, 0 100%); }
.arrow-border { width: 0; height: 0; border: 6px solid transparent; border-bottom-color: #111; }
144. Build a full-bleed section inside a centered container.
Use a grid with named lines: content goes in a centered column and full-bleed children span the whole row. This avoids the 100vw trick, which causes a horizontal scrollbar on desktops where the scrollbar takes up space.
.wrapper {
display: grid;
grid-template-columns: [full-start] minmax(1rem, 1fr) [content-start] min(65ch, 100% - 2rem) [content-end] minmax(1rem, 1fr) [full-end];
}
.wrapper > * { grid-column: content; }
.wrapper > .full-bleed { grid-column: full; }
145. Write a function that compares two selectors’ specificity.
Interviewers sometimes ask you to code the specificity rule. Represent specificity as a tuple of IDs, classes, and types, then compare left to right; the first unequal number decides. Parsing real selectors is out of scope in most rounds, so take the tuples as input and say so.
function compareSpecificity(a, b) {
for (let i = 0; i < 3; i++) {
if (a[i] !== b[i]) return a[i] > b[i] ? 1 : -1;
}
return 0; // tie: the later rule in source order wins
}
console.log(compareSpecificity([1, 0, 0], [0, 11, 0])); // 1 (#id beats 11 classes)
console.log(compareSpecificity([0, 2, 1], [0, 2, 1])); // 0
console.log(compareSpecificity([0, 1, 3], [0, 2, 0])); // -1
Practice: Find By Computed Style and querySelectorAll
146. Build a star rating widget with HTML and CSS.
Use a radio group inside a <fieldset>, one radio per star, so keyboard arrows and screen readers work without ARIA. Hide the radios visually (not with display: none), style the labels as stars, and highlight the checked star and all earlier ones with :has() or a reversed-order sibling selector.
.stars label { color: #cbd5e1; cursor: pointer; }
.stars:has(input:nth-of-type(3):checked) label:nth-of-type(-n + 3) { color: #f59e0b; }
The example shows the rule for three stars; generate one rule per star or set the fill with a custom property. Practice: Star Rating System
How to Use This List
Don’t read 146 questions the night before. Answer each one out loud before you read the answer, and build every hands-on question in a blank file without looking anything up. HTML and CSS interview questions reward people who have debugged real layouts, so tie your answers to bugs you’ve actually fixed.
| Level | Focus on | Then practice |
|---|---|---|
| Junior | HTML fundamentals, forms, box model, flexbox basics | Centering, a labeled form with validation, a navbar |
| Mid-level | Accessibility, script loading, grid, responsive design, specificity | Holy grail layout, a responsive card grid, a sticky header |
| Senior / staff | Stacking contexts, cascade layers, modern CSS, performance, CSS architecture | An accessible dialog, a design-token theme, a performance audit of a slow page |
CSS interview questions for experienced developers usually arrive as debugging scenarios, so practice explaining how you’d find the cause in DevTools before naming the fix. When you mention a newer feature, say whether it’s Baseline and what your fallback is; that’s the trade-off interviewers want to hear.
Where to go next on FrontendLead
- Frontend interview questions: JavaScript, browser, performance, and system design questions for the rest of the loop.
- HTML interview questions and CSS interview questions in the handbook, plus HTML fundamentals and CSS fundamentals if you need a refresher.
- Accessibility interview questions: the deeper ARIA, focus, and screen reader questions.
- JavaScript coding questions: UI and DOM problems with tests, in the browser.
- Frontend system design questions: where layout, accessibility, and performance decisions come together.
- Study plans: a day-by-day schedule that slots HTML and CSS in with everything else.
The official references are the best place to settle a debate: MDN HTML, MDN CSS, and the W3C ARIA Authoring Practices Guide for widget patterns.
Related reading: 120+ TypeScript interview questions and answers · Frontend testing: unit, integration, and E2E with interview questions
