Chapter 4

Cascading Style Sheets

Cascading Style Sheets

Bringing style to the web since 1998

Subsections of Cascading Style Sheets

Introduction

Style sheets are collections of rules for modifying how a SGML document appears. Cascading Style Sheets (CSS) are the specific implementation adopted by the W3C for HTML.

The core concept of CSS is that defines rules altering the appearance of HTML elements that can be selectively applied. These rules are held in a document (the style sheet) and are applied in a well-defined priority order (the cascading part of CSS).

As of CSS Version 3, CSS technologies were split into separate modules allowing them to be revised and maintained separately. Each module adds or extends features to those defined in CSS Version 2, in order to maintain backwards compatibility.

Info

The CSS standards, along with many other web technologies, are maintained by the World-Wide-Web Consortium (abbreviated W3C), stakeholders who create and maintain web standards. The full drafts of the Cascading Style Sheets standards can be found here w3c’s CSS page.

CSS Rules

CSS properties consist of key-value pairs separated by a colon (:). For example:

color: red

indicates that the styled HTML elements should be given a red color.

Multiple properties are separated by semicolons (;), i.e.:

color: red;
background-color: green;

Rules are CSS properties grouped within curly braces ({}) and proceeded by a CSS selector to identify the HTML element(s) they should be applied to:

p {
  color: red;
  background-color: green;
}

In this example, all paragraph elements (<p>) should have red text on a green background (how festive!).

And difficult to read!

Short Forms

Some properties have multiple forms allowing for some abbreviation. For example, the CSS property:

border: 1px solid black

is a short form for three separate border-related properties:

border-width: 1px;
border-style: solid;
border-color: black;

Experimental Features and Prefixes

As new features are considered for inclusion in CSS, browsers may adopt experimental implementations. To separate these from potentially differing future interpretations, these experimental properties are typically prefixed with a browser-specific code:

  • -webkit- Webkit Browsers (Chrome, Safari, newer Opera versions, and iOS)
  • -moz- Mozilla
  • -ms- Microsoft browsers (IE, Edge)
  • -o- Older Opera versions

For example, most browsers adopted the box-shadow property before it achieved candidate status, so to use it in the Mozilla browser at that point you would use:

-moz-box-shadow: black 2px 2px 2px

To make it work for multiple browsers, and future browsers when it was officially adopted, you might use:

-webkit-box-shadow: black 2px 2px 2px;
-moz-box-shadow: black 2px 2px 2px;
-ms-box-shadow: black 2px 2px 2px;
box-shadow: black 2px 2px 2px;

The browser will ignore any properties it does not recognize, hence in Chrome 4, the -webkit-box-shadow will be used and the rest ignored, while in Chrome 10+ the box-shadow property will be used.

Note

You should always place the not-prefixed version last, to override the prefixed version if the browser supports the official property.

Tip

The Mozilla Developer Network maintains a wiki of comprehensive descriptions of CSS properties and at the bottom of each property’s page is a table of detailed browser support. For example, the box-shadow property description can be found at: https://developer.mozilla.org/en-US/docs/Web/CSS/box-shadow. By combining the css property name and the keyword mdn in a Google search, you can quickly reach the appropriate page.

CSS Selectors

In the example from the previous section, we saw:

p {
  color: red;
  background-color: green;
}

Here the p is a CSS Selector, which tells us what elements on the page the CSS rules should be applied to.

Simple Selectors

The most basic CSS selectors come in several flavors, which we’ll take a look at next. Simple selectors are a string composed of alphanumeric characters, dashes (-), and underscores (_). Certain selectors also use additional special characters.

Type Selector

Type selectors apply to a specific type of HTML element. The p in our example is a type selector matching the paragraph element.

A type selector is simply the name of the HTML element it applies to - the tag name from our discussion of HTML element structure.

Class Selector

A class selector is a proceeded by a period (.), and applies to any HTML element that has a matching class attribute. For example, the CSS rule:

.danger { 
  color: red;
}

would apply to both the paragraph and button elements:

<h1>Hello</h1>
<p class="danger">You are in danger</p>
<button class="danger">Don't click me!</button>

as both have the class danger. A HTML element can have multiple classes applied, just separate each class name with a space:

<p class="danger big-text">I have two classes!</p>

ID Selector

An ID selector is proceeded by a hash (#) and applies to the HTML element that has a matching id attribute. Hence:

<p id="lead">This paragraph has an id of "lead"</p>

would be matched by:

#lead {
  font-size: 16pt;
}

It is important to note that the id attribute should be unique within the page. If you give the same id to multiple elements, the results will be unpredictable (and doing so is invalid HTML).

Universal Selector

The asterisk (*) is the universal selector, and applies to all elements. It is often used as part of a reset - CSS rules appearing at the beginning of a CSS document to remove browser-specific styles before applying a site’s specific ones. For example:

* {
  margin: 0;
  padding: 0;
}

sets all element margins and paddings to 0 instead of a browser default. Later rules can then apply specific margins and padding.

Attribute Selector

The attribute selector is wrapped in square brackets ([]) and selects HTML elements with matching attribute values, i.e.:

[readonly] {
  color: gray;
}

will make any element with a readonly attribute have gray text. The value can also be specified exactly, i.e.

[href="www.k-state.edu"]

or partially. See MDN’s documentation for details.

Compound Selectors

Simple selectors can be used in conjunction for greater specificity. For example, a.external-link selects all <a> elements with a class of external-link, and input[type=checkbox] selects all <input> elements with an attribute type set to checkbox.

Pseudo-Class

Pseudo-class selectors are proceeded with a single colon (:), and refer to the state of the element they modify. Pseudo-classes must therefore be appended to a selector.

The most commonly used pseudo-class is :hover, which is applied to an element that the mouse is currently over. Moving the mouse off the element will make this selector no longer apply. For example, a:hover applies only to <a> elements with the mouse directly over them.

Another extremely useful pseudo-class is :nth-child(), which applies to the nth child (specify as an argument), i.e. ul:nth-child(2) will apply to the second child of any unordered list. Additionally, tr:nth-child(odd) will apply to the odd-numbered rows of a table.

