So far, layout has been a linear process that handles open tags and and close tags independently. But web pages are trees, and look like them: borders and backgrounds visually nest inside one another. To support that, this chapter switches to tree-based layout, where the tree of elements is transformed into a tree of layout objects for the visual elements of the page. In the process, we’ll make our web pages more colorful with backgrounds.

Right now, our browser lays out an element’s open and close tags separately. Both tags modify global state, like the cursor_x and cursor_y variables, but they aren’t otherwise connected, and information about the element as a whole, like its width and height, is never computed. That makes it pretty hard to draw a background color behind text. So web browsers structure layout differently.

In a browser, layout is about producing a layout tree, whose nodes are layout objects, each associated with an HTML element,Elements like <script> don’t generate layout objects, and some elements generate multiple (<li> elements have a layout object for the bullet point!), but mostly it’s one layout object each. and each with a size and a position. The browser walks the HTML tree to produce the layout tree, then computes the size and position for each layout object, and finally draws each layout object to the screen.

Let’s start a new class called BlockLayout, which will represent a node in the layout tree. Like our Element class, layout objects form a tree, so they have a list of children and a parent. We’ll also have a node field for the HTML element the layout object corresponds to.

class BlockLayout:
    def __init__(self, node, parent, previous):
        self.node = node
        self.parent = parent
        self.previous = previous
        self.children = []

I’ve also added a field for the layout object’s previous sibling. We’ll need it to compute sizes and positions.

Each layout object also needs a size and position, which we’ll store in the width, height, x, and y fields. But let’s leave that for later. The first job for BlockLayout is creating the layout tree itself.

We’ll do that in a new layout method, looping over each child node and creating a new child layout object for it.

class BlockLayout:
    def layout(self):
        previous = None
        for child in self.node.children:
            next = BlockLayout(child, self, previous)
            self.children.append(next)
            previous = next

This code is tricky because it involves two trees. The node and child are part of the HTML tree; but self, previous, and next are part of the layout tree. The two trees have similar structure, so it’s easy to get confused. But remember that this code constructs the layout tree from the HTML tree. So it reads from node.children (in the HTML tree) and writes to self.children (in the layout tree).

So this creates layout objects for the direct children of the node in question. Now those children’s own layout methods can be called to build the whole tree recursively:

def layout(self):
    # ...
    for child in self.children:
        child.layout()

We’ll discuss the base case of the recursion in just a moment, but first let’s ask how it starts. Inconveniently, the BlockLayout constructor requires a parent node, so we need another kind of layout object at the root.You couldn’t just use None for the parent, because the root layout object also computes its size and position differently, as we’ll see later this chapter. I think of that root as the document itself, so let’s call it DocumentLayout:

So we’re building a layout tree with one layout object per HTML node, plus an extra layout object at the root, by recursively calling layout. It looks like this:

In this example there are four BlockLayout objects, in green, one per element. There’s also a DocumentLayout at the root.

The browser must now move on to computing sizes and positions for each layout object. But before we write that code, we have to face an important truth: different HTML elements are laid out differently. They need different kinds of layout objects!

Elements like <body> and <header> contain blocks stacked vertically. But elements like <p> and <h1> contain text and lay that text out horizontally in lines.In European languages, at least! Abstracting a bit, there are two layout modes, two ways an element can be laid out relative to its children: block layout and inline layout.

We’ve already got BlockLayout for block layout. And actually, we’ve already got inline layout too: it’s just the text layout we’ve been implementing since Chapter 2. So let’s rename the existing Layout class to InlineLayout and refactor to match methods with BlockLayout.

Rename Layout to InlineLayout and rename its constructor to layout. Add a new constructor similar to BlockLayout’s:

In the new layout method, replace the tree argument with the node field: