
To Liz and Benjamin – you are my whole world.
In this book, we will take a tour of modern CSS. Whether you’re brand new to CSS or you have some experience and need a refresher, this book will have something for you.
However, this book will not teach you color theory or good design techniques. The intent of this book is to give you a strong foundation with the various CSS technologies.
In Chapter 1, we’ll start at the very beginning and talk about what CSS is and how it works. We’ll explore the DOM, the CSSOM, and the render tree as well as take a quick detour to look at CSS preprocessors (though we won’t cover them further in the book).
In Chapter 2, we will tackle CSS selectors. These are critical to understand. Selectors determine what CSS styles are applied to what elements. We’ll also explore the concept of specificity.
Once we’ve laid the groundwork, we’ll start to talk about CSS concepts in Chapter 3 like the box model, units, colors, and overflow. We’ll also look at CSS custom properties, better known as variables.
We’ll finally start applying styles in Chapter 4, where we’ll look at borders, box shadows, and opacity. We will see several ways to hide an element on the page.
In Chapter 5, we’ll learn all about backgrounds and gradients (which are actually a type of background image).
Chapter 6 deals with the important topic of styling text. We’ll learn about text styles and layout, as well as how to use web fonts.
We’ll see how to lay out and position elements in Chapter 7. This covers the different positions such as static, relative, absolute, fixed, and sticky. Also, in this chapter, we’ll see the topic of stacking contexts and Z-index, which often trip up even experienced developers.
In Chapter 8, we’ll cover CSS transforms. This allows you to apply transformations such as rotation, scale, and skew to elements. We’ll also see a few examples of creating shapes with CSS.
Transforms can be combined with transitions, which is one topic of Chapter 9, to create all kinds of interesting effects. Transitions can be applied to transforms or a slew of other CSS properties. Chapter 9 also covers animations, which takes the concepts of transitions to the next level.
Chapter 10 is dedicated to the flexible box layout, or flexbox, which is a powerful one-dimensional layout tool that has excellent browser support. With flexbox, we can finally easily center a div!
Chapter 11 is a gentle introduction to responsive design techniques. While it is not an exhaustive guide – entire books have been written on the subject – it lays a good foundation, covering topics such as media queries and fluid typography.
Finally, we save the best for last. Chapter 12 is all about CSS Grid, the latest and greatest layout tool in the CSS toolbox. It doesn’t have great Internet Explorer support, but all of the other major browsers have full support for it.
In Chapter 13 we’ll see some other topics for further learning, such as CSS methodologies like BEM and OOCSS, as well as utility-first CSS and Houdini, the future of CSS.
Let’s get started!
I’d like to start by thanking my wonderful wife, Liz, for her constant love and encouragement throughout the whole writing process – and for understanding when I locked myself away in solitude to write. And my little toddler, Benjamin, for giving me much-needed breaks from writing for play time.
Thanks to all my friends and family for always supporting and encouraging my interest in computers and technology.
This book began its life as a self-published work, and I’d like to thank Apress for making it what it is today. I’d also like to thank the awesome team at Apress – Louise Corrigan, Nancy Chen, and Jim Markham – for guiding me through the process every step of the way. I appreciate their patience with me as a first-time author.
Thanks to Alexander Nnakwue, the technical reviewer for this book, for his time and excellent feedback, helping make this book even better.
Special thanks also to Stephanie Eckles and Ellie Baker, two extremely talented CSS experts who reviewed some of the chapters in the previous, self-published version of this book.

is a software engineer specializing in front-end development. He has over 15 years’ experience working with JavaScript, HTML, and CSS and has worked extensively with front-end technologies such as Angular and React. He currently works at Salesforce and has worked in the past with companies such as Dell and Nortel. He is also the author of Using Gatsby and Netlify CMS, an Apress title. He lives in the Boston area with his wife and son. You can find him on Twitter at @JoeAttardi.

has a background in Mechanical Engineering from the University of Ibadan, Nigeria, and has been a front-end developer for over 3 years working on both web and mobile technologies. He also has experience as a technical author, writer, and reviewer. He enjoys programming for the Web, and occasionally, you can also find him playing soccer. He was born in Benin City and is currently based in Lagos, Nigeria.
Chances are that you already have some idea of what CSS is, or else you probably wouldn’t have been interested in this book. But let’s start at the beginning, to make sure we’re all on the same page.
CSS stands for Cascading Style Sheets. It’s a language for specifying how an HTML document is displayed. Without CSS, every website would just be Times New Roman with tiny buttons. It’s capable of much more than styling text, however. CSS lets us define entire layouts and position elements and even perform animations.
Style sheets are self-explanatory, but what is a cascading style sheet? Because more than one style rule could apply to a given HTML element, there needs to be some way to determine which rule should apply in the event of a conflict. The styles “cascade” from less specific to more specific selectors, and the most specific rule wins. Specificity is an important concept in CSS, and we will discuss that in more detail later.
This resulted in a tight coupling between the semantics and the presentation of a document, which made websites harder to maintain. There were some basic layout options, such as the center tag. For more advanced layouts, the only real option was to use HTML tables. Tables were meant to display tabular data. One disadvantage of using tables for layout is that someone using a screen reader will have a very hard time navigating the page.
There were several other proposals for style sheets for HTML documents as well, but CSS was first proposed at a conference in Chicago in 1994. The following year saw the birth of the World Wide Web Consortium (W3C), and the initial version of the CSS standard was published in late 1996. In the years since, CSS has evolved into a powerful tool for styling HTML documents.
A CSS style sheet consists of rules. CSS rules target HTML elements by using selectors that describe the elements to which the styles should be applied. As we will see later, elements can be selected in many ways.
A rule consists of a selector followed by a block of CSS properties contained inside curly braces. The properties consist of a property and value separated by a colon and are delimited with semicolons. A value may be a single value or a collection of multiple values, depending on the property.

The structure of a CSS rule
This rule targets any element with the class header (more on classes later). Any element with this class will have a red background and a 1-pixel solid blue border.
In the previous example, background-color and border are CSS properties. The border width is specified as 1px. px is a CSS unit. There are many units including em, rem, and %. We will learn more about the different CSS units later.
In Listing 1-2, the element with the class header will have a blue background because it is the last one in the rule. The second background-color property overrides the first.
@charset: Defines the character encoding used in the CSS file.
@import: Imports, or includes, the contents of another style sheet.
@media: Defines a media query. We will cover media queries in Chapter 11.
@keyframes: Defines a set of keyframes for a CSS animation. Animations will be covered in Chapter 9.
There are several ways to use CSS in an HTML document. They all have the end result: a style sheet that is applied to the document.
The element in Listing 1-4 will have a red background. The properties specified in an element’s inline style will apply to that element only. If there are conflicts in the rules that apply to an element, its inline style always takes precedence. For example, if there was a CSS rule somewhere that made all div elements have a blue background, this element’s inline style would override that and give it a red background.
In the preceding document, all div elements will have a red background.
The CSS features discussed in this book are well supported in modern browsers: recent versions of Chrome, Firefox, Edge, and Safari. Some features have limited or no support in older browsers such as Internet Explorer 11. These compatibility issues will be called out where applicable.
The main disadvantage of vendor prefixes is that they require duplication, as shown in Listing 1-8. For each block, the same keyframes have to be specified.
More recent browser versions tend to use experimental feature flags rather than vendor prefixes. These are special configuration flags that are exposed in an advanced configuration interface where experimental features can be turned on and off.
If you still need to support older browsers that use prefixes, there are tools such as Autoprefixer which lets you write prefix-free CSS. The tool then parses the CSS and generates a new style sheet containing the duplicated vendor-prefixed rules and properties.
There are many resources and references that are useful when using CSS. Here are a few of the best.

