Menu
CoddyTech

CSS Selectors and Specificity Interview Questions

Which selector matches what, and which rule wins when several match.

10 questions
  1. What types of CSS selectors are there?

    Simple (*, p, .btn, #main), attribute ([type="email"], [href^="https"]), combinators (nav a, ul > li, h2 + p, h2 ~ p), pseudo-classes for a state (:hover, :nth-child(2n), :has(img)), pseudo-elements for a part (::before, ::placeholder), and grouping with commas. See selectors.

  2. What is the difference between div p, div > p, div + p and div ~ p?

    div p matches any p inside a div at any depth; div > p only direct children; div + p only the p right after a div; div ~ p every later p sibling of a div.

    HTML
    <style>
      .list li { color: gray; }
      .list > li { font-weight: bold; }
      h3 + p { color: crimson; }
      h3 ~ p { font-style: italic; }
    </style>
    <ul class="list"><li>Child of .list: gray and bold</li></ul>
    <div class="list"><ul><li>Grandchild of .list: gray, not bold</li></ul></div>
    <h3>Heading</h3>
    <p>Right after the heading: crimson and italic</p>
    <p>Later sibling: italic only</p>

    Sibling combinators only look forward; reaching a previous sibling or a parent needs :has().

  3. How is CSS specificity calculated?

    A three-part score (a, b, c): a counts ids, b classes, attributes and pseudo-classes, c type selectors and pseudo-elements. Compared left to right, so one id beats any number of classes.

    HTML
    <style>
      #intro { color: green; }
      .lead.big { color: blue; }
      p.lead { color: red; }
      p { color: gray; }
    </style>
    <p id="intro" class="lead big">Green: the id wins</p>
    <p class="lead big">Blue: (0, 2, 0) beats (0, 1, 1)</p>
    <p class="lead">Red: (0, 1, 1) beats (0, 0, 1)</p>

    * and combinators add nothing; :not(), :is() and :has() take their most specific argument; :where() is always zero.

  4. What does !important do, and why is it considered bad practice?

    !important puts a declaration in a higher cascade tier: it beats every normal declaration, inline styles included, whatever the specificity, and loses only to another !important that wins the usual comparison, or to a running transition. It is bad practice because each later override needs another one. It is fine for utilities like .hidden and against a third-party widget's inline styles. More in !important.

  5. What is the difference between a pseudo-class and a pseudo-element?

    A pseudo-class selects an element in a state or position (:hover, :checked, :first-child); a pseudo-element styles a part of an element or adds a box that is not in the HTML (::before, ::first-letter, ::placeholder). Pseudo-classes take one colon, pseudo-elements two (:before still works for old CSS2 code).

    a:hover { text-decoration: underline; }        /* a state */
    li:nth-child(odd) { background: #f3f4f6; }     /* a position */
    p::first-line { font-weight: bold; }           /* a part */
    
  6. How do ::before and ::after work, and why is my ::before not showing?

    They insert a generated first and last child, rendered only when content is set (content: "" for a shape).

    HTML
    <style>
      .required::after { content: " *"; color: crimson; }
      .tag { position: relative; padding-left: 18px; }
      .tag::before {
        content: "";
        position: absolute; left: 0; top: 50%;
        width: 10px; height: 10px; border-radius: 50%;
        background: #16a34a; transform: translateY(-50%);
      }
    </style>
    <label class="required">Email</label>
    <p class="tag">Online</p>

    If one does not show: content is missing; the element is a replaced element like img, whose content comes from outside the document; or it is empty and inline, so it needs display: inline-block to take a size.

  7. What is the difference between :nth-child() and :nth-of-type()?

    :nth-child(n) counts all siblings whatever their tag, then checks the rest of the selector; :nth-of-type(n) counts only siblings with the same tag.

    HTML
    <style>
      p:nth-child(2) { background: #fde68a; }
      p:nth-of-type(2) { color: #2563eb; font-weight: bold; }
    </style>
    <div>
      <h3>Heading (child 1)</h3>
      <p>Paragraph, child 2: yellow background</p>
      <p>Paragraph, child 3, second p: blue text</p>
    </div>

    So p:nth-child(1) matches nothing here: the first child is the h3. Both take formulas such as 2n+1 or -n+3 (the first three).

  8. How do attribute selectors work?

    They match by an attribute's presence or value: [attr], [attr="x"] (exact), ^= (starts with), $= (ends with), *= (contains), ~= (one word in a list). Add i for a case-insensitive match: [type="EMAIL" i]. Specificity equals a class.

    HTML
    <style>
      input[type="email"] { border: 2px solid #2563eb; }
      [data-state="error"] { color: crimson; }
      [class*="badge-"] { padding: 2px 6px; border-radius: 4px; background: #e5e7eb; }
    </style>
    <input type="email" placeholder="email: blue border">
    <input type="text" placeholder="text: default border">
    <p data-state="error">data-state="error" is red</p>
    <span class="badge-new">badge-new</span> <span class="badge-sale">badge-sale</span>

    A common use is JavaScript state: set data-state="open" and style [data-state="open"].

  9. What do :is(), :where() and :has() do?

    :is() and :where() match any selector in their list. :is() takes its most specific argument's specificity and :where() zero, which suits defaults meant to be overridden. :has() matches an element by what it contains or what follows it, the parent selector CSS lacked, in Chrome, Safari and Firefox (since 121).

    HTML
    <style>
      .task { padding: 6px; border-radius: 6px; }
      .task:has(input:checked) { text-decoration: line-through; background: #dcfce7; }
      .btn { background: #7c3aed; } /* wins, even though it comes first */
      :where(.btn) { color: white; background: #2563eb; } /* specificity (0, 0, 0) */
    </style>
    <label class="task"><input type="checkbox"> Tick me, the row styles itself</label>
    <p><button class="btn">.btn overrides :where(.btn)</button></p>

    body:has(...) on a large page can be slow; anchor it to a small container, as the MDN :has() performance notes advise.

  10. What is the difference between :root and html in CSS?

    In an HTML page both select the <html> element, but :root is a pseudo-class, so its specificity is (0, 1, 0) against html's (0, 0, 1). It also matches the root element of SVG and XML documents. That is why custom properties go on :root: a later html rule cannot override them by accident.

    HTML
    <style>
      :root { --brand: #2563eb; }
      html { --brand: crimson; }
      p { color: var(--brand); }
    </style>
    <p>Blue: :root (0, 1, 0) beats html (0, 0, 1), even though the html rule comes later.</p>
Coddy programming languages illustration

Learn to code with Coddy

Get started