CSS for Beginners - Cascading Style Sheets Crash Course

CSS - Level: Beginner

34 Video Lectures
7.2 Hours of Video
82 Pages of Notes
1855 Lines of Code
113 Inline Math Snippets
234 Inline Code Snippets
59 Custom Visual Graphics
Updated and Maintained

Introduction to CSS Layouts - Quick Overview

Cascading Styles Sheets - Level: Beginner

CSS Layouts - Quick Overview

If you're looking for a speedy introduction to Cascading Style Sheets (CSS), this quick overview is a fantastic resource.

Chris crafted this document while learning about CSS, and its brevity makes it a handy reference for anyone seeking a quick rundown on the topic.

What is CSS?

CSS is a language used for coding the presentation information, i.e. the style, of markup documents. For our purposes, the documents we will be focusing on are the ones used on the web like:

CSS is stands for cascading style sheets. The word cascading refers to the algorithm used for applying style rules across a document.

CSS Box Model

The CSS box model is the model that CSS uses to represent elements on a web page. Each element is represented using a collection of four boxes.


These boxes have the following names:


The content box is specified by the content of the element. Examples of content include the following:

The remaining boxes are specified by the these CSS properties:

To understand how these boxes work together in CSS, we can think of them as containing one another and expanding in size.


Visualizing these boxes on a two-dimensional plane, we can see that, when taken together, the boxes create areas. These areas are knowns as follows:

  1. Content area
  2. Padding area
  3. Border area
  4. Margin area

This idea becomes concrete as soon as we have actual content, and we have specified values for the padding, border and margin.

