Part 1 · 4 chapters · ~30 min

Elements and Styles

The tree as the parser built it, Styles as the cascade argued and Computed as it decided, the box model and the flex and grid overlays drawn where layout ran, the Accessibility pane, forced states, DOM breakpoints that catch who changed the tree, and the Event Listeners pane.

4

The tree is the DOM, not the source

The Elements panel shows the DOM the parser built, after every repair, after every script that ran, as it is right now. It is not the HTML the server sent: View Source (⌥⌘U) is that. The two differ in exactly the ways the Browser course part 2 described: implied tags inserted (html, head, body, tbody), misnested tags adopted, text foster-parented out of tables, unclosed elements closed at the end, and then everything JavaScript added or removed since.

reading the tree
  1. Select with the inspector arrow (⇧⌘C) and hover the page, or click a node; $0 in the console is the selected node, $1 the previous one. Right-click → Copy → selector, JS path, outerHTML.
  2. Search (⌘F in the panel) by text, CSS selector or XPath; "== $0" marks the selection in the tree.
  3. Edit live: double-click an attribute or text; drag nodes to reorder; H hides (visibility: hidden); Delete removes; F2 edits as HTML. All of it is lost on reload; it is for experiments.
  4. The breadcrumb at the bottom is the ancestor chain; "…" collapses it. The badges (flex, grid, scroll, container, ad) are the engine naming each box's role.
  5. Shadow roots appear as #shadow-root (open) when "Show user agent shadow DOM" is on; the internals of <video> controls and <input type=range> are there.
go to the lab
  1. /parse/broken-markup: compare the source on the left with the tree on the right. Name each repair the tree builder applied: the adoption agency for the misnested b and i, the implied end tags for p and li, the foster-parented p from inside the table cell, and the div closed at EOF.
5

Styles versus Computed

the two panes
  1. Styles is every rule that matched, in cascade order: element.style, then matched rules from most to least specific (later source order wins ties), then inherited rules per ancestor, then the user-agent stylesheet greyed at the bottom. Losing declarations are struck through. Each rule shows its file and line (or <style>, or "injected stylesheet" for an extension).
  2. Computed is the final value of every longhand, after the cascade, inheritance, unit resolution and the initial value. The triangle beside a property lists the winning rule and the overridden ones with their locations. "Show all" includes properties nothing set; the filter finds one.
  3. Styles is the argument; Computed is the answer. When a value is not what you expect: read Computed for what it is, expand it for who set it, then read Styles for why the others lost.
what people misread
  1. A struck-through declaration is the cascade working, not an error.
  2. A property missing from Styles is inherited (check the inherited section) or came from a shorthand (Computed shows the longhand).
  3. A value in Computed that differs from what you wrote: unit resolution (em, %, vw to px), a clamp (min-width over width; max-height over height), or box-sizing. The box model diagram shows what layout used.
  4. !important and layers: a declaration marked !important sorts above everything in its origin; cascade layers (@layer) show their layer name beside the rule and sort below unlayered styles unless important. Styles orders them correctly; read the order, not the specificity.
the editing surface
  1. Click a value to edit; arrow keys step numbers (⇧ by 10, ⌥ by 0.1); the colour swatch opens a picker with the page's palette and a contrast checker; the shadow, angle and easing editors open from their icons.
  2. "+" adds a new rule targeting the selected element, written to an inspector stylesheet; ".cls" adds classes; the Filter box narrows to a property.
  3. Edits are live and ephemeral. Part 3's workspaces map the inspector to a folder on disk so an edit saves to the file.
STYLES VERSUS COMPUTED
the argument and the answer, for one element
swipe the figure sideways, or tap expand for full screen
1/6
the element
The selected element: a button with class "primary" inside a form with class "compact", under a stylesheet with a reset. Four rules match: the UA stylesheet (button { padding: 1px 6px }), the reset (button { padding: 0 }), .btn { padding: 8px 14px }, and .compact .btn { padding: 4px 10px }. Plus an inline style attribute: style="padding-left: 20px".
6

The box model and the layout overlays

