CoreHRx Jobs

Apply in one click, save jobs and take free mock tests.

Register free Log in
Hiring? Post jobs with CoreHRx
My account
user

View & update profile
Interview guide

HTML and CSS Interview Questions and Answers: 60 Questions from Easy to Hard

This guide is for freshers in India preparing for their first front-end or web developer interview, and for developers with a few years of experience who want to answer HTML and CSS questions with real depth. It has 60 questions in three levels: the basics every fresher is asked, the everyday debugging and layout problems of working developers, and senior topics such as cascade layers, stacking contexts, container queries and rendering performance. Each answer gives the point first, then the reason, an example and a common mistake, and most come with a short snippet. Every number, from specificity to box sizes, was worked out step by step. Try each question aloud before reading the answer, run the snippets in your browser, then take the mock test.

What HTML and CSS interviews cover

  • HTML: semantic elements, document structure, forms and validation, and images with good alt text and responsive sources.
  • Accessibility: labels, the basics of ARIA, keyboard focus, colour contrast and accessible dialogs. Many product companies now test this directly.
  • The cascade: specificity, inheritance, !important and cascade layers, usually asked as a puzzle about why a style is not applying.
  • Layout: the box model, positioning, stacking contexts, flexbox and grid, often as a small layout you must build or debug live.
  • Responsive design and performance: media and container queries, units, fluid type, render-blocking CSS and layout shift.

How to prepare (freshers and experienced)

  • Freshers: build three small pages by hand, such as a portfolio, a job listing and a sign-up form, without a framework, and make each one work well on a phone.
  • Learn to explain the basics aloud: the box model, specificity, flexbox versus grid. Interviewers want the reason, not a list of properties.
  • Practise in the browser devtools: read the computed styles, the box model diagram and the flex and grid overlays until you can find why a rule lost.
  • Experienced developers: prepare one short story each about a layout bug, a performance fix (layout shift or render-blocking CSS) and an accessibility fix, with numbers.
  • Use your own pages with the keyboard only, and with a screen reader such as NVDA or TalkBack, for ten minutes. It changes how you answer accessibility questions.

Common mistakes and interview-day tips

  • Do not answer with a framework class name from Bootstrap or Tailwind when asked how something works; explain the CSS underneath.
  • Avoid fixing every conflict with !important or a bigger z-index. Interviewers read it as a sign that the cascade or stacking contexts are not clear to you.
  • Be careful with browser support claims: say a feature works in all current major browsers only when it does, and mention the fallback.
  • In a live coding round, write semantic HTML first, then the phone layout, then wider breakpoints, and talk through each step as you go.
  • When asked to work out a size or a specificity, write the numbers down step by step. A wrong mental shortcut is the most common slip.

HTML and CSS interview questions: Easy questions (freshers)

20 questions

1. What is semantic HTML, and why do interviewers care about it?

Semantic HTML means choosing elements for what the content is, not for how it should look: header, nav, main, article, aside and footer instead of a page built only from div elements. It matters for three reasons. Screen readers use these landmarks to let users jump straight to the navigation or the main content. Search engines understand the structure better. And other developers can read the markup without guessing. Looks are a separate job for CSS, so a semantic element can still be styled any way the design needs.

Example
<body>
    <header>
        <a href="/">Logo</a>
        <nav aria-label="Main">...</nav>
    </header>
    <main>
        <article>
            <h1>Hiring drive in Pune</h1>
            <p>...</p>
        </article>
        <aside>Related jobs</aside>
    </main>
    <footer>Contact and policies</footer>
</body>

2. What do the DOCTYPE, the lang attribute and the meta lines at the top of a page do?

The DOCTYPE line tells the browser to use standards mode; without it the browser falls back to quirks mode, where it imitates old, non-standard layout behaviour for backward compatibility. The lang attribute on the html element tells screen readers which language to pronounce and helps translation tools. The meta charset line declares UTF-8 within the first 1024 bytes so text such as the rupee sign shows correctly. The viewport meta line makes phones use the device width instead of a zoomed-out desktop width, the first step of any responsive page.

Example
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <title>Job openings in Chennai</title>
    <link rel="stylesheet" href="/css/site.css">
</head>
<body>...</body>
</html>

3. How should you write the alt attribute for images?

Alt text is what a screen reader reads and what shows if the image fails to load, so it should give the purpose of the image in its context, not describe every pixel. A product photo might say 'Blue cotton kurta, front view'. A logo inside a link should say where the link goes, such as 'Home'. A purely decorative image gets an empty alt, so screen readers skip it. Leaving the attribute out entirely is the real mistake: many screen readers then read the file name aloud. Do not start with 'image of', because the reader already announces an image.

Example
<img src="kurta-blue.jpg" alt="Blue cotton kurta, front view">

<a href="/"><img src="logo.svg" alt="Home"></a>

<img src="divider-wave.svg" alt="">

4. How do you connect a label to a form field, and why does it matter?

Give the input an id and put the same value in the label's for attribute, or wrap the input inside the label. Either way the browser links them: clicking the label focuses the field or toggles the checkbox, which gives a bigger tap target on phones, and a screen reader announces the label when the field gets focus. A placeholder is not a replacement. It disappears as soon as the user types, usually has weak contrast and is not reliably announced as the field's name. Every field needs a real label, even when the design hides it visually.

Example
<label for="email">Work email</label>
<input id="email" name="email" type="email">

<label>
    <input type="checkbox" name="terms">
    I agree to the terms
</label>

5. What is the difference between GET and POST in an HTML form?

With method get the browser puts the fields in the URL as a query string, so this form opens /jobs?city=Pune&role=tester. That suits searches and filters, because the result can be bookmarked, shared and cached. With post the data goes in the request body, which suits logins, sign-ups, payments and anything that changes data on the server. Get is the default when no method is given. Two common mistakes: thinking post is secure on its own, when only HTTPS encrypts the data, and forgetting the name attribute, because a field without a name is never sent.

Example
<form action="/jobs" method="get">
    <input name="city" value="Pune">
    <input name="role" value="tester">
    <input value="ignored">   <!-- no name: not sent -->
    <button>Search</button>
</form>
<!-- submits to /jobs?city=Pune&role=tester -->

6. What is the difference between block, inline and inline-block elements?