Additional pseudo-classes can be found in the MDN documentation

Combinators

Combinators can be used to combine both simple and compound selectors using an operator.

Adjacent Sibling Combinator

The plus symbol (+) can be used to select an adjacent sibling HTML element. To be siblings, the first element must be followed by the second, and both must be children of a shared parent. I.e.:

h1 + p {
  font-weight: bold;
}

will bold all paragraphs that directly follow a first-level header.

General Sibling Combinator

The tilde symbol (~) also selects a sibling, but they do not need to be adjacent, just children of the same parent. The first element must still appear before the second (just not immediately after).

Child Combinator

The greater than symbol (>) selects elements that are direct children of the first element. For example:

p > a {
  font-weight: bold;
}

Will bold all anchor elements that are direct children of a paragraph element.

Descendant Combinator

A space ( ) selects elements that are descendants of the first element.

Multiple Selectors

Finally, we can apply the same rules to a collection of selectors by separating the selectors with commas, i.e.:

a, p, span {
  font-family: "Comic Sans", sans-serif;
}

Applies Comic Sans as the font for all <a>, <p>, and <span> elements.

Pseudo-Elements

An interesting newer development in CSS is the development of psuedo-elements, selectors that go beyond the elements included in the HTML of the page. They are proceeded by two colons (::). For example, the ::first-letter selector allows you to change the first letter of a HTML element. Thus:

p:first-child::first-letter {
  font-size: 20px;
  font-weight: bold;
  float: left;
}

creates drop caps for all initial paragraphs.

A second use of pseudo-elements is to create new elements around existing ones with ::before or ::after. For example:

a.external-link::after {
 content: url(external-link-icon.png); 
}

Would add the external-link-icon.png image after any <a> elements with the external-link class.

More information can be found in the MDN Documentation.

Applying CSS Rules

There are multiple ways CSS rules can be applied to HTML elements. A document containing CSS rules can be attached to a HTML document with a <link> element, embedded directly into the html page with a <style> element, or applied directly to a HTML element with the style attribute. Let’s look at each option.

Linked CSS Documents

The <link> HTML element can be used to link the HTML page it appears in to a text file of CSS rules. These rules will then be applied to the HTML elements in the HTML document.

The <link> element should provide a hypertext reference attribute (href) providing a location for the linked document, and a relationship attribute (rel) describing the relationship between the HTML document and the stylesheet (hint: for a stylesheet the relationship is "stylesheet"). If either of these attributes is missing or invalid, the stylesheet’s rules will not be used.

For example, if the stylesheet is in the file styles.css, and our page is page.html, and both reside at the root of our website, the <link> element would be:

<link href="/styles.css" rel="stylesheet" type="text/css"/>

By placing our CSS rules in a separate file and linking them to where they are used, we can minimize code duplication. This approach also contributes to the separation of concerns. Thus, it is widely seen as a best practice for web development.

The <link> element should be declared within the <head> element.

HTML <style> Element

The <style> HTML element can be used to embed CSS rules directly in an HTML page. Its content is the CSS rules to be applied. The <style> element must be a child of a <head> or <body> element, though placing it into the <head> element is best practice.

To repeat our earlier efforts of making paragraphs have red text and green backgrounds with the <style> element:

<!DOCTYPE html>
<html>
  <head>
    <title>Style Element Example</title>
    <style>
      p { 
        color: 'red';
        background-color: 'green';
      }
    </style>
  </head>
  <body>
  </body>
</html>

Unlike the <link> element approach, CSS rules defined with the <style> element can only be used with one file - the one in which they are embedded. Thus, it can lead to code duplication. And embedding CSS rules in an HTML document also breaks the separation of concerns design principle.

However, there are several use cases for which the <style> element is a good fit. Most obvious is a HTML page that is being distributed as a file, rather than hosted on a server. If the style information is embedded in that HTML file, the recipient only needs to receive the one file, rather than two. Similarly, emails with HTML bodies typically use a <style> element to add styling to the email body.

The HTML Element Style Attribute

The style attribute of any HTML element can be used to apply CSS rules to that specific element. These rules are provided a string consisting of key/value pairs separated by semicolons. For example:

<p>This is a normal paragraph.</p>
<p style="color: orange; font-weight: bold">But this one has inline styles applied to it.</p>

Produces this:

This is a normal paragraph.

But this one has inline styles applied to it.

It should be clear from a casual glance that inline styles will make your code more difficult to maintain, as your styling rules will be scattered throughout a HTML document, instead of collected into a <style> element or housed in a separate CSS file. And while inline styles can be used to selectively modify individual elements, CSS selectors (covered in the previous section) are typically a better approach.

Where inline styles make the most sense is when we manipulate the elements on the page directly using JavaScript. Also, the browser developer tools let us manipulate inline styles directly, which can be helpful when tweaking a design. Finally, some component-based design approaches (such as React) pivot the Separation of Concerns design principle to break a user interface into autonomous reusable components; in this approach content, style, and functionality are merged at the component level, making inline styles more appropriate.

CSS Cascade

Now that we know how to create an apply CSS rules to our HTML, let’s explore how they actually are used. A core idea behind CSS is the cascade algorithm, the cascading in cascading style sheets (CSS). The core idea behind the cascade algorithm is that as the browser encounters and parses CSS rules, they are collectively applied to the elements they match with. If the same rule is set multiple times, say color, the cascading algorithm decides which should be applied.

CSS Sources

Before we look at how cascades work, we need to understand the sources of CSS rules. So far we’ve focused on CSS rules created by the author - that is, the developer of the website. But there are two other sources of CSS rules, the user-agent and the user.

User-Agent

The term user-agent is the technical way of describing the browser (or other software) that is accessing the webpage. Most browsers define default styles for their browser that help differentiate them from other browsers. These default values work well for less-styled websites, but for a carefully designed user experience, an unexpected rule can wreak havoc.

For this reason, many websites use a special CSS file that overrides user-agent sheets to allow the website to start from a well-known state. This is possible because rules appearing in sheets defined by the author override those defined in user-agent sheets.

