Browser DevTools: Debug HTML, CSS, and Accessibility
Introduction
This is the final lesson of chapter 4, aligned with course 4 of the Meta Front-End Developer pathway ("HTML and CSS in Depth"). You have learned forms, semantics, layout, states and motion. Real work now looks like this: a page exists, something is wrong, and you must find out why quickly and prove the fix. This lesson teaches HTML and CSS debugging with browser DevTools, then shows how to review a whole page against an objective acceptance checklist instead of "it looks fine to me."
The principle running through the lesson: test observable behavior, not visual guesses. "The button looks centered" is a guess. "The button's bounding box is centered within a 360 px viewport, its accessible name is 'Book seats', and Tab reaches it" is evidence. The practice scenario is original: you audit the Harbor Workshops landing page against a concrete checklist.
Learning objectives
- Use the Elements/Inspector panel to read the DOM, computed styles, the box model, and the cascade.
- Diagnose layout bugs: overflow, collapsed heights, unexpected stacking, and specificity conflicts.
- Use responsive/device mode and throttling to test small screens and slow networks.
- Read the Network panel: status codes, sizes, caching, and failed requests.
- Use the Console and Sources panels for errors and basic breakpoints.
- Check metadata, media, and form validation systematically.
- Run accessibility checks: keyboard, accessibility tree, contrast, automated tools.
- Write an acceptance checklist whose items are objectively verifiable.
1. A debugging method
Random edits waste time. Use a loop:
- Reproduce. Write down exact steps, browser, viewport size, and expected versus actual result.
- Isolate. Find the smallest piece of HTML/CSS that shows the bug. Disable rules in DevTools one at a time.
- Hypothesize. State a cause that makes a prediction ("the card is wider than 360 px because of a fixed
width: 400px"). - Test. Change one thing in DevTools and see whether the prediction holds.
- Fix in the source, not only in DevTools. Temporary DevTools edits usually vanish on reload; features such as Local Overrides or Workspaces can persist edits, so check whether they are enabled.
- Verify and guard. Re-run the steps, check other sizes and browsers, and add a note or test if the bug could return.
2. Inspecting the DOM and CSS
Open DevTools with F12, Ctrl+Shift+I (Cmd+Option+I on macOS), or right-click then Inspect. Names differ slightly (Chrome and Edge: Elements; Firefox: Inspector), but the capabilities match.
Elements and Styles
- Select an element to inspect matching rules. Crossed-out declarations may be overridden; invalid declarations have warning details, and inactive properties can have separate explanations. Check origin, importance, layers, specificity, scope, and order, not just the selector count.
- Tick or untick checkboxes to disable declarations and watch the effect.
- Add a temporary declaration with the
+icon, or edit a value with the arrow keys. - The Computed tab shows the final value of each property and which rule supplied it. Use it to resolve CSS cascade priorities and accessible focus states and answer which rule won.
The box model diagram
The Styles/Layout pane draws content, padding, border, and margin with numbers. Check it when spacing is off: is the gap from padding, margin, or a gap property? Hover over elements in the DOM tree to highlight them on the page and reveal sizes.
Flexbox and Grid overlays
Elements that use display: flex or grid show a badge in the DOM tree. Click it to overlay track lines, gaps, and item sizes. This quickly shows why a grid has an extra empty column or why a flex item refuses to shrink.
Finding the element that overflows
When a page scrolls sideways, test in the Console:
// Find candidate boxes outside either edge; allow 1px rounding tolerance.
const viewportWidth = document.documentElement.clientWidth;
[...document.querySelectorAll('*')]
.filter(el => {
const rect = el.getBoundingClientRect();
return rect.right > viewportWidth + 1 || rect.left < -1;
})
.map(el => ({ element: el, rect: el.getBoundingClientRect() }));This is a heuristic: transformed or intentionally off-canvas elements may be listed without causing document scroll, while pseudo-elements are not queried. Confirm the document's scroll width and inspect each candidate.
Or temporarily outline everything in the Styles pane with * { outline: 1px solid red; } and look for the box that sticks out. Then fix the cause (a fixed width, an image without max-width, a long word in a flex item) rather than adding overflow-x: hidden.
Common layout symptoms and causes
| Symptom | Likely cause | Check |
|---|---|---|
| Horizontal scrollbar | Element wider than viewport | Outline all, check fixed widths and images |
| Container collapsed to zero height | Children are floated or absolutely positioned | Computed height, layout type |
| Rule has no effect | Overridden by specificity, typo, or invalid value | Struck-through declaration, warning icon |
| Element hidden behind another | z-index without positioning or in another stacking context |
Computed position and z-index, parents |
| Unexpected space above heading | Margin collapse | Box model of parent and child |
| Text clipped | Fixed height with overflow: hidden |
Replace with min-height |
| Flex item will not shrink | Automatic minimum size | Add min-width: 0 |
| Mobile layout looks like tiny desktop | Missing viewport meta tag | <head> source |
3. Responsive and compatibility checks
Device mode
Toggle the device toolbar (Ctrl+Shift+M) to resize to presets or custom widths, change the device pixel ratio, and simulate touch. Check at least 320, 375, 768, 1024, and 1440 px, plus every width where your breakpoints change (test just below and just above, such as 767 and 768). Apply the responsive layout and Bootstrap foundations and drag across the whole range: bugs often hide between standard sizes.
Also test:
- Text resizing to 200%: check for loss of content or functionality. Separately check reflow at 320 CSS pixels for horizontal writing, commonly obtained with 400% page zoom from a 1280 px starting viewport. Content requiring two-dimensional layout has exceptions. These are distinct WCAG checks; device emulation alone does not establish either.
- Landscape orientation on phones.
- Print preview when the page might be printed.
- Dark mode, reduced motion, and forced colors (DevTools "Rendering" panel emulates media features such as
prefers-reduced-motionandprefers-color-scheme).
Cross-browser checks
Emulation is not the real thing. Before release, open the page in current Chrome/Edge, Firefox, and Safari, and on at least one real phone. When something differs, look up the property on MDN: its compatibility table shows support per browser and the Baseline label (widely available, newly available, or limited availability). If support is limited, use a fallback or @supports.
4. Network and performance
The Network panel lists each request: URL, status code, type, size, time, and caching.
| Status | Meaning | What to do |
|---|---|---|
| 200 | OK | Check size and time |
| 301 / 302 | Redirect | Avoid chains; update links |
| 304 | Not modified (cached) | Healthy caching |
| 404 | Not found | Fix the path; check case-sensitivity on Linux servers |
| 500 | Server error | Look at server logs |
Look for:
- Broken paths. A missing image (404) or stylesheet explains a page that "lost its styles" in production only. Relative paths like
images/a.jpgand/images/a.jpgresolve differently. - Large files. A 4 MB hero photo should be resized and compressed, served with
srcset, and use a modern format. - Blocking resources. Ordinary linked stylesheets in
<head>are normally render-blocking. An external classic script withdeferdownloads without blocking parsing and executes after parsing, beforeDOMContentLoaded; its execution still uses the main thread. Module scripts defer by default. Do not adddeferto inline classic scripts and expect it to work. - Throttling. Select a "Slow 4G" profile to feel what a mobile visitor sees; check that content appears progressively and layout does not jump.
- Cache disabled while DevTools is open: tick Disable cache to avoid being fooled by stale files, and remember that real users do cache.
Lighthouse (in DevTools or via the command line) produces a report for performance, accessibility, best practices, and SEO. Treat it as a starting point: the score does not replace manual testing, but its list of audits (image sizes, contrast, missing alt text, missing meta description) is a free checklist.
Web performance metrics worth knowing: Largest Contentful Paint (when the largest eligible image or text block is rendered), Cumulative Layout Shift (how much content moves unexpectedly), and Interaction to Next Paint (responsiveness to input). Dimensions help reduce layout shifts; image loading and fonts can affect LCP. INP also depends on event handlers and main-thread work. Lighthouse provides laboratory diagnostics, not proof of real-user Core Web Vitals: assess field measurements at the 75th percentile for mobile and desktop separately.
5. Console and Sources
The Console shows errors, warnings, and your own console.log output. Red errors such as Uncaught TypeError: Cannot read properties of null or Failed to load resource: 404 tell you where to look. Warnings from the browser about forms (for example "Password field is not contained in a form") or mixed content (http resources on an https page) are also actionable.
In Sources (Debugger in Firefox) you can set a breakpoint on a line, reload, and step through code, inspecting variables. For event-driven bugs, Event Listener Breakpoints pause on click or submit. You will use this much more in the JavaScript and React chapters, but even a static page benefits when a form submit handler misbehaves.
6. Page review: metadata, media, forms
A page is more than what you see. Review these layers:
Metadata
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Pottery for Beginners | Harbor Workshops</title>
<meta name="description" content="A relaxed Saturday pottery class: shape a bowl, glaze it, and take it home. Ages 16 and over.">
<link rel="canonical" href="https://example.org/workshops/pottery">
<meta property="og:title" content="Pottery for Beginners">
<meta property="og:description" content="Shape a bowl and take it home.">
<meta property="og:image" content="https://example.org/img/pottery-og.jpg">
<meta name="viewport" content="width=device-width, initial-scale=1">
</head>
<body>
<!-- Page content -->
</body>
</html>Check: a unique, descriptive <title> on every page; a meta description; lang on <html>; a canonical URL when the same page can be reached several ways; Open Graph tags if the page is shared; a favicon. Validate by viewing source, not just the rendered page.
Media
- Every
<img>hasalt(descriptive or empty if decorative),widthandheight. - Sizes are appropriate; below-the-fold images use
loading="lazy"; the main hero does not. - Prerecorded synchronized video needs appropriate captions; a transcript alone does not replace them. Provide other alternatives as needed, such as descriptions of visual information. Avoid autoplay; muting alone does not address distracting motion.
- Text is not baked into images.
Forms
- Data-entry controls have associated visible labels, submission names, appropriate types, and applicable autocomplete tokens. Buttons can be named by their text; hidden inputs do not need visible labels.
- Submit with empty, invalid, and valid values and compare with the expected message.
- Error text is visible and associated with its field; verify announcements and focus behavior. A live region is not a replacement for persistent field associations.
- Focus order follows meaningful reading order. Tab visits enabled controls; arrow keys move within radio groups. Enter in a single-line field may submit, while Enter in a textarea inserts a line break.
Accessibility
- Keyboard only: unplug the mouse. Every operable control must be keyboard accessible, with a visible focus indicator; disabled controls are normally skipped, and there must be no keyboard trap. A "skip to content" link helps on long headers.
- Accessibility tree: the Accessibility pane shows the name, role, and state that assistive technology receives. A button with an empty name (an icon-only button without text or
aria-label) is a defect. - Contrast: DevTools shows the contrast ratio in the color picker. WCAG AA requires 4.5:1 for normal text and 3:1 for large text (at least 18 pt regular or 14 pt bold). Relevant authored component/state visuals need 3:1 non-text contrast, with exceptions; not every decorative border must meet that threshold.
- Headings: a clear page heading and logical nested levels. One
h1is a useful convention, not a complete accessibility test; check landmark names and structure. - Automated tools (Lighthouse, axe DevTools, WAVE) detect some accessibility failures; there is no universal percentage that applies to every page. They cannot judge whether alt text is meaningful or the reading order makes sense, so combine them with manual checks and, when possible, a screen reader pass (NVDA, VoiceOver).
7. Test observable behavior
Visual inspection is subjective and unrepeatable. Replace guesses with checks that anybody can repeat and that could later be automated:
| Guess | Observable check |
|---|---|
| "It's responsive" | document.documentElement.scrollWidth <= document.documentElement.clientWidth + 1 at 320, 375, 768, 1024 |
| "The form works" | Submitting with an empty email shows the text "Enter your email address" and focus moves to the field |
| "The reviewed accessibility checks pass" | Operable controls work by keyboard; reviewed contrasts pass; text alternatives fit their purpose |
| "It loads fast" | LCP under the target on throttled Slow 4G; no image over 200 KB without a reason |
| "Looks right in Safari" | Screenshot comparison or manual check at the same sizes |
In the Console you can run quick assertions:
// 1. No horizontal scroll
document.documentElement.scrollWidth <= window.innerWidth; // expect true
// 2. Images missing alt
[...document.images].filter(i => !i.hasAttribute('alt')).length; // expect 0
// 3. Accessible names require inspection of the accessibility tree.
// A labels/aria-label count misses aria-labelledby, button text,
// input-button values, and whether referenced text is meaningful.These snippets identify candidates; they do not prove accessibility. An empty but inappropriate alt can pass a presence check. Responsive layout and accessible names require contextual review.
In the JavaScript chapters you will turn such checks into automated tests with Jest, and in the React chapters into component tests with React Testing Library, which query the page the way a user (and assistive technology) does: by role, label, and text.
8. Writing an acceptance checklist
An acceptance criterion is specific, observable, and has a clear pass or fail. Compare:
- Weak: "The page works on mobile."
- Strong: "At 320 px wide there is no horizontal scrollbar, the Book button is at least 44 by 44 CSS pixels, and all text is readable without zooming."
The 44 by 44 target above is a project usability goal. WCAG 2.2 Target Size (Minimum), Level AA, uses 24 by 24 CSS pixels or its spacing/other exceptions; the enhanced 44 by 44 criterion is Level AAA. Do not label the stricter project goal as the universal AA threshold.
A good checklist groups items (layout, content, forms, accessibility, performance, metadata), records the browser and viewport, and ends with who tested and the date. Items should be independent so that one failure does not hide others.
Worked example: audit the Harbor Workshops landing page
Brief. The landing page is going live tomorrow. Audit it against the checklist and report defects with evidence.
Checklist (excerpt):
- No horizontal scroll at 320, 375, 768, 1440 px.
- Title and meta description present and unique.
- All images have
alt,width,height. - Booking form: labels, native validation, error text.
- Keyboard: every control reachable, focus visible.
- Contrast of body text at least 4.5:1.
- No failed requests in Network.
Step 1: layout at 320 px. In device mode, set 320 px. A horizontal scrollbar appears. Run the console snippet: it returns DIV.hero-banner. In Styles: .hero-banner { box-sizing: content-box; width: 100vw; padding: 0 2rem; }. The content width plus padding exceeds the viewport (and 100vw includes the scrollbar on desktop). Hypothesis: change to width: 100% with box-sizing: border-box (or remove the width, since block elements fill their container). Test in DevTools: scrollbar gone. Fix in the source.
Step 2: metadata. View source: the <title> is "Document" and no description exists. Defect. Fix with the title and description above.
Step 3: images. The console count of images without alt returns 2. Inspect: the logo and a decorative wave. The logo needs alt="Harbor Workshops"; the decorative wave should have alt="". Network shows hero.jpg at 3.8 MB. Defect: resize, compress, and add srcset.
Step 4: form. Submit empty, then invalid, then valid values. The browser's native message appears, but an existing custom error paragraph remains unassociated and stale after correction. Link it with aria-describedby, set aria-invalid after failure, and clear both error text and state after correction. If switching to custom submission handling, check every eligible field and preserve native validation as a fallback. Tab order is correct.
Step 5: keyboard. Tab from the top: the first focus ring is invisible on the nav links. Inspect: a:focus { outline: none; } in reset.css. Defect: replace with a:focus-visible { outline: 3px solid #0b5cad; outline-offset: 2px; } on this white background, then verify contrast and that it is not clipped or obscured.
Step 6: contrast. The date text #9aa5b1 on white is below 3:1. Defect: darken to #4b5560 (above 7:1).
Step 7: network. The page's requested assets pass, but Open Graph image URLs require a separate check: a browser does not necessarily fetch og:image during page load. Opening /img/pottery-og.jpg returns 404; the file is Pottery-OG.jpg on this case-sensitive server. Fix the URL and verify the actual image response, not just a 200 response that could be an HTML fallback.
Report. Seven review areas produced several defects, each with element, evidence, expected result, and fix. Re-run the full checklist after fixes. Why this method? Each finding is reproducible and verifiable by someone else. A casual desktop glance cannot reliably establish keyboard behavior, contrast, metadata, or resource correctness.
Common pitfalls
- Editing in DevTools and forgetting to change the source.
- Testing only one browser or only on a fast desktop connection.
- Trusting emulation without a real device check.
- Hiding overflow instead of finding the cause.
- Treating a Lighthouse score as proof of accessibility.
- Cached files hiding a fix or a bug; test with cache disabled and in a private window.
- Case-sensitive file names that work on Windows or macOS but 404 on Linux servers.
- Checking only widths of popular devices and missing bugs between them.
- Vague acceptance criteria ("looks good") that cannot fail.
- Fixing several things at once so you cannot tell which change worked.
Study tip: When asked how to find why a style is not applied, choose inspecting the element and reading the Styles/Computed panes (struck-through rules, specificity) over rewriting the CSS. When asked how to verify responsiveness, choose resizing across a range and checking for horizontal scroll rather than testing a single "mobile" size. When asked to verify accessibility, a correct answer includes keyboard testing, not only an automated score.
Mini case study: it works on my machine
Situation. After deployment, the Harbor Workshops page has no images and no styling on the production site, though it looked perfect locally. The Network panel on production shows styles/Main.css and several images with status 404. The project folder contains styles/main.css and images/hero.jpg. The HTML contains <link rel="stylesheet" href="styles/Main.css"> and <img src="/Images/hero.jpg">. The developer works on Windows; the server runs Linux.
Questions. (1) What is the cause? (2) How do you verify it? (3) What prevents it from returning?
Solution.
- File names on the Linux server are case-sensitive (
Main.css≠main.css,Images≠images), while the developer's Windows directory is configured to ignore case. Case sensitivity depends on filesystem and directory configuration. The paths in the HTML do not match the real names. - The Network panel shows the requested URL and the 404 status. Request the correctly-cased URL directly in the browser and confirm it returns 200. After fixing the HTML, reload with cache disabled and confirm required requests succeed with the expected content. Valid cached responses may be 304, and some successful endpoints use other 2xx statuses.
- Adopt a lowercase naming convention for files, add a check (a link checker or a simple script run in CI) that fails on missing files, and test on an environment that matches production before release.
Key terms
- DevTools: the browser's built-in developer tools.
- Computed style: the final value of a property after the cascade.
- Device mode: DevTools emulation of screen sizes, pixel ratio, and touch.
- Throttling: simulating a slower network or CPU.
- Lighthouse: an auditing tool for performance, accessibility, best practices, and SEO.
- Accessibility tree: the representation of the page given to assistive technology.
- Contrast ratio: the luminance ratio between text and its background.
- Layout shift: unexpected movement of page content while loading.
- Acceptance criterion: an observable, pass/fail condition for a feature.
- Baseline: a label summarizing how widely a web feature is supported across major browsers.
Summary checklist
- I follow a loop: reproduce, isolate, hypothesize, test, fix in source, verify.
- I read the Styles and Computed panes to find which rule wins.
- I use device mode across a range of widths, including the width at each breakpoint.
- I find the element causing overflow rather than hiding it.
- I read status codes and sizes in the Network panel and check case-sensitive paths.
- I check title, description,
lang, canonical, Open Graph, and favicon in the source. - I verify alt text, image dimensions, and appropriate image sizes.
- I test forms with empty, invalid, and valid input, and with the keyboard alone.
- I check contrast and the accessibility tree and know automated tools are not enough.
- I write acceptance criteria that are specific and observable.
Official sources
- Meta Front-End Developer Professional Certificate (opens in a new tab)
- Course 4: HTML and CSS in Depth (opens in a new tab)
- Chrome DevTools documentation (opens in a new tab)
- Firefox Source Docs: DevTools user docs (opens in a new tab)
- MDN: Debugging HTML (opens in a new tab)
- MDN: Cross browser testing (opens in a new tab)
- W3C WAI: Easy checks, a first review of web accessibility (opens in a new tab)
- web.dev: Core Web Vitals (opens in a new tab)
- W3C: Resize Text (AA) (opens in a new tab)
- W3C: Reflow (AA) (opens in a new tab)
- W3C: Target Size (Minimum, AA) (opens in a new tab)