A block box starts on a new line and stretches to the full width of its container; div, p, h1 and ul are block by default. An inline box sits inside a line of text and is only as wide as its content; span, a, strong and em are inline. Width and height do not apply to inline boxes, and vertical margins do not push the lines apart. Inline-block flows like text but accepts width, height and vertical spacing, which suits badges or buttons in a row. These are CSS display values, so any element can be switched with the display property.

Example
.tag  { display: inline;       width: 80px; }  /* width ignored */
.chip { display: inline-block; width: 80px; }  /* width applied */
.row  { display: block; }  /* new line, full width */

7. What does the cascade in CSS mean?

The cascade is the set of steps the browser uses to pick one value when several declarations set the same property on the same element. It looks first at origin and importance (your stylesheet beats the browser defaults, and an !important declaration beats normal ones), then at specificity, and last at source order, where the later rule wins. So two rules with the same selector are settled by whichever comes last. Inheritance is a different idea: properties such as color and font-family pass down to children, while border and padding do not.

Example
.btn { background: #1c7ed6; }
.btn { background: #2f9e44; }   /* same specificity, later: wins */

.card { color: #333; border: 1px solid #ddd; }
/* a p inside .card inherits the color, not the border */

8. How is specificity calculated, and which colour does this link get?

Specificity is counted in three columns: IDs, then classes, attribute selectors and pseudo-classes, then element types and pseudo-elements. The universal selector and combinators add nothing. Columns are compared from the left, so one ID beats any number of classes. Here #main .menu a scores (1,1,1), .menu li a:hover scores (0,2,2) and nav ul li a scores (0,0,4). The first rule wins, so the link stays red even on hover. Inline styles beat all of these, and the usual fix for a rule that loses is a better selector, not !important.

Example
<nav id="main">
    <ul class="menu">
        <li><a href="/jobs">Jobs</a></li>
    </ul>
</nav>

#main .menu a     { color: red; }    /* (1,1,1) */
.menu li a:hover  { color: green; }  /* (0,2,2) */
nav ul li a       { color: blue; }   /* (0,0,4) */

9. Explain the CSS box model. How wide is this box on screen?

Every element is drawn as a box with four layers: the content, then padding, then the border, then margin on the outside. With the default box-sizing: content-box, the width property sets only the content, so padding and border are added on top. Here the visible box is 300 + 20 + 20 + 5 + 5 = 350px wide, and with the 10px margins on both sides it takes 370px of space in the row. The background paints under the content, padding and border, never under the margin. Forgetting this addition is the classic reason a full-width box overflows its parent.

Example
.box {
    width: 300px;
    padding: 20px;
    border: 5px solid #333;
    margin: 10px;
}
/* visible width 350px, space taken 370px */

10. What does box-sizing: border-box change, and why do most projects set it on every element?

With border-box, the width you set includes the padding and border, so the box stays exactly that size and the content area shrinks instead. The same box with width 300px, padding 20px and a 5px border now measures 300px on screen, and the content gets 300 - 40 - 10 = 250px. This makes layouts predictable: two 50% columns with padding still fit side by side. Teams put it in the reset for every element and pseudo-element so nobody has to do the subtraction. Margin stays outside the box in both models.

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

.box { width: 300px; padding: 20px; border: 5px solid #333; }
/* rendered width 300px, content 250px */

11. Why is the gap between these two paragraphs 30px and not 50px?

This is margin collapsing. When the bottom margin of one block meets the top margin of the next block in normal flow, they do not add up; the larger one wins, so 20px and 30px give a 30px gap. It happens only to vertical margins of block boxes in normal flow. Margins do not collapse between flex or grid items, floats or absolutely positioned boxes. Many teams avoid the surprise by setting margins on one side only, usually the bottom, or by using gap inside flex and grid layouts, which never collapses.

Example
<p class="one">First</p>
<p class="two">Second</p>

.one { margin-bottom: 20px; }
.two { margin-top: 30px; }
/* gap between them: 30px */

12. What are the values of the position property, and how do they differ?

Static is the default: the element follows normal flow and top or left do nothing. Relative keeps the element's space but lets you nudge it, and it becomes the reference box for absolutely positioned children. Absolute takes the element out of flow and places it against the nearest ancestor whose position is not static. Fixed places it against the viewport, so it stays put while the page scrolls. Sticky acts like relative until a threshold such as top: 0 is reached, then sticks inside its parent. A classic use of absolute is a 'New' badge on a card.

Example
.card {
    position: relative;
}
.card .badge {
    position: absolute;
    top: 8px;
    right: 8px;
}

13. In flexbox, what is the difference between justify-content and align-items? How do you centre a box?

A flex container has a main axis, set by flex-direction and a row by default, and a cross axis at right angles to it. justify-content places items along the main axis and align-items places them along the cross axis. So in a row, justify-content works horizontally and align-items vertically, and switching to flex-direction: column swaps them, which confuses many people. To centre a box inside a section, make the section a flex container and set both to center. The container needs a height for vertical centring to show.

Example
.hero {
    display: flex;
    justify-content: center;
    align-items: center;
    min-height: 60vh;
}

14. What is the fr unit in CSS grid? How wide are these three columns?

fr means a fraction of the free space in the grid container, the space left after fixed tracks and gaps are taken out. Here the container is 1020px with two 20px gaps, so 980px is free, split into 1 + 1 + 2 = 4 parts of 245px. The columns are 245px, 245px and 490px. Because fr already accounts for the gaps, it avoids the old problem of three 33.33% columns plus gaps overflowing the row. Grid is two-dimensional: you define rows and columns together and items line up in both directions.

Example
.layout {
    display: grid;
    width: 1020px;
    grid-template-columns: 1fr 1fr 2fr;
    gap: 20px;
}
/* columns: 245px, 245px, 490px */

15. What does mobile-first mean in responsive CSS?

You write the base styles for the smallest screen first, with no media query, and then add min-width media queries that adjust the layout as the screen gets wider. The phone styles stay simple, and each larger screen adds columns or spacing on top. Choose breakpoints where the content starts to look cramped or stretched, not by the size of popular phone models. Check the viewport meta line first: without it, most phones lay the page out about 980px wide, so your tablet and desktop rules apply on a phone and the text looks tiny.

Example
.jobs { display: grid; gap: 16px; }   /* phones: one column */

@media (min-width: 600px) {
    .jobs { grid-template-columns: 1fr 1fr; }
}
@media (min-width: 1024px) {
    .jobs { grid-template-columns: repeat(3, 1fr); }
}

16. What is the difference between px, em and rem? Work out the sizes here.

px is a fixed CSS pixel. rem is relative to the font size of the root html element, 16px by default, so 1.25rem is 20px wherever it is used. em is relative to the element's own font size, or to the parent's when used for font-size itself. Here .card is 20px, the .title inside it is 1.5em of 20px = 30px, and its 1em padding is also 30px, because em in padding uses the element's own font size. A plain 1.5rem would be 24px. Use rem for font sizes so the user's browser setting is respected; em compounds when nested.

Example
html   { font-size: 100%; }      /* 16px by default */
.card  { font-size: 1.25rem; }   /* 20px */
.title {
    font-size: 1.5em;            /* 30px */
    padding: 1em;                /* 30px */
}

17. What are CSS custom properties (CSS variables), and how do you use them?

A custom property is a property you name yourself, starting with two dashes, such as --brand. You usually define it on :root so the whole page can read it, and use it with var(--brand). You can add a fallback, as in var(--radius, 8px), used when the variable is not defined. Unlike Sass variables, which disappear at build time, custom properties live in the browser: they follow the cascade, are inherited by children, can be overridden inside one component and can be changed from JavaScript. Names are case-sensitive, so --Brand and --brand differ.

Example
:root {
    --brand: #e64980;
    --space: 16px;
}
.btn {
    background: var(--brand);
    padding: var(--space);
    border-radius: var(--radius, 8px);
}
.footer .btn { --brand: #333; }   /* override in one area */

18. Why use a button element instead of a div with a click handler?

A button is focusable with the Tab key, fires its click on both Enter and Space, is announced as a button by screen readers, shows a focus style and can submit a form. A div gives you none of that. To copy it you would need role button, tabindex 0, key handlers for both keys and a focus style, and teams usually miss one of them. Use a button for actions on the page and a link, an a element with an href, for going to another URL. Mixing them up breaks things users expect, such as opening a link in a new tab.

Example
<!-- works for mouse, keyboard and screen readers -->
<button type="button" class="save">Save job</button>

<!-- mouse only, unless you add role, tabindex and key handlers -->
<div class="save" onclick="saveJob()">Save job</div>

19. What is the difference between a CSS transition and a keyframe animation?

A transition animates a property between two states when something changes it, such as hover, focus or a class added by JavaScript. You list the property, the duration and the easing, and the browser fills in the frames. A keyframe animation is defined with @keyframes, starts on its own as soon as it applies, can have many steps, can repeat or alternate, and can be paused. Use transitions for interaction feedback and animations for loaders and attention effects. Prefer animating transform and opacity, which stay smooth, over width or top.

Example
.btn { transition: background-color 0.2s ease; }
.btn:hover { background-color: #c2255c; }

@keyframes spin {
    to { transform: rotate(360deg); }
}
.loader { animation: spin 1s linear infinite; }

20. What is the difference between display: none, visibility: hidden and opacity: 0?

display: none removes the element from the layout completely: it takes no space, cannot be clicked or focused, and screen readers ignore it. visibility: hidden keeps the space but hides the element, and also removes it from the tab order and from screen readers. opacity: 0 only makes it transparent: it still takes space, can still be clicked and focused, and is still read aloud. That last one causes bugs such as invisible buttons catching taps. For text meant only for screen readers, teams use a visually-hidden utility class instead.

Ready to check yourself? Take the free HTML and CSS mock test

HTML and CSS interview questions: Medium questions (1-3 years)

20 questions

21. How do you use built-in HTML form validation, and is it enough?

Attributes such as required, type email, minlength and pattern let the browser check values on submit, show a message and block the form, and the :invalid pseudo-class lets you style errors. A pattern must match the whole value, so the one here accepts exactly a six-digit PIN code that does not start with 0. Use type tel with inputmode numeric for phone numbers, not type number, which is meant for quantities and accepts values like 1e5. It is never enough on its own: anyone can skip it with devtools or by calling the API, so the server must validate again.

Example
<form action="/apply" method="post">
    <label for="pin">PIN code</label>
    <input id="pin" name="pin" required
           inputmode="numeric" pattern="[1-9][0-9]{5}"
           autocomplete="postal-code">

    <label for="mobile">Mobile</label>
    <input id="mobile" name="mobile" type="tel" required
           pattern="[6-9][0-9]{9}" autocomplete="tel">
    <button>Apply</button>
</form>

22. How should a group of radio buttons be marked up?

Give every radio in the group the same name, so the browser treats them as one choice: selecting one clears the others and the arrow keys move between them. Give each one a value, because that is what gets submitted; a radio without a value sends the text 'on'. Wrap the group in a fieldset with a legend holding the question, so a screen reader announces 'Notice period' along with each option. Each radio still needs its own label. A common bug is giving each radio a different name, which lets users select all of them at once.

Example
<fieldset>
    <legend>Notice period</legend>
    <label><input type="radio" name="notice" value="0"> Immediate</label>
    <label><input type="radio" name="notice" value="30"> 30 days</label>
    <label><input type="radio" name="notice" value="60"> 60 days</label>
</fieldset>

23. A 'Show password' button inside a login form keeps submitting the form. Why, and what else trips people up here?

A button inside a form defaults to type submit, so every click submits the form, even if your JavaScript only meant to toggle the password field. Set type button on any button that is not the submit action, and keep one real submit button. A related trap is treating disabled and readonly as the same. A disabled field cannot be focused and is not sent with the form. A readonly field can be focused and selected and is sent. Use readonly for a value the user must see and submit but not edit, such as an employee code.

Example
<form action="/login" method="post">
    <input id="pwd" name="password" type="password">
    <button type="button" id="toggle">Show password</button>

    <input name="emp_code" value="TN-1042" readonly>  <!-- sent -->
    <input name="branch" value="Noida" disabled>      <!-- not sent -->
    <button type="submit">Sign in</button>
</form>

24. What are the basic rules of ARIA, and when do you use aria-label, aria-labelledby and aria-describedby?

The first rule is to use a native HTML element when one exists, because ARIA only changes what assistive technology is told, not how the element behaves; role button adds no keyboard support. aria-label gives an accessible name directly and suits icon-only buttons. aria-labelledby builds the name from visible text elsewhere, by id, so the name stays in sync with the screen. aria-describedby adds extra help, such as a hint or an error message, read after the name. Wrong ARIA is worse than none, because it overrides what the browser would have said.

Example
<button type="button" aria-label="Close">
    <svg aria-hidden="true">...</svg>
</button>

<h2 id="pay-title">Payment details</h2>
<section aria-labelledby="pay-title">...</section>

<label for="pan">PAN</label>
<input id="pan" name="pan" aria-describedby="pan-hint">
<p id="pan-hint">10 characters, for example ABCDE1234F</p>

25. How should keyboard focus be handled in your HTML and CSS?

Never remove the focus outline without a visible replacement, or keyboard users cannot see where they are. Use :focus-visible, which browsers apply for keyboard focus but usually not after a mouse click on a button, so the design stays clean and keyboard users keep the ring. For tabindex, 0 adds a custom widget to the normal tab order and -1 makes an element focusable only from script. Avoid positive values, which reorder the whole page. Keep the visual order the same as the source order, because CSS order or grid placement does not change the tab order.

Example
/* bad: hides focus for everyone */
button:focus { outline: none; }

/* good: a clear ring for keyboard users */
button:focus-visible {
    outline: 3px solid #1c7ed6;
    outline-offset: 2px;
}

26. How do you check colour contrast, and does grey #777777 text on white pass?

WCAG 2 level AA asks for a contrast ratio of at least 4.5:1 for normal text, and 3:1 for large text (24px regular, or about 18.7px bold) and for icons and focus indicators. The ratio comes from the relative luminance of the two colours. On white, #777777 gives 4.48:1, which just fails, while #767676 gives 4.54:1 and passes; #999999, a common placeholder grey, is only 2.85:1. Check with the devtools colour picker or an audit tool rather than by eye, and remember that text over a photo needs a solid or darkened backdrop.

Example
.hint    { color: #777777; }   /* 4.48:1 on white: fails AA */
.hint-ok { color: #767676; }   /* 4.54:1 on white: passes AA */
.body    { color: #595959; }   /* 7.0:1 on white: passes AAA */

27. A developer chains eleven classes to beat an ID selector and still loses. Why?

Specificity is not one base-10 number. It is compared column by column: IDs first, then classes, attributes and pseudo-classes, then element types. #promo scores (1,0,0) and the chain of eleven classes scores (0,11,0), so the ID wins at the first column and the class count never matters. The real fix is to stop styling with IDs: give the element a class, keep selectors to one or two classes, and use IDs for anchors, label targets and JavaScript hooks. Reaching for !important here only starts a war that the next developer has to win again.

Example
<p id="promo" class="a b c d e f g h i j k">Offer</p>

#promo { color: red; }                    /* (1,0,0) */
.a.b.c.d.e.f.g.h.i.j.k { color: green; }  /* (0,11,0) */
/* the text is red */

28. When do you use article, section, aside and a plain div?

Use article for content that makes sense on its own and could be shared, such as a job post, a blog entry or a comment. Use section for a themed part of a page or an article that normally has its own heading, such as Responsibilities inside a job post. Use aside for content related to the main flow but separate from it, such as similar jobs. Use div when you only need a wrapper for styling or layout. Keep headings in order, usually one h1 for the page topic, and never skip a level just to get a smaller font; size text with CSS.

Example
<main>
    <article>
        <h1>QA Engineer, Kochi</h1>
        <section>
            <h2>Responsibilities</h2>
            <p>...</p>
        </section>
        <section>
            <h2>Skills</h2>
            <p>...</p>
        </section>
    </article>
    <aside>
        <h2>Similar jobs</h2>
    </aside>
</main>

29. Your position: sticky filter panel does not stick. What do you check?

First, sticky needs a threshold such as top: 0; without one it behaves like relative. Second, it sticks only within its parent, so if the parent is no taller than the panel there is no room to move. Third, an ancestor with overflow hidden, auto or scroll becomes the scroll container, and the panel sticks inside that box instead of the page, which often means it never moves. Fourth, a sticky sidebar in a flex row is stretched to the full row height by default, so set align-self: flex-start. Walk up the ancestors in devtools to find the overflow.

Example
.page { display: flex; }
.page > .filters {
    position: sticky;
    top: 0;
    align-self: flex-start;
}

/* this on any ancestor breaks it: */
.wrapper { overflow: hidden; }

30. Why does a dropdown with z-index: 9999 still appear behind another section?

z-index only compares elements inside the same stacking context. If the dropdown sits inside a header with position relative and z-index 1, the header forms a stacking context and everything inside it is painted as one layer at level 1. A later section with z-index 2 then covers the whole header, dropdown included, whatever number the dropdown has. The fix is to raise the header's own z-index, remove what creates its stacking context, or render the dropdown outside it. Also remember z-index works only on positioned elements and on flex or grid items.

Example
.header { position: relative; z-index: 1; }
.menu   { position: absolute; z-index: 9999; }  /* inside .header */
.banner { position: relative; z-index: 2; }     /* after .header */
/* .banner covers .menu: the contexts compare 1 with 2 */

31. What does flex: 1 mean, and how wide does each item end up here?

flex: 1 is shorthand for flex-grow 1, flex-shrink 1 and flex-basis 0%, so items start from zero and share all the space equally. When a basis is set, only the free space is shared out by the grow ratio. Here the container is 600px and the three bases add up to 300px, leaving 300px free. Grow values 1, 2 and 3 split it into six parts of 50px, so the items end at 150px, 200px and 250px, not in a 1:2:3 ratio of the total width. flex: auto instead keeps the item's own width or content size as the basis.

Example
.row { display: flex; width: 600px; }
.a { flex: 1 1 100px; }   /* 150px */
.b { flex: 2 1 100px; }   /* 200px */
.c { flex: 3 1 100px; }   /* 250px */

32. A long URL inside a flex item pushes the page wider than the phone screen. Why, and what is the fix?

Flex items have min-width: auto by default, which stops them shrinking below their min-content width, the width of the longest piece that cannot break. A long URL or an email address is one very long word, so the item refuses to shrink and overflows on phones. Set min-width: 0 on the item so it can shrink, then let the text wrap with overflow-wrap: anywhere. Grid has the same trap, because 1fr means minmax(auto, 1fr), so use minmax(0, 1fr) for columns that hold code blocks, tables or long strings.

Example
.contact { display: flex; gap: 12px; }
.contact .text {
    flex: 1;
    min-width: 0;
    overflow-wrap: anywhere;
}

.grid { grid-template-columns: minmax(0, 1fr) 300px; }

33. What is the difference between auto-fill and auto-fit in a grid like this one?

Both create as many columns as will fit. With minmax(200px, 1fr) in a 1000px container and 16px gaps, five columns would need 1064px, so four fit, each (1000 - 48) / 4 = 238px wide. The difference shows when there are fewer items than columns. auto-fill keeps the empty tracks, so two cards stay 238px wide with blank space beside them. auto-fit collapses the empty tracks, so the same two cards stretch to 492px each. Use auto-fill when cards should keep a steady size, and auto-fit when a few items should fill the row.

Example
.cards {
    display: grid;
    width: 1000px;
    gap: 16px;
    grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
}
/* use auto-fit instead to stretch a short row */

34. How do grid-template-areas work, and what accessibility care do they need?

You give each child a name with grid-area and draw the layout on the container as strings, one per row; a dot marks an empty cell. Each named area must form a rectangle, or the whole declaration is ignored. Changing the strings in a media query rearranges the page without touching the HTML, which makes the switch from phone to desktop easy to read. The care point: moving boxes visually does not change the reading or tab order, which follow the source. Keep the HTML in a sensible order and use areas for placement, not to rescue a messy source.

Example
.page {
    display: grid;
    grid-template-areas:
        "header"
        "main"
        "side";
}
@media (min-width: 900px) {
    .page {
        grid-template-columns: 3fr 1fr;
        grid-template-areas:
            "header header"
            "main   side";
    }
}
header { grid-area: header; }
main   { grid-area: main; }
aside  { grid-area: side; }

35. How do srcset and sizes make images responsive?

srcset lists the same image at several widths, and sizes tells the browser how wide the image will be shown, so it can pick a file before the CSS has even loaded. Here a phone 360px wide with a device pixel ratio of 3 needs about 1080 image pixels, so the 1200w file is the smallest that covers it, while a 1440px desktop at ratio 1 shows the image at 33vw, about 475px, so 800w is enough. The browser makes the final choice. Use the picture element for art direction or newer formats, and always set width and height.

Example
<img src="office-800.jpg"
     srcset="office-400.jpg 400w,
             office-800.jpg 800w,
             office-1200.jpg 1200w"
     sizes="(min-width: 1024px) 33vw, 100vw"
     width="1200" height="800"
     alt="Team at work in the Hyderabad office">

36. Why does a 100vh hero section get cut off on phones, and what replaces it?

On mobile browsers vh is based on the large viewport, the height with the browser toolbars hidden. When the page loads the address bar is showing, so a 100vh section is taller than the visible area and its bottom, often the main button, is cut off. Current browsers support three newer units: svh for the small viewport with toolbars shown, lvh for the large one, and dvh, which changes as the toolbars appear and hide. svh is the safe choice for layout, since dvh can make content jump while scrolling. A related trap: 100vw includes the scrollbar on Windows desktops.

Example
.hero {
    min-height: 100vh;    /* fallback for old browsers */
    min-height: 100svh;   /* visible height with toolbars shown */
}

37. How does clamp() create fluid font sizes? What size is this heading on different screens?

clamp(min, preferred, max) uses the preferred value but never goes below the minimum or above the maximum. With clamp(1rem, 0.75rem + 1.5vw, 2rem) and a 16px root, the preferred size is 12px plus 1.5% of the viewport width. On a 360px phone that is 17.4px, on a 768px tablet 23.52px, and at 1440px it would be 33.6px, so it is capped at 32px. Keep a rem part in the middle value: a size made only of vw ignores the user's font setting and does not grow with browser zoom, which fails accessibility checks.

Example
h1 {
    font-size: clamp(1rem, 0.75rem + 1.5vw, 2rem);
}
/* 360px: 17.4px   768px: 23.52px   1440px: 32px (capped) */

38. How would you build light and dark themes with CSS variables?

Define colour tokens as custom properties on :root, use only the tokens inside components, and redefine the tokens for dark mode. A prefers-color-scheme media query follows the operating system setting, and a data-theme attribute on the html element, set by JavaScript and saved in localStorage, lets the user override it. Because variables are resolved in the browser, changing the attribute restyles the page at once with no second stylesheet, which Sass variables cannot do. Also set color-scheme so form controls and scrollbars match the theme.

Example
:root {
    --bg: #ffffff;
    --text: #212529;
    color-scheme: light;
}
@media (prefers-color-scheme: dark) {
    :root:not([data-theme="light"]) {
        --bg: #1a1b1e;
        --text: #e9ecef;
        color-scheme: dark;
    }
}
:root[data-theme="dark"] {
    --bg: #1a1b1e;
    --text: #e9ecef;
    color-scheme: dark;
}
body { background: var(--bg); color: var(--text); }

39. What is render-blocking CSS and JavaScript, and how do defer and async help?

The browser does not paint the page until it has downloaded and parsed the stylesheets in the head, to avoid a flash of unstyled content, so a large or slow stylesheet delays the first paint. A plain script in the head is worse, because it stops HTML parsing until it is fetched and run. defer downloads in parallel and runs the scripts in order after parsing, which suits app code. async runs each script as soon as it arrives, in any order, which suits independent scripts such as analytics. A stylesheet whose media value does not match, such as print, does not block rendering.

Example
<head>
    <link rel="stylesheet" href="/css/site.css">
    <link rel="stylesheet" href="/css/print.css" media="print">
    <script src="/js/app.js" defer></script>
    <script src="/js/analytics.js" async></script>
</head>

40. What causes layout shift (CLS), and how do you prevent it?

Cumulative Layout Shift measures how much visible content jumps around while the page is used; a good score is 0.1 or less. The usual causes are images and iframes without dimensions, ads or banners injected above existing content, and web fonts that swap in with different metrics. Set width and height attributes on images: browsers turn them into an aspect ratio, so a 1200 by 675 image in a 360px column gets 202.5px reserved before it loads. Reserve fixed space for ad slots, add late content below what the user is reading, and animate with transform.

Example
<img src="banner.jpg" width="1200" height="675" alt="Campus drive">

img { max-width: 100%; height: auto; }
.ad-slot { min-height: 250px; }

Ready to check yourself? Take the free HTML and CSS mock test

HTML and CSS interview questions: Hard questions (experienced)

20 questions

41. What are cascade layers (@layer), and how do they change the way conflicts are settled?

Layers add a step to the cascade that is checked before specificity. You declare the order once, for example reset, vendor, components, utilities, and for normal declarations a later layer always beats an earlier one, whatever the specificity of the selectors. Styles outside any layer beat all layered styles. For !important the order flips: earlier layers win, and layered important beats unlayered important. This lets you import a UI library into a low layer so that a one-class rule overrides it without ID chains. All major browsers have supported layers since 2022.

Example
@layer reset, vendor, components, utilities;

@import url("datepicker.css") layer(vendor);

@layer components {
    .btn { padding: 8px 16px; }
}
@layer utilities {
    .p-0 { padding: 0; }   /* beats vendor and components rules */
}

42. How do :is() and :where() affect specificity, and why do libraries use :where()?

Both match when any selector in their list matches. :is() takes the specificity of its most specific argument, even when a weaker argument did the matching, while :where() always counts as zero. So :is(#hero, .banner) h2 scores (1,0,1) for an h2 inside .banner and beats .banner h2 at (0,1,1), which surprises people. :where(#hero, .banner) h2 scores only (0,0,1). Resets and design-system libraries wrap their selectors in :where() so that any single class in app code overrides them. :not() and :has() count the same way as :is().

Example
:is(#hero, .banner) h2    { color: red; }    /* (1,0,1) */
.banner h2                { color: green; }  /* (0,1,1) */
:where(#hero, .banner) h2 { color: blue; }   /* (0,0,1) */

/* an h2 inside div.banner is red */

43. What creates a stacking context, and how would you manage z-index in a large app?

Besides the root, common triggers are a positioned element with a z-index other than auto, fixed and sticky elements, flex or grid items with a z-index, opacity below 1, and any transform, filter, clip-path or mask. isolation: isolate creates one with no visual side effect. Children can never escape their context, so a stray opacity or transform on a card can trap a tooltip. In a big app keep a small named z-index scale in variables, isolate each component, and render modals and toasts at the end of the body or in the top layer.

Example
:root {
    --z-dropdown: 100;
    --z-sticky: 200;
    --z-modal: 1000;
    --z-toast: 1100;
}
.card { isolation: isolate; }   /* keeps its z-index inside */
.dropdown { position: absolute; z-index: var(--z-dropdown); }

44. A position: fixed modal scrolls with the page and gets clipped inside a sliding drawer. What is happening?

A fixed element is normally placed against the viewport, but an ancestor with a transform, perspective, filter, will-change: transform or contain: paint becomes its containing block instead. The modal is then positioned and sized against that ancestor, moves with it, and is clipped by its overflow. Drawers and carousels slide with transform, so this bug shows up there often. Fix it by rendering the modal outside the transformed tree, at the end of the body, or by using a dialog element opened with showModal, which goes into the top layer above everything else.

Example
.drawer {
    transform: translateX(0);   /* creates a containing block */
    overflow: hidden;
}
.drawer .modal {
    position: fixed;   /* now fixed to .drawer, not the viewport */
    inset: 0;
}

45. What is a block formatting context, and how does it fix margin and float problems?

A block formatting context (BFC) is an independent layout region: blocks inside it are laid out without affecting the outside. Inside a new BFC, floats are contained so the parent wraps around them, the box does not slide under neighbouring floats, and a child's top margin no longer collapses through the parent. That last bug shows when a card with no padding or border lets its heading's margin push the whole card down. display: flow-root creates a BFC with no side effects, unlike the old overflow: hidden trick, which also clips shadows and dropdowns.

Example
.card { background: #f1f3f5; }
.card h2 { margin-top: 24px; }   /* escapes: card moves down */

.card { display: flow-root; }    /* new BFC: margin stays inside */

46. Why does padding-top: 56.25% give a 16:9 box, and what does aspect-ratio change?

Percentage padding, top and bottom included, is calculated from the width of the containing block, not its height. So padding-top: 56.25% inside an 800px wide wrapper gives 450px, and 450 / 800 is exactly 9 / 16. Before aspect-ratio this was the standard way to keep video embeds at 16:9, with the iframe positioned absolutely inside. Today aspect-ratio: 16 / 9 does it directly, keeps the element in normal flow and needs no wrapper. It is a preferred ratio, so content taller than the box can still stretch it unless you set min-height: 0 or an overflow other than visible.

Example
/* old way */
.video { position: relative; padding-top: 56.25%; }
.video iframe {
    position: absolute; inset: 0; width: 100%; height: 100%;
}

/* today */
.video iframe { width: 100%; height: auto; aspect-ratio: 16 / 9; }

47. How does flex-shrink decide how much each item shrinks?

Shrinking is weighted by flex-shrink multiplied by flex-basis, not by flex-shrink alone, so bigger items give up more. Here the bases add up to 500px in a 400px row, so 100px must go. Both items have shrink 1, so the weights are 300 and 200: the first loses 60px and ends at 240px, the second loses 40px and ends at 160px. Many candidates say 50px each, which is true only for equal bases. Items also stop at their min-content width because of min-width: auto, and the overflow that is left is then shared by the other items.

Example
.row { display: flex; width: 400px; }
.a { flex: 0 1 300px; }   /* ends at 240px */
.b { flex: 0 1 200px; }   /* ends at 160px */

48. What problem does subgrid solve? Show a card row that uses it.

In a row of cards each card is its own box, so titles of different lengths push the descriptions and buttons to different heights and the row looks ragged. With subgrid, each card spans several rows of the parent grid and sets grid-template-rows: subgrid, so its title, body and footer sit on row tracks shared by every card in that row. The tallest title then sets the height of all the titles. The subgrid inherits the parent's gaps unless you override them. It works in all major browsers since 2023; older ones simply show the ragged layout.

Example
.cards {
    display: grid;
    grid-template-columns: repeat(3, 1fr);
    gap: 16px;
}
.card {
    display: grid;
    grid-row: span 3;
    grid-template-rows: subgrid;
}
/* each card: title, body, footer on shared rows */

49. How do you decide between flexbox and grid for a layout?

Flexbox is one-dimensional and content-out: items in a row or a column size themselves from their content and share the space, which suits toolbars, navigation, chips, form rows and centring. Grid is two-dimensional and layout-in: you define the tracks first and place items into them, so rows and columns line up, which suits page layouts, card galleries and forms whose labels and fields must align. They work together, and a grid page with a flex header is normal. A sign you picked wrong is fighting with widths to make flex items line up in columns across rows.

50. How do container queries differ from media queries, and what are their limits?

Media queries respond to the viewport, so a card cannot tell whether it sits in a wide main column or a narrow sidebar. Container queries respond to the size of an ancestor you mark with container-type: inline-size, so the same card can switch to a horizontal layout wherever it gets 400px. Units such as cqi, one percent of the container's inline size, let text scale with the container. Limits: an element cannot query itself, only an ancestor, and the container's width cannot depend on its content, so a shrink-to-fit container such as an inline-block collapses.

Example
.slot { container: card / inline-size; }

.job-card { display: grid; gap: 8px; }

@container card (min-width: 400px) {
    .job-card { grid-template-columns: 120px 1fr; }
    .job-card h3 { font-size: 5cqi; }
}

51. Why do accessibility reviews flag font sizes in px, and how do em-based media queries behave?

Browser zoom scales everything, px included, but many users instead raise the default font size in the browser settings, and that setting only scales rem and em. Text sized in px ignores it. With rem, a user who sets 20px gets text 1.25 times larger everywhere. em in a media query is relative to that browser default, not to your html font-size rule, so 48em is 768px for most users and 960px with a 20px default, and the layout switches earlier when text is bigger. A px breakpoint stays at 768px and may squeeze the larger text.

Example
html { font-size: 100%; }    /* follows the user's setting */
body { font-size: 1rem; }

@media (min-width: 48em) {   /* 768px at 16px, 960px at 20px */
    .layout { grid-template-columns: 1fr 1fr; }
}

52. Why should line-height usually be unitless? Work out the heading's line height here.

A unitless line-height is inherited as the number, and each element multiplies it by its own font size. A percentage or em value is computed once on the parent, and the resulting length is what children inherit. Here the body text is 16px with line-height 150%, which computes to 24px, and the 40px h1 inherits 24px, so a two-line heading overlaps itself. With line-height 1.5 the h1 gets 40 x 1.5 = 60px instead. The same compounding explains why nested lists set to font-size 0.9em shrink at every level.

Example
body { font-size: 16px; line-height: 150%; }  /* passes down 24px */
h1   { font-size: 40px; }                     /* lines 24px apart */

body { line-height: 1.5; }   /* h1 gets 40 x 1.5 = 60px */

53. What happens when a CSS variable holds an invalid value, and why do you need @property to animate one?

var() is substituted when the computed value is worked out, after the cascade has already picked the winning declaration. If the result is invalid for that property, the browser does not go back to the earlier rule; the property becomes invalid at computed-value time and takes its inherited or initial value. So margin: var(--gap) with --gap set to red gives a margin of 0, not the 10px set before. Unregistered variables are untyped, so transitions on them jump. Registering one with @property and a type such as angle lets the browser interpolate it, in all major browsers since 2024.

Example
.box { margin: 10px; --gap: red; margin: var(--gap); }  /* 0 */

@property --angle {
    syntax: "<angle>";
    inherits: false;
    initial-value: 0deg;
}
.ring {
    background: conic-gradient(#e64980 var(--angle), #eee 0);
    transition: --angle 0.6s ease;
}
.ring:hover { --angle: 270deg; }

54. How would you structure design tokens with custom properties for a large product?

Use three tiers. Primitive tokens hold raw values, such as a pink-600 colour or a 4px spacing step. Semantic tokens give them a job, such as --color-action or --color-text, and are the only ones components read. Component tokens, such as --btn-bg, default to semantic tokens and give each component a small override API. Themes and white-label clients then swap only the semantic layer. Watch the runtime cost: changing a variable on :root makes the browser recalculate styles across the document, so fast-changing values such as a cursor position belong on the one element that uses them.

Example
:root {
    --pink-600: #d6336c;               /* primitive */
    --gray-900: #212529;
    --color-action: var(--pink-600);   /* semantic */
    --color-text: var(--gray-900);
}
.btn {
    --btn-bg: var(--color-action);     /* component */
    background: var(--btn-bg);
    color: #fff;
}
.btn.secondary { --btn-bg: var(--gray-900); }

55. Why are transform and opacity animations smooth while animating top or width is janky?

Each frame can need style, layout, paint and composite. Changing width, height, top or margin forces layout, which can move every box after it, and then a repaint. At 60 frames per second there are only about 16.67ms per frame, and 8.33ms on a 120Hz phone. transform and opacity skip layout and paint, and the browser can run them on the compositor even while JavaScript is busy. So slide with translate, grow with scale and fade with opacity. will-change: transform prepares a layer in advance, but layers cost GPU memory, so use it sparingly.

Example
/* janky: layout on every frame */
.panel { position: absolute; left: -300px; transition: left 0.3s; }
.panel.open { left: 0; }

/* smooth: compositor only */
.panel { transform: translateX(-100%); transition: transform 0.3s; }
.panel.open { transform: translateX(0); }

56. How do you design motion that respects users who prefer reduced motion?

Some users feel dizzy or sick from large motion such as parallax, zooming and sliding, so they turn on the reduce motion setting in their operating system. CSS exposes it as the prefers-reduced-motion media query, and JavaScript can read the same query with matchMedia. A good approach is to put big movement only inside a no-preference query, and in reduced mode swap slides for short fades instead of removing all feedback. Content that moves automatically for more than five seconds also needs a way to pause it under WCAG. Test with the setting on.

Example
.toast { opacity: 0; transition: opacity 0.2s; }
.toast.show { opacity: 1; }

@media (prefers-reduced-motion: no-preference) {
    .toast {
        transform: translateY(20px);
        transition: opacity 0.2s, transform 0.3s;
    }
    .toast.show { transform: none; }
}

57. How would you speed up the first paint of a page whose CSS bundle is large?

Measure first: the Coverage panel in Chrome devtools shows how much of the bundle the first screen uses. Then inline the critical rules for the header and hero in a style element, keep the start of the HTML small (about 14KB is a common target, roughly the first round trip), and load the full stylesheet without blocking, for example with preload and a switch to stylesheet once it loads. Remove @import inside CSS, which chains downloads one after another. The trade-off: inlined CSS is not cached between pages and must be kept in sync with the real stylesheet.

Example
<head>
    <style>
        /* critical rules only: header, hero, base type */
        .header { ... }
        .hero { ... }
    </style>
    <link rel="preload" href="/css/site.css" as="style"
          onload="this.onload=null;this.rel='stylesheet'">
    <noscript><link rel="stylesheet" href="/css/site.css"></noscript>
</head>

58. How is a layout shift scored? Score this shift and say how to fix it.

Each unexpected shift scores impact fraction times distance fraction. The impact fraction is the share of the viewport covered by the moving element before and after the move, combined; the distance fraction is how far it moved divided by the larger side of the viewport. On a 360 by 800 phone, a 400px tall list pushed down 200px by a late banner covers 600px of height in total, so 0.75, and moved 200 / 800 = 0.25. The shift scores 0.1875, past the 0.1 limit for a good score. Reserve the banner's space up front, or show it as an overlay.

Example
/* viewport 360 x 800: list moves from y=200 to y=400 */
/* impact   = (800 - 200) / 800 = 0.75             */
/* distance = 200 / 800 = 0.25                     */
/* score    = 0.75 x 0.25 = 0.1875                 */

.offer-banner { min-height: 200px; }   /* reserve the space */

59. What is layout thrashing, and how do you fix the loop below?

Layout thrashing happens when JavaScript alternates between writing styles and reading layout values such as offsetHeight or getBoundingClientRect. Each read after a write forces the browser to run layout at once, so a loop over 500 rows can run layout 500 times in one frame. Batch the work: read every value first, then make every change, or move the writes into requestAnimationFrame. In CSS, contain: layout on independent widgets limits how far a change spreads, and content-visibility: auto skips rendering work for off-screen sections of long pages.

Example
const rows = [...document.querySelectorAll('.row')];

// slow: each read after a write forces layout
rows.forEach(row => {
    const h = row.offsetHeight;           // read
    row.style.height = (h + 10) + 'px';   // write
});

// fast: all reads first, then all writes
const heights = rows.map(r => r.offsetHeight);
rows.forEach((r, i) => {
    r.style.height = (heights[i] + 10) + 'px';
});

60. How do you build an accessible modal dialog today?

Use the dialog element opened with showModal. The browser then puts it in the top layer above every z-index, makes the rest of the page inert so keyboard and screen reader users cannot wander behind it, moves focus inside and closes it on Escape. Give it an accessible name with aria-labelledby pointing to its heading, and make sure focus goes back to the button that opened it when it closes. Calling show instead of showModal gives a non-modal dialog: no top layer, and the page behind stays usable. A hand-built div modal must do all of this itself, and teams often forget to keep focus inside.

Example
<button id="open">Apply now</button>

<dialog id="apply" aria-labelledby="apply-title">
    <h2 id="apply-title">Apply for this job</h2>
    <form method="dialog">
        <button>Close</button>
    </form>
</dialog>

<script>
const dlg = document.getElementById('apply');
const opener = document.getElementById('open');
opener.addEventListener('click', () => dlg.showModal());
dlg.addEventListener('close', () => opener.focus());
</script>

Practice questions for November 2026

New every month

3 questions from the HTML and CSS mock test, the way they appear in it. A new set comes on the 1st of every month. Pick your answer, then open it.

  1. FresherResponsive Design

    What does a mobile-first stylesheet mean?

    • The site is built only for phones and ignores desktops
    • Base styles target phones; min-width queries add larger layouts
    • Desktop styles come first, and phones are sent to a separate mobile site
    • Every breakpoint is written as a max-width media query
    Show the answer

    Answer: Base styles target phones; min-width queries add larger layouts. Mobile-first starts with the simplest layout as the default and layers enhancements with min-width queries as space grows. Phones apply fewer overrides, and the base styles still work in very narrow windows.

  2. Intermediate (1-5 years)Display and Positioning

    Where the popup and .b overlap, which one is on top?

    <div class="a"><div class="popup"></div></div>
    <div class="b"></div>
    
    <style>
        .a { position: relative; z-index: 1; }
        .popup { position: absolute; z-index: 9999; }
        .b { position: relative; z-index: 2; }
    </style>
    • The popup, because its z-index 9999 is far larger than 2
    • Neither; the two layers blend together
    • .b, because the popup is trapped in .a's stacking context
    • Whichever of the two comes first in the HTML
    Show the answer

    Answer: .b, because the popup is trapped in .a's stacking context. A positioned element with a z-index other than auto creates a stacking context, so the popup's 9999 only competes inside .a. Between .a (1) and .b (2), .b wins, and all of .a, popup included, is painted under .b.

  3. Experienced (5+ years)Flexbox

    The .list grows taller than the screen instead of scrolling. What is the fix?

    .app { display: flex; flex-direction: column; height: 100vh; }
    .body { flex: 1; display: flex; flex-direction: column; }
    .list { flex: 1; overflow: auto; }
    • Add flex-shrink: 0 to .body
    • Add min-height: 0 to .body
    • Add align-items: center to .app
    • Use overflow: visible on .list
    Show the answer

    Answer: Add min-height: 0 to .body. A column flex item has min-height: auto, its content height, so .body refuses to shrink and pushes the list past the screen. min-height: 0 on .body lets it fit the space, and then the overflow: auto on .list can scroll.

Take the full HTML and CSS mock test

Ready to apply?

There are 902 open jobs asking for HTML and CSS on CoreHRx right now. Create a free profile once and apply with it.