Author

The author is simply the creator of the webpage. Thus, rules included with the <link> or <style> elements, as well as in-line styles defined on the elements themselves with the style attribute, fall into this category. Author styles always override user-agent styles, and are overridden in turn by user styles.

User

The user is the actual user of the browser, and they can add their own styles to an HTML document in a variety of ways. One that you may encounter the most is adding custom styles with the web developer tools. One that you may have not encountered, but is common in practice, are styles intended to make the website easier for the vision impaired to read and work with. Increasing or decreasing text size with [CTRL] + [+] or [CTRL] + [-] is a simple example of this kind of tool.

Cascading Order

Thus, the general order of rules applied by the cascading algorithm is user-agent, author, user. However, there is also the !important directive that can be added to CSS rules, i.e.:

p {
  color: red !important
}

which escalates them to a higher pass. Also, CSS animations and transitions are evaluated at their own priority level. Thus, we have a cascade order of:

  Origin Importance
1 user agent normal
2 user normal
3 author normal
4 animations  
5 author !important
6 user !important
7 user agent !important
8 transitions  

A more thorough discussion of the Cascade Algorithm can be found in the MDN Documentation.

CSS Specificity

But what about two rules that conflict that appear in the same level of the cascade order? For example, given the CSS:

p {
  color: black;
}
.warning {
  color: red;
}

what would the color of <p class="warning"> be? You might say it would be red because the .warning CSS rules come after the p rules. And that would be true if the two rules had the same specificity. An example of that is:

p {color: black}
p {color: red}

Clearly the two selectors are equivalent, so the second color rule overrides the first. But what if we go back to our first example, but flip the rules?

.warning {
  color: red;
}
p {
  color: black;
}

In this case, the color is still red, because .warning is more specific than p. This is the concept of specificity in CSS, which has an actual, calculable value.

Specificity Calculations

The specificity of a selector increases by the type and number of selectors involved. In increasing value, these are:

  1. Type selectors and pseudo-elements
  2. Class selectors, attribute selectors, and pseudo-classes
  3. ID selectors

Each of these is trumped by in-line CSS rules, which can be thought of as the highest specificity. Thus, we can think of specificity as a 4-element tuple (A, B, C, D):

Specificity Tuple Specificity Tuple

We can then calculate the values of A,B,C,D:

  • Count 1 if the declaration is from a ‘style’ attribute (inline style) rather than a rule with a selector, 0 otherwise (= A).
  • Count the number of IDs in the selector (= B).
  • Count the number of Classes, attributes and pseudo-classes in the selector (= C).
  • Count the number of Element names and pseudo-elements in the selector (= D).

The !important Loophole

Finally, there is an extra trick that we can use when we are having trouble creating enough specificity for a CSS rule. We can add !important to the rule declaration. For example:

p {
  color: red !important;
}

The !important rule overrides any other CSS declarations, effectively sidestepping specificity calculations. It should be avoided whenever possible (by making good use of specificity), but there are occasions it might be necessary.

If multiple rules use !important, then their priority is again determined by specificity amongst the group.

CSS Units

When specifying CSS rules, you often need to provide a unit of measurement. Any time you provide a measurement in a CSS rule, you must provide the units that measurement is being expressed in, following the value. For example:

#banner {
  width: 300px;
}

sets the width of the element with id banner to 300 pixels.

There are actually a lot of units available in CSS, and we’ll summarize the most common in this section.

Units of Absolute Size

Absolute units don’t change their size in relation to other settings, hence the name. The most common one encountered is pixels, which are expressed with the abbreviation px.

Other absolute measurements include:

  • q, mm, cm, in which are quarter-millimeters, millimeters, centimeters, and inches. These are rarely used outside of rules applied when printing websites.

  • pt, pc which are points and picas, common units of measurement within the publishing industry.

Units of Relative Size

Relative units are based on (relative to) the font-size property of the element, the viewport size, or grid container sizes. These are expressed as proportions of the units they are relative to, i.e.

.column {
  width: 30vw;
}

sets columns to be 30% of the width of the viewport.

Units Relative to Font Size

Setting dimensions of borders, padding, and margins using units relative to font sizes can help make these appear consistent at different resolutions. Consider a margin of 70px - it might make sense on a screen 1024 pixels wide, but on a phone screen 300 pixels wide, nearly half the available space would be margins!

Units that are relative to the font-size include:

  • em one em is the font-size of the current element (which may be inherited from a parent).
  • rem is same measurement as em, but disregards inherited font sizes. Thus, it is more consistent with intent than em, and is largely displacing its use (though it is not supported in older versions of Internet Explorer).

Units Relative to Viewport Size

The units vh and vw refer to 1/100th the height of the viewport (the size of the screen space the page can appear within).

It may be helpful to think of these as the percentage of the viewport width and viewport height, and they work much like percentages. However, in specifying heights, the vh will always set the height of an element, unlike %.

Fraction Units

The Grid model introduced the fraction (fr) unit, which is a fraction of the available space in a grid, after subtracting gutters and items sized in other units. See the discussion of the CSS Grid Model or CSS Tricks’ A Complete Guide to Grid for more details.

Percentage Units

You can also specify percentages (%) for many properties, which are typically interpreted as a percentage of the parent element’s width or height. For example:

.column {
  width: 33%;
}

sets elements with the class column to be 33% the width of their parent element. Similarly:

.row {
  height: 20%;
}

sets elements with a class row to be 20% the height of their parent element.

If the parent does not have an explicit width, the width of the next ancestor with a supplied width is used instead, and if no ancestor has a set width, that of the viewport is used. The same is almost true of the height; however, if no elements have a specified height, then the percentage value is ignored - the element is sized according to its contents, as would be the case with height: auto.

Tip

One of the most frustrating lessons beginning HTML authors must learn is the difference in how width and height are handled when laying out a webpage. Using the percentage unit (%) to set the height almost never accomplishes the goal you have in mind - it only works if the containing element has a set height.

While there are hacky techniques that can help, they only offer partial solutions. It is usually best not to fight the design of HTML, and adopt layouts that can flow down the page rather than have a height determined at render time.

