A box model describes how to measure an element for layout. It defines what size is and how it relates to the content, margin and padding. It may seem trivial, but clear definitions here are essential to building a UI engine.
This article is part of a series on Writing a UI Engine.
Common Box Model
An ordinary box model comprises several boxes, each enclosing a smaller box. From inside to outside these are below, as in the CSS model:
- Content: This is the body of an element, it may be children or direct content such as an image or text.
- Padding: The padding box is offset from the content. Often a background is drawn within this area.
- Border: The border may wholly, or partially surround the padding box. Let's call this one "optional", see my comment below.
- Margin: The margin box is space used to separate elements from each other.
These are general ideas of what the parts of the box model. The precise meaning is tightly coupled to how these interact with other elements and layout properties. Let's have a look at some of the possibilities.
I'll ignore the border-box. The layout engine I wrote did not have it, nor do I see substantial value in it, nor has anybody using the engine ever even mentioned it. I haven't found a UI that is simplified by having it.
Size
What is the size of an element? Consider this UI fragment:
Panel{Color=#008}
Rectangle{Margin=10 Alignment=TopLeft Padding=5 Width=20 Height=40 Color=#080}
Ellipse{Color=#800}
Does the rectangle cover an area of 20x40, or does the Padding
add to the area making it 30x50 (5 applies to all sizes)? From a design perspective, it's more convenient to use the former approach, where changing the padding does not alter the size of that rectangle. It's easy to create the latter, additive model, by sizing the children instead.
Panel{Color=#008}
Rectangle{Margin=10 Alignment=TopLeft Padding=5 Color=#080}
Ellipse{Color=#800 Width=20 Height=40}
If we change the padding on the rectangle, its overall size will change now. This happens because the child no longer expands to fill the size, but rather defines the size, and the parent adds the padding size. The box model necessarily depends on the definition of natural and dependent sizes, or we could instead say it's part of that definition.
To rephrase the above, does a Width
property define the width of the context-box, padding-box, or margin-box? All of them make sense in some situations, but I can assure you the users of a UI system would scream at the complexity of a system that offers all of them. Additionally, it would be quite hard to write that UI engine -- the properties are not just aliases but end up with specific rules during box-sizing.
Defining Width
to be that of the padding box seems like a reasonable, and natural choice.
CSS is notorious for the box model getting in the way or having the wrong definition of properties. A lot of that is likely attributable to missing layouts, not necessarily the box model itself. If you are designing a new layout engine, it's good to keep the troubles in mind and make sure you don't duplicate them!
Collapsing Margins
In a flow-based layout, such as HTML, we may define collapsing margins.
StackPanel
Text{Margin=10 Value="One"}
Text{Margin=10 Value="Two"}
With collapsing margins, there will be 10 points between item "One" and "Two". With strict margins, there will be 20 points between the two items.
For flowing layouts, especially for text, collapsing margins can make sense. In this mode, they become more of a minimum space from surrounding elements, rather than an added space. For typical application layouts, additive margins are easier to implement and provide more consistency.
The goal of collapsing margins can be achieved in other ways. In a listing, you may wish to have an equal amount of space between items. I achieved this with an ItemSpacing
property.
StackPanel{ItemSpacing=10}
Item
Item
In this approach, the StackPanel
decides the spacing between the items, rather than the items deciding. Each item may still use a Margin
to give itself additional space. I also defined a CellSpacing
for grids and ColumnSpacing
for column layouts. Each layout type may have a ...Spacing
property.
Placement
We need to define two points that determine the placement of an element: where in the parent, and what point in the child is placed there (the anchor).
Basic alignment covers some simple examples. A panel aligned to the top-left is assumed to have the top-left corner of the element flush in the corner. A centered element has the center of the content at the center of the parent.
This leads to the question, top-left of what? In this case, we're usually speaking about the margin-box. The content of an element aligned to the top-left is separated from the corner by the size of its margin. That makes sense. What about a margin applied to a centered element? That's a bit weird, but the engine better have it well defined.
And what about explicit anchors? I've presented these before in reference to CSS. They are a valuable addition to layout. For example, you may wish to have the top-left of an image aligned to the center of the parent.
Image {Alignment=Center Anchor=0%,0%}
Or perhaps you're decorating a panel with a caption floating off the bottom.
Panel
GoodStuff
CaptionPanel {Alignment=Bottom Anchor="50%,0%"}
Text{Value="Something about good stuff"}
Regardless of which properties you offer, you'll always need to define placement in terms of two positions: one in the parent, called the position, and one in the child, called the anchor. In the parent, you're referring to a point within the content-box; in the child, you're referring to a point the margin box.
I'll have to come back to placement, in particular, anchoring, when we get to implementation. I realize now it's perhaps more complicated than these few paragraphs here.
Boxing it up
This article introduces the box model but doesn't offer a complete definition. As we go further in layouts, we'll continue to refer to the box model and define behaviour in relation to the content-box, padding-box and margin-box.
The box model will also be referenced when we talk about rendering and hit testing. A lot of properties refer to position and size, often to different boxes. Coming up with a consistent definition and behaviour will save the engine users a lot of headaches when designing layouts.
Top comments (1)
Nice post!, this kind of knowledge puts web developers on next level.