Opening the developer tools

Every desktop browser has developer tools (often called DevTools) built in. They let you look inside the page you are viewing: its HTML, its CSS, its network requests and its JavaScript. This page is about the HTML and CSS side; for the console, breakpoints and the network panel, see debugging JavaScript.

To Windows and Linux macOS
Open or close the developer tools F12 or Ctrl+Shift+I Cmd+Option+I
Pick an element on the page Ctrl+Shift+C Cmd+Shift+C
Open with one element selected Right-click the element, then Inspect

Chrome, Edge and other Chromium-based browsers share the same tools (the HTML panel is called Elements). Firefox's tools look different but offer the same ideas (its HTML panel is the Inspector). Safari hides its Web Inspector until you enable it in Safari's settings, under Advanced ("Show features for web developers" in recent versions). The names below follow Chrome, with Firefox's names where they differ.

Everything you change in the developer tools is temporary: it affects only your own browser tab and disappears when you reload. That makes them a safe place to experiment, even on a live site. It also means a fix you found there still has to go into the real files.

The element inspector

The element inspector (the Elements panel in Chrome, the Inspector in Firefox) shows the page's HTML as a tree you can expand and collapse. Hover over a line and the matching element lights up on the page; click it to select it and see its styles. You can also edit: double-click a tag, an attribute or a piece of text to change it, drag elements to another place, or press Delete to remove one and see what the page looks like without it.

The DOM, not your source file

The inspector does not show the file you wrote. It shows the Document Object Model (DOM): the tree of elements the browser built from your HTML, after any changes by JavaScript. Usually the two look alike. When they do not, that difference is a clue. The HTML standard defines exactly how browsers repair broken markup, and they do it silently. In this example, a <div> was placed inside a paragraph, which HTML does not allow:

<p class="meter">
  Meter KE-1042
  <div class="badge">offline</div>
</p>
p {
  border: 2px dashed #6d28d9;
  padding: 0.5rem;
}

.badge {
  display: inline-block;
  padding: 0.1rem 0.5rem;
  background: #fee2e2;
  color: #991b1b;
}

Instead of one dashed box with the badge inside, the result shows a dashed box with only the meter number, the badge below it, and a second, empty dashed box. The parser closed the paragraph as soon as it met the <div> (a paragraph cannot contain one), and then turned the stray </p> into a new, empty paragraph (without the class, which is why the CSS uses the selector p: so you can see it). Open the developer tools on this page and inspect the result frame: the DOM reads <p>…</p><div>…</div><p></p>. The fix is in the HTML, not the CSS: use a <span> for the badge (see text and semantic elements for which elements may go where).

Forcing hover and focus

A style for :hover or :focus-visible is hard to inspect: the moment you move the mouse to the developer tools, the hover is gone. The :hov button in the Styles pane (a :hov toggle in Firefox's Rules pane too) lets you switch on :hover, :focus, :focus-visible, :active and other states for the selected element, so they stay on while you work.

Reading the Styles pane: the cascade made visible

Next to the element tree, the Styles pane (Firefox: Rules) lists every CSS rule whose selector matches the selected element. It is the cascade laid out for you: the rule that wins is at the top, followed by rules with lower priority, then the browser's own defaults (marked "user agent stylesheet"), and finally sections "Inherited from" the parent elements. Each rule shows its file and line number; click it to jump to the source.

Here is a typical help request for Kestrel Energy's portal team: "The Send button on the meter reading form is gray instead of blue, and its text sticks to the edges." Kwame Asante opens the developer tools on the button. Try the same on the result below:

<form id="reading-form">
  <label for="kwh">Reading (kWh)</label>
  <input id="kwh" type="number">
  <button type="submit" class="btn-primary">Send</button>
</form>
#reading-form button {
  background: #6b7280;
  color: white;
  border: 0;
}

.btn-primary {
  background: #1d4ed8;
  padding: 0.5rem 1rem 0.5rem 1 rem;
  justify-content: center;
}

The Styles pane for the button shows three different problems:

  • Crossed out: background: #1d4ed8 in .btn-primary has a line through it. Another rule won: #reading-form button contains an ID, so its specificity is higher, and it is listed above .btn-primary. Fix: lower the specificity of the first rule (for example to form button, two type selectors, which loses to one class), not an !important.
  • Crossed out with a warning icon: the padding line. 1 rem with a space is not a valid length, so the browser throws away the whole declaration, including the three values that were fine. That is why the text sits tight against the button's edges. Hover the icon and the tools say "Invalid property value". The same icon appears for a misspelled property name such as backgroud.
  • Dimmed, with an info icon: justify-content is valid, but has no effect, because the button is not a flex or grid container. Chrome and Firefox both mark such inactive declarations and explain why when you hover over them.

You can test fixes on the spot: click a value to edit it (the ↑ and ↓ keys change numbers step by step), untick the checkbox next to a declaration to switch it off, or click inside a rule to add a new declaration. The Changes panel (in both Chrome and Firefox) lists everything you edited, so you can copy the working version back into your stylesheet.

Computed styles