CSS Functions

CSS provides a number of useful functions that calculate values. Functions are written in the form name(arg1, arg2, ...) and are provided as values to CSS properties. For example, this CSS code sets the height of the content area to the available space on screen for content after subtracting a header and footer:

#header {height: 150px}
#footer {height: 100px}
#content {
  height: calc(100vh - 150px - 100px);
}

Here 100vh is the height of the viewport, and the header and footer are defined in terms of pixels.

Note

You might want to apply the box-sizing: border-box on these elements if they have padding and borders, or these additional dimensions will need to be included in the calculation. See the section on the CSS Box Model for more details.

Math Functions

CSS provides a number of useful math functions:

The Calc Function

As you have seen above, the calc() function can be used to calculate values by performing arithmetic upon values. These values can be in different units (i.e. calc(200px - 5mm) or even determined as the webpage is being interpreted (i.e. calc(80vw + 5rem)). See the MDN Documentation for more details.

The Min and Max Functions

CSS also provides min() and max() function, which provide the smallest or largest from the provided arguments (which can be arbitrary in number). As with calc(), it can do so with interpretation-time values.

The Clamp Function

The clamp() function clamps a value within a provided range. Its first argument is the minimum value, the second the preferred value, and the third the max. If the preferred value is between the min and max value, it is returned. If it is less than the minimum, the min is instead returned, or if it is greater than the maximum, the max value is returned.

Color Functions

Several CSS functions are used to create an modify colors. These are described in the CSS Color section.

Transformation Functions

Many CSS functions exist for specifying CSS transforms. See the MDN documentation for details.

Image Filter Functions

CSS allows for filters to be applied to images. More details can be found in the Mozilla Documentation.

Counter Functions

Finally CSS uses counters to determine row number and ordered list numbers. These can be manipulated and re-purposed in various ways. See the MDN Documentation for details.

CSS Colors

CSS also has many properties that can be set to a color, i.e. color, background-color, border-color, box-shadow, etc. Colors consist of three or four values corresponding to the amount of red, green, and blue light blended to create the color. The optional fourth value is the alpha, and is typically used to specify transparency.

Colors are stored as 24-bit values, with 8 bits for each of the four channels (R,G,B,and A), representing 256 possible values (2^8) for each channel.

Colors can be specified in one of several ways:

  • Color Keywords like red, dark-gray, chartreuse correspond to well-defined values. You can find the full list in the MDN Documentation.

  • Hexidecimal Values like #6495ed, which corresponds to cornflower blue. The first two places represent the red component, the second the green, and the third the blue. If an additional two places are provided the last pair represents the alpha component. Each pair of hex values can represent 256 possible values (16^2), and is converted directly to the binary color representation in memory.

  • RGB Function a third option is to use the RGB() CSS function. This take decimal arguments for each channel which should be in the range 0-255

  • HSL Function a fourth option is the HSL() function, which specifies colors in terms of an alternative scheme of hue, saturation, and lightness.

  • RGBA and HSLA Functions finally, the RGBA() and HSLA() functions take the same arguments as their siblings, plus a value for the alpha channel between 0 and 1. This alpha channel represents the opacity/transparency of the color, with 0 being fully transparent and 1 being fully opaque. Thus, a value of .75 represents 3/4 opacity, or 1/4 transparency.

CSS and Text

As the original purpose of the World-Wide-Web was to disseminate written information, it should be no surprise that CSS would provide many properties for working with text. Some of the most commonly employed properties are:

  • font-family defines the font to use for the text. Its value is one or more font family or generic font names, i.e. font-family: Tahoma, serif, font-family: cursive or font-family: "Comic Sans". Font family names are typically capitalized and, if they contain spaces or special characters, double-quoted.
  • font-size determines the size of the font. It can be a measurement or a defined value like x-small.
  • font-style determines if the font should use its normal (default), italic, or oblique face.
  • font-weight determines the weight (boldness) of the font. It can be normal or bold as well as lighter or darker than its parent, or specified as a numeric value between 1 and 1000. Be aware that many fonts have a limited number of weights.
  • line-height sets the height of the line box (the distance between lines of text). It can be normal, or a numeric or percent value.
  • text-align determines how text is aligned. Possible values are left (default), center, right, justify, along with some newer experimental values.
  • text-indent indents the text by the specified value.
  • text-justify is used in conjunction with text-align: justify and specifies how space should be distributed. A value of inter-word distributes space between words (appropriate for English, Spanish, Arabic, etc), and inter-character between characters (appropriate for Japanese, Chinese, etc).
  • text-transform can be used to capitalize or lowercase text. Values include capitalize, uppercase, and lowercase.

Choosing Fonts

An important consideration when working with HTML text is that not all users will have the same fonts you have - and if the font is not on their system, the browser will fall back to a different option. Specifying a generic font name after a Font Family can help steer that fallback choice; for example:

body {
  font-family: Lucinda, cursive
}

will use Lucinda if available, or another cursive font if not. Some guidelines on font choice:

  • cursive fonts should typically only be used for headings, not body text.
  • serif fonts (those with the additional feet at the base of letters) are easier to read printed on paper, but more difficult on-screen.
  • sans-serif fonts are easier to read on-screen; your body text should most likely be a sans-serif.

Web-Safe Fonts

Fonts that commonly appear across computing platforms and have a strong possibility of being on a users’ machine have come to be known as web-safe. Some of these are:

  • Arial
  • Helvetica
  • Times
  • Courier New
  • Courier
  • Georgia
  • Lucidia Console
  • Palatino
  • Verdana

Font-Face at Rule

Alternatively, if you wish to use a less-common font, you can provide it to the browser using the @font-face rule. This defines a font and provides a source for the font file:

@font-face {
  font-family: examplefont;
  src: url('examplefont.ttf');
}

You can then serve the font file from your webserver.

Warning

Be aware that distributing fonts carries different legal obligations than distributing something printed in the font. Depending on the font license, you may not be legally allowed to distribute it with the @font-face rule. Be sure to check.

CSS Box Model

As the browser lays out HTML elements in a page, it uses the CSS Box Model to determine the size and space between elements. The CSS box is composed of four nested areas (or outer edges): the content edge, padding edge, border edge, and margin edge.

Box Areas

CSS Box Model CSS Box Model

Content Area contains the actual content of the element (the text, image, etc). By default the CSS properties width and height set this size, and the min-width, min-height, max-width, max-height constrain it (but see the discussion of box-sizing below).

Padding Area provides space between the content and the border of the HTML element. It is set by the padding properties (padding-top, padding-right, padding-bottom, and padding-left, as well as the shorthand versions).

Border Area draws a border around the element. Its size is set with the border-width property. Borders can also be dashed, inset, and given rounded corners. See the MDN Border Documentation for details.

Margin Area provides the space between the border and neighboring elements. Its size is set with the margin properties (margin-top, margin-right, margin-bottom, and margin-left, as well as the shorthand versions).

Box-Sizing

By default, an element’s width and height properties set the width and height of the content area, which means any padding, borders, and margins increase the size the element consumes on-screen. This can be altered with the box-sizing CSS rule, which has the following possible values:

content-box (the default) the width and height properties set the content area’s size.

border-box includes the border area, padding area, and content area within the width and height of the HTML element. Using the CSS rule box-sizing: border-box therefore makes it easier to lay out elements on the page consistently, without having to do a lot of by-hand calculations.

Backgrounds

The background property allows you to set a color, pattern, or image as the background of an HTML element. By default the background extends to the border-area edge, but can be altered with the border-clip property to border-box, padding-box, or content-box. See the MDN Background Documentation for more details on creating backgrounds.

Box-Shadow

The box-shadow property allows you to set a drop shadow beneath the HTML element. See the MDN Documentation for more details on creating box shadows.

CSS Positioning

By default HTML elements are positioned in the page using the HTML flow algorithm. You can find a detailed discussion in the MDN Documentation. However, you may want to override this and manually position elements, which you can do with the CSS properties position, left, top, right, and bottom.

The Positioning Context

First, we need to understand the positioning context, this is basically the area an element is positioned within. The left, top, right, and bottom properties affect where an element appears within its context.

Positioning Context Positioning Context

You can think of the context as a box. The left property determines how far the element is from the left side of this context, the top from the top, right from the right, and bottom from the bottom. These values can be numeric or percentage values, and can be negative.

Tip

If you define both a left and right value, only the left value will be used. Similarly, if both top and bottom are supplied, only top is used. Use the width and height properties in conjunction with the positioning rules if you want to control the element’s dimensions.

What constitutes the positioning context depends on the elements position property, which we’ll discuss next.

The Position Property

The position property can be one of several values:

Static Positioning

The default position value is static. It positions the element where it would normally be in the flow and ignores any left, top, right, and bottom properties.

Relative Positioning

The position value of relative keeps the element where it would normally be in the flow, just like static. However, the left, top, right, and bottom properties move the element relative to this position - in effect, the positioning context is the hole the element would have filled with static positioning.

Absolute Positioning

Assigning the position property the value of absolute removes the element from the flow. Other statically positioned elements will be laid out as though the absolutely positioned element was never there. The positioning context for an absolutely positioned element is its first non-statically positioned ancestor, or (if there is none), the viewport.

A common CSS trick is to create a relatively-positioned element, and then absolutely position its children.

Fixed Positioning

Assigning the value of fixed to the position property also removes the element from the flow. However, its positioning context is always the viewport. Moreover, if the page is scrolled, a fixed-position element stays in the same spot (an absolutely-positioned element will scroll with the page). This makes fixed position elements useful for dialogs, pop-ups, and the like.

Z-Order

By default, elements are drawn in the browser in the order they appear in the HTML. Thus, if we position an element further down the page, it may be covered up by succeeding elements. The z-index property provides us with a fix. The default value for the z-index is 0. Items with a larger z-index are drawn later.

CSS Layouts

We often speak of the separation of concerns principle in the context of web development as setting up the roles of HTML, CSS, and JavaScript. In this understanding, HTML provides the organization of content, CSS provides for the presentation of the content, and JavaScript provides for user interaction.

In this understanding, CSS is often tasked with the role of laying out elements on the page. More specifically, it overrides the default flow of HTML elements (see our earlier discussion of block vs. inline elements in the HTML chapter), altering how the browser arranges elements on the page.

The three most common layout approaches currently used in modern web development are float, flexbox, and grid, named for the CSS properties that make them possible. You may also encounter absolutely positioned layouts and table layouts, so we will briefly discuss those as well.

Float Layouts

By default a block-level element stretches the width of the parent element, and its siblings are pushed underneath. The CSS float property changes this behavior, allowing a block-level element to determine an appropriate width based on its content (or CSS rules), and allows sibling elements to “float” next to it. Thus, the float property has two primary values, left and right (as well as none and inherit). A float: left causes the element to float on the left side of its containing element, and a float: right floats it to the right.

A common use is to float figures and images within a page, i.e.:

<blockquote>
  <img src="/images/Marc-Andreessen.jpg"/>
  <p>People tend to think of the web as a way to get information or perhaps as a place  to carry out ecommerce. But really, the web is about accessing applications. Think of each website as an application, and every single click, every single interaction with that site, is an opportunity to be on the very latest version of that application.</p>
  <span>- Marc Andreessen</span>
</blockquote>

People tend to think of the web as a way to get information or perhaps as a place to carry out ecommerce. But really, the web is about accessing applications. Think of each website as an application, and every single click, every single interaction with that site, is an opportunity to be on the very latest version of that application.

- Marc Andreessen
But floats can also be used to create multi-column layouts, i.e.:
.column {
  float: left;
  box-sizing: border-box;
  width: 33%;
  height:60px;
  color: white;
}
.one {background-color: red}
.two {background-color: blue; margin-left: 0.5%; margin-right: 0.5%}
.three {background-color: green}
<div class="column one">
  One
</div>
<div class="column two">
  Two
</div>
<div class="column three">
  Three
</div>
One
Two
Three

Finally, when discussing the float property, we need to discuss the clear property as well. The clear property is used to move an element below the margin area of any floating elements - basically resetting the flow of the page. It can selectively clear floating elements in the left, right, or both directions. In our column example, if we wanted to add a footer that stretched across all three columns, we’d use something like:

footer {
  clear: both;
  border: 1px solid black;
}
<div class="column one">
  One
</div>
<div class="column two">
  Two
</div>
<div class="column three">
  Three
</div>
<footer>Footer</footer>
One
Two
Three
Footer

Flexbox Layouts

The Flexible Box Layout (flexbox) is intended to offer a greater degree of control and flexibility (pun intended) to laying out web pages. Its purpose is to provide an efficient way of laying out, aligning, and distributing elements within a container. Moreover, it can carry out this goal even when the sizes of the child elements are unknown or dynamic.

flexbox diagram flexbox diagram

The flexbox model therefore consists of two levels of nested elements - an outer container element and inner content item elements (the content item elements themselves can have many descendant elements). The flexbox properties help define how the content item elements are laid out within their parent container.

An HTML element is turned into a flexbox container by assigning it the display property of flex. Additional properties then control how the elements contained within our new flexbox container are laid out. These include:

flex-direction determines how items are laid out, either row, column, row-reverse, or column-reverse.

wrap-items determines if the row or column wraps into multiple rows or columns. Its values are no-wrap (default), wrap, and wrap-reverse.

justify-content defines how content items will be aligned along the main axis of the container (horizontal for rows, and vertical for columns). Its possible values are: flex-start, flex-end, center, space-between, and space-around.

align-items defines how content items are aligned along the secondary axis of the container (vertically for rows, and horizontally for columns). Its possible values are flex-start (the default), flex-end, center, stretch, and baseline.

Thus, to re-create our three-column layout with flexbox, we would:

.three-column {
  display: flex;
  flex-direction: column;
  justify-content: space-between;
}
.three-column > div {
  color: white;
  width: 33%;
  height: 60px;
}
<div class="three-column">
  <div class="one">
    one
  </div>
  <div class="two">
    two
  </div>
  <div class="three">
    three
  </div>
</div>
One
Two
Three

The items can also override these default behaviors with item specific CSS attributes, including allowing items to grow with flex-grow or shrink with flex-shrink to fit available space, override the default order of elements using the order attribute, or altering the alignment on a per-item basis with align-self.

You can also create very sophisticated layouts by nesting flex containers within flex containers. A superb reference for working with flexbox is CSS Tricks’ Complete Guide to Flexbox.

Grid Layouts

While flexbox brought a lot of power to the web designer, the Grid model is an even more powerful way to lay out web elements. Unlike flex, which focuses on arranging elements along one dimension (provided you aren’t wrapping), the Grid model lays elements out in a two-dimensional grid.

An HTML element is turned into a grid container by assigning it the display property of grid or inline-grid. Then you define the size of the grid elements with the properties grid-template-rows and grid-template-columns. These attributes take the measurements of the columns and rows. I.e. we could recreate our three-column layout with grid-template-columns: 33% 33% 33%. But the grid is far more powerful than that. Let’s expand our three-column layout to have a separate header and footer:

div#page {
  display: grid;
  grid-template-columns: 1fr 1fr 1fr;
  grid-template-rows:  150px auto 100px;
}

