CSS Basics for Testers
CSS (Cascading Style Sheets) decides how HTML elements look and where they sit: colours, fonts, sizes, spacing, layout, and what changes on hover, on keyboard focus or on a narrow screen. The HTML says a checkout page has a "Pay now" button; the CSS makes it blue, full width on a phone and outlined when it has focus.
Testers rarely write CSS, but they need to read it. When a label is cut off, elements overlap or a page scrolls sideways on a phone, the cause is usually a CSS rule, and knowing the basics lets you find it and describe the defect precisely.
Why it matters
Visual defects are what users notice first. A price wrapped under its button or a field hidden behind a sticky header makes a product look broken even when every function works, and some are accessibility failures: removing the focus outline without a replacement fails WCAG 2.2 success criterion 2.4.7 Focus Visible.
CSS knowledge turns "the layout is broken on mobile" into a report a developer can act on: the element, the viewport width, the computed sizes and the rule involved. It also tells you what else to retest, because one rule can style every page that uses the same class.
Rules, selectors and the cascade
A stylesheet is a list of rules. Each rule has a selector, which chooses elements, and a declaration block in braces with property: value pairs. In #checkout .btn:hover { background-color: #0a58ca; } the selector picks elements with the class btn inside the element with the id checkout, while the pointer is over them. CSS comes from linked stylesheets, <style> elements and the style attribute, on top of the browser's own default styles.
When several rules set the same property on one element, the cascade picks one. Simplified: !important wins; then a style attribute beats stylesheet rules; then the more specific selector wins; at equal specificity, the later rule wins. Specificity counts ids, then classes, attributes and pseudo-classes, then element types and pseudo-elements, so #checkout .btn is 1,1,0 and one id outweighs any number of classes. Some properties, such as color and font-family, are also inherited by child elements.
button(type: every<button>),.btn-primary(class: every element that hasbtn-primaryamong the classes in itsclassattribute) and#email(id: the one element withid="email").[type="email"]and[data-testid="pay-now"](attribute);[href^="https"]matches values that start withhttps.- Combinators join selectors: a space means "anywhere inside" (
form .error), and>means "a direct child of" (ul > li). :hover,:focus-visible,:disabled,:checkedand:invalidare pseudo-classes for states; each state has its own styles and needs its own check.::beforeand::placeholderare pseudo-elements: they style parts that are not elements in the HTML.
/* Three rules set one property on the same button */
.btn { background-color: #0b5ed7; } /* 0,1,0 */
#checkout .btn { background-color: #6c757d; } /* 1,1,0: wins, it has an id */
.btn { background-color: #198754; } /* 0,1,0: later, still loses */The box model
Every element is drawn as a box with four layers, from the inside out: the content; the padding, which shows the background and still counts as the element when clicked; the border; and the margin, transparent space that keeps neighbours apart.
width: 300px sets only the content, so 16px padding and a 2px border on each side make the box 336px wide.- 1MarginTransparent space that keeps neighbours apart
Never part of
width- 2BorderThe line drawn around the padding
Adds
2 × 2 = 4pxwithcontent-box- 3PaddingShows the background and counts as the element when clicked
Adds
2 × 16 = 32pxwithcontent-box- 4ContentWhat
widthsets with the defaultcontent-boxWith
border-boxthe box stays300pxand the content shrinks to264px
Example values from this section: width: 300px, padding: 16px and a 2px border.
box-sizing decides what width measures. With the default content-box it sets the content only, so padding and border are added: width: 300px with padding: 16px and a 2px border gives a box 336 pixels wide. With border-box the box stays 300 pixels and the content shrinks to 264. Margins are never part of width. Many sites set border-box on every element; a component that misses it produces boxes slightly too wide.
Hiding an element is also a question of its box. Three declarations all make an element invisible, but they behave differently:
display: noneremoves the element: no space, no clicks, hidden from screen readers.visibility: hiddenkeeps its space but hides it from sight, clicks and screen readers.opacity: 0only makes it transparent: it still takes clicks and is still announced, so an invisible layer over a button can block it.
Layout, media queries and the defects they cause
Block elements such as <div> stack vertically; inline elements such as <a> flow along a line. Flexbox (display: flex) arranges children in one row or column and controls how they grow, shrink, wrap and align; grid (display: grid) places them in rows and columns at once, so grid-template-columns: repeat(3, 1fr) makes three equal columns. Both follow the writing direction, so a flex row starts on the right in an Arabic page. position: absolute and fixed take elements out of the flow, sticky keeps one in view while the page scrolls, and z-index decides which overlapping element is on top.
A media query applies rules only when a condition is true. @media (max-width: 600px) { … } applies when the viewport, the area of the browser window that shows the page, is 600 CSS pixels wide or less, so 600 gets the phone layout and 601 does not. CSS pixels are not device pixels; a high-density phone uses several device pixels for each. On phones this works only when the page has the viewport <meta> element from the page skeleton in the HTML lesson; without it, the phone lays the page out as if on a wide screen and shrinks it. Each breakpoint is a boundary to test on both sides. Queries also react to prefers-color-scheme: dark, prefers-reduced-motion and printing.
Most visual defects trace back to these properties or to the box model:
- Clipped or overflowing text: a fixed
heightorwidth,overflow: hidden,white-space: nowrap; long names, translations and enlarged text expose it. - Overlap: absolute or fixed positioning, negative margins, a wrong
z-index. A sticky header that completely hides the focused field also fails WCAG 2.2 criterion 2.4.11 Focus Not Obscured (Minimum). - Sideways scrolling on phones: fixed widths, images without
max-width: 100%, flex rows that never wrap (flex-wrapdefaults tonowrap), box-sizing arithmetic. - Misalignment: similar elements with different margins, padding or alignment settings.
- Unreadable text: a colour too close to the background, often only in dark mode or on hover.
- No visible focus:
outline: nonewith no replacement style. - Mirroring defects in right-to-left pages: physical properties such as
margin-leftdo not flip, while flex, grid and logical properties such asmargin-inline-startdo.
Try it: Lorem Ipsum Generator Try it: Multilingual Test Data Generator
Worked example: a card that scrolls sideways
A teammate reports that the checkout page scrolls sideways on a phone. You open the browser's developer tools with F12 and switch on device mode (Ctrl+Shift+M in Chrome, Edge and Firefox), which sets the viewport to any width. At 360 pixels the promotion card sticks out past the edge of the screen; at desktop widths it looks fine. The CSS is below, and the card's parent is as wide as the viewport.
Right-click the card and choose Inspect. The Styles pane lists the rules that apply to it, with overridden declarations crossed out: the media query is active, so width: 100% applies. The box-model diagram in the Computed pane shows 16 pixels of padding and a 2-pixel border on each side. With the default content-box, 360 + 2 × 16 + 2 × 2 = 396 pixels: 36 pixels wider than the screen. The same 36 pixels are added at every width up to 480; from 481 the card is 320 + 36 = 356 pixels wide and fits.
The bug report states facts: page, element (.promo-card), browser, viewport (every width up to 480 pixels; 481 is fine), expected (the card fits the screen) and actual (a 396-pixel box and a horizontal scrollbar at 360), with a screenshot and the computed values. Mention box-sizing as the likely cause; the developer chooses the fix. Then retest at 480 and 481 pixels, in English and Arabic, and on the other pages that use .promo-card.
.promo-card {
width: 320px;
padding: 16px;
border: 2px solid #d0d7de;
}
@media (max-width: 480px) {
.promo-card { width: 100%; }
}
/* Viewport 360px, box-sizing: content-box (the default):
360 + 2 × 16 + 2 × 2 = 396px wide, 36px wider than the screen */Try it: Lorem Ipsum Generator Try it: Multilingual Test Data Generator
CSS selectors as locators: a preview
An automated test must find an element before it can click or check it, and a CSS selector is one way to describe it: Selenium's By.cssSelector, Cypress's cy.get and Playwright's page.locator all accept one. In the Console tab of the developer tools, document.querySelectorAll('[data-testid="pay-now"]').length counts the matches; a locator should match exactly one element.
A good styling selector is not always a good locator. Styling classes change with a redesign, long paths break when a wrapper is added, and generated names such as css-1q2w3e change between builds. Prefer short selectors on attributes meant for identification: a stable id, a field's name, or a test attribute such as data-testid. Playwright's documentation recommends user-facing locators (role, label, text) and test ids, and advises against CSS and XPath because they break when the page structure changes. In Selenium and Cypress, a test id is itself normally found with a CSS attribute selector.
- Brittle:
div.col-md-6 > div:nth-child(3) > button.btn-primary. - Better:
[data-testid="pay-now"],#emailorinput[name="email"].
Common mistakes
- Reporting "the layout is broken" without the element, viewport width, browser, language and theme; many visual defects appear in only one of them.
- Testing one desktop and one phone width, and never the widths on each side of a breakpoint.
- Checking only the default state: hover, focus, disabled, error and selected states have their own CSS and their own defects.
- Treating an invisible element as gone:
opacity: 0hides nothing from clicks or screen readers. - Pasting the long selector from the browser's "Copy selector" menu into a test; it encodes the page structure and breaks with the next layout change.
The QA perspective
In refinement and design reviews, ask what happens at each breakpoint, with long or translated text, in right-to-left layouts and in dark mode, and whether hover, focus and error states are designed. While testing, keep a short visual checklist per screen and read computed values in the developer tools instead of judging by eye; colours appear there as rgb() values such as rgb(11, 94, 215), while designs usually give hex values such as #0b5ed7, the same blue, so convert one into the other before comparing.
In bug reports, give the element, viewport size, browser, language and theme, the wrong computed values and a screenshot with the problem marked; offer the rule you suspect only as a hint. When developers add stable ids or test attributes, agree on a naming convention early: it is cheaper than repairing locators after every redesign.
Practice exercise
Read a stylesheet like a tester
The styles and markup below belong to a checkout page. On a phone, the viewport and main are both 360 pixels wide. Answer the four steps without running the page, and give the reason for each answer.
<style>
.card { width: 100%; padding: 12px; border: 1px solid #d0d7de; }
.btn { padding: 10px 16px; background-color: #0b5ed7; color: #ffffff; }
#checkout .btn { background-color: #6c757d; }
.btn:focus { outline: none; }
@media (max-width: 480px) {
.card { padding: 24px; }
}
</style>
<main id="checkout">
<div class="card">
<button class="btn" data-testid="pay-now">Pay now</button>
</div>
</main>- Which background colour does the button show, and why?
- How wide is the card's box on the phone, and does it fit?
- Which declaration removes something users need, and which test reveals it?
- Which CSS selector would you use to locate the button in an automated test?
Show the model answer
Grey, #6c757d: #checkout .btn (1,1,0) is more specific than .btn (0,1,0), and order only decides between equal specificities. If the design says blue, report it with the Styles view, where the losing declaration is shown crossed out.
The media query applies (360 is at most 480), so the padding is 24 pixels: 360 + 2 × 24 + 2 × 1 = 410 pixels, 50 more than main, so the page scrolls sideways. Test 480 and 481 pixels, where the padding changes; at wider sizes the card still overflows main by 26 pixels.
.btn:focus { outline: none; }. Press Tab until the button has focus: if you cannot see where focus is, the page fails WCAG 2.2 success criterion 2.4.7 Focus Visible.
[data-testid="pay-now"]: it exists for tests, matches one element and survives a redesign. .btn matches every button with that style, and main > div > button breaks when the structure changes.
Frequently asked questions
Do testers need to write CSS?
Not usually. You need to read it: recognise selectors and properties, find which rule applies in the developer tools, and do the box-model arithmetic. Editing a value live in the developer tools is a quick way to confirm a suspected cause, but the change disappears on reload and the fix belongs in the code.
What is the difference between a CSS selector and an XPath expression?
Both find elements. CSS selectors are shorter for ids, classes and attributes, and they are what stylesheets use. XPath can also match by text and move from a child to its parent; CSS cannot match text, and its :has() pseudo-class covers only some parent cases. Selenium and Playwright accept both; Cypress's cy.get takes CSS selectors.
Is a visual defect always low severity?
No. A slightly misaligned icon may be minor, but a clipped price, a payment button pushed off the screen or a missing focus indicator can stop a purchase or lock out keyboard users. Judge severity by the impact on the user, as for any defect.
References
- CSS: Cascading Style Sheets — MDNMDN's CSS reference and guides: selectors, the cascade, properties and the layout systems that decide how a page looks and adapts.
- Responsive web design — MDNResponsive design in practice: flexible layouts with flexbox and grid, media queries and the viewport meta tag, so one page works across screen sizes.
- Web Content Accessibility Guidelines (WCAG) 2.2The W3C Recommendation that defines the testable success criteria for accessible web content, organised under four principles and three conformance levels (A, AA, AAA).
- Locators — PlaywrightHow Playwright finds elements, why locators re-resolve and auto-wait, and why role-based locators are recommended first.
Practise with Mutqan tools
Beautify CSS with one declaration per line, indentation and sorted output options.
Convert colors between HEX, RGB(A), HSL(A), HSB/HSV, CMYK and CSS named colors with a live preview.
Beautify HTML with proper indentation of nested elements, preserving pre, script and style blocks.
Generate placeholder text as paragraphs, sentences or words — Latin, Arabic or Hindi.
Generate the same records in multiple languages (English, Arabic, Hindi, and more) to test i18n and RTL/LTR mixing.
Go deeper
Your progress on this topic
Not started
Mark the topic complete when you have studied it and done the exercise.
Sign in to track your progress
Mark topics complete, continue where you left off and unlock specialisation exams.