Screenshot of CanIUse.com
Another useful resource is the Mozilla Developer Network (https://developer.mozilla.org), or MDN for short. MDN has a complete reference to HTML, CSS, JavaScript, and more. It is an exhaustive reference to all CSS properties.
CSS is not without its limitations and pain points. CSS preprocessors, such as Sass, LESS, and Stylus, provide additional features not found in plain CSS. These tools provide an extended, or even completely different, syntax for writing CSS rules. When you build the application, the preprocessor takes your style sheets and converts them to plain CSS, ready to use in the browser.
We won’t cover CSS preprocessors in this book beyond this section, but here is a quick overview if you are interested in using them.
If you are supporting modern browsers, this is not as compelling of a reason to use a preprocessor, because recent versions of Edge, Firefox, Chrome, and Safari all support native CSS variables. However, if you need to support older browsers such as IE11, this can be a useful feature.
A mixin allows you to write a set of CSS properties and values, then apply that entire set of properties to another CSS rule without having to repeat all the code. If you have to support older browsers that expect vendor prefixes on some properties, this can be useful.
Mixins are very useful for cutting down on duplicated code. They can even take arguments to customize the resulting CSS .
Now, let’s take a look at how the browser renders a page with CSS.
The Document Object Model, or DOM, is a data structure in the browser. It is a tree of objects that represent the elements in the document and their structure and hierarchy. This tree is composed of DOM nodes. The DOM is created by reading the HTML markup, tokenizing it, parsing it, and finally creating the object hierarchy that makes up the DOM.

The DOM tree corresponds to the HTML document
Similar to the DOM, there is also a CSS Object Model, or CSSOM. This is another tree structure that represents the hierarchy of styles in the document. While they are both tree structures, the CSSOM is a separate structure from the DOM.

The CSSOM tree
Once the DOM and CSSOM are complete, they are combined to form the render tree. The render tree contains all the information the browser needs to render the page. To do this, the browser calculates which CSS rules apply to which elements in the DOM.

The render tree
Once the browser has created the render tree, it can begin laying out the elements on the page. This stage of the process looks at styles such as width, height, position, margin, and padding, to determine each element's size and location on the page. At the layout stage, however, nothing is actually shown on screen yet.
Once layout is complete, the browser can begin painting by applying styles such as color and font to determine the actual pixels to draw on the screen. Some types of styles, like gradients, have a higher performance impact than a solid color or image.
The syntax and structure of CSS rules
How conflicts are handled
The three ways to use CSS in an HTML document – inline styles, style blocks, and external style sheets
CSS preprocessors
The critical rendering path – the DOM, CSSOM, and render tree
One of the core concepts in CSS is that of selectors. A selector determines which element(s) a CSS rule applies to. There are several ways an element can be targeted with a selector, which we will cover in this chapter.
CSS selectors can target multiple elements on the page. That is, a single CSS rule can apply to multiple elements. An element or class selector can select multiple different elements that have that class or element name.
Similarly, a single HTML element can be affected by multiple CSS rules. An element will have the properties from all applicable CSS rules applied to it.
Universal
Element
ID
Class
Attribute
The CSS rule in Listing 2-1 will apply a margin of 0 to all elements in the document.
The CSS rule in Listing 2-2 will apply a margin of 25px to all p elements in the document.
An HTML element can have an id attribute. As a general rule, there should only be one element with a given id. If there are multiple elements with the same id, most browsers will match the rule with all elements having that id. However, this should be avoided as it violates the HTML specification.
The element with an id attribute whose value is header will receive 25px of padding. If there are other elements also having an id of header, they will receive 25px of padding in most browsers. Again, this should be avoided. If you need to apply a style to more than one element, you can use class selectors instead of ID selectors.
An HTML element can also have a class attribute. A class can be used to mark all elements of a related type.
While only a single element is intended to be targeted by an ID selector, any number of HTML elements can have the same class attribute. Similarly, a single HTML element can have any number of classes applied to it. Multiple classes are separated by a space in the value of the class attribute.
The rule in Listing 2-4 will match every element in the document with a class of nav-link and give it a color of darkcyan.
HTML elements can also be selected by their attribute values or by the presence of an attribute. The attribute is specified inside square brackets, and the attribute selector can take several forms.
Selects all elements that have the given attribute, regardless of its value.
Selects all elements that have the given attribute, whose value is the string value.
If we wrote a CSS rule with the selector [title~="World"], the first element would match but not the second. This is because in the second element, the word “World” in the title attribute is not surrounded by whitespace.
Selects all elements that have the given attribute, whose value contains the substring value. If we wrote another CSS rule, this time with the selector [title*="World"], it would match both of the preceding elements.
Selects all elements that have the given attribute, whose value begins with value.
Selects all elements that have the given attribute, whose value ends with value .
Matches all a elements with a class of nav-link that have an href attribute that contains the string example.com.
Matches all elements with a class of class-one as well as all elements with a class of class-two.
There's even more you can do with selectors. Combinators are used to select more specific elements. Combinators are used in conjunction with the basic selectors discussed earlier. For a given rule, multiple basic selectors can be used, joined by a combinator.
The descendant combinator matches an element that is a descendant of the element on the left-hand side. Descendant means that the element exists somewhere within the child hierarchy – it does not have to be a direct child.
Matches all div elements that are direct or indirect children of an element with a class of header. If any of these div elements have children that are also divs, those divs will also be matched by the selector.
Matches all div elements that are direct children of an element with a class of header. If those divs have children that are also divs, those divs will not be matched by the selector.
The selector .header ~ div would match two div elements: the one with class body and the one with class footer. Note that it does not match the div with class header.
The adjacent sibling combinator is similar to the general sibling combinator, except it only matches elements that are an immediate sibling. It is specified with a + character.
Looking back at the HTML in Listing 2-6, the selector .header + div would only match the body element, because it is the adjacent sibling of the heading element.
Another tool in the CSS selector toolbox is the pseudo-class. A pseudo-class allows you to select elements based on some special state of the element, in addition to all the selectors previously discussed.
Pseudo-classes start with a colon and can be used alone or in conjunction with other selectors.
Some pseudo-classes let you select elements based on UI state, while others let you select elements based on their position in the document (with more precision than the combinators).
There are many pseudo-classes (you can find a complete list at https://developer.mozilla.org/en-US/docs/Web/CSS/Pseudo-classes), but here are some of the more commonly used ones.
These pseudo-classes are based on some UI state.
Matches an element that is currently being activated. For buttons and links, this usually means the mouse button has been pressed but not yet released.
Matches a radio button, checkbox, or option inside a select element that is checked or selected.
Matches an element that currently has the focus. This is typically used for buttons, links, and text fields.
Matches an element that the mouse cursor is currently hovering over. This is typically used for buttons and links but can be applied to any type of element.
Used with form elements using HTML5 validation. The :valid pseudo-class matches an element which is currently valid according to the validation rules, and :invalid matches an element which is not currently valid.
Matches a link whose URL has already been visited by the user. To protect a user’s privacy, the browser limits what styling can be done on an element matched by this pseudo-class.
These pseudo-classes are based on an element’s position in the document.
The selector .my-list > li:first-child will match the first list item only, and the selector .my-list > li:last-child will match the last list item only.
This pseudo-class takes an argument. It matches an element that is the nth child of its parent. The index of the first child is 1. Referring back to Listing 2-7, we could also select the first item with the selector .my-list > li:nth-child(1), or the second item with the selector .my-list > li:nth-child(2).
The :nth-child pseudo-class can also select children at a given interval. For example, in a longer list, we could select every other list item with the selector .my-list > li:nth-child(2n). Or we could select every four items with the selector .my-list > li:nth-child(4n). We can even select all odd-numbered children with the selector .my-list > li:nth-child(odd) or even-numbered children with .my-list > li:nth-child(even).
Similar to :nth-child, except that it only considers children of the same type. For example, the selector div:nth-of-type(2) matches any div element that is the second div element among any group of children.
Matches the root element of the document. This is usually the html element. This selector can be useful for several reasons, one of which is that it can be used to declare global variables (we will discuss CSS variables in Chapter 3).
A selector can also include the :not() pseudo-class. :not accepts a selector as its argument and will match any element for which the selector does not match. For example, the selector div:not(.fancy) will match any div that does not have the fancy class.
A pseudo-element lets you select only part of a matched element. Pseudo-elements are specified with a double colon (::) followed by the pseudo-element name.
We haven’t discussed block vs. inline elements yet, but it should be noted that some pseudo-elements only apply to block-level elements.
Matches the first line of a block element.
Applies the styles only to the first letter of the first line of an element.
Two special pseudo-elements are ::before and ::after. These pseudo-elements don’t select part of the element; rather, they actually create a new element as either the first child or the last child of the matched element, respectively. These pseudo-elements are typically used to decorate or add effects to an element.
Suppose we want to add an indicator next to all external links on our website. We can tag these external links using a class, say, external-link.

The rendered link with an ::after pseudo-element
The ::after pseudo-element added the content (external) and made it green.
Sometimes, you may want to use a ::before or ::after pseudo-element for decorative purposes. In this case, you must still provide a value for the content property or else the element will not be displayed. For decorative elements, this can be set to an empty string.
We have a conflict. Our HTML element matches both selectors – it is indeed a div element, and it also has the profile class. Each rule specifies a different value for the background-color property. When the page is rendered, which background color will this div element have?

The rendered output
As you can see, the class selector rule’s background color was applied. This is because the class selector rule has a higher specificity. When there is a conflict of CSS properties across multiple rules, the rule with the most specific selector will be chosen. According to the rules of CSS, a class selector is more specific than an element selector.
Note that while the element has the background color from the class selector rule, it also has the color from the element selector rule. Specificity rules only matter for conflicting properties across multiple rules. Other properties in these multiple rules will still be applied.
Inline styles in an element’s style attribute
ID selectors
Class selectors, attribute selectors, and pseudo-classes
Element selectors and pseudo-elements
Neither the universal selector nor combinators factor into specificity.

Specificity calculator
If the element has an inline style, add a 1 to the first box. In this case, the inline style automatically wins.
For each ID in the selector, add 1 to the value in the second box. For each class, pseudo-class, or attribute in the selector, add 1 to the value in the third box. Finally, for each element or pseudo-element in the selector, add 1 to the value in the last box.

Calculating the specificity of a selector

Calculating the specificity values of the example selectors
Concatenating the four numbers a-b-c-d (in a number system with a large base) gives the specificity.
This would give us values of 10 for the class selector and 1 for the element selector. Looking at it this way, it is clear that the class selector is more specific.

Specificity calculation for two similar selectors
These selectors are very similar, but the one containing both an element and a class selector (specificity 11) is more specific than the one containing a class selector alone (specificity 10).
Lastly, if multiple conflicting rules are calculated to have the same specificity, the rule that appears last will win.
Any CSS property can have the keyword !important after it inside of a rule. This keyword will cause that property to always win in a conflict, even if the rule that contains it has lower specificity than another conflicting rule.
However, this is generally considered a bad practice. It can make CSS issues harder to debug and can make your style sheets less maintainable. In most cases, it’s better to determine the specificity of the rules you are trying to apply and use a more specific selector on the rule that you want to apply.
The most commonly used selectors are element, ID, class, and attribute selectors.
Combinators can be used to create even more specific selectors.
Multiple selectors can match an element.
Conflicts are resolved by using the rule that is more specific.
Specificity can be overridden by using !important, but this should be avoided.
Now that we’ve looked in detail at how to select elements, let’s start to explore how to style them. The next step is to look at some of the basic concepts in CSS.
Every element in CSS is treated like a rectangular box. This is sometimes referred to as the box model. The box is made up of four parts. Starting from the outside and moving toward the center, these are the margin, border, padding, and content.
The margin is the space between an element’s border and its surrounding elements. It is specified with the margin property.
The border is an outline around the box. Borders can be styled with a thickness, style, and color. It is specified with several properties: border-style, border-width, border-color, and border.
The padding is the space between the element’s border and the content itself. It is specified with the padding property .

The CSS box model

The rendered result

The same two elements with padding, border, and margin applied
The size of an element is specified with the width and height properties. How exactly this is interpreted, however, depends on the value of the box-sizing property . This property supports two values, content-box and border-box.
This is the default. With content-box , the width and height properties are treated as the width and height of the content area of the box only. The actual width and height taken up by the element’s box is the sum of the specified width and height (the content box), the padding on each side, and the border width on each side.

The full rendered size of the element using content-box

The full rendered size of the element using border-box
There are two types of HTML elements: block and inline (or a combination of the two, inline-block). Both block and inline elements follow the box model but are different in some important ways.
Some HTML elements are block elements (e.g., div) and some are inline elements (e.g., span). An element’s type can be changed by setting the display property to block, inline, or inline-block.
A block element always appears on its own line and takes up the full width of its containing element, unless an explicit width is set with the width property. The height of a block element, by default, is just enough to fit the height of its content, but this height can also explicitly be set with the height property.

The div elements rendered as block elements
The outer container element has an explicit width of 350px set, so it will be 350 pixels wide (technically, 354 pixels, since it is using content-box sizing).
The inner elements have no explicit width set, so they take up the full width of the container element. They also have no explicit height set, so they only take up enough vertical space to fit the text content.

The rendered inline element
The span is an inline element, so its width and height are only enough to fit its content. As you can see, it does not appear on its own line. If we were to give the span an explicit width or height, it would be ignored.

The rendered result

The odd behavior of vertical padding on an inline element
The padding was applied to the element, but no extra vertical space was made to accommodate the top and bottom padding. The background color of the span element bleeds into the adjacent content.
Inline elements behave similarly when it comes to margins – extra space is made for the horizontal margins but not the vertical ones.
The third element type is a combination of the first two. An inline-block element flows with the text like an inline element, but the width and height properties are respected, as are the vertical padding and margin.

The rendered inline-block element
The span element does not break onto a new line – it flows with the text like an inline element. However, its width and height properties are respected, as is the vertical padding. Extra space is made to account for the padding – it doesn’t bleed over into the surrounding content like with an inline element.
To give an element a padding of 10 pixels, we would specify a value of 10px for the padding property. In this expression, we say that px is a unit. CSS units are equivalent to different units of measurement. In the physical world, measurements have units such as inches, feet, or meters. Similarly, CSS has units such as px, em, and rem. We will go over some of these in this section.
We’ve already seen the px unit in several examples. In the past, we could have said that this corresponds to physical pixels on the screen. However, in the modern age of ultrahigh-resolution displays, this is no longer exactly accurate. A CSS pixel does not necessarily have a one-to-one correspondence to a physical device pixel. On a very high-resolution 4K display, 1 pixel is so tiny that it would be hard to see with the naked eye. If CSS used device pixels, then a 1-pixel border would barely be visible to the user.
Instead, a so-called logical pixel corresponds to a certain number of physical device pixels. A 1px border looks roughly equivalent on a 4K display as it does on a lower resolution display, but it may use more physical device pixels.
It is generally not recommended to use px units in CSS. The main reason is that these pixel-based dimensions don’t always scale well when a user adjusts the browser zoom level. This can be an accessibility issue.
There is one property that is appropriate to use pixels for – the page’s base font size. In most browsers, this defaults to 16px. The base font size is set by applying the font-size property to the root html element.
It’s also appropriate to use pixels in media queries, which we will cover in Chapter 11.
The header element has a font size of 24px. The padding is specified as 0.5em, or half of the element’s font size. Therefore, the padding applied to this element will be 12px.
Because font size is inherited, the li elements inside the header also start out with a font size of 24px. Then in the li element’s CSS rule, we set the font size to 0.75em. This is relative to the element’s current font size of 24px, so the actual font size of the li element would be 24px * 0.75 = 18px. Finally, the a elements inside the li elements have a font-size of 0.5em, which is relative to the li element’s font size of 18px, so its font size would be 18px * 0.5 = 9px.
Due to this cascading effect , sometimes using em units for nested elements can cause unintended effects to properties such as font size, padding, and margin.
The rem unit is also a relative unit. It stands for “root em” and is relative to the page’s base font size. For example, if the base font size is 16px (remember that this usually doesn’t correspond to physical pixels), a size of 1rem is equal to 16px. 1.5rem would be 16px * 1.5 = 24px.
rem units are a good choice, especially for layout properties, since the size of 1rem remains constant throughout the document (unlike the em unit). If the browser is zoomed, everything resizes nicely because it’s all proportional to the base font size.
Because rem units are proportional to the base font size, there is no cascading effect like there is with em units.
The viewport is the area of the page that is currently visible in your web browser. CSS also has units that are relative to the viewport size: vw (viewport width) and vh (viewport height). Each of these units are 1% of the viewport size in that direction, that is, 1vw is 1% of the viewport width and 1vh is 1% of the viewport height.
If the viewport is resized, then any elements using vw units will have their sizes adjusted accordingly. Because vw and vh are relative to the viewport size, they are a good choice when using responsive design techniques.
There are also two related units, vmin and vmax. vmin is defined as whichever is smaller – the viewport width or the viewport height – and vmax is the larger of the two.
The % unit is relative to the size of another value. What exactly this is relative to depends on the CSS property. For example, for the font-size property, the % unit is defined as a percentage of the parent element’s font size. However, for the padding property, % is defined as a percentage of the element’s width.
Like the viewport units, the percentage unit is very useful for responsive design, as we will see in Chapter 11.
Some property values take no units at all but rather just a number. For example, the opacity property expects a number between 0 and 1. Another example of this is some flexbox properties such as flex-grow and flex-shrink, which expect integer numbers without units.
We have seen the most commonly used CSS units, but there are others as well. There are absolute units such as cm (centimeters), mm (millimeters), in (inches), and pt (points). These units are sometimes used for print style sheets but are rarely seen in screen style sheets.
There are also some experimental units that do not yet have wide browser support. For example, the lh unit is relative to the element’s line height, but this unit is currently not supported on any browsers.
CSS includes some helpful built-in functions.
The calc function lets you combine the different units we saw earlier to calculate an exact amount. It can be used anywhere a value is expected. The real power of the calc function is that you can have mixed units in the calculation.
For example, suppose you want the height of an element to be 10 pixels short of 1.5rem. This can easily be accomplished with the calc function: calc(1.5rem - 10px). This is likely easier than doing the size calculations yourself to specify an exact pixel value.
In this example, we establish a standard unit of spacing for the document as a variable and can reference that later in the call to the calc function. You might also notice the var function, which as we will see later is used to reference variables.
There are several other functions, which we will cover in the relevant sections. For example, there are some functions for defining colors, which we will explore in the next section.
One of the most common things done with CSS is to change colors. This can include background color, text color, and border color. There are a multitude of colors, and they can be expressed in multiple ways.
CSS has many predefined color values. So far, all of the examples in the book have used these predefined color values. They range from basics like red and green to other shades like tomato, orangered, and skyblue. A full list of these colors can be found at https://developer.mozilla.org/en-US/docs/Web/CSS/color_value.
One of the ways to define a color is by the values of the color's red, green, and blue components. Any color can be expressed as a combination of RGB values. Each value of red, green, and blue is expressed as a number between 0 and 255.
Color | Hex code |
|---|---|
Black | #000000 |
White | #FFFFFF |
Red | #FF0000 |
Green | #00FF00 |
Blue | #0000FF |
Color | RGB notation |
|---|---|
Black | rgb(0, 0, 0) |
White | rgb(255, 255, 255) |
Red | rgb(255, 0, 0) |
Green | rgb(0, 255, 0) |
Blue | rgb(0, 0, 255) |
RGB colors can also specify an alpha value, which determines the opacity of the color. The alpha is a value between 0 (fully transparent) and 1 (fully opaque) or a percentage between 0% and 100%. To specify an alpha value, the rgba function is used. For example, for pure red with 50% opacity, the color would be defined as rgba(255, 0, 0, 0.5).
A color can also be expressed as a combination of hue, saturation, and lightness values. Hue is specified as a degree of an angle on the color wheel (from 0 to 360 degrees). 0 degrees is red, 120 degrees is green, and 240 degrees is blue.
Saturation is a percentage value of how much color is applied. 0% saturation is a shade of gray, and 100% saturation is the full color from the color wheel.
Finally, lightness is also a percentage value. 0% lightness is pure black, and 100% is pure white.
An HSL color is specified using the hsl function. For the color with a hue of 120 degrees, a saturation of 50% and a lightness of 50% would be specified as hsl(120, 50%, 50%).
Like RGB colors, HSL colors can also have an alpha value, specified using the hsla function. As described earlier, the alpha value can be a number between 0 and 1 or a percentage between 0% and 100%. For the color in the previous example to have 75% opacity, it would be specified as hsla(120, 50%, 50%, 0.75).
Anywhere a color is expected, the transparent keyword can be used. This will apply no color. This can be useful when an element is positioned on top of another element, and you want the element underneath to show through. The background color of most elements defaults to transparent.
The usage of the rgb/rgba and hsl/hsla functions as previously described has been the standard for a long time. There is a newer syntax for these functions that are slightly different. This newer format is completely optional, and in fact, if you have to support older browsers, you won’t want to use it.
There are no commas between the numbers.
Because of this, the rgba and hsla functions are no longer necessary. The alpha value can be specified with a slash in the base rgb and hsl functions.
The alpha value can also be specified on a hex color, for example, #FF00007F for pure red with 50% alpha.
Old syntax | Equivalent new syntax |
|---|---|
rgb(0, 0, 0) | rgb(0 0 0) |
rgba(255, 0, 0, 0.5) | rgb(255 0 0 / 0.5) |
hsl(120, 50%, 50%) | hsl(120 50% 50%) |
hsla(120, 50%, 50%, 0.75) | hsl(120 50% 50% / 0.75) |
This new color syntax is not supported in Internet Explorer.

Text overflowing the container
We can clearly see that the text content is too long to fit in a 2rem by 10rem container, so the content overflows the container element. Note that the content only overflows vertically. This is because the default behavior is to wrap the text to the next line when it doesn’t fit on one line. This ensures it does not overflow horizontally, but because an explicit height is set, the container will not grow to fit its content.

Horizontal and vertical overflow

Horizontal overflow of the container’s text content
We have some control over how overflow is handled with the overflow property. This property handles both horizontal and vertical overflow together. They can also be handled independently with the overflow-x and overflow-y properties. The default value is visible, which results in what we saw in the previous examples. There are a few other options available.

The overflowing content is clipped with overflow: hidden
When the overflow property is set to scroll, the overflowing content is initially not visible. However, there are scrollbars provided so that the user can scroll and view the overflowing content. The scrollbars are always provided, even if the content does not overflow.
This behaves similarly to scroll. The difference is that when overflow is set to auto, the scrollbars are only provided if the content actually overflows.
Variables are a common feature in all programming languages. A variable is a way to store a piece of data, under a descriptive name, and that value can be referenced later by the variable name.
Variables have been available for many years with CSS preprocessing tools like Sass and Less. CSS variables, officially called CSS custom properties , were introduced later. Support for CSS variables was not great until more recently. They are now supported in all modern browsers.
CSS variables are not supported in Internet Explorer.
Why would we want to use variables in CSS? Suppose you're designing a website for a company. You use their brand color, #3FA2D9, in many places throughout your CSS. Later, the site is going through a rebranding, and the brand color is changing. You now have to change the brand color in every place you used #3FA2D9.
Instead, you can define a brand-color variable and reference that variable everywhere you need to use the brand color. Later, when that color changes, you simply need to change the color value once – in the variable declaration.

The rendered result

The rendered result

Three visible rows
We set two variables: an explicit row height of 1.5rem and a visible row count of 3. The container should be as high as three rows, so in the .container rule, we use calc to multiply the --row-height variable by the --visible-rows variable, which will yield exactly the correct height.
We also apply overflow: auto, which as we saw earlier hides the overflow and makes it scrollable.
All elements are represented by a rectangular box with content, padding, border, and margin.
There are three main types of elements: block, inline, and inline-block.
px should be avoided except when setting the document’s base font size.
em is relative to the element’s font size.
rem is relative to the document’s base font size.
vw, vh, vmin, and vmax are relative to the viewport size.
The calc function is used to compute CSS values by performing calculations with multiple values, potentially with different units.
Named colors: red, blue, and orangered
Hexadecimal RGB: #FF0000
rgb function: rgb(255, 0, 0)
hsl function: hsl(90, 50%, 25%)
If an element’s content cannot fit inside of it, the content will overflow.
Overflow handling can be changed with the overflow, overflow-x, and overflow-y properties.
A variable is declared with two leading slashes: --var-name.
A variable is referenced with the var function: var(--var-name).
Variable values are inherited by descendant elements.
By now, you have a solid grasp of the main underlying concepts of CSS. Now it's time to dive in and start learning some CSS properties and styling techniques. We'll start with the basics in this chapter.
First, a few notes on CSS property values.
initial: Uses the initial value set by the browser’s built-in style sheet.
inherit: Takes the value used by the element’s parent.
unset: If the property naturally inherits from its parent, such as font-size, it is set to the inherited value. Otherwise, it is set to the initial value from the browser’s style sheet.
We’ve seen how many CSS properties , such as border-width, padding, and margin, can take a single value. These are known as shorthand properties . For these properties, the single value given will be used for the top, bottom, left, and right.
Each shorthand property has four equivalent properties for each side of the element. For example, for the padding shorthand element, there are also padding-top, padding-bottom, padding-left, and padding-right properties.
If you want to specify different values for different sides of the element, you can still use the shorthand property – just give it multiple values.

All four borders have the same width

The rendered result

The rendered result

All borders have different widths
For most elements, the border is invisible by default. CSS has several properties for styling a border.
The border-color property , as its name implies, sets the color of the border.
The border-width property determines how thick the border is. The value of border-width can be a value like 3px. There are also some predefined values: thin, medium, and thick.

The different border styles

Borders are not collapsed

Borders are now collapsed
Any cell borders adjacent to another border have been collapsed into a single border.

A box with rounded corners

The meaning of border radius

The box with elliptical border radius

Explanation of the elliptical border radius

The resulting shape
X offset
Y offset
Blur radius: How far out the shadow is blurred
Spread radius: How far the shadow extends beyond the element’s dimensions

The rendered result

The rendered shadow with a blur radius

The rendered shadow

The full rendered shadow

The rendered result

The rendered result

The rendered result, showing two box shadows
By default, most elements start out with a transparent background. When a background color or image is assigned, that element becomes opaque. You cannot see through the element to what's behind it. Borders and text are also opaque.
You can change this behavior with the opacity property. opacity applies to the entire element – background, border, text, images, and any other content within that element or its children.

The rendered result

Decreasing the opacity
There are a few ways you can hide an element on the page using CSS.
When the display property is set to none, the element is removed from the flow of the document as if it was never there. Other elements will move to fill in the space.
Consider the example shown in Figure 4-22.
If we set the blue middle element’s display property to none, it is removed, and the other block moves to the left to fill the empty space, as shown in Figure 4-23.
Another way to hide an element is by setting the visibility property to hidden. This behaves a little differently than display: none – the flow of the document is not affected. This means that no elements will move to fill the empty space left by the hidden element.

Three boxes

The middle element hidden with display: none

The empty space left by visibility: hidden
To restore the hidden element, we can set the visibility property back to visible.
The last way to hide an element is to set its opacity property to 0. This has the same net effect as visibility: hidden – the element is effectively hidden, but the layout is unchanged. To show the element again, just set opacity back to 1.
One reason you may want to use opacity: 0 instead of visibility: hidden is while the visibility property will show or hide the element immediately, the opacity property can be transitioned gradually with a CSS transition, which we'll learn more about in Chapter 9. This can be used to create a subtle fade-in/fade-out effect.
A border has a style, width, and color.
Rounded corners can be created with the border-radius property.
An element can have an inner and/or outer box shadow.
The opacity property determines the transparency of an element.
An element can be hidden in three ways: display: none, visibility: hidden, or opacity: 0.
Any HTML element can have a background. Backgrounds can be images, solid colors, or even gradients.

The rendered result
Images can also be used as an element’s background. There are several properties that control the background image.
One or more background images can be applied using the background-image property. This accepts one or more image URLs. The background images will be stacked on top of each other, with the first image on top.
Absolute URL: url('https://imgur.com/my-image.png');
Relative URL: url('/header.png');

The rendered result

The tiled background image

The rendered result

The rendered result

The rendered result

The background image in the specified position

The background image is tiled

A gap inside the element

The rendered result
This looks better. The background fills the element without tiling or a gap. It’s still not ideal, though. Most of the image is cut off – we don’t see the mountains, which are the focal point of the image.

A better view of the background image

Setting background-size to contain

The rendered result
This behavior can be changed with the background-clip property . It can take the following values.

The background is behind the border and padding areas

The background does not extend behind the border

The background only extends into the content area
The background-size must come directly after background-position, separated by a slash.
The background-color must come last.
In addition to solid colors and images, CSS also supports gradient backgrounds. In the past, this was achieved by creating an image containing the desired gradient and setting that as a background-image. With modern browsers, and even IE11, this is no longer needed, as gradients are natively supported.
Linear gradients go along a straight line. They can go left to right, top to bottom, or at an arbitrary angle.
Radial gradients start at a central point and radiate outward.
There is no CSS property for gradients; they are treated as background images. Gradients are specified with the linear-gradient and radial-gradient functions in the value for the background-image property.

The rendered gradient

The rendered gradient

A gradient with transparent stops

A horizontal gradient

The rendered gradient
Notice how the gradient starts with a solid red color, then gradually transitions to blue at the 25% mark.

A gradient with a solid region in the middle
Note that between 25% and 75%, the color is solid green. Similarly, if the first stop is after 0%, or the last stop is before 100%, the remaining space before or after will be a solid color.
In addition to percentages, color stops can be specified with any valid length value in px, em, rem, or other units.

The rendered radial gradient

The circular gradient

The circle gradient at 25%
The center of the circle in Figure 5-26 is at the 25% mark along the horizontal axis.

The circle gradient in the top-left corner
closest-side: The gradient ends at the side closest to the center of the gradient. For a wide rectangle, this would be the top or bottom.
farthest-side: The gradient ends at the side farthest from the center of the gradient. For a wide rectangle, this would be the left or right.
closest-corner: The gradient ends at the closest corner to its center.
farthest-corner: The gradient ends at the farthest corner from its center. This is the default.

The rendered gradient

The rendered gradients
Similarly, a shadow effect could be achieved by using a dark color such as gray or black instead of white.

The rendered result
An element’s background can be a solid color, an image, a gradient, or a combination of the three.
The display of a background image can be customized with the background-repeat, background-position, background-size, and background-clip properties.
Gradients can be linear or radial.
Gradients can be combined with background images or colors.
Now that we’ve covered the basics of styling, let’s explore the styling of text.
You can use any font installed on your system for styling your website, but you should use a web-safe font. These are fonts that are generally considered safe to use because they are available on most users' systems.
Font | Family |
|---|---|
Arial Trebuchet MS Verdana | sans-serif |
Courier New | monospace |
Georgia Times New Roman | serif |
There are several CSS properties that control basic text styling. We’ll discuss a few of them in this section.
The font-family property sets the font to use for the element's text. This font is inherited by descendant elements.
In this example, the browser will try Georgia first. If Georgia is unavailable, it will try Times New Roman. Lastly, if that is not available, it will fall back to a built-in generic serif font. Custom web fonts can also be used. We will discuss that a little later.
An element inherits its parent's font size by default. This behavior can be overridden by using the font-size property, which sets the font size for the element. Recall that the document has a base font size - usually 16px. The value of font-size not only controls the size of the text, but it also determines base sizing for anything specified in em or other relative units. The em unit is not just for text. Borders, padding, and even width and height, can all be specified in ems.
In addition, there are several predefined font-size values, ranging from xx-small to xxx-large. A relative font size can also be specified, with a value of smaller or larger. A font-size can also be specified as a percentage of its parent's size.

The parent and child have different font sizes
Note that while the parent and child elements both have a font-size of 1.5em, the child's text is larger. This is the compounding effect. The parent's font size is 1.5em, or 1.5 times its parent's font size, which is the root element's font size of 16px. This comes out to 24px.
The child element's font size is also 1.5em, 1.5 times of its parent, which we just calculated to be 24px. 24px * 1.5 = 36px.
If we used rem units for the parent and child, they would have the same font size because rem is relative to the root element's font size.

The rendered result
The font-weight property defines how bold the text appears. This can be a simple value like normal or bold. It can also take numeric values: 100, 200, 300, 400, 500, 600, 700, 800, and 900. The higher the number, the bolder the font is. The normal value is equivalent to a weight of 400, and the bold value is equivalent to a weight of 700. Depending on the font used, not all weights may be available.
font-weight can also be specified as the values lighter or bolder. These values are relative to the weight of the element's parent.
The font-style property can be used to make text italic. It has three supported values: normal, italic, and oblique. Italic and oblique are similar but slightly different. Italic is typically an angled font face, sometimes with a completely different design than the normal version. On the other hand, oblique is typically just the normal version, slanted.
Not all fonts include both an italic and oblique version. In this case, the italic and oblique styles look the same.
The text-decoration property can be used to add decorative lines to text. These can be underlines, strikethrough lines, and even wavy lines (on most browsers).

The different text decoration types

The various text decoration styles
Some elements, such as links, have an underline by default. This can be removed by setting text-decoration to none.
In Internet Explorer 11, only the basic usage of text-decoration is supported. That is, text-decoration can be set to none, underline, or line-through. Colors and styles are not supported.

The text is transformed to all uppercase letters
Some of the other values for text-transform are none, capitalize, and lowercase.

The letters are spaced an extra 5px apart

The rendered text with small caps
In addition to styling, there are also several useful properties that affect the text layout.

The first line of text in the paragraph is indented by 50px
The white-space property is used to specify how whitespace is handled inside an element that contains text. The default value is normal. With this value, sequential whitespace characters are collapsed. If the text content exceeds the width of its container, it will be wrapped to the next line.

The whitespace is ignored
The extra spaces and line breaks were ignored, and the text only breaks to the next line when it automatically wraps .

The whitespace is preserved
Notice how the whitespace is preserved in the rendered output now. You might also notice that there is an extra blank line at the top of the element. This represents the first line break after the opening div tag .
When white-space is set to pre, lines of text are not automatically wrapped.
normal: The default behavior. Whitespace is collapsed, and text is automatically wrapped as needed.
nowrap: Same as normal, except that lines of text do not wrap.
pre-wrap: Same as pre, except that lines of text are also wrapped.
pre-line: Same as pre-wrap, except that consecutive whitespace characters are collapsed. Line breaks are still preserved.
break-spaces: Same as pre-wrap, except that line wrapping behavior is slightly different. Not supported in Internet Explorer.
Your design may require that text must fit within its container without overflowing or wrapping. This can easily be accomplished by using the white-space, overflow, and text-overflow properties together.

The overflowing text is truncated with an ellipsis
The line-height property controls the height of each line of text. It can be used to add spacing between lines of text.

The text is horizontally centered
text-align doesn’t just affect text . It sets the horizontal alignment of any inline element inside the containing element on which text-align is set.

The text defaults to top vertical alignment

The text is vertically centered

The rendered result

The elements are vertically aligned
If you don't want to use the web-safe fonts (and who wants to see another website in Arial or Times New Roman!), you are in luck. Web fonts allow the CSS to link to a font file that the browser can download. By using a web font, you can have a much more consistent look – plus, there are many beautiful web fonts out there that will enhance the look of your site or app.
Web Open Font Format version 1 or 2 (WOFF/WOFF2)
Embedded Open Type (EOT)
TrueType Font (TTF)
Scalable Vector Graphics (SVG)
Modern browsers support WOFF and WOFF2. The other font formats are for support with older browsers. A web font is typically packaged in several different formats, all of which can be referenced in the CSS.
A web font is registered using the @font-face at-rule . A @font-face rule declares a new font. The desired name of the font is given with the font-family property, and one or more source URLs are given with the src property. Each source URL is followed by a format declaration which tells the browser which font format to expect for that file.
Once you have declared the font in a @font-face rule, you can then use the name you gave it in any font-family property in a CSS rule.
Like any other resource, the browser must download the web font files before they can be used. If this is not done quickly, the browser may render the site in a fallback font while the web font is still loading. Once the font is loaded, the text is re-rendered in the new font. This results in unstyled text briefly appearing before being replaced by the correctly styled text – the so-called flash of unstyled text.
Some browsers will also hide the text (up to several seconds) until the font is loaded. This results in a different, but even more annoying, phenomenon – the "flash of invisible text."
The main issue with this is that the page may reflow once the font is loaded. If the user had already started reading the text rendered in the system fallback font, they may lose their place, and it can be jarring.
One way to address this issue is to use a tool like the Web Font Loader (https://github.com/typekit/webfontloader), a JavaScript library that manages the loading of web fonts. This tool gives you greater control over how fonts are used during the loading process. For example, a different fallback font that looks more similar to the actual font can be used, rather than the fallback used by the browser.
This will still result in a flash of unstyled text, however, which can affect the page layout. However, with Web Font Loader, you can also tweak the font-size and line-height of the fallback font. This can result in a smoother experience.
The flash of unstyled text can't be completely solved, but its severity can be greatly reduced by using a library like Web Font Loader.
Web fonts are great, but don't go overboard. The more fonts that are loaded, the longer the page takes to load, and the worse the flash of unstyled text can be. You should make sure to use only the web fonts that you absolutely need.
The text-shadow property allows you to add shadows to text. It works similarly to the box-shadow property we saw earlier. A text shadow has X and Y offsets, an optional blur radius, and a color. Unlike box-shadow, text-shadow does not have a spread radius.

The rendered text with shadow

The rendered result
You can customize the font size, color, weight, and style of text.
The text-decoration property can add underlines and strikethroughs.
There are other text effect properties such as text-transform, letter-spacing, and font-variant.
The white-space property controls how the browser renders whitespace. It can be ignored or respected.
The vertical-align property controls how inline elements are vertically aligned with each other.
Fonts can be downloaded by the browser and used with a @font-face rule.
Text shadows can be applied with the text-shadow property.
We’ve looked a lot at how to style with CSS. Let’s switch gears now and look at how to lay out and position elements.
The padding is the spacing between an element’s content and its border. By default, most elements have zero padding. An element’s padding is not inherited by its children.

The rendered result

The dimensions of the inner element
The margin is the space between an element’s border and other elements. The value of the margin property can be a size value, a percentage, or the keyword auto.

The rendered result

Margin between the inner and outer borders
Like padding, using a percentage for the margin property will set the given percentage of the containing block’s width as the margin.
The margin property also accepts the auto value. When the horizontal (left and right) margin is set to auto in a block or inline-block element, the element is centered horizontally within its containing element. The element takes up the specified width, and the margin is automatically distributed evenly between the left and right margins.

The element centered horizontally
When two elements with a vertical margin meet vertically, the two margins are collapsed into a single margin. The size of the collapsed margin depends on the size of the two margins being collapsed. If they are the same size, then the collapsed margin will be the same size as the common margin. If they are different sizes, the collapsed margin will take the size of the larger margin.
Margin collapse applies to vertical margins only.

The margins of the inner element collapse

The margin no longer collapses
The margin of the container element
The padding of the container element
The margin of the inner element
The CSS position property determines how an element is positioned. The top, right, bottom, and left properties are used in conjunction with position to determine an element’s final position. The default position is static.
If an element’s position property is set to any value other than static, it is considered a positioned element. This has important implications about the positioning of descendant elements.
static is the default value of the position property. A statically positioned element is positioned in the normal flow of the document. When position is set to static, the top, right, bottom, and left properties have no effect.
A relatively positioned element is positioned relative to where it would normally appear in the flow of the document. If position is set to relative, but top, right, bottom, or left are not specified, it essentially has the same effect as if position were set to static. The one difference would be that the element would now be considered a positioned element, which can affect child elements with other position values.
When an element has a position of relative, the other elements in the document flow are not affected, even if the element has an offset. The element’s original position remains in the document flow.

The blue square is offset from its original position
In Figure 7-8, the blue square is offset 10px below the top of its original position and 10px to the right of its original position. Note that the red and green squares remain in the same place they would be even if the blue square did not have the top and left offsets – they did not move to fill the space.
When a vertical or horizontal offset is given, the element is moved in the opposite direction. That is, top moves the element down, left moves the element to the right, right moves the element to the left, and bottom moves the element up.
What happens if you specify conflicting offsets? For example, an element can’t be 10 pixels below its top position and 10 pixels above its bottom position and have the correct size. Generally, if both top and bottom are specified, the top value is used, and the bottom value is ignored. Similarly, if both left and right are specified, left wins if the text direction is left to right and right wins if the text direction is right to left.
An absolutely positioned element can also have top, right, bottom, and left offsets that affect its position. There are two main differences, though.
First, an absolutely positioned element is removed from the document flow and “floats” above it. The layout of other elements will be adjusted as if the absolutely positioned element is not there.
The other difference is the interpretation of the offsets. While a relatively positioned element’s offsets are relative to the element’s original position in the document, an absolute positioned element’s offsets are relative to the closest ancestor positioned element. This is not necessarily the element’s direct parent.

The example setup

The rendered result
This may not be what you expected. The green box is now all the way on the right-hand side of the screen. This is because it has no ancestor element that is positioned. Thus, it becomes positioned relative to the document.

The green box has changed position
Now the green box is absolutely positioned relative to the outer red box, because that is the closest ancestor that is a positioned element.

The green box has moved again
Now that the blue box is the nearest positioned ancestor of the green box, the green box is now positioned relative to the blue box.
Like absolute, a position of fixed removes the element from the document’s flow. Its position is determined by setting top, right, bottom, and left properties. The difference is that for a fixed element, these offsets are always relative to the viewport. This means that even if the page is scrolled, a fixed element will remain in the same position. This is useful, for example, for a fixed header or navigation bar.
A block element with a position of static or relative will, by default, take up the full width of its container. However, if an element is given a position of absolute or fixed, this will no longer be the case. It will only be as wide as it needs to be to fit its content. This can usually be solved by adding a width: 100% to the element if the full width behavior is still desired.
A sticky element is a combination of relative and fixed. The element acts as a relatively positioned element, scrolling with the document. When the element reaches a specified point, it turns into a fixed element. This point is specified via a top, right, bottom, or left value.
position: sticky is not supported in Internet Explorer.

The header is above the overlay
The main text content is covered by the overlay, but the header is not. This can be solved by giving these elements z-index values. z-index determines the stacking order of elements along the z-axis – which element is on top of which. z-index is a relative measure that can take any numeric value. Items with a higher z-index will appear on top of those with a lower one.

The overlay now covers all content
Because the overlay has a higher z-index than the header, it now appears on top of the header.
As it turns out, z-index doesn’t control an element’s z-axis ordering globally within the entire document. It only controls the z-index relative to other elements within a given stacking context .
Any element that has a position other than static and a z-index other than auto
Any element with an opacity less than 1
Any element that is a child of a flex or grid container and a z-index other than auto
The background and borders of the element that creates the stacking context
Descendant elements of the element that creates the stacking context that are not positioned
Descendant elements of the element that creates the stacking context that are positioned
These rules, in conjunction with explicitly set z-index properties, determine the final stacking order of elements.

The rendered result
Note that the two container elements both have position set to relative and a z-index of 100. This means that each of these elements creates a new stacking context.

A cross-section view

The changed cross-section
By setting the z-index property on the relatively positioned green box, we’ve created a new stacking context rooted at the green box. To demonstrate this further, let’s try changing the z-index of the green box to 50.
The result, and the cross-section, are the same as before. Even though the green box has a lower z-index (50) than the red box (100), it’s in a higher stacking context, so it will still appear above the red box.
As you can see, z-index issues can be difficult to debug. Understanding how stacking contexts work is critical to solving these issues.

The floated red box
The red box is floated to the right, and the text flows around it. The float property can be set to left or right, or if you need to take text direction into account (left-to-right vs. right-to-left languages), you can use the more generic inline-start or inline-end.

The two floating boxes
First, the red box is floated right, to the edge of the container. Next, the blue box is floated right, to the edge of the red box.

The rendered result
The content has been moved to below the right-floated red box. Since the blue box on the left is taller, it is allowed to be floated alongside the content.
Padding is the spacing between an element’s content and its border.
Margin is the spacing between an element’s border and other elements.
An element is said to be positioned if it has a position other than static.
Statically positioned elements flow normally.
Relatively positioned elements are positioned relative to their normal position in the document.
Absolutely positioned elements are positioned relative to their nearest positioned ancestor.
Fixed-positioned elements remain fixed to the viewport.
Sticky-positioned elements are a hybrid between relative and fixed position.
The z-index property controls the vertical stacking order of an element within a given stacking context.
The float property allows an element to be floated to the left or right sides of its container.
CSS transforms take an element and apply one of several possible transformation functions to it. For example, an element can be rotated in 2D or 3D space, scaled, skewed, or translated (moved). Transforms can be used to create all kinds of interesting effects on their own and become more powerful when combined with transitions and animations.
Transforms are specified with the transform property. Its value is one or more transform functions . Multiple transforms can be applied by providing a space-separated list of transform functions. The following sections will go over the most common classes of transform functions.
The perspective transform activates 3D space for an element. It defines how “far away” the object is from the user, as if the screen had depth. Used by itself, the perspective transform has no visible effect. But when used in combination with other transforms, it can greatly affect the final result.
deg: Degrees. A full circle is 360deg.
grad: Gradians. A full circle is 400grad.
rad: Radians. A full circle is approximately 2π radians, or approximately 6.28rad.
turn: Number of turns. A full circle is 1turn.
A positive angle rotates the element clockwise; a negative angle rotates it counterclockwise. Note that an element can be rotated more than one full circle.
X-axis: Goes from left to right across the page
Y-axis: Goes from the top of the page to the bottom
Z-axis: 3D axis, goes from the “surface” of the page out toward you

The three axes of rotation

The default rotation origin

A different rotation origin
transform-origin is specified as one, two, or three values. These values correspond to the X, Y, and Z offsets of the transform origin point. These can be size values such as 10px or 25% or one of the keywords left, center, right, top, and bottom.

The rotated element

The rotated element

The rotated element

The rotated element
The next type of transform we’ll look at is translation. Translating an element means moving it from its original position.

The translated element
The translateX and translateY functions perform translation horizontally and vertically, respectively. The effect is the same as when using the translate function. That is, translateX(1rem) is equivalent to translate(1rem, 0), and translateY(1rem) is equivalent to translate(0, 1rem).

The translated element

The translated element
A scaling transform function alters the size of the element, scaling its contents as it grows or shrinks. Unlike the translation functions, which take a size value, the scaling functions take multiples of the original size as arguments. For example, scale(1, 1) would perform no scaling. scale(2, 3) would scale the element to two times as large in the horizontal direction and three times as large in the vertical direction.
The arguments to scaling functions do not have to be integers. For example, values such as scale(1.25, 2.6) are also accepted.

No scale (left), scale with origin at center (middle), and scale with origin at top (right)

The scaled element
If you only want to scale in one direction, you can also use the scaleX or scaleY functions.
scaleZ scales along the Z-axis. It has no noticeable effect when used alone. The effect is best seen when used in combination with perspective and another transform like rotateX.

The effect of the scaleZ function
Like rotate3d and translate3d, there is also a scale3d function . As with the other related functions, scale3d scales along a vector with X, Y, and Z components. Different magnitudes can be given for the different axes, so an element can be scaled at different rates in all three directions.

The skewed element
There are also skewX and skewY functions as well, if you only want to skew an element in one direction.

The rotated box

The rotated box’s coordinate system

The element’s resulting position
Note that the box moved along the rotated X-axis rather than the page’s X-axis.

The element’s new position
This time, the order of transformations was different. The box was first translated along the X-axis, and then it was rotated. Because the box wasn’t rotated when it was translated, it moved along the page’s X-axis.

Applying three transforms to the element
We moved the element 100 pixels along the X-axis then rotated it 45 degrees, as before. When we rotated the box, its coordinate system changed. When we applied the second translateX transform, the box moved along its rotated X-axis.

The component parts of the heart shape
All we need to do is take a square, rotate it 45 degrees, and place two circles in the proper position. We can actually do this with a single div element. The div will make up the square part of the heart. We’ll then use the ::before and ::after pseudo-elements to draw the circles.

The rotated square

The circle pseudo-elements
The circles are stacked on top of each other in the center of the square. Now we just need to move them out to either side. Remember that the coordinate system rotated with the square. The left-hand circle needs to be moved 5rem (half of the size of the square) to the left along the X-axis, and the right-hand circle needs to be moved 5rem higher along the Y-axis.

The completed heart
We’ve seen everything here except for the transform-style property. By default, an element’s children are flattened to be on the same plane. This means they are “squashed” down to 2D space. We want to make a 3D cube, so that won’t work here. Setting transform-style to preserve-3d will allow the cube’s child elements to exist in 3D space.
transform-style: preserve-3d is not supported in Internet Explorer.
Currently, all of the faces of the cube are lying flat, stacked on top of one another since they are absolutely positioned. What we need to do is rotate the faces, in 3D space, so that they are facing the correct way, then move them out from the center to form the cube.

The cube faces all stacked on top of each other

The rotated cube faces

The completed cube
Rotation
Translation
Scaling
Skewing
We also learned about concepts such as perspective, axes, transform origins, and 3D transformation vectors. Lastly, we saw a few practical examples of making shapes with CSS transforms.
CSS transforms are useful on their own, but they are even more powerful when used in combination with transitions and animations.
A CSS transition is a way of animating an element from one state to another. They are similar to, but not quite the same as, CSS animations.
During the lifetime of a page, an element’s CSS properties can change. For example, the user could hover over an element, triggering the :hover pseudo-class, which could apply some different styling. Or, maybe, a class is added to or removed from an element with JavaScript. In both of these cases, any style changes are applied immediately.
When the user hovers over a fancy button with their mouse, two things will happen. The background color will immediately change from blue to red, and the button will immediately snap to a scale factor of 1.1. This can be visually jarring. We can improve this experience with CSS transitions.
Now, when the user hovers the mouse over a fancy button, it will behave differently. Instead of immediately snapping to the new background color and size, the element will gradually transition to the new color and scale over a period of 500 milliseconds. You can think of these two states (blue background/scale 1, red background/scale 1.1) as keyframes in an animation. You don’t have to define all the states in between the two keyframes – the browser will animate the transition between the two states.
The color will gradually change from blue to shades of purple to the final color of red. At the same time, the size will animate from scale(1) to scale(1.1), making the element appear to grow over the course of the 500 milliseconds.
This is the simplest possible transition. All properties on the element that support animations/transitions – and not all properties do; see https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_animated_properties for an exhaustive list – will be transitioned to their new values over 500 milliseconds.
We can also have multistage transitions. For example, we could have the background color transition first, and then only after the color change is complete, we could then transition the scale transform.
Transition the color over 500 milliseconds.
Transform the scale over 500 milliseconds, with a 500-millisecond delay.
When specifying a transition in this way, the first argument is the name of the property, the second argument is the duration, and the third argument is the delay. Now, when we hover over the button, the color transitions from blue to red, while the scale remains unchanged. Once the color has transitioned after 500 milliseconds, then the scale transition begins, which lasts another 500 milliseconds.
The timing for transitions (and animations, as we’ll see later) can be specified in either seconds (s) or milliseconds (ms). Fractional values can be used; that is, 500 milliseconds can be specified as 500ms or 0.5s.
If you tried the given example , you may have noticed that the transition does not happen at a linear rate. That is, it appears to start out slow, speed up in the middle, then slow down again at the end. This is the default transition timing function, which is called ease. There are other built-in timing functions as well. Formally, these are specified as easing functions. These functions are visualized by plotting a graph, with the time on the X-axis and the animation progress on the Y-axis.
These functions are usually given as Cubic Bezier curves. A Cubic Bezier curve is created by specifying four points on a graph. The points are plotted, and a curve is drawn. A good way to think of how the curve is drawn is like this: Think of a straight line drawn between the first and last points. Then, the second and third points “bend” the line up and down toward them to make a curve.
In a Cubic Bezier curve for a CSS easing function, the first point is always (0, 0) and represents the start state of the animation or transition. The last point is always (1, 1) and represents the end state. The X-coordinate of the two middle points must be between 0 and 1, or else it is not considered a valid easing function and will be ignored.

The plotted Bezier curve
Function | Equivalent cubic-bezier values | Graph |
|---|---|---|
linear | 0.0, 0.0, 1.0, 1.0 |
|
ease | 0.25, 0.1, 0.25, 1.0 |
|
ease-in | 0.42, 0.0, 1.0, 1.0 |
|
ease-out | 0.0, 0.0, 0.58, 1.0 |
|
ease-in-out | 0.42, 0.0, 0.58, 1.0 |
|

An easing function with a “bounce” effect
Notice how the high and low points cause the curve to bend above 1 and below 0. This means, for the earlier example, that the scale transform would drop below 1 (the initial value) and go above 1.1 (the end value).
Ceaser by Matthew Lein
cubic-bezier.com by Lea Verou
Easing functions can also be specified as step functions. A step function divides the transition into equally sized steps in a given direction. Instead of a smooth transition like with a Bezier curve, they jump from step to step, skipping the intermediate states.
jump-start, start: The change in state happens at the beginning of each step, beginning with the start of the transition. Because the first “jump” happens immediately, the initial state of the transition is effectively lost.
jump-end, end: The change in state happens at the end of each step, ending with the end of the transition. With this value, the end state of the transition is lost.
jump-none: With this value, the start and end state of the transition are both preserved. The first step is the initial state, and the last step is the end state.
jump-both: With this value, the start and end state of the transition are both lost.
The jump- keywords are not supported in Internet Explorer 11. Also, at the time of writing, they are not supported in Safari.
CSS transitions can be a powerful tool to add better interactivity to a website or app. However, they are limited in transitioning from one initial state to one final state. With CSS animations, which we’ll look at next, we can have an arbitrary number of states to create even more stunning effects.
Where CSS transitions provide an animated transition from a start state to an end state, animations provide animated transitions between any arbitrary number of states.
Like transitions, animations are specified by the properties that change. Instead of being specified in a property like transition, they are specified in special at-rules: @keyframes. The @keyframes rule defines the various CSS properties to be applied at given steps during the animation, and the browser will automatically animate between these states.
Here, we are defining a @keyframes rule named colors. The initial state of an element using this animation will have a background color of red. At the halfway point of the animation, it will have a color of blue. Finally, at the end state, it will have a color of green. Like with transitions, the browser will automatically calculate all the intermediate colors for the animation.
0% can be replaced with the keyword from, and 100% can be replaced with the keyword to, but this is optional and has the same meaning.
animation: color 2s ease-in-out 2s both
animation: pulse 2s linear infinite
When using the shorthand animation property , you can specify the values in almost any order, and the browser will figure out which argument means what. The one exception to this is the duration and delay. The first time value encountered is treated as the duration, and the second is treated as the delay.
As mentioned earlier, the animation property is a shorthand for specifying multiple animation-related properties. Here are some of the more commonly used ones.
The identifier of the @keyframes rule that defines the animation that should be performed on the element.
The total duration of the animation. Can be specified in seconds (s) or milliseconds (ms).
The easing function to use for the animation.
By default, the animation will start immediately. Specifying an animation-delay will delay the start of the animation by the time given. Can be specified in seconds or milliseconds.
A negative value can be given for animation-delay. If a negative time value is given, the animation will start immediately at the given point of elapsed time. For example, if an animation has a duration of 1s, and the delay is given as -500ms, the animation will start immediately at the 500ms mark.
If you try the example in Listing 9-6, you will observe some interesting behavior. The element cycles through the colors, from red to blue to green over 2 seconds, then it disappears. This is because, by default, the properties applied during the animation are no longer applied once the animation ends, and we didn’t define a background color in the rule for the element. It reverts back to whatever properties were defined on the element. If the .box CSS rule had a background-color of yellow, it would run the animation then turn yellow.
Like most things in CSS, this can be changed with a property. The animation-fill-mode property defines how properties from the keyframes are applied to an element before the animation starts or after the animation ends (or both).
We have a box that is yellow. It animates its background color starting from red, transitioning to blue. The animation happens over the course of two seconds, and there is a 2-second delay before it starts. Let’s examine the different values that animation-fill-mode accepts and what effect they will have on this element’s animation.
The element is yellow for 2 seconds. It then immediately switches to red, and over the next 2 seconds, it transitions to blue. After the animation completes, the background color immediately switches back to yellow. With none, the styles in the keyframes are only applied for the duration of the animation. This is the default behavior.
The element is yellow for 2 seconds. It then immediately switches to red, and the animation runs over the next 2 seconds, transitioning to blue. After the animation completes, the background color stays blue. With forwards, the styles from the last keyframe of the animation remain applied to the element after the animation ends.
The element immediately becomes red, which it remains for the next 2 seconds. Then the animation runs for 2 seconds, transitioning to blue. Once the animation is done, the background color immediately switches back to yellow. With backwards, the styles from the first keyframe are applied during the animation delay period.
The element immediately becomes red for 2 seconds. Then the animation runs for 2 seconds, transitioning to blue. Once the animation is done, the element remains blue. With both, you get the effects of forwards and backwards.
By default, an animation runs only once. You can have the animation run several times by giving a number for animation-iteration-count, or you can have it loop forever by specifying the keyword infinite. Interestingly, this value does not have to be a whole number. For example, you can specify animation-iteration-count: 0.5, and the animation will play once to the halfway point only.
Defines whether the animation should run forward (the normal keyword) or backward (the reverse keyword). Other accepted values are alternate (runs the animation forward, then backward) and alternate-reverse (runs the animation backward, then forward).
Defines whether or not the animation is currently playing. This could be manipulated with JavaScript to pause and resume an animation, for example. Accepted values are running and paused. When the animation is changed from running to paused, and later changed back to running, the animation will continue from where it left off – it won’t start over from the beginning.

The multiple animations being applied
Transitions and animations are powerful. But with great power comes great responsibility. Overusing them, or using them for certain expensive properties, can result in poor performance of your page.
There are a few different categories we can put CSS properties into: layout, paint, and composite properties.
Layout properties are properties that affect how an element (and its surrounding content) is laid out on the page. This category includes properties like width, height, padding, and margin. These are generally the most expensive to animate.
If you animate the margin of an element, for every frame of the animation, the size taken up by the element changes. This will affect the layout of surrounding elements. When those elements’ layouts are adjusted, that could trigger even more elements to recalculate their layout. This chain reaction is known as layout thrashing and can be very costly for performance.
Paint properties affect how an element is painted on the screen, such as color or background-image. These properties don’t affect layout, so they aren’t as expensive as layout properties. However, if overused, they can still cause performance issues, particularly on mobile devices.
Composite properties are properties such as transform and opacity. These properties don’t affect layout and are much cheaper than the other property types. Additionally, the computer’s GPU can also assist with these animations, which takes a load off the CPU and makes the animations smoother.

A screenshot of the CSS Triggers site
This example provides a hint that the transform and opacity properties will be changing, so the browser can take that into account when optimizing for the animation.
However, this property should be used sparingly. The browser already does a good job optimizing layout, painting, and composite operations to help keep your animations and transitions smooth. Overusing will-change can interfere with that optimization and could actually make performance worse. It’s intended more as a last resort to improve performance.
The will-change property is not supported in Internet Explorer.
Try to limit the number of animations running simultaneously. Even performant animations can cause performance issues when combined with several others at once, particularly if one or more of them already have performance issues, such as animating a layout property.
When you do perform multiple animations at once, it can be useful to test each in isolation to get an idea of which ones will contribute to the most performance problems.

The accessibility options in macOS
This setting, by default, will not be honored by the HTML and CSS content of your site. Even if a user has disabled animations, your CSS animations and transitions will still run.
You can, and should, detect this scenario by using the prefers-reduced-motion media query and adjusting (or disabling) your animations accordingly. We’ll get more into media queries in Chapter 11, but here’s how we use it.
When this page is loaded on a system where the user has disabled motion, the box will not be animated.
The prefers-reduced-motion query has two supported values: reduce and no-preference .
The prefers-reduced-motion media query is not supported in Internet Explorer.
A transition is used to animate an element between two states, while an animation can use any number of states.
There are several built-in easing functions: linear, ease, ease-in, ease-out, and ease-in-out.
Transitions and animations have a duration, and an optional delay.
Animations have the animation-fill-mode property, which determines how styles are applied before and after the animation is performed.
Try to avoid animating layout properties, as these can negatively affect performance.
Use the prefers-reduced-motion media query to improve the accessibility of your site.
The Flexible Box Layout Module, more commonly known as flexbox, is a powerful tool for building layouts with CSS. It is not quite as powerful as CSS Grid, which we’ll look at in Chapter 12, but it can solve many layout problems. Flexbox is a one-dimensional layout that can lay out elements either horizontally or vertically (but not both). An element using flexbox as its layout is referred to as a flex container , and the elements inside it are flex items .
Let’s go over the basic concepts to understand flexbox.
A flex container has a direction , defined by the flex-direction property. It can be either a row (horizontal) or column (vertical). This is shown in Figure 10-1. There are actually four values for the flex-direction property: row, row-reverse, column, and column-reverse. The meaning of row/column and row-reverse/column-reverse depends on whether the system is using a left-to-right (LTR) language or a right-to-left (RTL) language. The following sections assume an LTR language; for an RTL language, they are reversed.

The different values for flex-direction

The main axis and cross axis
We can observe a few things here. First, the height of the container is just enough to fit its content. We can also see that the items are right up against each other – the flex container does not add any gap between its items. There is actually a gap property that will make the flex container set a gap between its items, but it does not have wide browser support yet.
Normally, a block element would start on a new line. But inside a flex container, this doesn’t happen. The items are instead laid out along the main axis.

The rendered result
The container only has a width of 500px and contains three items, each with a width of 300px. Yet, the items fit neatly inside the container and don’t overflow. In cases like this, the browser will try to shrink flex items to fit the container.

The items overflow the container
There are several properties that affect flex item sizing. Some of these properties are set on the flex container, and others are set on the flex items.

The flex items wrap to the next line

The rendered result

The flex items grow to fill the container

Item 2 grows to take more of the available space
You might notice that item 2 is not twice as large as items 1 and 3. Items 1 and 3 are 150px wide, and item 2 is 200px wide. Instead, item 2 takes twice as much of the available space as items 1 and 3.
All three items started at 100px. Items 1 and 3 grew by 50px, while item 2 grew by 100px – twice that of the others.
We saw earlier that if the flex items exceed the size of the container, the browser will try to shrink them to fit. By default, it will try to shrink all elements evenly. Just like we can control growth with flex-grow, we can also control shrinking with flex-shrink .
The flex-shrink property specifies the amount of shrinking that can be done to a flex item relative to the other items. This defaults to 1, which is why all of the elements were shrunk evenly.

Item 2 shrinks twice as much as items 1 and 3
Each item is 200px, which means the total width taken up by the items is 600px. But the container is only 300px wide, so the items have to be shrunk. With the default flex-shrink of 1 for all items, each item would be shrunk by 100px, to be 100px wide. But because item 2 has a flex-shrink of 2, it will shrink twice as much as the others.
The flex-basis property sets the initial size of a flex item along the main axis before flex-grow and flex-shrink factors are applied.
So far, we’ve seen how to size the flex items in a flexbox layout. In this section, we’ll look at alignment (what happens when all the items are not the same size in the cross axis?) and spacing (how is leftover space distributed?).
Some of the values for these properties depend on the writing mode. The writing mode is defined by the writing-mode property . This property determines the way block elements are laid out, and how inline elements flow inside them.
Furthermore, the way the writing mode behaves depends on the user’s language. In some languages, text flows from left to right (LTR); in others, it flows from right to left (RTL).
The default value is horizontal-tb. For LTR languages, elements flow from left to right; for RTL languages, elements flow from right to left. Block elements and lines of text flow from top to bottom.
Other values include vertical-rl and vertical-lr. For these values, elements flow vertically from top to bottom (for LTR languages) or bottom to top (for RTL languages). With vertical-rl, block elements and lines of text flow from right to left (for LTR languages) or left to right (for RTL languages). Finally, with vertical-lr, block elements and lines of text flow from left to right (for LTR languages), or right to left (for RTL languages).
Like with sizing properties, some of these properties are applied to the flex container, and others are applied to the flex items.
Value | Description | Example |
|---|---|---|
flex-start | Lays out all the items next to each other at the beginning of the main axis |
|
flex-end | Lays out all the items next to each other at the end of the main axis |
|
center | Lays out all the items next to each other, centered along the main axis |
|
space-between | Maximizes the space between the items. The first item is flush with the start of the main axis, the last item is flush with the end of the main axis, and the other items are distributed evenly |
|
space-around | Similar to space-between, except items have space on both ends. These spacings are not collapsed between the middle items. The end result is a smaller space before the first item and after the last item |
|
space-evenly | Similar to space-around, except the space is even on all sides of all items |
|
Value | Description | Example |
|---|---|---|
stretch | Stretches the items to fill all available space along the cross axis. An element won't be stretched beyond its height or max-height |
|
flex-start | Items are aligned to the start of the cross axis |
|
flex-end | Items are aligned to the end of the cross axis |
|
center | Items are aligned to the center of the cross axis |
|
baseline | Items are aligned along the baseline of their text |
|
Value | Description | Example |
|---|---|---|
stretch | Stretches the items to fill all available space along the cross axis. An element won't be stretched beyond its height or max-height |
|
flex-start | Aligns the rows at the beginning of the cross axis |
|
flex-end | Aligns the rows at the end of the cross axis |
|
center | Aligns the rows at the center of the cross axis |
|
space-between | Maximizes the spacing between the rows |
|
space-around | Spacing around the rows, with half spaces at the beginning and end |
|
space-evenly | Equal spacing around all the rows |
|
The align-items property applies the alignment to all of the flex items, but an individual item can specify its own individual alignment that overrides what was set on the container with the align-self property .
The order they occur in the HTML
The value of the flex-direction property
This ordering can be changed by using the order property on flex items. This property, when set on a flex item, defines the order in which it appears.
Multiple flex items can have the same value for the order property. If more than one item has the same order, those items will be laid out in the order they appear in the source HTML.
The order property only affects the displayed order on screen. It does not affect the order of the elements in other contexts. A screen reader, for example, will read the elements in their source order, not the displayed order.
This makes the display of the items out of sync from how they are displayed on screen, which could be confusing. For this reason, the order property is best used sparingly.
The following sections show some examples of what can be achieved with a flexbox layout.

The inner box is centered on both the main axis and cross axis

The flexbox layout
The root container defines a column flexbox layout (the black border). It contains a header, a main element which contains the other content (the blue border), and a footer. The main element gets a flex-grow of 1. This means that while the header and footer use just enough space for their content, the main element will grow to fill the remaining space.
Then, the main element has a row flexbox layout. The two sidebars on either end just use their natural width, and the content area in the middle again uses a flex-grow of 1 to fill horizontally.
We could have even deeper nesting. For example, the header could contain a row flexbox layout with navigation links, or the sidebar could contain a column flexbox layout.
A flexbox layout is made up of a flex container and its flex items.
A flexbox layout is one-dimensional; it is either a single row or a single column.
A flex container is created by setting display to flex or inline-flex.
A flex container has a direction and two axes, the main axis and the cross axis.
Flex containers can be nested inside other flex containers.
Flexbox can be used to absolutely center an element inside its container.
Responsive design is a technique for designing page layouts so that they are usable on devices with a variety of different screen sizes. Media queries are one of the main tools used for responsive design, as are flexbox and CSS Grid.
A layout that looks good on a high-resolution desktop display might not look so good on an iPhone. Media queries allow you to apply different CSS rules, or even entire style sheets, depending on the size of the viewport.
Media queries have other uses, too. For example, you can apply a different set of styles for when a page is printed than when it is displayed on a screen by using the print medium.
This tells the browser how to set the page’s dimensions and zoom level. The typical value used for width is device-width; however, this could also be set to an explicit pixel width as well.
A media query is defined as an at-rule, @media. It specifies a medium (all, print, screen, or speech) and a condition. If no medium is specified, it defaults to all. If the condition is met, the CSS rules inside the block are applied to the document. If it is not met, the CSS rules are ignored.
In a viewport that has a width of 400px or less, h1 elements will be blue. Otherwise, they will be red. Media queries are constantly being evaluated and styles conditionally applied. They aren’t only calculated when the page first loads. You can test the given CSS by opening the page using it and notice the h1 elements are red. If you resize the window to below 400px, the h1 elements will change from red to blue. Resize the screen back above 400px, and they will turn red again.
The only operator is also supported. Using only assures that a media query’s rules are not applied on older browsers that don’t understand the full query.
A breakpoint is the threshold at which a page’s layout will change due to the viewport size with a media query. Defining your breakpoints is not an exact science, as there are many different devices in use today, all with different screen sizes. It’s better to set breakpoints based on the content. That is, you should try to avoid targeting specific devices with media queries. Instead, experiment with different viewport sizes, and find the points where your layout and design start looking cramped. Then you know where to set your breakpoints.

An example layout
The only CSS that has been applied is that the h1 element at the top has been set to a font-size of 5rem .

The heading text wrapped

The heading text is smaller at this viewport size

The heading is wrapping again

The heading text gets even smaller at this viewport size

The image is smaller at this viewport size
Now, the image looks better sized relative to the rest of the content.

The rendered layout with a wide viewport

The layout with a narrower viewport

The layout with an even narrower viewport

The wrapped layout with a narrow viewport

The layout on an even narrower viewport
Earlier, we saw how to leverage media queries to adjust the font size as the viewport size changes. With fluid typography, it’s possible to automatically scale the font size to viewport size without having to use media queries.

Using a variable font size proportional to the viewport width

A very large font size

The heading font is very small for a narrow viewport
Now the font size adjusts automatically with the viewport width as before, but now it will never drop below 48px or go above 64px.
Previously, we used a media query to change the size of an image if the viewport was narrower than a certain threshold. There is another technique we can use that allows images to scale with the viewport width without using media queries.

An 800 pixel wide image

The image with a viewport width of less than 800 pixels
This sets the image so that its width never exceeds the width of its container. The height: auto makes sure that the image retains its aspect ratio.

The image now resizes with the viewport

The rendered layout

The narrow layout

The adjusted layout at its threshold of 700 pixels

The navigation links laid out horizontally

The layout as it might appear on a mobile device

The wrapped navigation links
This looks better – the navigation links no longer overflow the viewport. There are still some other ways we could improve on this design. For example, at the narrow viewport size, we could replace the navigation links with a dropdown or popup menu. This would probably involve some JavaScript and DOM manipulation for toggling the menu.

The layout with smaller links, which are not ideal for mobile
In this instance, the best option would probably be to add a menu of some kind in place of the navigation links on a narrow viewport like on a mobile device.
Media queries allow CSS rules to be applied conditionally.
A breakpoint is a threshold used in a media query, where there is a layout change.
An element’s font size can be proportional to the width of the viewport and limited by using the clamp function.
Sometimes, only minor changes are needed inside a media query, such as setting flex-wrap to wrap or the flex-direction from row to column in a flex container.
Images can automatically resize to the viewport by setting max-width to 100% and height to auto.
We’ve looked at flexbox layouts in depth and have seen how powerful they can be. Still, there are limitations. Flexbox is a one-dimensional layout where items are arranged horizontally or vertically in rows or columns.
CSS Grid is a relatively new feature that allows you to create two-dimensional grid layouts with rows and columns. It is the most powerful layout system available with CSS today. CSS Grid enjoys wide browser support – even IE11 supports it, although its support is only partial and uses an older version of the specification.
Let’s start with the basic concepts of CSS Grid.
The grid container is the outer element that contains the grid layout. All of its direct children are grid items. To make an element a grid container, set its display property to grid or inline-grid. The difference is an element with display: grid will be a block element, where an element with display: inline-grid will be an inline element.
All immediate children of the grid container are grid items. Beyond the immediate children, descendant elements are not grid items. No special CSS properties need to be applied to make an item a grid item. The child elements automatically become grid items, and by default they are laid out in the order that they appear in the HTML markup.

Grid lines

Grid tracks

A grid area
When grid rows and columns are explicitly defined with CSS properties, this is known as the explicit grid .
If more items are added than are accounted for in the explicit grid, the grid layout creates additional rows and/or columns to fit these extra items. This is the implicit grid .
Grid sizes can be specified with any of the units we’ve looked at so far – px, em, rem, even percentages. CSS Grid introduces a new unit, the fr unit . This is a fractional unit that refers to a fraction of the free space. For example, if there are four columns each at a width of 1fr, then each column will take up 25% of the total width of the grid.
To set up the rows and columns of the explicit grid, the grid-template-rows and grid-template-columns properties are used. These are used to specify the widths of the rows and the heights of the columns, respectively. Technically speaking, they are used to specify the size of the grid tracks.
Like with flexbox, by default grid items are flush up against one another. With grid items, we can use the gap property to add a gap between grid items. This can help us better visualize the grid layout.

The rendered grid
We have defined an explicit grid with two 10rem column tracks and two 5rem row tracks. As you can see, you don’t need to specify a row or column in the grid – by default, the grid container places its children in order, starting at the first column of the first row.

The rendered grid

The rendered grid
Now, all of the grid items are the same size.
Let’s look at grid sizing in more detail, starting with the fr unit.

The middle column using 2fr

Overflowing cell content

The rendered grid
Now the rows have a minimum height of 3rem but can grow to automatically fit the content if necessary.

The grid in a wide viewport

The grid items automatically wrap with auto-fill

The same example, with a border around the container

The grid using auto-fill and minmax
The browser is using a minimum size of 5rem. However, when there’s space left over, using 1fr for each column means that each column will grow equally to use up the extra space.

The auto-fit behavior
By default, the grid items are placed automatically, filling each column then each row. There are options that can be used to customize the positioning of grid items.

The rendered result
The first grid item is positioned in row 2, column 3. The rest of the grid items are automatically placed.

Spanning two columns
Grid lines can be referenced by their numerical index, as we have already seen. But we can also assign names to the grid lines and reference those names instead. The grid lines are named within a grid-template-rows or grid-template-columns expression, in between the grid track definitions. The grid line names are placed inside square brackets.
These grid line names can then be referenced from the grid-row and grid-column properties.
In addition to named grid lines, we can also define named grid areas . This allows us to place grid items in the desired areas without having to specify start and end lines. The areas are defined with the grid-template-areas property. If two adjacent areas have the same name, then an item placed in that area will span those areas.

The rendered grid layout
Just like with flexbox, CSS Grid gives you total control over the alignment of items when they don’t fill their container. There are several properties that control this.
Value | Description | Example |
|---|---|---|
stretch | Items are stretched along the row axis to fill the entire width of the cell. This is the default |
|
start | Items are aligned with the starting edge of the cell along the row axis |
|
end | Items are aligned with the ending edge of the cell along the row axis |
|
center | Items are centered along the row axis in their cells |
|
Value | Description | Example |
|---|---|---|
stretch | The default value. Items are stretched along the column axis to fill the entire height of the cell |
|
start | Items are aligned at the top edge of the cell |
|
end | Items are aligned at the bottom edge of the cell |
|
center | Items are centered vertically in the cell |
|
Value | Description | Example |
|---|---|---|
start | Aligns the grid at the start of the row axis |
|
end | Aligns the grid at the end of the row axis |
|
center | Aligns the grid in the center of the row axis |
|
stretch | Stretches the grid columns to fill the row axis, if the columns are set to auto |
|
space-around | Adds even spacing between columns, with half-sized spaces at the start and end |
|
space-evenly | Adds even spacing between columns, with full-sized spaces at the start and end |
|
space-between | Places the first column flush with the start of the container and the last column flush with the end of the container and adds even spacing in between the other columns |
|
Value | Description | Example |
|---|---|---|
start | Aligns the grid rows at the start of the column axis |
|
end | Aligns the grid rows at the end of the column axis |
|
center | Aligns the grid rows at the center of the column axis |
|
stretch | Stretches the rows to fill the column axis, if the rows are set to auto |
|
space-around | Adds even spacing between rows, with half-sized spaces at the start and end |
|
space-evenly | Adds even spacing between rows, with full-sized spaces at the start and end |
|
space-between | Places the first row flush with the top of the container and the last row flush with the bottom of the container and adds even spacing in between the other rows |
|
The justify-items and align-items properties of the grid container define the alignment of the grid items along the row and column axis, respectively. These alignments can be overridden for an individual grid item by setting the following properties on the grid item itself.
Sets the alignment of an item inside its cell along the row axis.
Sets the alignment of an item inside its cell along the column axis.
An element can be made a grid container by setting its display property to grid or inline-grid.
A grid container’s immediate children become grid items.
A grid has lines, tracks, and areas.
The explicit grid is made up of the rows and columns defined by the grid-template-rows and grid-template-columns properties.
The implicit grid contains any elements placed after all explicit grid areas are filled.
The fr unit uses a fraction of the available free space.
A grid item can span multiple rows and/or columns.
Grid lines and areas can have names. These names can then be referenced when placing grid items.
The justify-items, align-items, justify-content, and align-content properties define how extra space is handled in grids.
An individual grid item can override its alignment with the justify-self and align-self properties.
We have reached the end of our exploration of modern CSS. We have covered a lot of topics, starting from the basics, to layout, to transitions and animations, to more advanced layouts with flexbox and CSS Grid.
The goal of this book was to make CSS less intimidating and more accessible. I hope you have learned something of value by reading this book.
There are some other parts of the CSS ecosystem that we haven’t looked at, that you should definitely look into for further learning.
BEM (Block Element Modifier): Uses strict naming rules. Each element’s name has a block, element, and modifier. An example of this is form__button--red. In this example form is the block, button is the element, and red is the modifier.
OOCSS (Object-Oriented CSS): Applies object-oriented principles to CSS. Separates container elements, or “skins,” from content.
SMACSS (Scalable and Modular Architecture for CSS): Categorizes CSS rules into five categories: base, layout, module, state, and theme.
padding: 1.5rem (from p-6)
background-color: white (from bg-white)
border-radius: 0.5rem (from rounded-lg)
Frameworks like Tailwind allow you to style your document without writing a single line of CSS.
Houdini is a work-in-progress set of APIs that expose various parts of the CSS Object Model, and new APIs, to the developer. This allows for the creation of custom CSS properties, layouts, and more. Browser support is currently very limited, and some of the specifications are still at an early stage.
There are many new and exciting developments in the world of CSS on the way that will give you, the web developer, more power and control over styling your websites and apps. I hope this book has helped you chart your journey into further exploration of CSS.