Here we use the unit fr for our column width, which proportions out the “free space remaining” after hard-coded measurements are accounted for. In our case, this translates into three equal-sized columns taking up all the available space.

For rows, our first row will be the header, and we’ve sized it to 150 pixels. The next row is our content, we’ve used the auto value to allow it to size appropriately to contain its content. The last row is the footer, and we’ve sized it to 100 pixels.

Our HTML would look like:

<div id="page">
  <header></header>
  <div id="one"></div>
  <div id="two"></div>
  <div id="three"></div>
  <footer></footer>
</div>

And finally, we can assign these HTML elements to part of the grid with the grid-area property, which takes the values for row start, column start, row end, column end separated by slashes (/) to define the area of the grid they span:

header {
  grid-area: 1/1/2/4; 
  border: 1px solid black;
}
#one {
  grid-area: 2/1/3/2; 
  height: 50px; 
  background-color: red;
}
#two {
  grid-area: 2/2/3/3; 
  height: 200px; 
  background-color: blue;
}
#three {
  grid-area: 2/3/3/4; 
  height: 300px; 
  background-color: green
}
footer {
  grid-area: 3/1/4/4; 
  border: 1px solid black;
}
Header
One
Two
Three
Footer

We’ve really only scratch the surface of what is possible with the grid. Items can be aligned and justified, and tweaked in other ways, just as with flexbox. Names can be assigned to grid rows, grid columns, and grid areas, and used to make the resulting CSS more approachable and understandable.

