Debug your own HTML page with Chrome DevTools

Press F12 on your page and Chrome shows you what it actually did with your HTML. Four panels answer the four usual questions, and each one is practised below on a small broken page.

Chrome DevTools is a set of panels built into Chrome that show what the browser actually did with your HTML. Open it with F12 (or Cmd+Option+I on a Mac), or right-click the part of the page that looks wrong and choose Inspect.

Try it on this page first. The button below has lost its label. Right-click it, choose Inspect, and find out why before you open the answer.

Live exampletry it here, then copy the code
Share it as a link
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Find the bug: the invisible button</title>
<style>
  body { margin: 0; padding: 16px; font-family: system-ui, sans-serif; background: #f4f5f7; color: #1d2330; }
  .card {
    max-width: 340px; padding: 18px 20px; border-radius: 12px;
    background: #fff; box-shadow: 0 6px 20px rgba(0, 0, 0, .1);
  }
  .card h2 { margin: 0 0 6px; font-size: 19px; }
  .card p { margin: 0 0 14px; color: #555; font-size: 14px; }
  .card button {
    padding: 10px 18px; border: 0; border-radius: 8px;
    background: #2563eb; color: #fff; font-size: 15px; cursor: pointer;
  }
  /* written later, for "primary" links elsewhere on the site */
  button.primary { color: #2563eb; }

  .task { margin: 16px 0 0; max-width: 340px; font-size: 14px; line-height: 1.5; }
  details { margin-top: 8px; max-width: 340px; font-size: 14px; line-height: 1.5; }
  summary { cursor: pointer; font-weight: 600; color: #2563eb; }
  code { background: #eef1f5; padding: 0 4px; border-radius: 4px; }
</style>
</head>
<body>
<div class="card">
  <h2>Weekly notes</h2>
  <p>One short email every Monday.</p>
  <button class="primary" id="join">Subscribe</button>
</div>

<p class="task"><b>Find the bug:</b> the button has lost its label. Right-click the blue button, choose <i>Inspect</i>, and look for a crossed-out line in the Styles pane.</p>
<details>
  <summary>Show the answer</summary>
  <p><code>.card button</code> sets <code>color: #fff</code>, but the later rule <code>button.primary</code> has the same specificity and wins, so the text is blue on blue. DevTools shows the white color crossed out.</p>
  <p>Fix: delete the <code>button.primary</code> rule, or scope it to links. Try it in the code.</p>
</details>
</body>
</html>
A button with no visible text. Inspect it, then open the answer. Edit the code to fix it.

Nothing on your own page needs to change for this to work. DevTools reads any page Chrome has open, including a file opened from your disk.

Four panels, four questions

Four questions come up again and again on a hand-made page, and each has its own panel. You do not need the rest of DevTools to start.

Elements, Console, Network and device mode, and the keyboard shortcut for each.
Elements, Console, Network and device mode, and the keyboard shortcut for each.
Question Panel What to look at
Why does it look like that? Elements The Styles pane for the selected element
Why does the button do nothing? Console Red error lines
Why is the image or stylesheet missing? Network Rows with 404 or a failed status
Why does it break on a phone? Device mode The page at a phone's width

If this is a page that looked fine and suddenly does not, start with HTML page not displaying properly in Chrome instead. It covers the cache, extensions and profile problems that sit outside your code.

Inspect an element and read the Styles pane

Right-click an element and choose Inspect, or press Ctrl+Shift+C (Cmd+Shift+C on a Mac) and click it. The Elements panel highlights its HTML, and the Styles pane lists every CSS rule that matches it.

Rules are listed with the one that takes precedence at the top. A declaration that lost to another rule is crossed out. That is the whole trick behind the demo above.

Left: the white color is crossed out because a later rule with equal weight set blue. Right: after the fix.
Left: the white color is crossed out because a later rule with equal weight set blue. Right: after the fix.

When two selectors have the same specificity, the one later in the file wins. Here both rules are one class plus one element, so the later one sets the text to the same blue as the background.

Edit CSS live, then copy it back

Every value in the Styles pane is editable. Click a value and type, or use the arrow keys on a number. Untick the checkbox next to a line to switch it off. The page updates as you type.

These edits live only in that tab. Reload and they are gone, so once something works, copy it into your file. Three more controls in the Elements panel help:

  • Computed tab: the final value of each property, after every rule is applied.
  • Box model diagram: the element's content size, padding, border and margin, with numbers.
  • :hov button: forces states such as :hover and :focus, so you can style them without holding the mouse still.

Read the Console when a script fails

When a script throws an error, the rest of that function stops running. The page gives no sign of it. The Console does: it prints the error in red, with a link to the file and line.

Live exampletry it here, then copy the code
Share it as a link
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Find the bug: the button that does nothing</title>
<style>
  body { margin: 0; padding: 16px; font-family: system-ui, sans-serif; background: #f4f5f7; color: #1d2330; }
  .shop {
    display: flex; align-items: center; gap: 14px; flex-wrap: wrap;
    max-width: 360px; padding: 16px 18px; border-radius: 12px;
    background: #fff; box-shadow: 0 6px 20px rgba(0, 0, 0, .1);
  }
  .shop button {
    padding: 10px 16px; border: 0; border-radius: 8px;
    background: #16a34a; color: #fff; font-size: 15px; cursor: pointer;
  }
  .count { font-size: 15px; }
  .task { margin: 16px 0 0; max-width: 360px; font-size: 14px; line-height: 1.5; }
  details { margin-top: 8px; max-width: 360px; font-size: 14px; line-height: 1.5; }
  summary { cursor: pointer; font-weight: 600; color: #2563eb; }
  code { background: #eef1f5; padding: 0 4px; border-radius: 4px; }
  #said { font-family: ui-monospace, Consolas, monospace; font-size: 13px; color: #b91c1c; }
</style>
</head>
<body>
<div class="shop">
  <button id="add">Add to cart</button>
  <span class="count">In cart: <b id="count">0</b></span>
</div>

<p class="task"><b>Find the bug:</b> click <i>Add to cart</i>. Nothing happens. Open DevTools (F12), go to the Console tab, click again, and read the red line.</p>
<details>
  <summary>Show the answer</summary>
  <p>The console says: <span id="said">(click the button first)</span></p>
  <p><code>total</code> is used but never declared. Add <code>let total = 0;</code> above the listener in the code and the counter works.</p>
</details>

<script>
  const add = document.getElementById('add');
  const count = document.getElementById('count');

  add.addEventListener('click', () => {
    total = total + 1;          // the bug: total was never declared
    count.textContent = total;
  });

  // Copies uncaught errors onto the page, for readers on a phone with no DevTools
  window.addEventListener('error', (e) => {
    document.getElementById('said').textContent = 'Uncaught ' + e.message.replace(/^Uncaught /, '');
  });
</script>
</body>
</html>
Click Add to cart, then open the Console and click again. Fix it by editing the code.

In Chrome the red line reads like this:

Uncaught ReferenceError: total is not defined

Click the file name on the right of the error to jump to the line.

An error that happens on a click only appears after the click, so keep the Console open while you reproduce the problem. For other reasons scripts do nothing, see HTML JavaScript not working.

The Console also runs JavaScript you type. After selecting an element in the Elements panel, type $0 in the Console to get that element.

Find 404s in the Network panel

A missing image, stylesheet or script usually leaves no error on the page. The Network panel lists every file the page requested, with a status code. It records only while DevTools is open, so open it and then reload.

The red row is the missing file. The path in the HTML does not match the folder the file is in.
The red row is the missing file. The path in the HTML does not match the folder the file is in.

A 404 means the server was asked for a file at that address and did not have it. On a page opened from disk there is no server, so the row fails instead.

In the Console, Chrome reports the same miss in one of two forms:

Failed to load resource: the server responded with a status of 404
Failed to load resource: net::ERR_FILE_NOT_FOUND

Click the red row and look at the full address Chrome asked for. Compare it with where the file really is: a wrong folder, a typo, or Logo.png against logo.png.

Relative vs absolute paths explains how the address is built. The Disable cache box at the top of the panel makes sure you see fresh files while DevTools is open.

Test a phone width with device mode

Press Ctrl+Shift+M (Cmd+Shift+M on a Mac) with DevTools open, or click the phone-and-tablet icon at its top left. The page shrinks to a phone-sized viewport. Pick a phone from the list, or type a width such as 390.

Live exampletry it here, then copy the code
Share it as a link
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Find the bug: sideways scroll on a phone</title>
<style>
  body { margin: 0; padding: 16px; font-family: system-ui, sans-serif; background: #f4f5f7; color: #1d2330; }
  .page { max-width: 560px; }
  h2 { margin: 0 0 6px; font-size: 20px; }
  p { margin: 0 0 12px; font-size: 14px; line-height: 1.5; }
  .promo {
    width: 480px;               /* fine on a laptop, too wide for a phone */
    padding: 14px 16px; border-radius: 10px; box-sizing: border-box;
    background: linear-gradient(90deg, #fde68a, #fca5a5); font-weight: 600;
  }
  .tools { display: flex; gap: 8px; flex-wrap: wrap; margin: 14px 0 8px; }
  .tools button { padding: 8px 12px; border: 1px solid #cbd2dc; border-radius: 8px; background: #fff; font-size: 14px; cursor: pointer; }
  .outline * { outline: 1px solid #ef4444; }   /* shows every box's edge */
  #out { font-family: ui-monospace, Consolas, monospace; font-size: 13px; white-space: pre-wrap; margin: 0; }
  details { margin-top: 8px; font-size: 14px; line-height: 1.5; }
  summary { cursor: pointer; font-weight: 600; color: #2563eb; }
  code { background: #eef1f5; padding: 0 4px; border-radius: 4px; }
</style>
</head>
<body>
<div class="page" id="page">
  <h2>Spring menu</h2>
  <p>Fresh pasta, seasonal greens, and a short list of natural wines.</p>
  <div class="promo">Book before Friday for a free dessert</div>
  <p><b>Find the bug:</b> on a laptop this looks fine. Turn on device mode (Ctrl+Shift+M, or Cmd+Shift+M on a Mac) at 390 wide, or open it on a phone. The page scrolls sideways. Which element is too wide?</p>
</div>

<div class="tools">
  <button id="outline">Outline every box</button>
  <button id="check">List elements wider than the screen</button>
</div>
<pre id="out"></pre>

<details>
  <summary>Show the answer</summary>
  <p><code>.promo</code> has a fixed <code>width: 480px</code>. Change it to <code>max-width: 480px</code> and it shrinks to fit a narrow screen.</p>
</details>

<script>
  document.getElementById('outline').addEventListener('click', () => {
    document.body.classList.toggle('outline');
  });

  // Same check you can paste into the Console on any page
  document.getElementById('check').addEventListener('click', () => {
    const screenW = document.documentElement.clientWidth;
    const right = (el) => el.getBoundingClientRect().right + scrollX;  // right edge on the page
    const wide = [...document.querySelectorAll('body *')].filter((el) => right(el) > screenW);
    document.getElementById('out').textContent = wide.length
      ? 'Screen is ' + screenW + 'px. Too wide:\n' + wide.map((el) =>
          el.tagName.toLowerCase() + (el.className ? '.' + el.className : '') +
          ' ends at ' + Math.round(right(el)) + 'px').join('\n')
      : 'Screen is ' + screenW + 'px. Nothing sticks out.';
  });
</script>
</body>
</html>
Looks fine on a laptop. At 390 wide the page scrolls sideways. Find the element that sticks out.

The usual cause of sideways scrolling is one element with a fixed width wider than the screen. To find it on any page, paste this into the Console:

const w = document.documentElement.clientWidth;
[...document.querySelectorAll('body *')]
  .filter(el => el.getBoundingClientRect().right > w);

The result lists every element whose right edge is past the screen. Replace a fixed width with max-width and it shrinks to fit.

If the whole page looks tiny in device mode, the <head> is missing the viewport meta tag.

In our test with Chromium's phone emulation, a page without it was laid out 980 pixels wide and scaled down, while the same page with it was laid out at 390.

Device mode is still Chrome on your computer. It will not show problems specific to Safari or another phone browser, so check a finished page on a real phone too.

When it does not work

What you see Cause Fix
Your CSS change has no effect Another rule wins Find it above the crossed-out line in Styles
The rule is not listed at all The selector does not match, or the file did not load Check the class name, then the Network panel
A value is crossed out with a warning icon The value is invalid, such as a typo in a unit Correct the value
Edits vanish on reload DevTools changes are not saved to your file Copy the change into your file
Network panel is empty DevTools opened after the page loaded Reload with DevTools open
Console shows nothing but the button still fails The code never ran, or no listener is attached Check the script tag and the element id
Page is tiny in device mode No viewport meta tag Add it to the head

Once the page works, the people you built it for still need to see it. A screenshot shows the layout but none of the behaviour, and an .html attachment may open as plain text on a phone.

To send the working version, paste the page into a NOS document and choose Create share link. HTML to link walks through it.

The page renders as written and its scripts run, so the people you send it to can click and scroll it themselves, and open DevTools on it too. If you fix something later, the same link shows the new version.

Questions people ask

How do I open DevTools in Chrome?

Press F12 or Ctrl+Shift+I on Windows and Linux, or Cmd+Option+I on a Mac. You can also right-click any part of the page and choose Inspect, which opens DevTools with that element already selected.

Are the changes I make in DevTools saved to my file?

No. Edits in the Elements panel and the Styles pane change only the page in that tab. Reload and they are gone. Once a change works, copy the rule or value back into your .html or .css file.

Why is the Network panel empty?

It records only while DevTools is open. Open DevTools first, then reload the page, and every file the page requests appears in the list.

Is device mode the same as testing on a real phone?

No. It is still Chrome on your computer, set to a phone-sized viewport. It is good for layout and width problems. For behaviour specific to Safari on an iPhone, or to a particular phone browser, open the page on the phone itself.

Can I use DevTools on a page inside an iframe?

Yes. Inspect finds elements inside frames, and errors from frames appear in the Console. To type commands that run inside a frame, pick the frame in the context menu at the top of the Console, which normally reads top.

Keep reading