The Styles pane shows the competition; the Computed tab shows the result. A computed style is the final value of every property for the selected element, after the cascade, inheritance and conversions: 2em becomes a number of pixels, bold becomes 700, a color in any notation becomes one form. Strictly, the developer tools show the resolved values, which for sizes such as width are the actual pixels used in the layout.

The Computed tab is the place to go when you ask:

  • "What is the font size really?" Find font-size (type in the filter box) and read the pixel value.
  • "Where does this value come from?" Expand the property: the tools list the rules that set it, with the winning one first and a link to its file and line. For an inherited property such as color, that may be a rule on a parent element far up the tree.
  • "Is this the browser's default?" Tick "Show all" to include properties you never set; their values come from the defaults and from inheritance.

At the top of the Computed tab (in Firefox: the Layout tab), a diagram shows the element's box model with its real numbers:

The box model diagram from the developer tools, as four nested rectangles. The outer one is labeled margin, with 16 above and below and 0 left and right. Inside it the border, 2 on all sides. Inside that the padding, 8 above and below and 16 left and right. In the center the content box, 240 by 19. margin 16 16 0 0 border 2 2 2 2 padding 8 8 16 16 240 × 19
The box model diagram for a paragraph: content 240 by 19 pixels, then padding, border and margin. Hover a layer in the real tools and the same layer lights up on the page.

Reading it answers most spacing questions at once. "Why is there a gap above this heading?" Look at its margin, then at the margin of the element before it; if both have one, they may have collapsed into a single gap (see the box model). You can also double-click a number in the diagram to try another value.

Layout overlays

Flexbox and grid layouts are hard to debug by looking at numbers, because the lines and tracks they create are invisible. A layout overlay draws them on top of the page. In Chrome's Elements panel, a flex container has a small flex badge and a grid container a grid badge next to its tag; click the badge to show the overlay. Firefox has the same badges and a Layout tab with Flexbox and Grid sections. Inspect the result below and click the badges:

<nav class="tabs" aria-label="Account">
  <a href="#">Usage</a>
  <a href="#">Bills</a>
  <a href="#">Meter</a>
</nav>
<div class="cards">
  <section>Electricity</section>
  <section>Gas</section>
  <section>Solar</section>
  <section>Contract</section>
</div>
.tabs {
  display: flex;
  gap: 1.5rem;
  margin-bottom: 1rem;
}

.cards {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(8rem, 1fr));
  gap: 0.75rem;
}

.cards section {
  padding: 1rem;
  border: 1px solid #9ca3af;
}

With the grid overlay on, you see the column tracks, the gaps between them and (if you switch them on in the Layout settings) the line numbers and area names, which are exactly the numbers you need for grid-column and grid-row (see CSS grid). The flex overlay marks the free space and the gaps between items, which shows at a glance why items are or are not spreading out (see flexbox). Make the browser window narrower and watch the overlay follow the cards as they wrap.

Finding what causes horizontal scrolling

A page that scrolls sideways on a phone is one of the most common layout bugs. Something is wider than the screen, but what? In this result the frame gets a horizontal scrollbar:

<p class="banner">Scheduled maintenance on Sunday</p>
<p>Details: https://www.example.com/service/maintenance/2026/october/customer-portal-upgrade-window</p>
.banner {
  width: 640px;
  padding: 0.5rem;
  background: #fef3c7;
}

Two elements are to blame here, for different reasons: the banner has a fixed width, and the long address contains no place where the line may break. Ways to find the culprit on a real page:

  • Hover over elements in the inspector and watch which highlight sticks out past the right edge of the page.
  • Firefox marks the element that causes overflow with an overflow badge in the Inspector.
  • Temporarily add * { outline: 1px solid red; } in the Styles pane. Outlines do not take up space, so they show every box without changing the layout.

Typical fixes are max-width: 100% instead of a fixed width, and overflow-wrap: anywhere for long words and addresses in narrow columns.

Device emulation

Device emulation makes your desktop browser behave like a smaller screen. Click the device icon in the developer tools, or press Ctrl+Shift+M (Cmd+Shift+M on macOS in Chrome, Cmd+Option+M in Firefox) to open Chrome's device mode or Firefox's Responsive Design Mode. You can then:

  • choose a preset phone or tablet, or drag the edges to any width, to see your responsive breakpoints switch;
  • set the device pixel ratio, to check that images stay sharp on screens with a high pixel density;
  • simulate touch input instead of a mouse;
  • slow down the network and the processor (throttling), to feel what a cheap phone on a weak connection experiences.

The Rendering panel in Chrome and Edge (open it from the command menu, Ctrl+Shift+P, by typing "Rendering") emulates user preferences as well: prefers-color-scheme for dark mode, prefers-reduced-motion (see transitions and animations), forced-colors, the print style sheet, and several kinds of color vision deficiency and blurred vision. That is a quick way to test a page the way readers with other settings see it.

Validating HTML and CSS