A great resource for deeper exploration is CSS Trick’s Complete Guide to Grid.

Table Layouts

At one point in the 1990’s, it was common practice for graphic designers to create a web page using graphic design software, export it as an image, and then slice up the image. These pieces were then used as the background-image property of a table cell, and text was overlaid on this graphics as the contents of the cell.

Thankfully, this practice has largely been abandoned, but you may still encounter it from time to time. There are some very real problems with the approach: if you increase the text size on the page, the cells may expand beyond the size of their background image, and the seams between the images will open up. Also, if a screen reader is used, it will often read content out-of-order and will describe the portions in terms of table rows and columns.

In web design best practices, tables should only be used for tabular data. If you desire to use this kind of slice-and-dice approach, use the Grid instead. It provides the same control over the placement of text, and can use a single background-image on the container element or multiple background images for the items.

Responsive Web Design

Modern websites are displayed on a wide variety of devices, with screen sizes from 640x480 pixels (VGA resolution) to 3840x2160 pixels (4K resolution). It should be obvious therefore that one-size-fits-all approach to laying out web applications does not work well. Instead, the current best practice is a technique known as Responsive Web Design. When using this strategy your web app should automatically adjusts the layout of the page based on how large the device screen it is rendered on.

Media Queries

At the heart of the responsive CSS approach is a CSS technique called media queries. These are implemented with a CSS media at-rule (at-rules modify the behavior of CSS, and are proceeded by an at symbol (@), hence the name). The original purpose of the media rule was to define different media types - i.e. screen and print, which would be selectively applied based on the media in play. For example, the rule:

