Flow Layout
The flow layout model is the default layout mechanism used by CSS. It operates on the concept that there are two types of elements - (1) inline and (2) block. There are, indeed, more types (even inline-block
), but in the original specifications - it was just these two. We've seen these two before, but it's worth repeating, and describing within the context of what we now have learned.
Inline Elements
An inline element's horizontal screen space is completely defined by it's content, and it's margin
, border
and padding
widths - along the inline direction.
For example, given the following:
- content width (based on font size, image elements, whatever) is 150px
margin-inline-start
andmargin-inline-end
are both 10pxborder-inline-start
andborder-inline-end
are both 0pxpadding-inline-start
andpadding-inline-start
are both 5px
Then the total pixel width (not the width
property, just the overall space taken up) is 150 + 20 + 10 = 180px.
Inline elements "stack" horizontally. Meaning the first element starts at the leftmost pixel, takes up the space needed, and then is followed, left to right, by the next element (unless the dir
attribute of the parent element is set to rtl
).
Here's an example of three span
elements. Their text content is short, so they "stack" left to right - we've added some background coloring, borders, and inline margins for clarity.
Wrapping
Depending on your screen size, all of those might have flowed right across in a row - or, if your screen size is smaller, they may have wrapped. This is an important aspect of inline elements - they typically will be wrapped to the next vertical position (the next line box). Of course, wrapping only happens with text, in terms of breaking up elements. If an img
element (also inline) were to not fit at the end of a line entirely, the entire image would be shifted to the next line.
With more spans, you can see the wrapping more evidently:
Text is wrappable, so if we have large spans, the span in question can be broken into multiple lines:
CSS will not break words unless you specify it to do so. Here's an example of two spans - the first one uses normal wrapping, and overflows the width of the line. The second allows breaking within a word, avoiding that problem.
In the example above, the second span has the css property overflow-wrap:anywhere
. Learn more about text wrapping here.
Block Elements
Block elements differ from inline primarily in that they do not share horizontal screen space. Absent some other work (which we will learn soon), block elements never appear side by side. By default, their width is always 100% of the available space.
What is the available space? It's the horizontal space between the parent element's left and right interior padding. How is that element's width computed? The same thing! It goes all the way up to the html
element's width, which will be the same width as the window or viewport. All along the way, margins, borders, and padding are applied - creating a nesting doll of screen space.
Here's three elements, each within each other. Each element has a margin
of 1rem, a border
of 1px, and a padding
of 1rem.
<style>
div {
margin: 1rem;
padding:1rem;
border: solid 1px;
}
div.a {
border-color: aqua;
background-color:white;
}
div.b {
border-color: black;
background-color:yellow;
}
div.c {
border-color: black;
background-color:yellow;
}
</style>
<div class="a">
<div class="b">
<div class="c">
</div>
</div>
</div>
We didn't specify the width of any of the elements, they simply grew to expand across all available space. If we stack siblings together, they will automatically go to the next line. Here's the same structure, but the interior blocks are duplicated.
That's really it. That's the flow layout, or at least most of it. When an HTML document is just text, with some headings and other purely text elements, the flow layout actually does the job pretty well. It is, however, simplistic.
Height, Width, and Controlling Space
The first modification we might begin to think about making is to the height and width of elements. Without intervention, height and width are calculated as follows:
- inline: Width is dictated by content width. Height is dictated by how much wrapping is occurring, and the height of each line (which could contain more than just text - for example,
img
elements are also inline elements). - block: Width is 100% of the available space. Height is dictated by the content's height - whether the content inside is text, or other HTML child elements - block or inline.
That's the default. We can change these dimensions - using width
and height
. Let's look at width
first.
Setting the Width
span {
width: 200px;
background-color:yellow;
margin:1rem;
}
div {
width:200px;
background-color:lime;
margin:1rem;
}
With the following rules in place, let's see how they behave:
<span>span</span>
<span>span</span>
<span>span</span>
<span>span</span>
<div>div</div>
<div>div</div>
<div>div</div>
<div>div</div>
spanspanspanspan
Notice what we see here, because it's really important. The width
CSS rule applied to block elements, however the block elements still occupy the entire horizontal space. Just because the width has been limited, doesn't mean the next div
gets to slide in right next to the previous!
For inline span, width
actually has no effect. Inline elements do not control their dimension directly - they are sized by their content, padding, margins, and border *only.
There is (as mentioned earlier) a different type of display mode, called inline-block
however. First, let's take a step back and understand how an element becomes inline
or block
though.
Inline or Block, or something else?
We learned when we first started HTML that certain elements were inline - span
, img
- and certain elements were block. It seemed fixed, but it's not. The difference between inline
and block
is actually controlled by the CSS property display
.
The display
property has several valid values - of which we will only look at 4 in this section. We will see more afterwards.
display: block
- sets the element to block. Some elements are, by default, this way.
display: inline
- sets the element to inline. Some elements are, by default, set this way.
display: none
- set the element to be not rendered. This may seem puzzling, but if you've ever notice parts of HTML appearing and disappearing on the screen when you interact with the page, it's probably done by setting various element's to display:none
!
display: inline-block
- finally, an oddball. It sets the element to behave like an inline element in most ways, but the element can control it's width and height. The element also can have margins and padding at the top and bottom, where those values had no effect on inline elements.
Let's quickly revisit the span
examples with width, but this time with span
having display:inline-block
:
spanspanspanspan
Notice that not only does the width now apply, but the elements still wrap to the next line as needed. This is a good start towards many of the types of layouts you see every day, and that we'll be looking at soon.
Display or Visible?
The display:none
rule on an element causes the element to not be rendered at all. Here's an example of three span
elements, each with display:inline
except the middle element.
<span style="border: 1px white solid;">SPAN 1</span>
<span style="border: 1px white solid; display:none">SPAN 2</span>
<span style="border: 1px white solid;">SPAN 3</span>
SPAN 1 SPAN 3
There are times where you do not want the element to display at all, in which case the above technique is preferred. On the other hand, there are situations where you don't want the element to display, but you want the space it took up to remain. Here's the very same html structure, but this time with the middle span having a different CSS property modified: visibility
. The visibility
property can be set to hidden
, and in such circumstances the element is invisible - but the space it normally would take up on the screen is honored.
<span style="border: 1px white solid;">SPAN 1</span>
<span style="border: 1px white solid; visibility:hidden">SPAN 2</span>
<span style="border: 1px white solid;">SPAN 3</span>
SPAN 1 SPAN 3
Setting Height
Height is specified by the height
property, unsurprisingly. Height is only honored with display:block
and display:inline-block
(among the display types we've already covered).
Here's an example of a few block elements getting a height, from CSS instead of it's content.
<div style='height:200px'></div>
<div style='height:200px'>This is some text, which probably needs some vertical space. The height property just works with it, applying additional space as needed so the height is as specified.</div>
<div style='height:5rem'>The height of this element is 5 times as large as a typical line height.</div>
Overflow
Now let's take a look at what happens when we set height and width to smaller sizes than their content demands. Let's start with width, and block elements.
What happens if we set the height as well though, constraining the element's ability to grow?
That's not great. Notice that the content grew, but the parent didn't The text content broke right out of the border. This is overflow, and the default mechanism is to simple overflow. It looks better when there are no borders, but generally it's pretty undesirable. This is a situation where we can ask the browser to treat overflow differently.
div {
overflow: hidden;
}
The hidden
value hides the content that overflows. Alternatively, we could use scroll
,which instructs the browser to create scroll bars on the element.
Note that when specifying scroll
, the scrollbars are present within the element whether they are needed or not. So, if the content does not overflow, we still have a (disabled) scrollbar - depending on the browser being used.
If you really don't want scrollbars unless they are necessary, you can use the auto
value for overflow
.
The overflow
property controls the overflow behavior for both horizontal and vertical overflow. We saw that horizontal overflow is possible when text is too wide, and either cannot be broken by spaces, or is set as such.
As an aside, you can control vertical and horizontal scrolling independently - using overflow-x
and overflow-y
.
Using CSS Lengths for Height and Width
Remember that CSS lengths can be specified in pixels, em
, rem
, centimeters, inches, points, and others. When specifying margins and padding, we typically use lengths that are some how connected to reference font size - namely rem
, or we use view width (vw
) and view height (vh
). There is no perfect answer, but much like as we discussed in the previous chapter, you should try to opt for relative sizes (relative to font, or the viewport's dimension) wherever possible. This helps you manage changes in screen size, screen density, and window size more effectively.
Min-Height, Min-Width, Max-Height, Max-Width
What about when you have dynamic content - but you want to also try to control the height or width? Let's say, you really need to make sure a section
element takes up 100px in vertical screen space. You could of course set the section
element to have height:100px
.
<section style='border: thin red solid;height:100px'>
It's a good thing we can set height, since we really can't afford for this
section element to occupy more than 100px in vertical real estate. We have
something very important to add below it!
Of course, how big is this actually right now? And what if this text was
computed, or retrieved from a database - and we didn't know ahead of time
how much there was?
</section>
Of course, how big is this actually right now? And what if this text was computed, or retrieved from a database - and we didn't know ahead of time how much there was?
Clearly, since we set the height, we probably want to add overflow. So let's add overflow-y:auto
.
Of course, how big is this actually right now? And what if this text was computed, or retrieved from a database - and we didn't know ahead of time how much there was?
At least now we can scroll.
OK, that's nice. But what if the text is short?
Ideally, we don't want to use all 100px if we don't need it. Instead, we can use max-height
!
<section style='border: thin red solid;max-height:100px'>
Now this won't take up more than 100px, but it won't grow unnecessarily.
</section>
If the content does need 100px or more, scroll still activates.
As we will discuss in moment, and again when we cover more of layout, less
control you take, usually the better!
More Goodies
CSS is a large language. We are covering the most commonly used CSS properties. You are learning the skills to use any CSS property, so just because it's not discussed in this book doesn't mean there's any mystery to them.
For example, we've seen background-color
as a way to set the color of an inline or block element. There are other related properties too:
background-image
- Sets the background image for an elementbackground-repeat
- Sets how a background image will be repeatedbackground-position
- Sets the starting position of a background imagebackground-attachment
- Sets whether a background image is fixed or scrolls with the rest of the page
There's so many ways to combine the above with different aspects of CSS!
There are other mechanisms that help you control layout within some elements. For example, in a block element you can
request that the browser divide text content into columns. The columns
property can be specified with an integer, indicating the number of columns, or width - indicating how wide the columns should be - where the number of columns is computed.
.three-columns {
columns: 3;
}
.skinny-columns {
columns: auto 3em;
}
.wide-columns {
columns: auto 18em;
}
3 Columns
Skinny Columns
Wide Columns
Centering (Horizontally)
Let's say we have a div
with a width set to 30% of the screen's width. Here it is:
We can center the div within it's parent using two methods:
- Set the
text-align
property on the parent - Set the
margin-inline
toauto
on the element.
<div style='text-align:center'>
<div style='width:30%'>
30% of the width of the parent
</div>
</div>
<div>
<div style='width:30%; margin-inline:auto'>
30% of the width of the parent
</div>
</div>
In the first example, notice that the text-align
has the unwanted side effect of setting the text within the element to be centered. This is because the text-align
property is inherited, and so setting it on the parent effects the child. We could undo this,by setting text-align:left
on the element that we are centering, but that's a bit of a nuisance.
The second option - using margin-inline:auto
is preferred, in that it does not require the use of any modifications to the parent. We could also set both margin-left
and margin-right
to auto
to achieve the same effect.
Vertical Alignment (Inline Elements)
As we've seen, inline elements line up left to right, wrapping as needed. Text of varying sizes all get put into the same set of line boxes, with the height being defined by the largest font.
<p>
This is a normal string of text, but there are some spans with much larger font sizes.
For example, <span style="font-size:5em">this is really big.</span> The large font
sizes create gaps in the lines, as you'd expect. <span style="font-size:5em">There really
isn't any choice</span>, is there?
</p>
This is a normal string of text, but there are some spans with much larger font sizes. For example, this is really big. The large font sizes create gaps in the lines, as you'd expect. There really isn't any choice, is there?
Where things get really tricky is when text and other inline images also get included. For example, text can have images embedded.
This is a normal string of text, but there are some images emebedded inside. These images also break out of the
font's vertical limits, causing the line height to adjust. Here's one, and here's another
This is sort of the nature of things, but we do have some control. For example, the vertical-align
attribute can instruct an element to
align itself with it's sibling elements using it's bottom
(default), top
, or middle
. Here's an example where the larger text spans
use top
instead of bottom
:
This is a normal string of text, but there are some spans with much larger font sizes. For example, this is really big. The large font sizes create gaps in the lines, as you'd expect. Now we are aligning with the top of the smaller text though, by setting the span's vertical-align.
Middle also works, and often provides the best visual design:
This is a normal string of text, but there are some spans with much larger font sizes. For example, this is really big. The large font sizes create gaps in the lines, as you'd expect. Now we are aligning with the middle of the smaller text though, by setting the span's vertical-align.
vertical-align
has it's limits. For example, trying to center somethign vertically within it's parent is decidedly not what vertical-align is meant to do.
Vertically Centering?
Vertically centering elements within a parent is more difficult to do reliably than you might think. Take a simple case, trying to center a child element 50% of the height of it's parent. Can we achieve it using the same principles as we used with horizontal centering: setting margin-block:auto
?
<div style='height:200px; background-color:pink'>
<div style='height:50%;color:black;background-color:white;margin-block:auto;'>
NOPE
</div>
</div>
Alas, margin-top
, margin-bottom
, and margin-block
do not accept auto
as their values. There are tricks we can use. CSS supports some dynamic computations, so we could try to compute the margins (or the parent's padding) such that the element is centered vertically. Ultimately though, it becomes
very difficult to do this really well. Instead, we'll accomplish this elegantly using flexbox in later sections!
Going with the flow
If there's one thing you will learn the hard way, after doing this for long enough, it's that layout is hard. Not hard in terms of syntax and concepts - that might be hard at first, but you'll get past that. The hard part about layout is that your page is going to be displayed on lots of machines. Screen sizes vary. Pixel density varies. Users prefer large fonts, or small fonts, and control their browser's zoon features. They view your page in landscape mode. They view it in portrait mode. You have very little control.
Over the next few sections we will do more and more to deal with this - and allow our layouts to become more complex. We will need to work hard to keep those complex layouts looking good across a wide variety of screen sizes too - we'll need to make them responsive. These techniques will be powerful, and your skill level will increase dramatically.
All that said... less is more. The less control you try to exert over layout, the more you trust the browser. Guess what - when dealing with mostly text based documents - the browser is amazingly good. The more control you leave to the browser, the more confidence you will have have that it will always look good. It won't be flashy, but it will get the job done.
Really, the next few section are about exerting more control over layout. This is often necessary to meet your objectives - but keep in mind - you shouldn't try to control any more than you need to. Your designs will be simpler, and in most case easier for a broader set of users to work with.