Browsers forgive almost everything: the HTML standard defines how to parse documents "whether they are syntactically correct or not", so a broken page rarely shows an error. It just behaves strangely, as the paragraph example above showed. An HTML validator reads your HTML against the rules of the standard and reports every error the browser would otherwise repair silently. The standard tool is the Nu Html Checker at validator.w3.org/nu: give it a web address, upload a file, or paste the markup.

This fragment looks fine in a browser:

<img src="outage-map.png">
<ul>
  <p>Current outages:</p>
  <li id="area">Utrecht-Oost</li>
  <li id="area">Amersfoort-Noord</li>
</ul>
<a href="/report"><button type="button">Report an outage</button></a>

The validator disagrees:

Error: An img element must have an alt attribute, except under certain conditions.
Error: Element p not allowed as child of element ul in this context.
Error: Duplicate ID area.
Error: The element button must not appear as a descendant of the a element.

Each of these matters even though nothing looks broken: a screen reader cannot describe the map, the list structure is wrong, a link to #area or a <label for="area"> finds only the first element, and a button inside a link is two controls in one that keyboard and screen reader users cannot use reliably. Fix errors from the top; one early mistake often causes several reports further down.

The validator also exists as a program you can run on your own machine or in a build pipeline, so every change is checked automatically. It is a Java program (vnu.jar) that takes file names:

java -jar vnu.jar --errors-only public/index.html

For CSS there is the W3C CSS Validation Service at jigsaw.w3.org/css-validator. It catches typos in properties and values, the same things the Styles pane marks with a warning icon. Treat its reports on very new features with some care: a validator can lag behind what browsers already support, so check such a report against current documentation before "fixing" valid code.

Lighthouse and accessibility checks

Lighthouse is an open-source tool from Google that loads a page, measures it and produces a report with scores from 0 to 100 and a list of concrete problems. It is built into Chrome and Edge as the Lighthouse panel of the developer tools, and runs online as PageSpeed Insights. Its categories are:

  • Performance: how fast the page loads and becomes usable, including the Core Web Vitals that can be measured in a test run, with advice such as "Improve image delivery".
  • Accessibility: automated checks such as missing alternative text, form fields without labels, low color contrast, missing lang attribute and buttons without a name.
  • Best Practices: for example HTTPS, errors in the console, images with the wrong aspect ratio.
  • SEO: basics such as a title, a meta description and crawlable links (see metadata, sharing and search basics).
Performance      78
Accessibility    84
Best Practices   96
SEO              91

Accessibility
  ▲ Background and foreground colors do not have a sufficient contrast ratio.
  ▲ Form elements do not have associated labels.
  ▲ Image elements do not have [alt] attributes.

Two warnings about the numbers. First, a Lighthouse run is a lab test on your own machine and connection: scores vary from run to run, and real visitors on real phones may have a very different experience. Run it a few times, in a private window without browser extensions, and treat the score as a trend, not a grade. Second, an accessibility score of 100 does not mean the page is accessible. Automated tools can only check what a program can decide, such as whether an image has an alt attribute, not whether the text makes sense.

So add a few manual checks, using the developer tools:

  • Accessibility pane (in Elements; Firefox has a separate Accessibility panel): shows the selected element's role and accessible name as a screen reader will announce them, and the accessibility tree.
  • Contrast in the color picker: click a color swatch in the Styles pane, and Chrome shows the contrast ratio of that text color against its background, with a check mark for the WCAG AA and AAA levels.
  • The keyboard: put the mouse away and Tab through the whole page. Can you reach and operate everything, and can you always see where the focus is?

A routine for "it looks wrong"

Most CSS problems fall into a handful of causes. Working through them in order, with the developer tools open, is faster than changing values at random. It is the general troubleshooting method applied to a web page:

  1. Is the stylesheet loaded at all? Look for your file in the Network panel (reload with it open). A 404 means a wrong path, often a relative path that works on your machine but not on the server.
  2. Does the selector match? Select the element: if your rule is not in the Styles pane at all, the selector does not match (a typo, a missing class, a different structure than you thought).
  3. Is the declaration valid? A warning icon means a typo in the property or value, and the whole declaration is ignored.
  4. Does it lose the cascade? Crossed out means another rule wins; the winner is listed above it.
  5. Does the property apply here? A dimmed declaration with an info icon has no effect in this context, such as width on an inline element or z-index on an element that is not positioned (see positioning and stacking).
  6. Is it the HTML? Compare the DOM with your source, and run the validator.
  7. Is it an old file? If your change does not show up at all, the browser may still use a cached copy. A hard reload (Ctrl+Shift+R, or Cmd+Shift+R), or "Disable cache" in the Network panel while the tools are open, rules that out (see cache busting).
Symptom Usual cause Where to look
Unexpected gap above or below an element Margin, often a browser default or collapsed margins Box model diagram (Computed)
Element wider than you set Padding and border added to width (box-sizing: content-box) Box model diagram, box-sizing in Computed
z-index has no effect Element not positioned, or a different stacking context Styles (dimmed declaration), parent elements
Flex items do not shrink or wrap flex-wrap not set, or a minimum content width Flex overlay, Computed min-width
Works locally, broken online Path or file-name case wrong, file not uploaded, old cached copy Network panel (404s), hard reload