@media print {
  img {
    display: none;
  }
  .advertisement {
    display: none;
  }
}

would hide all images and elements with the advertisement class when printing a web page. You can also specify the media type with the media attribute in a <link> element, i.e. <link href="print.css" rel="stylesheet" media="print"> would apply the rules from print.css only when printing the website.

However, the real usefulness of the @media rule is when it is combined with a media query, which determines if its nested rules should be applied based on some aspect of the display.

The media query consists of:

  1. The @media keyword
  2. An optional media type (typically screen, but could also be all, print, and speech)
  3. The desired media features surrounded by parentheses. Multiple features can be joined by logical operators and, or logically or-ed using a ,. More advanced queries can use not to invert an expression (if using not you must specify the media type).
  4. A block of CSS surrounded by {}

An example media query that applies only when the screen is in portrait orientation (taller than it is wide) is:

@media (orientation: portrait) {
  /* rules for a portrait orientation go here... */ 
}

The most commonly used media features are max-width, min-width, max-height, min-height, and orientation. The sizes can be specified in any CSS unit of measurement, but typically px is used. The orientation can either be portrait or landscape.

We can also combine multiple queries into a single @media rule:

@media (orientation: landscape) and (max-width: 700px) {
  /* rules for a landscape-oriented screen 700 pixels or less wide */
}

The and in this case works like a logical and. You can also use the not keyword, which inverts the meaning of the query, or commas ,, which operate like a logical or.

More details on the use of media queries can be found in the MDN Documentation on the subject.

By combining the use of media queries, and CSS layout techniques, you can drastically alter the presentation of a web application for different devices. Most browser development tools will also let you preview the effect of these rules by selecting a specific device size and orientation. See the Chrome Device Mode documentation, Safari Developer Documentation, and Firefox Responsive Design Mode Documentation for details.

The Viewport Meta Tag

The media query size features relate to the viewport, a rectangle representing the browser’s visible area. For a desktop browser, this is usually equivalent to the client area of the browser window (the area of the window excluding the borders and menu bar). For mobile devices, however, the viewport is often much bigger than the actual screen size, and then scaled to fit on the screen:

The mobile viewport scaling strategy The mobile viewport scaling strategy

This strategy helps legacy websites reasonably appear on mobile devices. However, with responsive designs, we want the viewport and the device size to match exactly. We can clue mobile browsers into this desire by adding a specific meta tag to the <head> of the HTML:

<meta name="viewport" content="width=device-width, initial-scale=1.0">

For a responsive design to work, it is critical that this <meta> element be included, and use the exact syntax specified.

Advanced CSS Layouts

Finally, responsive designs tend to make heavy use of two new CSS layout strategies - The Flexible Box Module (flex) and the CSS Grid Layout. These two layout tools allow for easily changing layouts within media queries - even allowing for the rearranging of elements!

Two great visual resources for learning the ins and outs of these layouts are CSS Trick’s A Complete Guide to Flexbox and A Complete Guide to Grid. In fact, you may want to bookmark these now.

CSS Transformations

With powerful graphics cards becoming commonplace, the W3C standards expanded the CSS rules to take advantage of their abilities. Specifically, the CSS transform property allows us to apply a transformation (scaling, rotating, translating, stretching, skewing, or any combination) to elements on a page. These are performed using a mathematical operation based on the use of matrices. Graphics cards are optimized to perform these matrix operations in parallel, allowing them to quickly transform graphical objects in 3-dimensional space. While a full discussion of the underlying mathematical operations is outside the scope of this course, we don’t have to create the matrices ourselves - the CSS rules provide function for building and combining the most common transformation matrices.

To explore these transformations, we will use a demonstration <div> and css rules:

<div class="transform-example">Element to be Transformed</div>
div.transform-example {
  display: flex;
  align-items: center;
  justify-content: center;
  border: solid;
  color: purple;
  width: 230px;
  height: 120px;
}
Element to be Transformed

Coordinate Space

An important concept to grasp before tackling transformations is the concept of coordinate spaces. A coordinate space is simply a way of expressing position, relative to an origin point. You have likely studied the Cartesian coordinate system in your math courses. CSS uses a similar approach for applying transforms in 2d or 3d space, with one important difference - the y-axis is reversed. Thus, the 2d coordinate system looks like:

Translation

The first transformation we’ll discuss is translation, moving an element along the x, y, or z axes. In most cases, we’re focused on just the x and y coordinates, for which we can use the translate(x, y) function:

<div class="transform-example" id="translation-example">Element to be Translated</div>
div#translation-example {
  transform: translate(100px, -30px);
}
Element to be Translated

We can also translate in three dimensions with the translate3d() function, but note that visually a change in the z position will not be visually apparent in most cases (unless we are using a perspective transformation). Additionally, we can create a translation matrix with only the x, y, or z axis using translateX(x), translateY(y), and translateZ(z), respectively.

Rotation

Rotation can happen around any of the primary axes, facilitated by the rotateX(angle), rotateY(angle), and rotateZ(angle). Rotation angles can be expressed in degrees (deg) or radians (rad). Rotation about the z is perhaps the most commonly used, as it “spins” an element on the page:

<div class="transform-example" id="rotation-z-example">Element to be Rotated</div>
div#rotation-z-example {
  transform: rotateZ(45deg);
}
Element to be Rotated

But rotations about the x or y can also be interesting, as they rotate around the screen, but to avoid looking like a squished version of the untransformed element, should be combined with a perspective transform. We’ll revisit this after discussing combining transformations.

Scale

Scaling refers to increasing or decreasing the size of the element, either all together or along a single axis. We can use the scale(x, y) to scale in two dimensions, scale(x, y, z) to scale in three, or scaleX(x), scaleY(y), or scaleZ(z) to scale along a single axis. Scale values are expressed as floating point numbers (1 = 100%, 1.5 = 150%, -0.5 = -50%).

