Intro to Layout
The layout of pages is fundamentally dynamic. Even if a page looks completely static to a given user with a certain browser window of a certain size, the page will – and should! – look different to someone else with a different screen.
When reasoning about the layout of our applications, we must think in three directions: for any given element, what is its relationship to its parent element, sibling elements, and its own inner content? Different layout modes in CSS affect these relationships in different ways. Creating layouts in CSS largely consists of defining these relationships and carefully placing constraints on their behavior. So, when thinking about the design of any given element, we ask ourselves: what are its outer constraints, and what are its inner constraints?
Ultimately, our outermost layout constraint is the size of the user’s browser window. Our innermost layout constraints are the atomic size of the content we want to display: the smallest pieces of words, images, video, individual controls or inputs, that cannot be broken apart without losing their meaning or functionality. We can usually make very few assumptions about these ultimate constraints: our users have a multitude of devices and screen sizes, and might resize their browser window at any time, or zoom the text size larger or smaller to make it more comfortable to read. On the other hand, the content being displayed may be largely unknown in advance, generated by your end-users, others in your organization, or arbitrary third-parties, human or machine.
And so our job becomes interestingly challenging.
In this chapter, we will start out looking at the atomic level – how individual elements and their contents are sized and rendered. With those building blocks, we will then explore the different overall layout contexts that CSS provides.
The Box Model
The size of each element, whatever shape it may appear on-screen, is defined by a set of rectangular dimensions. CSS defines a standard Box Model that allows us to size elements as well as the space around them.
There are four layers of essential dimensions on an element. From innermost to outermost, they are the content, padding, border, and margin, as shown below.1
If an element is given a background (such as a background color or image), the background will extend behind the content and padding. Margins are always transparent as their primary purpose is – using terms from the world of graphic design – to define the negative space or whitespace between elements.
Box Sizing
By default, an element’s width
and height
dimensions are calculated relative
to the element’s content area and do not include padding, border, or margin.
This can make it very difficult to reason about layout when trying to precisely
size elements, say, to fit in a single line of given width without wrapping. It
is common practice to override the default box-sizing
property to instead
calculate size from the outermost edges of the element’s border:
html {
box-sizing: border-box;
}
*,
*:before,
*:after {
box-sizing: inherit;
}
We set the root html
element’s box-sizing
property to border-box
so that
its calculated dimensions include content, padding, and border – the visual
size of the entire rendered element. We then use the universal selector (*
) to
specify that all elements (and, here, all :before
and :after
pseudo-elements) should inherit the box-sizing
scheme of their parents. This
means that all elements on the page will inherit the border-box
scheme from
the root element unless the property is explicitly overridden for an
intermediate ancestor, which allows us to opt-out of border-box
at the
component level for a given sub-tree of elements.
In the rest of this text, assume that the style box-sizing: border-box;
applies everywhere unless otherwise stated.
Flow Context: block
and inline
Elements
The term document flow is used to describe the behavior of how element boxes are laid out on the page in relation to each other. When discussing document flow, we often refer to the block and inline dimensions. The inline dimension is the primary direction in which text is read – in English, for example, this is horizontally from left to right. The block direction is orthogonal, from top to bottom. In other languages and writing modes, these logical directions (inline, block) may correspond to different physical directions (left, right, top, bottom).
In a block formatting context, HTML elements can be generally categorized as block or inline based on their element type. While there are many subtleties to these flow behaviors, it can be generally described that:
- block elements take up the entire width of their container, always creating a line break before and after themselves, while
- inline elements only take up as much vertical and horizontal space as the size of their content necessitates. This means that many short inline elements can flow onto one line, and the contents of a single long inline element – usually text – can, if needed, break and wrap onto multiple lines to fit within the width of its containing block context.
For example, a paragraph p
element is block-level by default. If multiple p
elements are siblings then they will each break and start on a new line, even if
their combined text content is short enough such that it could theoretically fit
onto one line if “squished”.
On the other hand, a hyperlink a
element is inline by default; its contained
text can “run in” with surrounding text or other inline elements on the same
line, or break in the middle such that it starts on the end of one line and
wraps to the beginning of another line.
To prescribe these behaviors, CSS defines an appropriately-named display
property.
display: block;
Explicitly causes an element to create a block-level box in the layout.
display: inline;
Explicitly causes an element to create an inline-level box in the layout.
For layout purposes, inline elements collapse their size to the minimum required
to fit their contained content. If an element has a computed display
value of
inline
, then its vertical sizing parameters (height
, margin-{top,bottom}
,
and padding-{top,bottom}
) do not affect the layout of other inline
elements around it.
![figure.png]
<style>
span.decorated {
border: 1px solid blue;
padding: 1em;
background-color: rgba(0, 0, 255, 0.1);
}
</style>
<!-- -->
<div>
Lorem ipsum sit amet, consectetur adipiscing elit, sed do eiusmod tempor
incididunt ut labore et dolore <span class="decorated">magna aliqua.</span>
Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut
aliquip ex ea commodo consequat.
</div>
display: inline-block;
As one might expect, a display
value of inline-block
exhibits behaviors of
both inline
and block
values.
display: none;
Setting display: none;
will remove the element, along with all of its
descendants, from the render tree. They will still exist in the DOM, but for
layout purposes it will appear as though they do not exist.
Position
An element with a position
value other than static
is considered
positioned. Keep this in mind when reading documentation for other CSS
properties, as some can have different end results depending on whether or not
an element or any of its ancestors are positioned. (Re-read the definition for
absolute
above.)
static
With a position of static
, the element is laid out as normal in the document
flow (this is the default value).
relative
If an element’s computed position is relative
, the browser will reserve space
for the element where it would normally appear in the flow (as if it had a
position of static
). The element is then offset relative to its normal
position depending on other assigned movement values. Other elements remain laid
out as if the element were still in its normal static position.
If no movement values are specified, then the element appears as if it had a
position of static
. However, the element is still considered positioned.
absolute
A position of absolute
removes the element completely from the document flow;
that is, other elements in the flow are laid out as if this element did not
exist. The element is positioned relative to its closest positioned ancestor
based on other assigned movement values.
fixed
Like absolute
, a position of fixed
also removes the element from the
document flow. Its position, specified by other assigned movement values, is
relative to its closest transformed† ancestor, or to the
viewport (the browser window) if it has no transformed ancestor. If relative to
the viewport, the element is thus fixed onscreen and does not move when the page
is scrolled.
The “movement values” mentioned in these definitions are the other CSS
positioning properties top
, right
, bottom
, and left
, which can be
assigned length or percentage values in order to define the element’s position.
The names of these properties refer not to the direction of the movement, but
rather to the side of the element which is being “pushed”. This naming can seem
non-intuitive at first, but you will quickly get used to it as you work with
CSS.
We might usually think of elements as flat boxes taking up 2-dimensional space
on the screen, pushing one another around in the document flow. However, as you
can see in the example above, positioned elements can overlap. The visual
layering of these elements is determined by their calculated stacking order
in
the current stacking context
, and for positioned elements this order can be
manipulated using the property z-index
. I recommend reading MDN’s guide
Understanding CSS z-index,2 especially if you find that your
interface requires complex element stacking.