Ch. 05

HTML & CSS

The fundamentals that separate good frontend engineers: semantics, accessibility, the cascade and layout.

57 interview questions28 quiz questions1 notes
your progress0%

Notes in this chapter

Filter all notes →
read ✓HTML & CSS · easy

The Cascade, Specificity & the Box Model

How the browser decides which CSS rule wins (origin, @layer, specificity, order), how inheritance and box-sizing work, and why z-index sometimes seems broken.

~7 min readread →

Top 57 HTML & CSS interview questions most asked first

  1. 1.Explain the CSS box model and what box-sizing: border-box changes.easy

    Every element is laid out as a rectangular box made of four layers, from the inside out: content, padding, border and margin. Padding and border belong to the element's box; margin is transparent space outside the border that separates it from its neighbours.

    By default box-sizing: content-box applies, so width and height size only the content area and padding and border are added on top. A box with width: 200px, 20px padding and a 1px border actually takes up 242px horizontally.

    With box-sizing: border-box, width and height include the content, padding and border, so that box is exactly 200px wide and the content area shrinks to make room. Margins are never included in either mode.

    Most codebases apply border-box globally because it makes sizing predictable, especially when you mix percentage widths with fixed padding.

    *, *::before, *::after {
      box-sizing: border-box;
    }
    What interviewers listen for
    • Four layers: content, padding, border, margin
    • Default content-box: width excludes padding and border
    • border-box: width includes padding and border, never margin
    • A global border-box reset makes sizing predictable

    Likely follow-up: Why do resets also target ::before and ::after? · What happens in border-box if padding plus border exceed the declared width?

  2. 2.What is semantic HTML, and why does it matter?easy

    Semantic HTML means choosing elements for their meaning, not their appearance: <nav> for navigation, <main> for the primary content, <button> for actions, <h1>–<h6> for a real heading hierarchy, <ul> for lists and <table> for tabular data, instead of generic <div>s and <span>s styled to look right.

    It matters because:

    • Accessibility: elements map to roles in the accessibility tree, so screen readers can announce them and users can jump between headings and landmarks. Native controls like <button> come with focus, keyboard activation and a role for free.
    • SEO: search engines use headings, links and structure to understand the page.
    • Maintainability: the markup describes itself and is easier to read and style.
    • Resilience: content still makes sense without CSS, in reader modes and on other devices.

    A <div onclick> may look like a button, but it isn't focusable or keyboard-operable unless you rebuild all of that by hand.

    What interviewers listen for
    • Pick elements by meaning, not by appearance
    • Native semantics feed the accessibility tree and screen readers
    • Native controls give focus and keyboard support for free
    • Helps SEO, readability and maintainability

    Likely follow-up: What would you need to add to make a <div> behave like a button?

  3. 3.How does CSS specificity work, and where does it fit in the cascade?mid

    When several declarations set the same property on an element, the cascade picks a winner by comparing, in order: origin and importance (user-agent, user and author styles, with !important reversing their order), shadow DOM context, inline style attributes, cascade layers, then specificity, and finally order of appearance (the last one wins).

    Specificity is compared as three columns (A, B, C):

    • A: ID selectors
    • B: classes, attribute selectors and pseudo-classes
    • C: type selectors and pseudo-elements

    Columns are compared left to right, so one ID beats any number of classes: #nav a (1,0,1) beats .menu .item a (0,2,1). The universal selector * and combinators add nothing; :where() counts as zero, while :is(), :not() and :has() take the specificity of their most specific argument.

    Inline styles aren't a specificity column: they beat any selector in normal author styles, and only !important declarations override them.

    /* <p class="text"> inside .card inside #main */
    #main p     { color: red; }   /* (1,0,1) wins */
    .card .text { color: blue; }  /* (0,2,0) */
    p.text      { color: green; } /* (0,1,1) */
    What interviewers listen for
    • Cascade: origin/importance, inline, layers, specificity, source order
    • Columns: IDs, then classes/attributes/pseudo-classes, then types
    • Compared column by column; one ID beats any number of classes
    • * and combinators add nothing; :where() is zero
    • Equal specificity: the later declaration wins

    Likely follow-up: How do you override a style without increasing specificity? · What is the specificity of :is(#main, .card) p?

  4. 4.When would you use Flexbox and when would you use CSS Grid?mid

    Flexbox is one-dimensional: it lays items out along a single axis, a row or a column, and distributes space based on the items' content sizes. It's ideal for components: navbars, toolbars, button groups, centering, or a row of tags that should wrap.

    Grid is two-dimensional: you define rows and columns on the container and place items into that structure, so things line up in both directions. It's ideal for page layouts, card galleries, forms with aligned labels, and anything where the layout should drive the content rather than the other way round.

    A useful rule of thumb: Flexbox is content-out, Grid is layout-in. They combine well, for example a Grid page layout with Flexbox inside each area. Both support gap and the same box alignment properties (justify-content, align-items, align-self), and subgrid lets nested grids align to their parent's tracks.

    What interviewers listen for
    • Flexbox: one axis at a time, content-driven sizing
    • Grid: rows and columns together, container-defined tracks
    • Flexbox for components, Grid for page and 2D layouts
    • They nest well and share gap and alignment properties

    Likely follow-up: How would you build a card grid that wraps without any media queries?

  5. 5.How do you center an element both horizontally and vertically? Give a few approaches.easy

    Modern options:

    • Grid: display: grid; place-items: center; on the parent centers the child on both axes.
    • Flexbox: display: flex; justify-content: center; align-items: center; on the parent.
    • Auto margins: margin: auto on a child of a flex or grid container absorbs the free space on all sides.
    • Absolute positioning: position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%); works when the element's size is unknown, e.g. for overlays. With a known width and height you can use inset: 0; margin: auto; instead.

    For horizontal centering only, use margin-inline: auto on a block with a set width, or text-align: center for inline content. Setting line-height equal to the height vertically centers a single line of text, but it breaks as soon as the text wraps.

    .parent {
      display: grid;
      place-items: center;
      min-height: 100vh;
    }
    What interviewers listen for
    • Grid: place-items: center on the parent
    • Flexbox: justify-content and align-items set to center
    • margin: auto on a flex or grid child
    • Absolute plus translate(-50%, -50%) for unknown sizes
    • margin-inline: auto for horizontal block centering
  6. 6.Explain the position values static, relative, absolute, fixed and sticky.easy
    • static (default): normal flow; offsets like top and left have no effect.
    • relative: stays in normal flow and keeps its space, but can be shifted with offsets relative to where it would have been. It also becomes the containing block for absolutely positioned descendants.
    • absolute: removed from the flow (takes no space) and placed relative to its containing block: the nearest ancestor that is positioned (anything but static) or has a transform, filter or similar; otherwise the initial containing block.
    • fixed: removed from the flow and positioned relative to the viewport, so it stays put while scrolling. Exception: an ancestor with transform, filter or perspective becomes its containing block instead.
    • sticky: behaves like relative until it crosses a threshold such as top: 0 within its nearest scroll container, then sticks until its parent scrolls out of view. It needs an offset, and an ancestor with overflow: hidden or auto becomes the scroll container it sticks within, a common reason it "doesn't work".
    What interviewers listen for
    • relative keeps its space and is offset from its normal position
    • absolute leaves the flow; placed against nearest positioned ancestor
    • fixed is relative to the viewport unless an ancestor has transform
    • sticky acts relative, then sticks within its scroll container

    Likely follow-up: Why might position: sticky not stick? · Why would a position: fixed modal move when its parent scrolls?

  7. 7.What is the difference between a plain <script>, async, defer and type="module"?mid
    • Plain <script src>: the parser stops, the script is downloaded and executed, then parsing continues. It blocks everything below it, which is why scripts used to go at the end of <body>.
    • defer: downloads in parallel without blocking the parser and executes after the document has been parsed, just before DOMContentLoaded, in document order. Best for scripts that need the DOM or depend on each other.
    • async: downloads in parallel and executes as soon as it arrives, pausing the parser at that moment. Execution order isn't guaranteed. Good for independent scripts such as analytics.
    • type="module": deferred by default, runs in strict mode with its own scope and supports import. Adding async makes it run as soon as it and its dependencies have loaded.

    For classic scripts, async and defer only apply to external files (src); on inline module scripts, async does work. If both are present, async wins.

    <script src="legacy.js"></script>             <!-- blocks parsing -->
    <script src="app.js" defer></script>           <!-- after parsing, in order -->
    <script src="analytics.js" async></script>     <!-- whenever it arrives -->
    <script type="module" src="main.js"></script>  <!-- deferred by default -->
    What interviewers listen for
    • Plain scripts block parsing while they download and run
    • defer: parallel download, runs after parsing, in order
    • async: parallel download, runs when ready, order not guaranteed
    • Module scripts are deferred by default

    Likely follow-up: Why do scripts placed after a stylesheet sometimes wait for that stylesheet?

  8. 8.What is the difference between block, inline and inline-block elements?easy
    • Block boxes (<div>, <p>, <h1>, <ul>, <section>) start on a new line and by default stretch to the full width of their container. width, height, margins and padding all apply.
    • Inline boxes (<span>, <a>, <strong>, <em>) flow within a line of text and are only as wide as their content. width and height are ignored, and vertical margins don't push other lines away; vertical padding is painted but doesn't change the line's height.
    • inline-block sits in the line like text but is laid out internally as a block, so width, height and vertical margins apply. It's handy for badges or buttons within a line.

    These are CSS display behaviours, so any element can be switched. Replaced elements such as <img> are inline by default but still respect width and height. A classic gotcha: whitespace between inline-block elements in the HTML renders as a small gap.

    What interviewers listen for
    • Block: new line, fills container width, all box properties apply
    • Inline: flows with text; width and height ignored
    • Inline vertical margins and padding do not push lines apart
    • inline-block: sits in the line but accepts size and margins

    Likely follow-up: Why do inline-block items sometimes have a small gap between them?

  9. 9.Compare display: none, visibility: hidden and opacity: 0.easy
    • display: none removes the element from layout entirely: it takes no space, isn't rendered or focusable, and is removed from the accessibility tree. Descendants can't override it.
    • visibility: hidden keeps the element's space in the layout but doesn't paint it. It can't be clicked or focused and is hidden from assistive technology. Unlike display, a child can set visibility: visible to reappear. It can be transitioned, which is why it's often paired with opacity for fade-outs.
    • opacity: 0 keeps both layout and interactivity: the element is invisible but still receives clicks and focus, and screen readers still announce it. Opacity below 1 creates a stacking context, and it animates cheaply on the compositor.

    To hide something visually but keep it available to screen readers, none of these is right: use a "visually hidden" utility class.

    .visually-hidden {
      position: absolute;
      width: 1px;
      height: 1px;
      margin: -1px;
      padding: 0;
      overflow: hidden;
      clip: rect(0 0 0 0);
      white-space: nowrap;
      border: 0;
    }
    What interviewers listen for
    • display: none: no space, no rendering, not in the accessibility tree
    • visibility: hidden: keeps space, not interactive, children can override
    • opacity: 0: keeps space, still clickable, focusable and announced
    • Use a visually-hidden class for screen-reader-only content
  10. 10.Explain px, em, rem, %, vw/vh and ch. When would you use each?easy
    • px: a CSS pixel, a fixed reference unit rather than a device pixel. Good for borders and small fixed details.
    • em: relative to the element's own computed font-size (for font-size itself, the parent's). It compounds when nested, which is handy for padding that scales with a component's text but can surprise you.
    • rem: relative to the root (html) font size. Predictable, and it respects the user's browser font-size setting, so it's the usual choice for font sizes and spacing.
    • %: relative to a property-specific reference, usually the containing block. Note that vertical padding and margin percentages also use the containing block's width.
    • vw / vh: 1% of the viewport width or height. On mobile, 100vh can be taller than the visible area while browser toolbars are showing; dvh (dynamic), svh (small) and lvh (large) address that.
    • ch: the width of the "0" glyph in the current font, great for readable line lengths such as max-width: 65ch.
    What interviewers listen for
    • em is relative to the element font size and compounds
    • rem is relative to the root font size
    • % depends on the property, usually the containing block
    • dvh, svh and lvh fix mobile 100vh issues
    • ch suits line-length limits

    Likely follow-up: Why is rem better than px for font sizes from an accessibility point of view?

  11. 11.What is the difference between a pseudo-class and a pseudo-element? Give examples of each.easy

    A pseudo-class (single colon) selects an existing element based on a state or position that a simple selector can't express: :hover, :focus-visible, :checked, :disabled, :first-child, :nth-child(2n), :not().

    A pseudo-element (double colon) targets a part of an element, or generates a box that doesn't exist in the DOM: ::before and ::after (generated content, which only render when content is set), ::first-line, ::first-letter, ::placeholder, ::selection, ::marker, ::backdrop.

    The double colon was introduced to tell the two apart; browsers still accept the single-colon form for the original four (:before, :after, :first-line, :first-letter). In specificity, pseudo-classes count like classes and pseudo-elements like type selectors.

    Generated content can't be selected from JavaScript and should be decorative rather than carry essential meaning. ::before and ::after generally don't work on replaced elements such as <img> and <input>.

    a:hover { text-decoration: underline; }
    
    .required::after {
      content: " *";
      color: crimson;
    }
    What interviewers listen for
    • Pseudo-class: selects an element in a state; single colon
    • Pseudo-element: styles a part or generated box; double colon
    • ::before and ::after need content to render
    • Specificity: pseudo-class like a class, pseudo-element like a type
  12. 12.What does <!DOCTYPE html> do, and what happens if you leave it out?easy

    The doctype must be the first thing in an HTML document, and its only job today is to make the browser render the page in standards mode. <!DOCTYPE html> is the short HTML5 form; it's case-insensitive and doesn't reference a DTD.

    If it's missing, or you use certain legacy doctypes, browsers switch to quirks mode, which emulates old browser behaviour so that pages from the 1990s still render as their authors expected. In quirks mode, for example, unitless lengths like width: 100 are accepted, hex colors without # work, and percentage heights and line heights are calculated differently. Your modern CSS can then behave unexpectedly.

    Some transitional doctypes trigger limited-quirks ("almost standards") mode, which mainly changes how images in table cells are laid out.

    You can check the mode with document.compatMode: "CSS1Compat" means standards mode and "BackCompat" means quirks mode.

    What interviewers listen for
    • Triggers standards mode rendering
    • Missing doctype means quirks mode with legacy behaviour
    • HTML5 doctype is short, case-insensitive, and has no DTD
    • Check with document.compatMode
  13. 13.How does z-index work, and what is a stacking context? Why might z-index: 9999 fail to bring an element to the front?hard

    z-index sets the stacking order of positioned elements (anything other than position: static) and of flex and grid items. Higher values paint in front, but values are only compared within the same stacking context.

    A stacking context is an isolated group: its children are stacked relative to each other, then the whole group is painted as one unit at its own level in the parent context. Common triggers:

    • the root element
    • a positioned element with z-index other than auto (fixed and sticky always create one)
    • a flex or grid item with z-index other than auto
    • opacity below 1, transform, filter, clip-path, mask, mix-blend-mode
    • isolation: isolate, contain: paint, or will-change naming one of these properties

    So a child with z-index: 9999 inside a parent context at z-index: 1 can never appear above that parent's sibling at z-index: 2. Fix the ancestor's stacking level, remove the accidental context, or render the element at the top of the document, for example with a portal or the top layer (<dialog>, popover).

    .header { position: relative; z-index: 2; }
    .main   { position: relative; z-index: 1; }
    
    /* Can never cover .header: it is trapped in .main's stacking context */
    .main .tooltip { position: absolute; z-index: 9999; }
    What interviewers listen for
    • z-index applies to positioned elements and flex/grid items
    • Values only compete within the same stacking context
    • Triggers: positioned plus z-index, opacity below 1, transform, isolation
    • A child can never escape its parent context's level

    Likely follow-up: What does isolation: isolate do, and when would you use it?

  14. 14.Walk me through the critical rendering path: what does the browser do between receiving HTML and showing pixels on screen?hard
    • Parse HTML into the DOM. Parsing is incremental, and a preload scanner looks ahead to start fetching resources early. Classic synchronous scripts pause the parser.
    • Parse CSS into the CSSOM. CSS is render-blocking: the browser won't render until the stylesheets in the head have loaded, to avoid a flash of unstyled content. Synchronous scripts also wait for pending stylesheets, since they might read styles.
    • Style calculation / render tree. The DOM and CSSOM are combined into the tree of visible boxes with computed styles. display: none elements are left out; pseudo-elements are included.
    • Layout (reflow) computes the size and position of every box.
    • Paint records the drawing of text, colors, borders, shadows and images, often split into layers.
    • Composite: the layers are rasterized and combined, often on the GPU, into the final frame.

    Optimizing the path means fewer and smaller render-blocking resources: inline critical CSS, defer scripts, preload late-discovered critical assets, and reduce the bytes needed for the first render.

    What interviewers listen for
    • HTML to DOM, CSS to CSSOM, combined into the render tree
    • CSS is render-blocking; sync scripts are parser-blocking
    • Layout computes geometry, paint draws, composite assembles layers
    • Optimize with critical CSS, deferred JS, fewer blocking bytes

    Likely follow-up: Why is CSS render-blocking but not parser-blocking?

  15. 15.What is the difference between reflow and repaint, and how do you avoid layout thrashing?mid
    • Reflow (layout) recalculates the geometry of elements: their sizes and positions. It's triggered by changing things like width, height, margin, padding, top/left or font size, by adding or removing DOM nodes, or by resizing the window. It can affect descendants, siblings and ancestors, so it's the most expensive step.
    • Repaint redraws pixels without changing geometry, for example when color, background-color, visibility or box-shadow change. Cheaper, but still main-thread work.
    • Composite-only changes (transform, opacity on a layer) can often skip both.

    Layout thrashing happens when JavaScript interleaves writes and reads: after a style change, reading offsetHeight, getBoundingClientRect() or scrollTop forces the browser to run layout synchronously, and doing that in a loop runs layout many times per frame. Avoid it by batching all reads first and then all writes, scheduling writes with requestAnimationFrame, and animating transform and opacity instead of layout properties.

    What interviewers listen for
    • Reflow recalculates geometry; repaint redraws pixels
    • Reflow is more expensive and can spread to other elements
    • Reading layout after a style write forces synchronous layout
    • Batch reads then writes; animate transform and opacity
  16. 16.How do you approach responsive design, and what does "mobile-first" mean for media queries?easy

    Responsive design means one codebase that adapts to any screen: fluid layouts (Flexbox, Grid, percentages, fr), flexible media (max-width: 100% on images, srcset), relative units, and media queries where the layout genuinely needs to change. The viewport meta tag is required so mobile browsers don't lay the page out at a fake desktop width.

    Mobile-first means writing the base styles for small screens and layering enhancements with min-width queries as space grows. The base CSS stays simple, small screens don't have to undo desktop rules, and it forces you to prioritize content. Desktop-first is the opposite, using max-width queries.

    Choose breakpoints where the content breaks, not for specific devices. Intrinsic techniques (auto-fit grids, clamp() for fluid type) and container queries reduce how many breakpoints you need. You can also query capabilities and preferences, such as (hover: hover) or (prefers-reduced-motion: reduce).

    .grid { display: grid; gap: 1rem; }
    
    @media (min-width: 48em) {
      .grid { grid-template-columns: 1fr 1fr; }
    }
    
    @media (min-width: 64em) {
      .grid { grid-template-columns: repeat(3, 1fr); }
    }
    What interviewers listen for
    • Fluid layouts, flexible images, relative units, media queries
    • Mobile-first: small-screen base styles, add min-width queries
    • Breakpoints follow the content, not specific devices
    • Requires the viewport meta tag
  17. 17.What does <meta name="viewport" content="width=device-width, initial-scale=1"> do, and why is it needed?easy

    Without it, mobile browsers assume the page was designed for desktop: they lay it out on a wide virtual viewport (typically around 980px) and zoom out to fit the screen. Text ends up tiny and your media queries see a desktop-sized width, so the responsive styles never apply.

    • width=device-width sets the layout viewport to the device's width in CSS pixels, so media queries and percentage widths match the actual screen.
    • initial-scale=1 sets the initial zoom level to 100%.

    It goes in the <head> and is required for responsive design to work on phones. Avoid adding user-scalable=no or maximum-scale=1: blocking pinch-zoom is an accessibility failure for low-vision users who need to enlarge text, and some mobile browsers ignore those values for that reason.

    What interviewers listen for
    • Without it, mobile uses a wide virtual viewport, zoomed out
    • width=device-width matches layout width to the device
    • initial-scale=1 sets the initial zoom level
    • Never disable zoom with user-scalable=no
  18. 18.Explain flex-grow, flex-shrink and flex-basis. What does flex: 1 actually mean?mid

    They control how flex items share space along the main axis (set by flex-direction):

    • flex-basis: the item's starting size before free space is distributed. auto means "use width/height, or the content size if that's also auto".
    • flex-grow: the proportion of positive free space the item takes. An item with 2 gets twice as much of the extra space as an item with 1, not twice the total size.
    • flex-shrink: how much the item gives up when there's negative free space (overflow), weighted by its basis. Default 1; 0 prevents shrinking.

    The initial value is flex: 0 1 auto: don't grow, may shrink, size from content. flex: 1 expands to 1 1 0, so the basis is zero and all space is shared by the grow ratio, giving equal widths. flex: auto is 1 1 auto, sharing only the leftover space.

    Gotcha: flex items default to min-width: auto, so they won't shrink below their content's minimum size; set min-width: 0 to let long text truncate.

    .row { display: flex; gap: 1rem; }
    
    /* fixed 240px, never grows or shrinks */
    .sidebar { flex: 0 0 240px; }
    
    /* takes the remaining space and can shrink below its content */
    .content { flex: 1; min-width: 0; }
    What interviewers listen for
    • flex-basis is the starting size on the main axis
    • flex-grow shares positive free space proportionally
    • flex-shrink handles overflow, weighted by basis
    • flex: 1 means 1 1 0: equal shares of the space
    • Flex items default to min-width: auto

    Likely follow-up: Why does a flex item containing a long URL overflow instead of shrinking?

  19. 19.Compare localStorage, sessionStorage and cookies. When would you use each?mid
    • localStorage: string key-value pairs per origin that persist until cleared by code or the user. It holds a few megabytes (commonly around 5 MB per origin). The API is synchronous, so avoid large reads and writes on the main thread.
    • sessionStorage: the same API, but scoped to one tab (top-level browsing context) and origin. It survives reloads and is cleared when the tab closes.
    • Cookies: small (about 4 KB each) and sent automatically with every matching HTTP request, which makes them the right place for session identifiers. They're scoped with Domain and Path, expire via Expires or Max-Age, and are hardened with HttpOnly (unreadable from JavaScript), Secure (HTTPS only) and SameSite (limits cross-site sending, which helps against CSRF).

    Web Storage is readable by any script running on the origin, so an XSS bug can steal everything in it: don't keep auth tokens there. For large or structured data, use IndexedDB.

    What interviewers listen for
    • localStorage persists; sessionStorage lasts for the tab
    • Cookies are sent with every matching request
    • HttpOnly, Secure and SameSite harden cookies
    • Web Storage is exposed to XSS; avoid storing tokens
  20. 20.What is ARIA, and when should you not use it?mid

    WAI-ARIA is a set of attributes that add or change semantics in the accessibility tree: roles (role="dialog", role="tab"), states (aria-expanded, aria-checked, aria-selected) and properties (aria-label, aria-labelledby, aria-describedby, aria-controls). It only changes what assistive technology is told; it adds no behaviour, focusability or keyboard support.

    The first rule of ARIA: if a native HTML element or attribute already has the semantics and behaviour you need, use it. A <button> beats <div role="button">, which still needs tabindex="0" and Enter and Space handling.

    The other rules: don't change native semantics unless you must; every interactive ARIA control must be keyboard operable; never put role="presentation" or aria-hidden="true" on focusable elements; and every interactive element needs an accessible name. Keep states like aria-expanded in sync with the UI. "No ARIA is better than bad ARIA": wrong roles actively mislead screen-reader users.

    <button aria-expanded="false" aria-controls="menu">Menu</button>
    <ul id="menu" hidden>
      <li><a href="/docs">Docs</a></li>
      <li><a href="/blog">Blog</a></li>
    </ul>
    What interviewers listen for
    • ARIA adds roles, states and properties, not behaviour
    • First rule: prefer native HTML elements
    • Custom ARIA widgets must handle keyboard and focus
    • Never hide focusable elements with aria-hidden
    • Keep ARIA states in sync with the UI

    Likely follow-up: What is the difference between aria-label and aria-labelledby?

  21. 21.How do you write good alt text for images, and when should alt be empty?easy

    alt is the text alternative that screen readers announce and that browsers show if the image fails to load. Good alt text conveys the image's purpose in context rather than a literal description: the key takeaway of a chart, or, for a linked logo, where the link goes.

    Guidelines:

    • Be concise, and don't start with "image of": screen readers already announce that it's an image.
    • If the image is decorative, or its information is already in the nearby text, use an empty alt="" so assistive technology skips it.
    • Never leave the attribute out: without alt, many screen readers fall back to reading the file name.
    • For a functional image, such as the only content of a link or button, describe the action or destination.
    • Complex images such as charts and diagrams need a short alt plus a longer description in the surrounding text.

    CSS background images have no text alternative, so use them only for decoration.

    <img src="q3-sales.png" alt="Sales rose 40% in Q3, driven by mobile">
    <img src="divider.svg" alt="">
    <a href="/"><img src="logo.svg" alt="Acme home page"></a>
    What interviewers listen for
    • Describe the purpose in context, concisely
    • Decorative images get alt="", never a missing attribute
    • Linked or button images describe the action
    • Complex images need a longer description nearby
  22. 22.What is the difference between <div>, <section> and <article>?easy
    • <div> has no semantics. Use it purely as a styling or layout hook when no meaningful element fits.
    • <section> is a thematic grouping of content, typically with its own heading, such as the parts of a long page. If you're only wrapping something for styling, it should be a <div> instead. A <section> is exposed as a region landmark only when it has an accessible name, for example via aria-labelledby pointing at its heading.
    • <article> is self-contained content that would make sense on its own or could be syndicated: a blog post, a news story, a product card, a comment. Articles can be nested (comments inside a post) and can contain sections.

    A quick test: would it make sense on its own in a feed? Use <article>. Is it a titled part of something bigger? Use <section>. Neither? Use <div>. Related elements are <aside> for tangential content, <nav> for major navigation, and <header>/<footer> for introductory and closing content.

    What interviewers listen for
    • <div> has no semantic meaning; a styling hook only
    • <section>: a thematic group, usually with a heading
    • <article>: self-contained, independently distributable content
    • A named <section> becomes a region landmark
  23. 23.What are CSS custom properties (CSS variables), and how do they differ from Sass variables?mid

    Custom properties are properties whose names start with --, read with var(): color: var(--brand, blue), where the second argument is a fallback. They're live at runtime: they take part in the cascade and inherit down the DOM, so you can redefine them per component, per media query or per theme, and the page updates immediately. JavaScript can read them with getComputedStyle(el).getPropertyValue('--x') and set them with el.style.setProperty('--x', value).

    Sass variables are compile-time: they're replaced by static values when the CSS is built, know nothing about the DOM, and can't react to a class or change inside a runtime media query.

    Worth knowing: names are case-sensitive, and values aren't checked until they're substituted. If the substituted value is invalid for the property, the declaration becomes "invalid at computed-value time" and behaves like unset rather than falling back to an earlier declaration. @property registers a type, initial value and inheritance, which also makes custom properties animatable.

    :root {
      --space: 1rem;
      --brand: #2563eb;
    }
    .card { padding: var(--space); border-color: var(--brand); }
    .card--compact { --space: 0.5rem; }
    
    @media (prefers-color-scheme: dark) {
      :root { --brand: #60a5fa; }
    }
    What interviewers listen for
    • Declared with --name, used via var() with optional fallback
    • They cascade and inherit, and can change at runtime
    • Sass variables are compile-time and static
    • Readable and writable from JavaScript
    • @property adds a type, enabling animation

    Likely follow-up: What happens when a var() substitution produces an invalid value?

  24. 24.Explain fr, minmax() and the difference between auto-fill and auto-fit in CSS Grid.mid
    • fr is a fraction of the space left in the grid container after fixed-size tracks and gaps. 1fr 2fr splits the remainder 1:2. 1fr behaves like minmax(auto, 1fr), so a track won't shrink below its content's minimum size; use minmax(0, 1fr) to allow that.
    • minmax(min, max) gives a track a size range, e.g. minmax(200px, 1fr).
    • auto-fill vs auto-fit: repeat(auto-fill, minmax(200px, 1fr)) creates as many columns as fit. auto-fill keeps the empty tracks when there are fewer items than columns, so items keep their track width and the row has empty space at the end. auto-fit collapses empty tracks to zero, so the existing items stretch to fill the row. The difference only shows when there aren't enough items to fill a row.

    Together these give a responsive card grid without media queries. grid-template-areas complements them by naming regions with strings such as "header header" "sidebar main" and placing items with grid-area: header, which keeps layouts readable and easy to rearrange.

    .cards {
      display: grid;
      gap: 1rem;
      grid-template-columns: repeat(auto-fit, minmax(min(200px, 100%), 1fr));
    }
    What interviewers listen for
    • fr divides the space left after fixed tracks and gaps
    • minmax() sets a size range for a track
    • auto-fill keeps empty tracks; auto-fit collapses them
    • repeat(auto-fit, minmax()) is responsive without media queries
    • Template areas name regions for readable layouts
  25. 25.What is margin collapsing, and when does it happen?mid

    In normal block flow, adjoining vertical (block-direction) margins combine into a single margin equal to the largest of them rather than their sum. With negative margins, the most negative one is added to the largest positive one. Horizontal margins never collapse.

    It happens in three situations:

    • Adjacent siblings: margin-bottom: 20px followed by margin-top: 30px gives a 30px gap, not 50px.
    • Parent and first or last child: if no border, padding, inline content or clearance separates them, the child's margin "leaks" out and becomes the parent's margin.
    • Empty blocks: an element with no height, padding, border or content collapses its own top and bottom margins together.

    It doesn't happen for flex and grid items, floats or absolutely positioned elements, or between an element that establishes a new block formatting context (for example display: flow-root or overflow: hidden) and its children. Using gap in flex and grid layouts, or applying margins in one direction only, avoids most surprises.

    .a { margin-bottom: 20px; }
    .b { margin-top: 30px; }   /* gap between .a and .b is 30px, not 50px */
    
    /* stops children's margins leaking out of the parent */
    .parent { display: flow-root; }
    What interviewers listen for
    • Only vertical margins in normal block flow collapse
    • The result is the largest margin, not the sum
    • Cases: adjacent siblings, parent and child, empty blocks
    • Not in flex/grid, floats, abspos, or into a new BFC
  26. 26.What is a block formatting context (BFC), how do you create one, and what problems does it solve?hard

    A block formatting context is an independent layout region in which block boxes are laid out one after another and floats are contained. What happens inside it doesn't affect layout outside, and vice versa.

    An element that establishes a BFC:

    • contains its floats: its height includes floated children, the problem clearfix used to solve.
    • stops margin collapsing between itself and its children.
    • doesn't overlap floats: placed next to a float, it narrows to sit beside it instead of flowing underneath, which enables simple media-object layouts.

    Things that create one include the root element, floats, position: absolute or fixed, display: inline-block, table cells, overflow other than visible and clip, flex and grid items, contain: layout or paint, and multi-column containers. The purpose-built option is display: flow-root, which creates a BFC with no side effects, unlike overflow: hidden, which also clips content, shadows and outlines.

    .media { display: flow-root; }   /* contains the floated image */
    .media img { float: left; margin-right: 1rem; }
    What interviewers listen for
    • Independent layout region; inside does not affect outside
    • Contains floats and stops parent-child margin collapsing
    • A BFC box does not overlap adjacent floats
    • display: flow-root creates one without side effects
  27. 27.How do you build an accessible form? Why do labels matter, and which input types would you use?easy
    • Every control needs a label. Wrap the input in a <label>, or link them with for matching the input's id. That gives the control an accessible name for screen readers, and clicking the label focuses or toggles the input, a bigger hit area for checkboxes and radios. A placeholder isn't a label: it disappears as you type and often has poor contrast.
    • Use the right input type: email, tel, url, number, date, search, password, checkbox, radio. You get built-in validation and, on mobile, a suitable keyboard. autocomplete values such as email help users and password managers.
    • Group related controls with <fieldset> and <legend>, especially radio groups.
    • Use a real <button type="submit"> inside a <form>, so Enter submits and the form works without JavaScript.
    • Connect hints and error messages with aria-describedby, and flag invalid fields with aria-invalid.
    <form>
      <label for="email">Email</label>
      <input id="email" type="email" name="email" autocomplete="email" required>
    
      <fieldset>
        <legend>Plan</legend>
        <label><input type="radio" name="plan" value="free"> Free</label>
        <label><input type="radio" name="plan" value="pro"> Pro</label>
      </fieldset>
      <button type="submit">Sign up</button>
    </form>
    What interviewers listen for
    • Every input gets a <label>, via for/id or wrapping
    • Placeholders are not labels
    • Semantic input types give validation and mobile keyboards
    • Group radios and checkboxes with <fieldset> and <legend>
    • Link hints and errors with aria-describedby
  28. 28.What built-in form validation does HTML provide, and how do you style and customize it?mid

    HTML has constraint validation built in, driven by attributes: required, minlength/maxlength, min/max/step for numbers and dates, pattern (a regular expression the whole value must match), and the input type itself (email, url). On submit, the browser blocks submission, focuses the first invalid field and shows a native message. novalidate on the form, or formnovalidate on a submit button, turns that off.

    For styling there are :valid, :invalid, :required, :in-range/:out-of-range, and :user-invalid, which only matches after the user has interacted with the field, so fields aren't shown as errors on page load.

    In JavaScript, the Constraint Validation API gives you input.validity (flags like valueMissing, typeMismatch, patternMismatch), checkValidity(), reportValidity() and setCustomValidity('message') for custom rules; an empty string clears the error.

    Client-side validation is a UX feature only. Always validate on the server as well, because it's trivial to bypass.

    <form>
      <input name="zip" required pattern="[0-9]{5}" title="Five-digit ZIP code">
      <input type="number" name="qty" min="1" max="10" step="1">
      <button>Save</button>
    </form>
    What interviewers listen for
    • Attributes: required, pattern, min/max, minlength, type
    • Style with :invalid and :user-invalid
    • Constraint Validation API: validity, setCustomValidity()
    • Always validate again on the server
  29. 29.How does keyboard navigation work, and what do tabindex="0" and tabindex="-1" do? Why avoid positive values?mid

    Keyboard users move between interactive elements with Tab and Shift+Tab, activate them with Enter (links and buttons) or Space (buttons, checkboxes), and use arrow keys inside composite widgets such as radio groups, menus and tabs. Native links, buttons and form controls are focusable automatically, in DOM order.

    • tabindex="0" puts a non-interactive element into the tab order at its DOM position. Use it only for custom widgets that also get a role and keyboard handling.
    • tabindex="-1" makes an element focusable programmatically with el.focus() but keeps it out of the Tab sequence. Useful for moving focus to a dialog or to a heading after a route change, and for roving tabindex inside composite widgets.
    • Positive values jump ahead of everything else, creating a tab order that doesn't match the visual layout and is fragile to maintain.

    Also keep a visible focus indicator (style :focus-visible rather than removing outlines), keep DOM order consistent with visual order, and provide a "skip to main content" link.

    What interviewers listen for
    • Native interactive elements are focusable, in DOM order
    • tabindex="0" joins the tab order; -1 is programmatic only
    • Positive tabindex breaks the natural order; avoid it
    • Keep a visible focus style with :focus-visible
    • DOM order should match visual order
  30. 30.What is the difference between CSS transitions and CSS animations?easy
    • Transitions animate a property from one value to another when it changes, for example on :hover or when a class is toggled. You declare which property, how long, and the easing: transition: transform 200ms ease-out. They only have a start and an end state, need something to trigger the change, and run once per change, reversing smoothly if the change is undone midway.
    • Animations use @keyframes to define any number of intermediate steps and can run on their own, without a state change. They support animation-iteration-count (including infinite), animation-direction (such as alternate), animation-delay, animation-fill-mode (for example to keep the final state) and animation-play-state (to pause).

    Rule of thumb: transitions for simple interactive state changes, animations for loaders, attention effects, and multi-step or looping motion. In both cases prefer animating transform and opacity, and respect prefers-reduced-motion.

    .btn { transition: transform 150ms ease-out; }
    .btn:hover { transform: translateY(-2px); }
    
    @keyframes spin { to { transform: rotate(360deg); } }
    .spinner { animation: spin 1s linear infinite; }
    
    @media (prefers-reduced-motion: reduce) {
      .btn { transition: none; }
    }
    What interviewers listen for
    • Transitions animate between two states when a value changes
    • Animations use @keyframes and run without a trigger
    • Animations support iteration, direction, fill mode and pausing
    • Prefer transform/opacity; respect prefers-reduced-motion
  31. 31.Why is animating transform and opacity smoother than animating top, left or width? What does will-change do?hard

    Every frame goes through style, layout, paint and composite. Animating width, height, top or left changes geometry, so each frame re-runs layout (possibly for other elements too) and paint on the main thread. That easily blows the frame budget (about 16ms at 60fps) and causes jank.

    transform and opacity don't affect layout. When the element is on its own compositor layer, the compositor thread can apply them to already-painted content, often on the GPU, skipping layout and paint entirely. The animation can then stay smooth even while the main thread is busy running JavaScript.

    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 hiccup when the animation starts. Use it sparingly: every layer costs memory, and blanket will-change on many elements can make performance worse. Add it shortly before an animation and remove it afterwards, or keep it only on elements that animate constantly. Like the real property, it also creates a stacking context.

    What interviewers listen for
    • Layout properties re-run layout and paint every frame
    • transform and opacity can run on the compositor
    • Compositor animations stay smooth when the main thread is busy
    • will-change promotes a layer early; use it sparingly

    Likely follow-up: How would you animate an element from height: 0 to its natural height smoothly?

  32. 32.How do srcset, sizes and <picture> work for responsive images, and what does loading="lazy" do?hard
    • srcset with w descriptors lists the same image at several widths (hero-800.jpg 800w, hero-1600.jpg 1600w). sizes tells the browser how wide the image will be displayed at each viewport size ((min-width: 60em) 50vw, 100vw), because it must choose before layout is known. The browser combines that with the device pixel ratio to pick a candidate. Without sizes, it assumes 100vw.
    • x descriptors (logo.png 1x, logo-2x.png 2x) suit fixed-size images.
    • <picture> with <source> children handles art direction (different crops per breakpoint via media) and format fallbacks (type="image/avif", then WebP, then the <img>). The first matching <source> wins; the inner <img> is required and carries alt.
    • loading="lazy" defers off-screen images until the user scrolls near them. Don't lazy-load the LCP hero image; consider fetchpriority="high" for it instead.

    Always set width and height attributes so the browser can reserve space and avoid layout shift.

    <picture>
      <source type="image/avif"
              srcset="hero-800.avif 800w, hero-1600.avif 1600w"
              sizes="(min-width: 60em) 50vw, 100vw">
      <img src="hero-800.jpg"
           srcset="hero-800.jpg 800w, hero-1600.jpg 1600w"
           sizes="(min-width: 60em) 50vw, 100vw"
           width="1600" height="900" alt="Team planning session at a whiteboard">
    </picture>
    What interviewers listen for
    • srcset with w plus sizes lets the browser choose resolution
    • <picture> for art direction and format fallbacks
    • The inner <img> is required and holds alt
    • loading="lazy" defers off-screen images; not the LCP image
    • Set width and height to prevent layout shift
  33. 33.Which CSS properties are inherited, and what do inherit, initial, unset and revert do?mid

    Inheritance passes a parent's computed value to a child that has no declared value of its own. Mostly text-related properties inherit: color, font-*, line-height, letter-spacing, text-align, visibility, cursor, list-style. Most box and layout properties don't: margin, padding, border, width, background, display, position. Custom properties inherit by default.

    Form controls often don't pick up the page font because the browser's default styles set a font on them directly, which is why resets include font: inherit for button, input, select and textarea.

    The CSS-wide keywords:

    • inherit: use the parent's computed value, even for a non-inherited property.
    • initial: the property's initial value from the spec, not the browser default (display: initial is inline, even on a div).
    • unset: acts as inherit for inherited properties and initial for the rest.
    • revert: roll back to the previous cascade origin, usually the browser's default styles; revert-layer rolls back to the previous cascade layer.

    all: unset or all: revert apply a keyword to every property at once.

    What interviewers listen for
    • Text properties inherit; box and layout properties mostly do not
    • Children inherit the parent computed value
    • initial is the spec initial value, not the browser default
    • unset acts as inherit or initial; revert restores browser styles
  34. 34.What does !important do, and why is it usually considered a code smell?easy

    !important moves a declaration into a higher-priority tier of the cascade. It beats every normal declaration regardless of specificity or source order, including inline styles. When two important declarations compete, the usual rules (layers, specificity, order) decide between them.

    Importance also reverses some precedence: an important user-agent or user style beats an important author style, and important declarations in earlier cascade layers beat those in later layers.

    The problem is escalation. In practice the only way to override an !important is another !important with equal or higher priority, which leads to specificity wars and brittle CSS. It usually signals a deeper issue, such as overly specific selectors or fighting a third-party stylesheet.

    Legitimate uses exist: utility classes that must always win (.hidden { display: none !important; }), user stylesheets for accessibility, or overriding inline styles you don't control. Better long-term tools are flat specificity (single classes, :where()) and cascade layers.

    What interviewers listen for
    • Beats all normal declarations, including inline styles
    • Overriding it generally needs another !important
    • Reverses origin and layer precedence
    • Causes specificity wars; prefer flat selectors and @layer
  35. 35.What are cascade layers (@layer), and what problem do they solve?hard

    Cascade layers let you group styles into named layers whose order you declare explicitly. In the cascade, layer order is compared before specificity, so a rule in a later layer beats a rule in an earlier layer even when the earlier rule has a far more specific selector.

    That solves specificity battles between different sources of CSS: resets, third-party libraries, design-system components and utilities. You declare the order once, e.g. @layer reset, vendor, components, utilities;, and utilities beat components without !important or extra selector weight.

    Key rules:

    • The first time a layer name appears fixes its position; later @layer components { } blocks add to that layer.
    • Unlayered styles beat all layered styles for normal declarations, so existing code keeps winning while you migrate.
    • For !important declarations the order reverses: earlier layers win, and unlayered important styles are the weakest.
    • You can import a file into a layer: @import url(vendor.css) layer(vendor);.
    • Layers can be nested, e.g. components.buttons.
    @layer reset, vendor, components, utilities;
    
    @layer components {
      .card .title { color: navy; }
    }
    
    @layer utilities {
      .text-red { color: red; } /* wins despite lower specificity */
    }
    What interviewers listen for
    • Layer order is compared before specificity
    • Later layers beat earlier ones
    • Unlayered styles beat layered styles
    • For !important, the layer order reverses
    • Tames third-party and utility CSS without !important
  36. 36.What do :is(), :where() and :has() do, and how does each affect specificity?hard
    • :is() matches any element that matches one of the selectors in its list, which shortens repetitive selectors: :is(h1, h2, h3) a instead of three rules. Its specificity is that of its most specific argument, so :is(#id, p) counts as an ID even when it matches a p.
    • :where() matches exactly the same way but always has zero specificity, which makes it ideal for defaults and reset or library styles that should be trivially easy to override.
    • Both take a forgiving selector list: an invalid or unsupported selector in the list is ignored instead of invalidating the whole rule.
    • :has() is a relational pseudo-class, often called the "parent selector": it matches an element if a relative selector matches from it, e.g. .card:has(img) or label:has(+ input:invalid). It can look at descendants, children and following siblings. Its specificity is also that of its most specific argument, and unlike the other two its list is not forgiving.
    /* (0,0,0): trivially easy to override */
    :where(ul, ol) { margin: 0; padding: 0; }
    
    :is(article, aside) :is(h2, h3) { margin-top: 0; }
    
    .card:has(img) { grid-template-rows: auto 1fr; }
    form:has(:user-invalid) .submit { opacity: 0.5; }
    What interviewers listen for
    • :is() groups selectors; takes its most specific argument
    • :where() matches the same with zero specificity
    • :is() and :where() use forgiving selector lists
    • :has() selects based on descendants or following siblings
  37. 37.What are data-* attributes, and how do you use them from CSS and JavaScript?easy

    data-* attributes let you store custom data on any HTML element without inventing non-standard attributes, e.g. <li data-id="42" data-status="done">. The part after data- must not contain uppercase letters.

    • JavaScript: read and write them through the dataset property, which converts kebab-case to camelCase, so data-user-id becomes el.dataset.userId. Values are always strings. getAttribute and setAttribute work too.
    • CSS: select with attribute selectors such as [data-status="done"], and show values in generated content with content: attr(data-label).

    They're useful for state hooks, test selectors, and passing configuration from server-rendered HTML to scripts. Don't use them for content that should be visible or semantic (use proper elements), and don't store anything sensitive, since anyone can read the markup. Changing a data-* attribute conveys nothing to assistive technology; use ARIA states such as aria-expanded for that.

    What interviewers listen for
    • Custom data on any element with a data- prefix
    • JavaScript: el.dataset, kebab-case becomes camelCase
    • CSS: attribute selectors and attr() in content
    • Values are strings; not for sensitive or semantic data
  38. 38.What is the difference between <link rel="preload">, prefetch, preconnect and dns-prefetch?mid

    They're resource hints that tell the browser about work it would otherwise discover too late:

    • preload fetches a resource needed by the current page right away, at high priority, without executing it. Use it for late-discovered critical assets such as a web font referenced from CSS or a hero image set as a CSS background. It needs as (as="font", as="image") for the right priority and cache matching, and fonts also need crossorigin. Preloads that go unused waste bandwidth, and browsers warn about them in the console.
    • prefetch is a low-priority fetch of something likely needed for a future navigation, such as the next page's script, stored in the HTTP cache.
    • preconnect sets up the connection (DNS lookup, TCP and TLS handshakes) to an origin you'll use soon, like a font or API host. Limit it to a few critical origins.
    • dns-prefetch only does the DNS lookup: cheaper, and a common fallback.
    • modulepreload preloads a JavaScript module so it's ready to execute.

    Overusing hints makes them compete with the resources that really matter.

    What interviewers listen for
    • preload: high-priority fetch for the current page; needs as
    • prefetch: low-priority fetch for a likely next navigation
    • preconnect: warms up DNS, TCP and TLS early
    • dns-prefetch: DNS lookup only
    • Too many hints compete with critical resources
  39. 39.What is BEM, and why do teams use CSS naming conventions like it?easy

    BEM (Block, Element, Modifier) is a class-naming convention:

    • Block: a standalone component, .card.
    • Element: a part that only makes sense inside its block, joined with two underscores: .card__title, .card__image.
    • Modifier: a variant or state, joined with two hyphens: .card--featured, .card__title--large.

    Elements stay flat under their block (.card__body__title is discouraged), even if the markup is nested.

    Benefits: every selector is a single class, so specificity stays flat and predictable; names show where a style belongs; styles don't depend on DOM nesting, so markup can change without breaking CSS; and there are fewer accidental collisions in a global stylesheet.

    Downsides: long class names and verbose markup, and it's a discipline rather than enforced scoping. That's why many teams now get scoping from CSS Modules, Shadow DOM or framework-scoped styles, or use utility classes, although BEM-style naming is still common in design systems.

    What interviewers listen for
    • Block, Element (__), Modifier (--)
    • Single-class selectors keep specificity flat
    • Styles do not depend on DOM nesting
    • Trade-off: verbose names; a convention, not enforced scoping
  40. 40.How do calc(), min(), max() and clamp() work? Give a practical use of clamp().mid

    They're CSS math functions evaluated by the browser, so they can mix units that a preprocessor couldn't combine at build time.

    • calc() does arithmetic: width: calc(100% - 2rem). Spaces are required around + and - (calc(100%-2rem) is invalid) but not around * and /.
    • min() picks the smallest value: width: min(100%, 60rem) is a fluid container capped at 60rem.
    • max() picks the largest: padding: max(1rem, 3vw).
    • clamp(MIN, PREFERRED, MAX) is equivalent to max(MIN, min(PREFERRED, MAX)): the preferred value is used while it stays within the bounds.

    The classic use is fluid typography: font-size: clamp(1rem, 0.8rem + 1vw, 1.5rem) scales smoothly with the viewport but never drops below 1rem or exceeds 1.5rem. Including a rem term keeps it responsive to the user's zoom and font-size settings, unlike pure vw. The same works for fluid spacing and widths, and custom properties can be used inside: calc(var(--space) * 2).

    .container { width: min(100% - 2rem, 70rem); margin-inline: auto; }
    
    h1 { font-size: clamp(1.75rem, 1.2rem + 2.5vw, 3rem); }
    
    .stack > * + * { margin-top: calc(var(--space, 1rem) * 1.5); }
    What interviewers listen for
    • Math functions resolve in the browser, mixing units
    • calc() needs spaces around + and -
    • clamp(min, preferred, max) bounds a fluid value
    • Fluid type: combine rem and vw inside clamp()
  41. 41.What are container queries, and how are they different from media queries?mid

    Media queries respond to the viewport (and device features). Container queries respond to the size of an ancestor container, so a component adapts to the space it's actually given. The same card can stack vertically in a narrow sidebar and sit horizontally in a wide main column, without knowing anything about the page layout.

    You opt an element in with container-type: inline-size, which allows queries on its inline size (width in horizontal writing). container-type: size allows querying both axes, but the container then no longer sizes itself from its content in either axis, so it needs an explicit height. You can name it with container-name, then @container (min-width: 30rem) { ... } styles its descendants. An element can't restyle itself based on its own container query.

    There are also container query units such as cqi, cqw and cqh, which are relative to the container's size, and style queries such as @container style(--variant: compact), which test custom property values.

    .card-list { container-type: inline-size; container-name: cards; }
    
    .card { display: grid; gap: 1rem; }
    
    @container cards (min-width: 30rem) {
      .card { grid-template-columns: 10rem 1fr; }
    }
    What interviewers listen for
    • Media queries: viewport; container queries: an ancestor size
    • Opt in with container-type: inline-size
    • A container styles its descendants, not itself
    • Enables reusable, context-aware components
    • Container units like cqi are relative to the container
  42. 42.How do floats work, and what is a clearfix? Are floats still relevant?mid

    float: left or float: right takes an element out of normal flow and shifts it to one side of its container. Inline content such as text wraps around it, while following block boxes behave as if the float weren't there (only their line boxes get shorter).

    Two classic problems:

    • Collapsed parent: a container whose children are all floated has zero height, because floats don't contribute to its height.
    • Unwanted wrapping: later content wraps next to a float when you don't want it to. clear: both on an element pushes it below any preceding floats.

    The clearfix hack adds an ::after pseudo-element with content: "", display: table and clear: both to the parent, forcing it to contain its floats. Today the cleaner fix is display: flow-root, which makes the parent a block formatting context.

    Floats were used for whole page layouts before Flexbox and Grid; that's legacy now. Their remaining real use is their original purpose: wrapping text around an image or pull quote, optionally shaped with shape-outside.

    What interviewers listen for
    • Floats leave normal flow; inline content wraps around them
    • Parents collapse because floats do not add to their height
    • Clearfix or display: flow-root makes the parent contain floats
    • Modern use: wrapping text around media, not page layout
  43. 43.How do you truncate text with an ellipsis, both on a single line and after several lines?easy

    Single-line truncation needs three properties together:

    • white-space: nowrap keeps the text on one line,
    • overflow: hidden clips whatever overflows,
    • text-overflow: ellipsis draws "…" at the clip point.

    The element also needs a constrained width: a block with a set or available width, or a flex item with min-width: 0, because flex items won't shrink below their content by default.

    For multi-line clamping, the widely used form combines display: -webkit-box, -webkit-box-orient: vertical, -webkit-line-clamp: 3 and overflow: hidden. Despite the prefix, this works in all modern browsers; a standard line-clamp property is specified as its successor.

    Think about access to the hidden text: screen readers still read the full content because it's in the DOM, but sighted users may need a way to see it, such as an expand control or a tooltip.

    .truncate {
      white-space: nowrap;
      overflow: hidden;
      text-overflow: ellipsis;
    }
    .clamp-3 {
      display: -webkit-box;
      -webkit-box-orient: vertical;
      -webkit-line-clamp: 3;
      overflow: hidden;
    }
    What interviewers listen for
    • Single line: nowrap, overflow: hidden, text-overflow: ellipsis
    • Needs a constrained width; flex items need min-width: 0
    • Multi-line: -webkit-line-clamp with display: -webkit-box
    • Give users a way to read the full text
  44. 44.How should focus be managed when opening a modal dialog, and when navigating between routes in a single-page app?hard

    Modal dialogs, following the WAI-ARIA Authoring Practices dialog pattern:

    • On open, move focus into the dialog, usually to the first focusable element, or to a static element at the start if the content needs to be read first.
    • Keep Tab and Shift+Tab inside the dialog, and make the page behind it inert so neither focus nor screen readers can reach it.
    • Escape closes the dialog.
    • On close, return focus to the element that opened it.
    • Use role="dialog" with aria-modal="true" and an accessible name via aria-labelledby.

    The native <dialog> element opened with showModal() does most of this for you: it renders in the top layer, makes the rest of the page inert, closes on Escape, and restores focus to the previously focused element when it closes.

    SPA route changes don't reload the page, so focus stays on the clicked link and nothing is announced. Move focus to the new view's main heading or container (with tabindex="-1"), update document.title, and optionally announce the change through a live region.

    What interviewers listen for
    • Move focus into the dialog when it opens
    • Contain focus and make the background inert
    • Escape closes; return focus to the trigger
    • Native <dialog> with showModal() handles most of it
    • After SPA navigation, focus the new heading and update the title

    Likely follow-up: What happens to focus when the focused element is removed from the DOM?

  45. 45.What are landmark regions, and why does heading structure matter for accessibility?mid

    Landmarks are the major regions of a page, which screen-reader users can list and jump between. Native elements map to landmark roles:

    • <header> is banner and <footer> is contentinfo, but only when they aren't nested inside <article>, <aside>, <main>, <nav> or <section>
    • <nav> is navigation
    • <main> is main (one visible per page)
    • <aside> is complementary at the top level or inside <main> (or anywhere once it has an accessible name)
    • <form> and <section> become form and region landmarks only when they have an accessible name

    When there are several landmarks of the same type, such as two <nav> elements, label them with aria-label ("Primary", "Breadcrumb") so users can tell them apart.

    Headings are one of the most common ways screen-reader users navigate: they pull up a list of headings or jump from one to the next. Give the page one clear <h1> describing its topic, avoid skipping levels, and choose each level by the document structure rather than by visual size, which belongs in CSS.

    What interviewers listen for
    • Landmarks let screen-reader users jump between regions
    • Use <header>, <nav>, <main>, <aside>, <footer>
    • Label duplicate landmarks with aria-label
    • Headings are a primary navigation tool
    • Choose heading level by structure, not size
  46. 46.What are the color contrast requirements for accessible text, and what else should you consider about color?easy

    WCAG level AA requires a contrast ratio of at least 4.5:1 for normal text and 3:1 for large text, which is at least 18pt (about 24px) regular or 14pt (about 18.66px) bold. Level AAA raises these to 7:1 and 4.5:1. Non-text elements that users need to perceive, such as input borders, focus indicators and meaningful icons, need at least 3:1 against adjacent colors.

    Don't rely on color alone to convey information: an error shown only as a red border, or a chart that distinguishes series only by hue, fails for color-blind users. Add text, icons, patterns or underlines; links within body text usually need a non-color cue such as an underline.

    Check contrast with the color picker in browser DevTools or tools such as axe and Lighthouse. Test hover, focus and disabled states and dark mode too, and respect user preferences like prefers-contrast and forced colors mode.

    What interviewers listen for
    • AA: 4.5:1 for normal text, 3:1 for large text
    • UI components and meaningful graphics need 3:1
    • Never convey information by color alone
    • Check with DevTools, axe or Lighthouse
  47. 47.What can you do in the HTML itself to improve a page's SEO?easy
    • Give every page a unique, descriptive <title> and <meta name="description">: the title is the main clickable text in search results, and the description often becomes the snippet.
    • Use semantic structure: one clear <h1>, logical headings, and real <a href> links with descriptive text (not "click here"), so crawlers can discover and understand your pages.
    • Add alt text to meaningful images.
    • Use <link rel="canonical"> to point duplicate URLs at the preferred one, and hreflang alternates for translated pages.
    • Add structured data (JSON-LD using schema.org) to become eligible for rich results such as products, recipes or articles.
    • Control indexing with the robots meta tag and robots.txt, and provide an XML sitemap.
    • Make it fast and mobile-friendly: Core Web Vitals (LCP, INP, CLS) are part of Google's page experience signals.
    • Put important content in the server-rendered HTML where possible; client-only rendering can delay indexing.

    Open Graph tags improve social link previews but aren't a ranking factor, and the keywords meta tag is ignored by Google.

    What interviewers listen for
    • Unique <title> and meta description on every page
    • Semantic headings and crawlable links with descriptive text
    • Canonical URLs, structured data and a sitemap
    • Performance and mobile-friendliness matter
    • Server-render important content
  48. 48.What are Web Components, and what does the Shadow DOM give you?hard

    Web Components are a set of native browser APIs for building reusable custom elements:

    • Custom elements: customElements.define('user-card', UserCard) registers a new tag whose class extends HTMLElement and gets lifecycle callbacks such as connectedCallback, disconnectedCallback and attributeChangedCallback. Names must contain a hyphen.
    • Shadow DOM: attachShadow({ mode: 'open' }) attaches an encapsulated DOM tree. Styles inside don't leak out and page selectors don't reach in, although inherited properties like color and font and custom properties do cross the boundary.
    • Templates and slots: <template> holds inert markup, and <slot> projects the host's light-DOM children into the shadow tree.

    Styling hooks are :host for the element itself, ::slotted() for projected children, ::part() for parts the component exposes, and custom properties as a theming API. Declarative Shadow DOM (<template shadowrootmode="open">) enables server rendering.

    The trade-off: framework-agnostic and long-lived, but you manage rendering and state yourself.

    <user-card><span slot="name">Ada</span></user-card>
    <script>
      class UserCard extends HTMLElement {
        constructor() {
          super();
          this.attachShadow({ mode: 'open' }).innerHTML =
            '<style>p { color: teal; }</style><p>Hi, <slot name="name"></slot></p>';
        }
      }
      customElements.define('user-card', UserCard);
    </script>
    What interviewers listen for
    • Custom elements, Shadow DOM, templates and slots
    • Shadow DOM encapsulates markup and styles
    • Inherited and custom properties still cross the boundary
    • Style with :host, ::slotted() and ::part()
  49. 49.What are the security concerns with <iframe>, and how does the sandbox attribute help?hard

    There are two directions to think about.

    Embedding untrusted content such as user HTML, ads or third-party widgets: an empty sandbox attribute applies every restriction. Scripts, form submission, popups and top-level navigation are blocked, and the content is treated as a unique opaque origin, so it can't access its real origin's cookies or storage. You then re-enable only what's needed with tokens like allow-scripts, allow-forms, allow-popups and allow-same-origin. Never combine allow-scripts with allow-same-origin for content from your own origin: the framed script could simply remove the sandbox. The allow attribute (Permissions Policy) controls features such as camera and geolocation.

    Your own site being framed (clickjacking): an attacker overlays your page invisibly to trick users into clicking. Prevent it with the Content-Security-Policy: frame-ancestors header, or the older X-Frame-Options.

    Cross-origin frames communicate with the parent only through postMessage; always check event.origin on received messages. Also give every iframe a title for accessibility.

    What interviewers listen for
    • Empty sandbox applies every restriction; opt in with tokens
    • Never pair allow-scripts with allow-same-origin for same-origin content
    • Prevent clickjacking with CSP frame-ancestors
    • Use postMessage and verify event.origin
  50. 50.Compare CSS Modules, CSS-in-JS and utility-first CSS. How would you choose between them?mid

    All three address the same problems of plain global CSS: no scoping, name collisions, and styles nobody dares delete.

    • CSS Modules: ordinary CSS files whose class names are made locally unique at build time. No runtime cost, standard CSS syntax and easy migration; dynamic styling uses extra classes or custom properties.
    • CSS-in-JS (e.g. styled-components, Emotion): styles written in JavaScript next to the component, with props-driven dynamic styles and automatic scoping. Runtime libraries add bundle size and style-injection work and need extra setup for server rendering; zero-runtime variants extract static CSS at build time.
    • Utility-first (e.g. Tailwind): small single-purpose classes composed in the markup. You get consistent design tokens, no naming decisions and a stylesheet that stops growing, at the cost of long class lists and styling that lives in the markup.

    Choose based on the team and constraints: server rendering and tight performance budgets favour build-time approaches, while theming works with any of them, especially combined with custom properties. Consistency within one codebase matters more than the choice itself.

    What interviewers listen for
    • All solve global scope, collisions and dead CSS
    • CSS Modules: build-time scoping, no runtime cost
    • Runtime CSS-in-JS: colocated and dynamic, but has runtime cost
    • Utility-first: design tokens in markup, CSS stays small
    • Choose by team, rendering model and performance needs
  51. 51.What are CSS preprocessors like Sass, and are they still necessary with modern CSS?easy

    A preprocessor compiles an extended syntax into plain CSS at build time. Sass (usually its SCSS syntax), Less and Stylus added features CSS lacked:

    • variables ($brand),
    • nesting of selectors,
    • mixins and functions for reusable chunks of styles,
    • partials to split code across files,
    • loops, conditionals and color functions.

    Modern CSS has absorbed much of this: custom properties (more powerful than preprocessor variables because they're live and cascade), native nesting, calc() and clamp(), color-mix(), @layer for organization, and container queries. PostCSS-based tools handle vendor prefixing and bundling.

    So Sass is no longer essential, but it's still common in existing codebases and design systems for mixins, loops that generate utility classes, and other compile-time logic. A typical modern setup is plain CSS with PostCSS or a bundler, adding Sass only when you need its build-time features. With either, avoid deep nesting: it inflates specificity and couples styles to DOM structure.

    What interviewers listen for
    • Compile an extended syntax into plain CSS at build time
    • Features: variables, nesting, mixins, partials, loops
    • Native CSS now has custom properties, nesting, math functions
    • Still useful for mixins and generated utilities
    • Deep nesting inflates specificity
  52. 52.How would you implement dark mode in CSS, including a manual toggle?mid

    Build it on custom properties: define semantic tokens (--bg, --text, --surface) on :root, use only those tokens in components, then swap their values for dark mode.

    • Follow the OS setting with @media (prefers-color-scheme: dark).
    • Add a manual toggle by setting an attribute such as data-theme="dark" on the <html> element and saving the choice in localStorage. Apply it with a small inline script in the <head>, before first paint, to avoid a flash of the wrong theme. The explicit choice should override the media query.
    • Set color-scheme (to light dark, or to a single value once the user has picked a theme) so the browser renders form controls, scrollbars and the default page background to match. The light-dark() function can pick between two colors based on the active scheme.

    Don't just invert colors: avoid harsh pure white on pure black, adjust shadows, images and brand colors, and recheck contrast ratios in both themes.

    :root { color-scheme: light dark; --bg: #fff; --text: #111; }
    :root[data-theme="light"] { color-scheme: light; }
    :root[data-theme="dark"] { color-scheme: dark; --bg: #121212; --text: #e8e8e8; }
    
    @media (prefers-color-scheme: dark) {
      :root:not([data-theme="light"]) { --bg: #121212; --text: #e8e8e8; }
    }
    
    body { background: var(--bg); color: var(--text); }
    What interviewers listen for
    • Semantic custom property tokens, swapped per theme
    • prefers-color-scheme follows the OS setting
    • Toggle via an attribute on <html>, saved in localStorage
    • Apply the saved theme before first paint to avoid a flash
    • color-scheme themes native controls
  53. 53.What affects CSS performance, and how would you optimize the CSS of a large site?hard

    Loading is usually the biggest factor, because CSS is render-blocking:

    • Ship less: remove unused CSS, minify and compress it, and split it per route.
    • Inline the critical above-the-fold CSS and load the rest without blocking rendering. Avoid @import inside stylesheets, which chains requests.
    • Preload key fonts and choose a font-display value such as swap or optional so text isn't invisible while fonts load.

    Rendering:

    • Animate transform and opacity rather than layout properties, and use will-change sparingly.
    • Avoid layout thrashing from JavaScript that reads layout right after writing styles.
    • Reserve space for images and embeds (width/height attributes, aspect-ratio) to prevent layout shift.
    • content-visibility: auto skips rendering work for off-screen sections, and contain limits how far layout and paint changes spread.
    • Heavy paint effects, such as large blurs or many shadows and filters, can hurt on low-end devices.

    Selector matching is rarely the bottleneck: browsers match right to left and are highly optimized. DOM size and how often styles are invalidated matter more.

    What interviewers listen for
    • CSS is render-blocking: ship less, inline critical CSS
    • Avoid @import chains; preload fonts and set font-display
    • Animate compositor-friendly properties; avoid layout thrashing
    • content-visibility and contain limit rendering work
    • Selector matching is rarely the bottleneck
  54. 54.What are CSS logical properties, and why would you use margin-inline-start instead of margin-left?mid

    Logical properties describe directions relative to the writing mode and text direction rather than the physical screen:

    • the inline axis is the direction text runs: left to right in English, right to left in Arabic or Hebrew, vertical in some East Asian layouts;
    • the block axis is the direction blocks stack: top to bottom in horizontal writing modes;
    • start and end are the beginning and end along each axis.

    So margin-inline-start is the left margin on a left-to-right page and automatically becomes the right margin under dir="rtl". There are equivalents for most box properties: padding-block, border-inline-end, inset-inline-start, inline-size and block-size for width and height, text-align: start, and shorthands like margin-inline: auto.

    Using them makes right-to-left support mostly automatic instead of requiring mirrored overrides, and they work naturally with vertical writing modes. Flexbox and Grid alignment are already direction-aware: justify-content: start follows the text direction.

    What interviewers listen for
    • Directions follow writing mode and text direction
    • Inline axis follows text; block axis follows stacking
    • margin-inline-start flips automatically for RTL
    • RTL and vertical writing support come nearly free
  55. 55.What does the aspect-ratio property do, and how was the same effect achieved before it existed?mid

    aspect-ratio: 16 / 9 gives a box a preferred width-to-height ratio: when one dimension is set or determined by layout (like a block filling its container's width), the other is calculated from the ratio. It's ideal for video embeds, cards, thumbnails and loading skeletons that should keep their shape.

    Details worth knowing:

    • If both width and height are set, the ratio is ignored.
    • For a normal block, content that doesn't fit makes the box taller rather than overflowing; set min-height: 0 or an overflow other than visible to enforce the ratio.
    • aspect-ratio: auto 16 / 9 uses an element's natural ratio when it has one (such as an image) and the given ratio otherwise.
    • Browsers use the width and height attributes on <img> to derive a ratio before the image loads, which reserves space and prevents layout shift.
    • Pair it with object-fit: cover on images to crop without distortion.

    Before it existed, the padding hack was used: percentage padding resolves against the containing block's width, so a wrapper with padding-top: 56.25% (9 ÷ 16) and an absolutely positioned child filling it made a 16:9 box.

    What interviewers listen for
    • Sets a preferred width-to-height ratio
    • Ignored when both width and height are set
    • Content can make the box taller unless you constrain it
    • Old approach: the percentage padding-top hack
    • Image width/height attributes reserve space via the ratio
  56. 56.What are HTML entities, and why is escaping important?easy

    Character references, often called entities, represent characters that are reserved in HTML or hard to type. They can be named (&amp;, &lt;, &gt;, &quot;, &nbsp;, &copy;) or numeric (&#169; or &#xA9;).

    What to escape:

    • & as &amp; and < as &lt; in text, so the parser doesn't read them as the start of a character reference or a tag (> is usually escaped too, for symmetry);
    • quotes inside attribute values that use the same quote character, as &quot; or &#39;.

    With UTF-8 (<meta charset="utf-8">) you can type most other characters directly; &nbsp; is still handy to prevent a line break between two words.

    Escaping is essential for security: inserting user input into HTML without escaping enables cross-site scripting (XSS). Frameworks such as React and Angular escape interpolated text by default; the danger lies in APIs that bypass that, like innerHTML or dangerouslySetInnerHTML. Use textContent for text, and sanitize (for example with DOMPurify) when you genuinely need to render HTML. Escaping also depends on context: HTML text, attributes, URLs and JavaScript each need different encoding.

    What interviewers listen for
    • Entities encode reserved or hard-to-type characters
    • Escape & and < in text, and quotes in attributes
    • Unescaped user input leads to XSS
    • Prefer textContent; sanitize when rendering HTML
    • Correct escaping depends on the output context
  57. 57.How do you write print styles for a web page?mid

    Use @media print { ... }, or a separate stylesheet linked with <link rel="stylesheet" media="print">, to adapt the page for paper:

    • Hide what's useless on paper: navigation, ads, buttons, video, cookie banners.
    • Simplify: dark text on a white background, no decorative shadows. Browsers skip background colors and images by default to save ink; print-color-adjust: exact (often still written with the -webkit- prefix) asks them to keep them.
    • Expose link URLs with generated content: a[href^="http"]::after { content: " (" attr(href) ")"; }.
    • Control page breaks with break-before, break-after and break-inside: avoid (older code uses page-break-*), so headings aren't stranded at the bottom of a page and figures or table rows aren't split.
    • Set page margins with the @page rule, and use physical units like pt or cm where precise sizing matters.

    Test with print preview or the DevTools option to emulate print media. Printing is also how many users save pages as PDFs, so it's worth the effort.

    @media print {
      nav, .ads, button, video { display: none; }
      body { color: #000; background: #fff; font-size: 12pt; }
      a[href^="http"]::after { content: " (" attr(href) ")"; }
      h2, h3 { break-after: avoid; }
      figure, tr { break-inside: avoid; }
    }
    
    @page { margin: 2cm; }
    What interviewers listen for
    • Use @media print or a print stylesheet
    • Hide navigation and interactive elements
    • Show link URLs with attr(href) in generated content
    • Control page breaks with break-inside and break-after
    • @page sets page margins
esc