<div class="transform-example" id="scale-example">Element to be Scaled</div>
div#scale-example {
  transform: scale(0.5, 0.5);
}
Element to be Scaled

Skew

Skewing distorts an element, distorting each point in the element by a certain angle in the horizontal, vertical, or both (CSS skew functions are only expressed in two dimensions). You can use skew(xAngle, yAngle) to specify both vertical and horizontal, or skewX(angle) and skewY(angle) to do skew separately.

<div class="transform-example" id="skew-example">Element to be Skewed</div>
div#skew-example {
  transform: skewX(10deg);
}
Element to be Skewed

Combining Transformations

To combine multiple transformations, just list the transformation function one after another. For example, to translate and rotate we could:

<div class="transform-example" id="combo-example">Element to be Transformed</div>
div#combo-example {
  transform: translateX(50%) rotateZ(60deg);
}
Element to be Transformed

Perspective

When using three dimensions, it is helpful to apply a perspective projection. This is a mathematical transformation that makes points far from the viewer appear closer to one another than those closer to the viewer. This mimics how our eyes and brain interpret what we see in the world as depth.

The perspective projection is specified with the projection(distance) function, where the distance is how far you are from the element being transformed along the z-axis. In general, a small number will create a more dramatic transformation than a large one.

<div class="transform-example" id="projection-example">Element to be Transformed</div>
div#transform-example {
  transform: perspective(300px) rotateX(45deg);
}
Element to be Transformed

CSS Transitions

While most CSS rules are applied as the page is loaded, some are applied at a future point. One example is pseudo-classes that apply in limited circumstances - like :hover which applies only when the mouse is over an element, or :valid and :invalid which are applied to input elements based on their validation status. Another common instance is when elements are added to the page using JavaScript, as discussed in Chapter 5 - JavaScript.

In these instances, we may want the change in styling rules to not apply instantly, but rather transition between states over a short duration. The transition property provides us this functionality. It has three sub-properties:

  • transition-property - the property or properties to transition. This can include any of the CSS properties that are defined as being animatible (basically those that have continuous values).
  • transition-duration - how long the transition should take, expressed in seconds or milliseconds.
  • transition-timing-function - one of several timing functions that define how the value transitions over time

We can also express all three with the shorthand `transition: [property] [duration] [timing-function].

Let’s see an example:

<div class="transition-example">Hover over me!</div>
div.transition-example {
  color: #fff;
  display: flex;
  align-items: center;
  justify-content: center;
  width: 200px;
  height: 200px;
  background-color:#512888;
  transition: background-color 1s ease;
}
div.transition-example:hover {
  background-color: #A7A7A7;
}
Hover over me!

We can combine multiple transitions by providing a comma-separated list to the transition shorthand property:

<div class="transition-example-2">Hover over me!</div>
div.transition-example-2 {
  color: #fff;
  display: flex;
  align-items: center;
  justify-content: center;
  width: 200px;
  height: 200px;
  background-color:#512888;
  font-size: 1.5rem;
  transition: background-color 1s ease, font-size 2s linear;
}
div.transition-example-2:hover {
  background-color: #A7A7A7;
  font-size: 1rem;
}
Hover over me!

Alternatively, we can define multiple transitions with the sub-properties. In this case, the transition-property is a comma-separated list, and the same transition-duration and transition-timing-function apply to all specified properties:

<div class="transition-example-3">Hover over me!</div>
div.transition-example-3 {
  color: #fff;
  display: flex;
  align-items: center;
  justify-content: center;
  width: 200px;
  height: 200px;
  background-color:#512888;
  font-size: 1.5rem;
  transition-property: background-color, font-size;
  transition-duration: 500ms;
  transition-timing-function: ease-in;
}
div.transition-example-3:hover {
  background-color: #A7A7A7;
  font-size: 1rem;
}
Hover over me!

CSS Timing Functions

The transition property (and the animations we’ll look at shortly) both make use of timing functions - functions that define how the values of a CSS property change over time as we transition from one state to the next.

The simplest (and default) transition function is linear, which means the value smoothly transitions from its starting value to its ending value. It is specified as linear.

While linear is easy to reason about and represent mathematically, transitions often look more realistic if the start or end more slowly, speeding up in the middle. Graphically, these functions look like a curve, and can be mathematically represented by a Bezier curve. We can specify this curve with cubic-bezier(<x1>, <y1>, <x2>, <y2>) where the arguments are the coordinates of control points.

But for ease of use (and so we don’t need to learn the math behind Bezier curves), CSS also provides several pre-defined Bezier curves we can use instead:

  • ease - starts and ends slowly, with a rapid speeding up in the middle
  • ease-in - starts slowly, speeds up, and stops suddenly
  • ease-out - starts quickly but slows down at the end
  • ease-in-out - similar to ease, but the start and end transitions are more even

The transition timing functions we’ve just discussed are the most commonly employed in web design. But there are other transitions functions available in the CSS standards, and if you understand the mathematics behind Bezier curves, you can define your own as well. You can read more about the subject in the MDN Web Docs.

CSS Animation

As the web became more interactive, there was an increased desire to animate elements on webpages. Animations are a powerful tool for increasing visual interest and also conveying information. Consider the Apple sign-in dialog, which on an incorrect username/password combination shakes back and forth briefly, mimicking a human’s head shaking.

Early animations in the web relied on using JavaScript to change properties of elements on the page. While these techniques are still used, they are increasingly supplanted by new CSS rules that are easier to write and provide more precise control.

CSS Libraries

While you can write custom CSS to accomplish nearly any look you want for your webpages, many developers prefer to use a pre-written CSS library that provides rules covering many common user interface needs. For example, CSS-based menus are very challenging to write well, but a well-designed library makes the task very approachable. CSS libraries also provide a consistent UI look and feel across websites, which helps people browsing your website quickly identify how to move around.

One of the most popular CSS frameworks is Bootstrap, which is used by thousands of websites. Other popular CSS libraries include Tailwind, Semantic, and Animate which as the name suggests provides animation components.