seeing what layout decided
  1. The hover highlight: content (blue), padding (green), border (yellow), margin (orange); the tooltip has the element, size, and for text the font, colour and contrast ratio with its AA/AAA mark. A negative margin draws inward.
  2. The box model diagram at the bottom of Styles: the numbers per side, editable; position offsets when positioned; a dash for auto. "I set 300 and got 280" is box-sizing or a min/max constraint, and the diagram shows it.
  3. Flex overlay from the badge: axis, gaps, items, free space; the Flexbox editor's buttons change direction, wrap, justify and align live. An item that refuses to shrink is min-width: auto (the Browser course part 4).
  4. Grid overlay: line numbers and names, track sizes resolved (fr to px), gaps, areas; the Layout pane lists every grid on the page and toggles several overlays at once. An item in an implicit track was placed beyond the template.
  5. Badges: scroll (a real scroll container), container (container queries resolve here), ad. A missing scroll badge means overflow did not make a scroll container; a missing container badge means your @container query has no container.
the Accessibility pane
  1. Beside Styles: the accessibility tree node for the selected element with its computed role, name (and where the name came from: label, aria-label, contents, title), description, and ARIA attributes. The full-page accessibility tree view (the person icon in the tree's top corner) replaces the DOM tree with the a11y tree.
  2. Read it before shipping a widget: a div with a click handler has role generic and no name; a button has role button and its text as name; a label wired to an input gives the input its name. The lab's /input/div-button shows all three.
  3. Contrast, in the colour picker, uses the computed background; for text over images it cannot know, so check by eye.
go to the lab
  1. /input/div-button: select each of the three controls and read the Accessibility pane. Note role, name and name source. Tab through them. Then read what the repaired div needed to match the button.
  2. Open any grid in the lab (the cards on /layout/containment) and toggle its grid overlay from the badge; read the resolved track sizes.
THE BOX MODEL AND THE LAYOUT OVERLAYS
what layout decided, drawn on the page
swipe the figure sideways, or tap expand for full screen
1/6
hover highlight
The highlight on hover: content box (blue), padding (green), border (yellow), margin (orange). The tooltip shows the element, its classes, the content size, and for text its colour, font and contrast ratio against its background (with an AA/AAA mark). The margin band shows negative margins as an inward orange region.
7

Forced states, DOM breakpoints and listeners

holding the engine still
  1. :hov in Styles forces :hover, :active, :focus, :focus-within, :focus-visible, :visited, :target on the selected element. The dropdown that closes when you reach for DevTools stays open; the focus ring you cannot click on is inspectable.
  2. Emulate a focused page (Rendering drawer or Styles) keeps the document focused while you work in DevTools, so focus-dependent UI survives the switch.
catching who changed the tree
  1. DOM breakpoints: right-click a node → Break on → subtree modifications, attribute modifications, node removal. Listed in Sources → DOM Breakpoints; they persist by path across reloads.
  2. When it fires the page pauses on the mutating line with the call stack. For React the top frames are the renderer's commit; walk down to the component or effect. "Who keeps toggling this class" is one hit.
  3. Event Listener Breakpoints (Sources) pause before any handler of a category runs: mouse, keyboard, timer, animation frame, XHR, DOM mutation. XHR/fetch breakpoints pause when a URL contains a string. With DOM breakpoints they bracket an interaction: pause on the input, on the request, on the mutation.
  4. The Event Listeners pane (beside Styles) lists every listener on the element and, with Ancestors ticked, on its ancestors with source locations and a Remove button. React's delegated handlers appear on the root; an unknown file is an extension or an analytics script. Remove is a quick experiment: is the jank still there without it?
go to the lab
  1. /paint/hover-shadow: select a card, force :hover with :hov, and read which rule applies and the box-shadow value. Then, in the Event Listeners pane with Ancestors on, find the React root's delegated listeners.
  2. /nav/spa-router: set an attribute-modification breakpoint on the h2 (its text changes on navigation are subtree modifications; set that too). Click a link; read the stack from React's commit down to the router's handler.
  3. /style/theme-toggle: set a DOM breakpoint (attribute modifications) on the themed container, click the button, and confirm the stack ends in the toggle handler. Then remove the breakpoint and record the click in Performance for part 5.
FORCED STATES AND DOM BREAKPOINTS
making the engine hold still, and catching who changed the tree
swipe the figure sideways, or tap expand for full screen
1/6
:hov
The disappearing state: a dropdown that opens on hover closes when you move to DevTools; a focus ring you cannot inspect because clicking the tree blurs the input. Select the element, click :hov in Styles, tick :hover or :focus; the engine treats the pseudo-class as matched until you untick it; the Styles pane shows the rules that now apply.