🎨 CSS: Why It Breaks Locators

CSS: Why It Breaks Locators: CSS is the PAINT and DECORATION layer of a building — and that is exactly why it is dangerous ground for a locator.

CSS is the PAINT and DECORATION layer of a building — and that is exactly why it is dangerous ground for a locator. Classes exist for styling; when a developer changes a color, or modern tools (CSS Modules, styled-components) add a random hash (`__x7f2a`) to the class on every build, a locator bound to that class breaks silently. Why does this happen to us so often? Because the first thing you see in DevTools is the class, and "Copy selector" usually produces it — the most tempting yet most fragile choice. Java analogy: it is like comparing an object by its `toString()` output — change the format and the relationship breaks, when you should have bound identity to a stable id. In a QA context: a tester who knows the class exists for styling does NOT trust it as a locator; instead they ask the developer for a data-testid.

🧩 C1. Selector Logic: element, class, id, descendant, child, attribute

CSS selectors are like the precision of a POSTAL ADDRESS: an element selector (`button`) = "any house in the city", a class selector (`.card`) = "everyone in this neighborhood", an id selector (`#checkout`) = "this exact address", descendant (`ul li`) = "anywhere on this street, at any depth", child (`ul > li`) = "the houses directly on this street", attribute (`[data-testid]`) = "the house carrying this specific sign". So why is knowing this distinction useful? Because CSS selector logic carries over almost VERBATIM to Playwright/Cypress's `.locator()` method — a tester who can read CSS can read automation syntax too. Java analogy: like chaining table relationships (a JOIN chain) in a SQL `WHERE` clause — selectors likewise chain descendant/child relationships in the HTML tree. In QA context: a descendant selector (a space) is too wide and carries a false-positive risk (accidentally catching an unwanted nested element); a child selector (`>`) is narrower and usually safer.

Step by Step: The Difference Between `ul li` and `ul > li`

Descendant selector: `ul li`

The space-separated form means "any ul that has an li at ANY depth inside it" — it matches no matter how many wrapper levels are in between.

If there is a nested sub-list inside a BugCard (like comments), `ul li` can MISTAKENLY match the `li`s in that sub-list too.

Child selector: `ul > li`

Means "the li that is a DIRECT child of ul" — only one level down matches, nested sub-lists are excluded.

In single-level structures like a BugCard list, using `>` removes the risk of accidentally catching nested elements.

Carries over almost verbatim to Playwright/Cypress

A tester who can read CSS selector logic can directly read automation syntax like `page.locator('ul > li')` too — for syntax depth see /playwright, /cypress.

Inside a BugCard there is a "comments" sub-list that also contains ` ` elements. What is the risk difference between `ul li` and `ul > li`?

No difference, both find the same elements