Suppose we have the following HTML:

        p {
            padding: 5px;
            border: 5px;
            margin: 5px;
    <p>I am an element</p>

The code above gives us the boxes below for the paragraph element:


Sizing Elements

As we have seen, CSS represents elements using boxes, and so, if we want to know the size of an element, we need to consider the size of these boxes.

In CSS an element has an inner size and an outer size. The element's inner size is the size of the content box and the element's outer size is the size of the margin box.


With this in mind, we have two related sizing concepts:

Intrinsic sizing is when an element's size is based on the element's outer size, the size of the element's margin box. In CSS, an element's size is intrinsic by default.

Extrinsic sizing is when an element's size based set using sizing properties. All of the following are sizing properties:

In the CSS box model, extrinsic sizing is applied to an element's content box. This can lead to unintuitive behavior, and so it's common to manually change this behavior in our CSS. This can be done using the box-sizing CSS property.

* {
    box-sizing: border-box;

Note that the common practice is to set the box-sizing property to the border-box instead of the content-box.

See the box sizing code demo on MDN to see why it's preferable to change the default box sizing.

Here is a list of resources for learning more about the CSS box model:

Responsive Design

When we are thinking about responsiveness, we are usually thinking about the size of elements when screen sizes move from small to large.

Sizing Units

When using CSS we have two types of sizing units:

There are a wide range of values that fall into each of these categories, but for our purposes, we'll only discuss the most common options. For a full description, see this MDN article on CSS values and units.

For absolute sizing, we use px which stands for pixels. For relative sizing, we have the following:

Sizing Option Relative To
em Font size of parent when used with font-size.
em Font size of element when not used with font-size.
rem Font size of the root element.
ch Width of the 0 character of the element's font.
vw 1% of the viewport's width.
vh 1% of the viewport's height.

Note that all of the relative options ultimately translate into pixels values.

Sizing Units: em vs rem

When setting font-size values, em unit values are relative to the parent element's font size. If the parent element doesn't have a font-size, the em unit values are relative to the actual element's font-size value.


Note that the actual meaning of the word "em" is vague. Its origin seems to be connected to the pre-digital age when the actual letter "M" was used for sizing in printed documents.

See this StackExchange article that discusses the actual meaning of the word "em" for more details.

When em values are nested, the multiples cascade. This cascading can make is difficult understand CSS that utilizes em units. Consider the example below.


In this example, the only change is the font-size on the button. Now that the button is using em units, the calculation of the button's width depends on the font-size of the parent.

This show how we build in dependencies as we nest using em units. This can be problematic when the chain of dependencies becomes large. To solve this problem. We have rem units.

The rem unit behaves the same as the em unit with one important difference. Rem units are relative to the font-size of the root element. This is where the "r" comes from in the name "rem".

Rem units solve the problem of large dependencies because there is only a single dependency, the font-size of the root element.

What is the root element?

In HTML, the root element is the html element. In CSS we can target the html element in two ways.

html {
    font-size: 16px;

<!-- More common way -->
:root {
    font-size: 16px;

Note that the more common way of using the :root CSS pseudo-class has higher specificity than using html to target the root element.

Also note that most browsers have a default font-size of 16px on the root element. This means that most of the time 1rem is equal to 16px, 2rem is equal to 32px, and so on.

When should we use rem vs em?

Unit When to Use
rem Use as the first choice for all sizing needs, e.g. font-size, padding, margin, border.
em Use sparingly. Only use when property benefit from being sized relative to the element's font size or the parent's font size, e.g. padding on a button.

Sizing Units: ch

The ch unit in CSS is a relative unit that represents the width of the 0 (zero) character of the current font being used. It's a good idea to use the ch unit in the following situations:

Keep in mind that the ch unit is font-dependent, so the actual size can vary depending on the font being used. Always test our design with different fonts, devices, and screen sizes to ensure compatibility and consistent behavior.

Troubleshooting ch Issues

Here are a few common issues that may cause unexpected sizing behavior when using the ch sizing unit:

  1. Font-family inconsistencies: The ch unit is based on the width of the 0 (zero) character of the current font being used. If the font-family applied to the text inside the column is different from the font-family of the container (or any of its ancestors), the ch size calculation may not accurately reflect the text's actual width. To fix this issue, we should ensure that the font-family is consistent throughout the container and its contents.
  2. Font not loaded: If we are using a custom web font, there may be a delay in loading the font or the font may not have loaded at all. In such cases, the browser might use a fallback font to calculate the ch size, leading to unexpected sizing behavior. Make sure the custom font is properly loaded and applied to the container and its contents.
  3. CSS specificity: If there are other conflicting styles applied to the column or its contents, those styles might be taking precedence over the ch size definition. Check for any other CSS rules that could be affecting the sizing of the column or its contents, and adjust the specificity or cascade accordingly.
  4. Browser compatibility: While ch is widely supported by modern browsers, there might be inconsistencies in its implementation across different browsers. Make sure to test the layout on various browsers to ensure compatibility and consistent behavior.

Pages are Responsive by Default

By default, HTML pages behave responsively. For example, block level elements have width equal to 100% of their parent element.

Thus, as a parent element's width value changes, the child element will respond by changing with the parent.

Problems arise when we remove this responsiveness by setting fixed values for CSS properties like the following:

With this in mind, we can see why we should avoid using fixed values and opt for using dynamic values, e.g. percentages, for properties like height and width.


Problems with Fixed Sizes

With fixed widths, we will run into problems if the screen size becomes smaller than the fixed width. This results in a horizontal scroll bar.

With fixed heights, text can easily overflow element containers vertically when the screen size is becomes smaller.

Line Lengths Should be Limited

In general, we want to limit line lengths for text on the screen. Since small screen sizes have a constrained width by default, this is less of a problem on smaller screens. As a result, large line lengths are naturally more of an issue on larger screens.

Solutions to the line length problem are often the source of more complex layouts on larger screens. Limited line lengths for text is not only done for the look and feel of a page but also the functionality of a page. This is because shorter line lengths are easier to read.

To optimize for reading, the number of characters on a line should fall somewhere between 40 and 80 characters. This UX StackExchange article discusses this in more detail and links to additional resources.

We can use a combination of the following to achieve a responsive design:

Media Queries

We can use media queries to apply different styles based on the screen size. This allows us to create specific styles for mobile devices. For example, consider we are styling a header for a website:

/* Default styles for desktop */
.header {
    background-color: #f2f2f2;
    height: 100px;
    /* ...other styles... */

/* Styles for mobile */
@media (max-width: 768px) {
    .header {
        height: 60px;
        /* ...other mobile styles... */

Note that the max-width value of 768px is just an example. We can use any value that works for our design.

The value assigned to the max-width property is known as a breakpoint. Breakpoints are the points at which the layout of a website or application adapts or updates to accommodate a specific screen size.

The common breakpoints used in mobile design are specific screen widths at which the layout of a website or application adapts to provide an optimal user experience.

The actual breakpoints used can vary depending on the design and target audience, but some common breakpoints include:

Note that we actually have two properties that pair with the breakpoints:

The min-width property is used to apply styles to screens that are larger than the specified width. The max-width property is used to apply styles to screens that are smaller than the specified width.

The min-width property is used when changing styles on the way up to larger screens. The max-width property is used when changing styles on the way down to smaller screens.

Here is how the media queries for various breakpoints look in code when using the max-width property:

/* For smaller screens */
@media (max-width: 992px) {
    /* CSS styles for screens up to 992px */

/* For even smaller screens */
@media (max-width: 768px) {
    /* CSS styles for screens up to 768px */

/* For even smaller screens */
@media (max-width: 480px) {
    /* CSS styles for screens up to 480px */

/* For even smaller screens */
@media (max-width: 320px) {
    /* CSS styles for screens up to 320px */

Notice how when using the max-width media query, we start with the largest screen size and work our way down to the smallest screen size.

Media queries are a powerful tool for responsive design. However, excessive use of media queries can make the code difficult to maintain.

An approach that uses media queries is called mobile first. This approach starts with the mobile styles and then uses media queries to add styles for larger screens. Since mobile screens are smaller, it's easier to start with the mobile styles.

Smaller Screens are Easier

It's often the case that mobile user interfaces are more basic than their desktop counterparts. This is due to the constraints on available space.

These space constraints make is more common for mobile designs to be single column designs with widths that are 100% of the viewport. When we move to larger screens, user interfaces typically get more complex with multi-column designs.

This is why we generally design for mobile first. It is easier.

As screen sizes scale up, we can use min-width media queries to add complexity. One exception to this is with navigation. Quite often navigation is more complex on smaller screens. In this case, we can use a max-width media query to add the complexity.

Mobile First Approach

We can start with the mobile styles and then use media queries to add styles for larger screens. This ensures that the mobile version is the default, and we can progressively enhance it for desktop.

For example:

/* Mobile styles */
.header {
    background-color: #f2f2f2;
    height: 60px;

/* Styles for desktop */
@media (min-width: 768px) {
    .header {
        height: 100px;

We have already seen how smaller screens are easier to design. This is why the mobile first approach can be a good strategy. It's easier to start with the mobile styles and then use media queries to add styles for larger screens.

Note that the mobile first approach uses the min-width media query. This is because we are starting with the mobile styles and then adding styles for larger screens.

Here are some examples of with these media queries look like in code:

/* For larger screens */
@media (min-width: 480px) {
    /* CSS styles for screens 480px or wider */

/* For even larger screens */
@media (min-width: 768px) {
    /* CSS styles for screens 768px or wider */

/* For even larger screens */
@media (min-width: 992px) {
    /* CSS styles for screens 992px or wider */

/* For even larger screens */
@media (min-width: 1200px) {
    /* CSS styles for screens 1200px or wider */

Notice how when using the min-width media query, we start with the smallest screen size and work our way up to the largest screen size.

Hamburger Menu

We can consider using a hamburger menu for mobile devices to save space. We can hide the regular menu and show the hamburger icon that expands the menu when clicked.

Hamburger menus typically allow our menus to flow down a page vertically rather than across a page horizontally. This is a more natural flow as the vertical space is not constrained like the horizontal space.

The symbol is known as the hamburger icon. It consists of three horizontal lines stacked on top of each other. The hamburger icon has become widely recognized as a symbol for mobile menus.

The hamburger icon is often used to indicate the presence of a menu that can be expanded or collapsed. When clicked or tapped, it typically reveals a hidden navigation menu or additional options on a website or mobile application. Its purpose is to provide a compact and space-saving way to toggle the visibility of menu items or trigger certain actions.

The term hamburger icon is derived from its visual resemblance to the layers of a hamburger sandwich. The three horizontal lines are associated with the top bun, patty, and bottom bun. This metaphorical name has become widely adopted in the field of web design and user interface (UI) design.

Here's a basic example:

<div class="header">
    <div class="hamburger-menu">
        <button class="hamburger"></button>
    <ul class="menu">
.hamburger-menu {
    /* Hamburger icon styles go here */

.menu {
    display: none; /* Hide the regular menu by default */

@media (min-width: 769px) {
    .hamburger-menu {
        /* Hide the hamburger icon for larger screens */
        display: none;
    .menu.open {
        display: block;
        /* Show the menu when the hamburger icon is clicked */

The term hamburger menu for the mobile menu icon is believed to have originated from the design used in the Xerox Star, one of the earliest graphical user interfaces developed in the 1980s.

The Xerox Star used a set of three horizontal lines to represent a menu button. The lines resembled the layers of a hamburger, with the top bun, patty, and bottom bun stacked on top of each other.

When the design of mobile applications and websites started to adopt a similar button for the mobile menu, the term hamburger menu gained popularity due to its resemblance to the Xerox Star design.

The name stuck, and it became a commonly used term in the web design and development community to describe this particular style of mobile menu icon.

CSS Layouts

After we understand the CSS Box Model, we are ready to learn about CSS layouts. CSS layouts are concerned with how we arrange or layout our boxes on the screen.

Layout Types

There are three types of layouts in CSS:


Here, we've ordered the layout types from oldest to newest. Keeping this order and history in mind can help us understand how the usage of these layout types has morphed over time.

Often times, we will wonder why something exists or behaves in a particular way, and the answer will have a historical answer. This is especially true regarding the names of things.

Take for example the em sizing unit. Its name comes from pre-digital days when all documents were printed on paper.

Choosing a CSS Layout

All of these layouts are used together to build the overall page and the smaller components that live within a page. For this, reason, we usually work with all of the layout types to achieve the final layout of a page.

We set the layout using the display property.

.element {
    display: block;
    display: inline;
    display: flex;
    display: grid;

The table below gives a summary of each of these values:

Display Layout Also known As
inline Flow Flow layout or normal flow
block Flow Flow layout or normal flow
flex Flex Flexible box layout or flexbox
grid Grid Grid layout

CSS Flow Layout

By default, elements on a page are arranged using the flow layout. This is why flow is also called the normal flow. It's the default. The normal flow gives us two flow directions:

Every element has the CSS display property that determines how the element is displayed within the normal flow. The display property is assigned a default value of either inline or block.

.inline-element {
    display: inline;
.block-element {
    display: block;

Inline elements are displayed next to each other in the inline direction. We can also say that inline elements flow in the inline direction.

Block elements are displayed next to each other in the block direction, and we can also say that block elements flow in the block direction.

Note that the reason we talk about the directions this way is because the directions are arbitrary. The directions can be interchanged or flipped based on the writing mode CSS property.

In the English language, the writing mode is horizontal, left to right, and in this context, we can think of inline, and being on the same line, and block as being blocks on their own line.

Note that inline elements can live inside block elements.


CSS Display Property

The display property can be a little tricky. This is because there are several common short-hand syntaxes that are hiding details from us.

The display property actually controls two things:

This table shows us the meaning of the short-hand values.

Short-hand Value Element Children
inline inline flow
block block flow
flex block flex
grid block grid

Additionally, this table shows us how the layouts are related and used together, i.e. layouts and layout types are nested.

Notice how flex and grid elements have a value of block by default. This tells us that flex and grid layouts are nested inside the normal flow.

Note that the obscure organization and naming here is likely due to how CSS has evolved historically. Evolution can be messy.

If we want our flex or grid parent elements to be inline in the normal flow, we can do it using the following explicit display values:

.inline-flex-element {
    display: inline-flex;
.inline-grid-element {
    display: inline-grid;

There is also a two value syntax that makes all of this explicit and easier to understand.

.element {
    /* two-value syntax */
    display: block flow;
    display: inline flow;
    display: inline flow-root;
    display: block flex;
    display: inline flex;
    display: block grid;
    display: inline grid;
    display: block flow-root;

For the full list, see this MDN article on the CSS display property.

CSS Position Property

Once an element is displayed on a page inside the normal flow, it can be further adjusted with the position property.

For the CSS position property, we have the following possible values:

.element {
    position: static;
    position: relative;
    position: absolute;
    position: fixed;
    position: sticky;

To make use of the position property, we need to pair it with at least one of the following four CSS position offset properties:

The default positioning of all elements is static. The static value refers to the position inside the normal flow, and the positioning offset values have no effect.


Here are a few helpful observations:

The table above shows the basic concerns with these position values. To see more technical details, see this MDN article on the CSS position property.

CSS Float Property

Another CSS property that allows us to further position an element in the normal flow is the float property.

Before CSS Flexbox and Grid, the float property was used to allow block elements to be positioned next to each other on the same line.

Now, Flexbox and Grid are used for this and the use cases for the float property are now more limited. Values for the float property are as follows:

.element {
    float: none;
    float: left;
    float: right;

For more details on the float property, see this MDN article on the CSS float property.

Note that Flexbox and Grid are the preferred modern methods and we should avoid using float in most cases.

CSS Flexbox Layout

The CSS flexible box layout allows us to specify an element as a container element. This container element is known as a flex container.

When a flex container is created, the direct children of the container are then represented using boxes that have flexible sizes.

By saying flexible sizes, we mean that the boxes can either grow or shrink in size to fit inside their parent element, the flex container.

To configure a flexbox layout, we apply CSS flex and alignment properties to the container and the direct children of the container.

Flex Container and Flex Items

To use flexbox, we need to understand the following items:

Once an element has been specified as a flex container, the direct children of the container automatically become flex items.

    .container {
        display: flex;

<div class="container">
    <div>Flex item 1</div>
    <div>Flex item 2</div>
    <div>Flex item 3</div>

The flex items can be thought of as the flexible boxes in the flexbox model.

This is because flex items change their sizes automatically based on the size of the flex container. Hence, flex items are flexible.

Flex Container CSS Properties

The flex container has the following CSS properties that control the container's behavior.

Flexbox works in a single direction, and flex items are laid out in this direction which is known as the flex direction.

The flex direction is specified using the flex-direction CSS property.

.container {
    display: flex;
    flex-direction: row;
    flex-direction: column;

Next, we have the flex-wrap property.

.container {
    display: flex;
    flex-wrap: wrap;
    flex-wrap: nowrap;

The flex-wrap property specifies whether the flex items are allowed to wrap onto a new line. If allowed, flex items will automatically wrap to a new line whenever the items run out of room as determined by the flexbox algorithm.

Note that, when wrapping is allowed, overflows in the flex direction are prevented.

We also have a short-hand property called flex-flow that allows us to set both the flex-direction and the flex-wrap in one line.

.container {
    display: flex;
    flex-flow: row wrap;

Flex Item CSS Properties

Every flex item inside a flex container has the following CSS properties:

The flex-grow property specifies what proportion of the remaining space in the container should be allocated to the flex item. By default, the flex-grow property is set to 0.

The flex-shrink property specifies how an item should shrink if the total size of the combined flex items is larger than the container. By default the flex-shrink property is set to 1.

The flex-basis property specifies the size of the flex item if the size is not set externally.

For more information, see the links below:

We also have a short-hand property called flex that will allow us to set all of these values in a single line.

.flex-item {
    flex: 0 1 auto;

In most cases, the default flex values will work well. The more common adjustments are done using the alignment properties.

Flex Container Alignment Properties

With flexbox, we have the following CSS box alignment properties:

For a full list of alignment properties, see this MDN article on the Flexible Box Model.

To handle alignment inside the flex container using these CSS properties, there are two concepts we must to understand:

The main axis is defined by the flex-direction. The cross axis is the perpendicular direction.

When we use the CSS alignment properties, they target a specific axis. The table below shows the mapping.

CSS Alignment Property Flex Direction Axis
justify-content Yes Main
align-content No Cross
align-items No Cross
align-self No Cross

From this table, we can see an important pattern, that is, justifying is done on the main axis while aligning is done on the cross axis.

This set of CSS properties can be further categorized. Some of the properties are for space distribution, and some of the properties are for alignment.

The properties used to distribute space are:

The properties used for alignment are:

All of the above properties are used on the container excluding the align-self property which is used on individually on the flex items to override the container align-items property.

Note that we can also use the short-hand place-content property as a one-liner replacing the use of justify-content and align-content.

.container {
    place-content: center center;

CSS Grid Layout

CSS grid gives us a two dimensional layout system with rows and columns.

CSS Grid Layout is a powerful two dimensional layout system that allows us to create complex website designs with ease. Let's look at how to get started with CSS Grid. Here is a high level overview of the steps we will take:

  1. Define a container element.
  2. Apply display: grid to the container.
  3. Define grid columns and rows.
  4. Place grid items.
  5. Customize grid items (optional).
  6. Add gaps between grid items (optional).

Grid Container

First, we need to have an HTML container element that will hold our grid items. This could be a div, section, or any other suitable element.

<div class="grid-container">
    <!-- Grid items go here -->

To activate the Grid Layout on our container, set its display property to grid in our CSS.

.grid-container {
    display: grid;

We can specify the number and size of columns and rows using the grid-template-columns and grid-template-rows properties.

.grid-container {
    display: grid;
    grid-template-columns: repeat(3, 1fr);
    grid-template-rows: repeat(2, 200px);

In this example, we've created a grid with 3 equal-width columns and 2 rows, each 200 pixels tall.

Grid Container Items

Now, add our grid items inside the container element, and they will automatically be placed in the grid cells.

<div class="grid-container">
    <div class="grid-item">1</div>
    <div class="grid-item">2</div>
    <div class="grid-item">3</div>
    <div class="grid-item">4</div>
    <div class="grid-item">5</div>
    <div class="grid-item">6</div>

We can style and position our grid items using various properties like grid-column, grid-row, align-self, and justify-self.

.grid-item {
    background-color: #ccc;
    padding: 20px;
    border: 1px solid #000;
/* Position a specific item */
.grid-item:nth-child(2) {
    grid-column: 2 / 4; /* Span from the second to the fourth column line */
    grid-row: 1 / 3; /* Span from the first to the third row line */

We can use the grid-gap, grid-row-gap, or grid-column-gap properties to add spacing between our grid items.

.grid-container {
    display: grid;
    grid-template-columns: repeat(3, 1fr);
    grid-template-rows: repeat(2, 200px);
    grid-gap: 10px; /* Add a 10px gap between grid items */

That's the basics of CSS Grid Layout. We can now create various grid designs by adjusting the column and row definitions, positioning, and styling. There are many more advanced properties and techniques to explore, so we recommend checking out the MDN documentation to learn more.

Other Topics

This section contains a collection of various CSS topics.


When we're crafting websites or designing user interfaces, CSS plays a crucial role in defining the look and feel of our content. One of the properties that we often use, but perhaps don't fully understand, is the z-index property.

The z-index in CSS is called z to represent depth, the third dimension in geometry. While the x-axis represents horizontal positioning and the y-axis vertical, the z-axis handles layering, or how elements stack on top of each other on a webpage.

Higher z-index values put elements closer to the top of the stack, making them appear "on top" of others with lower values. It's a way to add a sense of depth to a flat webpage.

Let's take a closer look at how it works and when to use it.

The z-index property in CSS defines the stack order of an element. Imagine that our webpage is a stage, and all the elements are actors. The z-index is like the director telling the actors when to step forward or fall back.

An element with a higher z-index will appear in front of an element with a lower z-index, just like an actor stepping forward on the stage. The default z-index is 0, and it can accept both positive and negative integer values.

Here's the catch though, z-index only works on positioned elements. This means that the element must have a position property set to something other than static like relative, absolute, or fixed.

Valid positioning values when using z-index:

Let's see z-index in action with a simple example:

#div1 {
    position: absolute;
    z-index: 1;

#div2 {
    position: absolute;
    z-index: 2;

Here, we have two div elements, div1 and div2. Both have their position set to absolute, allowing us to use the z-index property.

We've given div1 a z-index of 1 and div2 a z-index of 2. Because div2 has a higher z-index, it will appear in front of div1.

But what happens if we have elements with the same z-index?

Well, if two positioned elements overlap without a z-index specified, the element positioned last in the HTML code will be shown on top. This is the stacking nature of the index. The last element in the stack will be shown on top.

Like any other CSS property, z-index should be used judiciously. It's a powerful tool for controlling the visual hierarchy of elements on our webpages, but misusing it can lead to complex and hard-to-debug stacking issues.