You'd describe an element to a colleague: 'the Login button, the blue one at the bottom of the form.'
A CSS selector is that description, written precisely enough that the browser finds exactly one element.
CSS selectors are patterns that match elements in the DOM. They're fast, readable, and the locator type you'll write most. Here's the whole practical toolkit, from most to least preferred:
/* by id — the # sign. Best when available. */ #login-button /* by class — the . sign (chain multiple, no spaces) */ .form_input .input_error.form_input /* by attribute — [name="value"] */ [data-test="username"] input[placeholder="Password"] /* by hierarchy — descendant (space) and direct child (>) */ form input /* any input anywhere inside a form */ form > input /* only inputs that are direct children */ /* by position — when there's truly nothing better */ .cart_item:nth-child(2) /* partial attribute matches */ [id^="login"] /* id starts with "login" */ [id$="button"] /* id ends with "button" */ [class*="error"] /* class contains "error" */
DevTools is a selector playground. On saucedemo.com press F12 → Console tab, and use $$("…") (double dollar), which returns every element matching a CSS selector:
$$("#login-button")
$$(".form_input")
$$("[data-test='username']")> $$("#login-button")
[input#login-button.submit-button.btn_action] // 1 match — perfect
> $$(".form_input")
[input#user-name.input_error.form_input, input#password.form_input] // 2 matches
> $$("[data-test='username']")
[input#user-name.input_error.form_input] // 1 match
images/devtools-console-matches.pngThe second technique you'll use forever: in the Elements panel, Ctrl+F opens a search box that accepts CSS selectors and shows "1 of N" — instantly telling you whether your selector is unique.
A good locator is unique (matches exactly one element), stable (survives redesigns), and readable (the next person understands it). Priority order: id → data-test attribute → a meaningful class or attribute → hierarchy → position. Position (nth-child) is the last resort — it breaks the moment someone reorders the page.
div > div:nth-child(3) > span.x2a, it will break within a sprint. Go up the tree and find something with meaning.Fix: $$() and $x() exist only in the DevTools Console — they're console utilities, not JavaScript. In Selenium you'll use By.cssSelector() instead.
Fix: Three usual suspects: a typo (ids are case-sensitive), the element lives inside an <iframe> (the console targets the top document — switch frames via the dropdown above the console), or the page re-rendered after you looked.
Fix: Usually XPath syntax fed to $$() (that's $x()'s job), or an unescaped special character. Check which dialect you're writing.
Log in to saucedemo (user standard_user, password secret_sauce) to reach the Products page. Using Console $$() or Ctrl+F in Elements, write a selector for each — and verify the match count in brackets:
.shopping_cart_link [data-test^="add-to-cart"] .inventory_item_name .product_sort_container #add-to-cart-sauce-labs-backpack