## TL;DR

Locators break because they depend on implementation details. Prefer user-facing locators (`getByRole`, `getByLabel`, `getByTestId`) in that order, and reserve CSS and XPath for cases where semantic locators cannot express the target.

## Error

```text
(Not an error; a maintenance strategy. Symptom: a CSS refactor breaks 40 tests that used class-based selectors.)
```

## Steps

1. Audit current selectors for implementation coupling: classes, structure (`div > div:nth-child(2)`), and text fragments. Expected: a list of the brittle ones.
2. Replace with role locators first: `page.getByRole('button', { name: 'Submit' })`. Expected: survives class and layout changes.
3. Where no good role exists, add `data-testid` attributes in the app and use `getByTestId`. Expected: a contract between app and tests.
4. Keep CSS only for truly structural targets (table cells by position). Expected: the minority of locators, documented as brittle.
5. Add a lint rule or code-review checklist banning new `page.locator('.some-class')` selectors. Expected: the suite stops getting more brittle.

## When to use

- Every refactor breaks dozens of tests.
- You are writing the locator conventions for a team.

## When not to use

- A single broken selector (fix that selector directly).
- Testing visual layout itself (then structure is the point).

## Tool compatibility

- Playwright 1.30 through latest; all locator types.

## Variant phrasings

### Playwright best locator practices

The same guidance; role-first is the headline.

### Selectors that survive UI refactors

The maintenance framing of the same strategy.

## Why it happens

CSS classes and DOM structure change for styling reasons constantly. Accessible roles and test IDs change only when behavior changes, which is exactly when tests should break.

## Edge cases

- `getByTestId` requires app cooperation; get the frontend team to own the attributes.
- Over-specific role names (`Submit the quarterly report form now`) are as brittle as classes; keep names user-meaningful.
- Third-party widgets may expose no roles; CSS is acceptable there with a comment.

## Provenance

Resolved from the public thread: https://vectle.com/posts/pst_v58DlgVvJDn4j-ldaBirDQ
