Chapter 4 CSS Fundamentals

CSS (Cascading Style Sheets) is a declarative language used to alter the appearance or styling of a web page. CSS is used to define a set of formatting rules, which the browser applies when it renders your page. Thus CSS can tell the browser to use a particular font for the page text, a certain color for the first paragraph in an article, or a picture for the page’s background. Files of CSS rules (called stylesheets) act kind of like Styles or Themes in PowerPoint, but are way more customizable. You can control nearly every aspect of an element’s appearance, including its overall placement on the page.

To give you some idea of just how much can be done with CSS, check out the examples in the CSS Zen Garden. Every one of those examples uses the exact same HTML content, but they all look completely different because each one uses a different CSS stylesheet.

This chapter will introduce how to include CSS in your web page and the syntax for declaring basic CSS rules. Further CSS syntax, details, and options will be discussed in later chapters.

4.1 Why Two Different Languages?

If you are new to web programming, you might be wondering why there are two different languages: HTML for your page content; and CSS for formatting rules. Why not just include the formatting right in with the content?

There is an old, tried-and-true principle in programming referred to as “separation of concerns”. Well-designed software keeps separate things separate, so that it’s easy to change one without worrying about the other. And one of the most common forms of separation is to keep the data (content) in a program separate from the presentation (appearance) of that data.

By separating content (the HTML) from its appearance (the CSS), you get a number of benefits:

  • The same content can easily be presented in different ways (like in the CSS Zen Garden). In web development, you could allow the user to choose different “themes” for a site, or you could change the formatting for different audiences (e.g., larger text for vision-impaired users, more compact text for mobile users, or different styles for cultures with different aesthetic sensibilities).

  • You can have several HTML pages to all share the same CSS stylesheet, allowing you to change the look of an entire web site by only editing one file. This is an application of the Don’t Repeat Yourself (DRY) principle.

  • You can also dynamically adjust the look of your page by applying new style rules to elements in response to user interaction (clicking, hovering, scrolling, etc.), without changing the content.

  • Users who don’t care about about the visual appearance (e.g., blind users with screen readers, automated web indexers) can more quickly and effectively engage with the content without needing to determine what information is “content” and what is just “aesthetics”.

Good programming style in web development thus keeps the semantics (HTML) separate from the appearance (CSS). Your HTML should only describe the meaning of the content, not what it looks like!

For example, while browsers might normally show <em> text as italic, you can use CSS to instead make emphasized text underlined, highlighted, larger, flashing, or with some other appearance. The <em> says nothing about the visual appearance, just that the text is emphatic, and it’s up to the styling to determine how that emphasis should be conveyed visually.

4.2 Including CSS

The best practice for adding CSS styling in your HTML page to create a separate CSS stylesheet file to contain the CSS code, and reference that file from your HTML content. These files are named with the .css extension, and are often put in a css/ folder in a web page’s project directory, as with the following folder structure:

my-project/
|-- css/
   |-- style.css
|-- index.html

style.css, index.css (the CSS for the index.html file), and main.css are all common names for the “primary” stylesheet.

You include the stylesheet in your HTML by adding a <link> element to the page’s <head> element:

<head>
  <!--... other elements here...-->

  <link rel="stylesheet" href="css/style.css">
</head>

The <link> element represents a connection to another resource. The element includes a rel attribute indicating the relation between the resources (e.g., that the linked file is a stylesheet). The href attribute should be a relative path from the .html file to the .css resource. Note also that a <link> is an empty element so has no closing tag.

It is both possible and common for a webpage to include multiple stylesheets, each specified with its own <link> element in the <head>. The stylesheet code will be applied in order: the first linked stylesheet is applied, then the second linked stylesheet, etc..

Note that the <link> element is not a hyperlink—it is different from the <a> element used to create navigational links! When someone says “add a link to a page”, you’ll need to determine from context if they mean a hyperlink (<a>) or a stylesheet link (<link>).

If your styling isn’t showing up in your page, double-check that the CSS file is linked correctly—especially that you have the correct path to the file!

While using a separate stylesheet is the best practice for keeping your code separated and organized, it is also possible to include CSS code directly in your HTML. You can do this by putting that code inside of a <style> element (usually inside of the <head>):

<head>
  <style>
    /* CSS code goes here */
  </style>
</head>

However this means that the CSS styling you write will only apply to that page (it can’t be shared across multiple files), and violates the separation of concerns principle. Embedded CSS should only be used for quick experiments.

It is also possible to specify CSS styling for a particular element by giving that element a style attribute whose value is the list of CSS properties to apply to that element. However, this is less flexible (and more repetitive!) than using separate CSS rules, so is also considered bad practice and should be avoided.

Always define CSS code in separate stylesheet files!

4.3 CSS Rules

A CSS stylesheet lists rules for formatting particular elements in an HTML page. The basic syntax a CSS rule looks like:

/* This is pseudocode for a CSS rule */
selector {
   property: value;
   property: value;
}

/* This would be another, second rule */
selector {
   property: value;
}

A CSS rule rule starts with a selector, which specifies which elements the rule applies to. The selector is followed by a pair of braces {}, inside of which is a list of formatting properties. Properties are made up of the property name (e.g., color), followed by a colon (:), followed by a value to be assigned to that property (e.g., purple). Each name-value pair must end with a semi-colon (;). If you forget the semi-colon, the browser will likely ignore the property and any subsequent properties—and it does so silently without showing an error in the developer tools!

Like most programming languages, CSS ignores new lines and whitespace. However, most developers will use the formatting shown above, with the brace on the same line as the selector and indented properties.

As a concrete example, the below rule applies to any h1 elements, and makes them appear in the ‘Helvetica’ font in white text on a dark gray background:

h1 {
  font-family: 'Helvetica';
  color: white;
  background-color: #333; /* dark gray */
}

Note that CSS comments are written using the same block-comment syntax used in Java (/* a comment */), but cannot be written using inline-comment syntax (//a comment).

CSS stylesheet files will often contain dozens, or even hundreds or thousands of rules. Do not attempt to keep the number of rules small; define any rules that you need to in order to achieve your desired appearance!

When you modify a CSS file, you will need to reload the page in your browser to see the changed appearance. Some development servers such as live-server may perform this reloading automatically.

CSS Selector Basics

The first part of the rule is the selector. Selectors are used to “select” which HTML elements the CSS rule should apply to. Any element on the page that “matches” what the selector indicates will have the properties of the rule applied to it. This means that each rule can and will style multiple elements on the page!

CSS supports multiple different types of selectors (with different syntax for selecting which specific elements to style). This chapter will introduce the two most basic selectors; more options and syntax are discussed in CSS Selectors.

The most basic selector, called the type selector, selects elements by their type (tag name). For example, the below rule will apply to the all <p> elements, regardless of where they appear on the page:

p {
   color: purple;
}

CSS rules can use the type selector to style any type of element:

/* style all h1 elements to be purple */
h1 {
  color: purple;
}

/* style all h2 elements to be gold and italic */
h2 {
  color: gold;
  font-style: italic;
}

/* style all images to fit within a maximum width */
img {
  max-width: 300px;
}

It’s important to note that that CSS rules will apply to all elements on the page that match the selector. So the above rules will style all <h1>, all <h2>, and all <img> elements regardless of where they are. This means that you can write one rule to style lots of parts of the page—one rule will change all of the <h1> headings.

But sometimes you want a rule to apply to only some elements of a particular type—to only some <p> or only some <img> for example. You will most often make these more limited selections by using a class selector. A class selector will select elements with a class attribute that contains the specified name. For example, if you had HTML:

<!-- HTML -->
<p class="highlighted">This text is highlighted!</p>
<p>This text is not highlighted</p>

You could color just the correct paragraph by using the class selector:

/* CSS */
.highlighted {
   background-color: yellow;
}

Class selectors are written with a single dot (.) preceding the name of the class (not the name of the tag!) The . is only used in the CSS rule, not in the HTML class attribute. In the above example, the HTML element has the class highlighted (no dot), and the CSS selector uses .highlighted to mean “elements with class highlighted”.

If you try to use a class selector and forget the dot, then the browser will think that you’re using an type selector instead—likely selecting an element type that doesn’t exist in HTML!

/* Bug: forgot the dot . in the class selector
Instead selects elements of type <highlighted> (do not exist in HTML) */
highlighted {
  background-color: yellow;
}

CSS class names should start with a letter, and can contain hyphens, underscores, and numbers. Words are usually written in lowercase and separated by hyphens rather than camelCased or snake_cased. Classes should be given informative names—like you would name a variable! See Naming CSS Classes for more details.

Class selectors will apply to any element that contains that class, regardless of the element’s type. This can let you apply a single consistent styling to multiple different types of elements:

<h1 class="alert-flashing">I am a flashing alert!</h1>
<p class="alert-flashing">So am I!<p>

HTML elements can have multiple classes; each class name is separate by a space (not a comma):

<p class="alert flashing">I have TWO classes: "alert" and "flashing"</p>
<p class="alert-flashing">I have ONE class: "alert-flashing"</p>
<p class="alert flashing fast">I have THREE classes: "alert", "flashing", and "fast"</p>

The class selector will select any element that contains that class in its list. So the first paragraph in the above example would be styled by either the selector .alert or the selector .flashing. The second paragraph would not be styled by either selector, because "alert-flashing" is not the "alert" or the "flashing" class.

It is common for HTML elements to be given lots of classes, particularly under a modular class naming scheme.

Class selectors are the best way to style a single particular element (just give it a class and then write a rule to refer to that), as well as one of the most clearest and effective ways of styling large portions of the page.

CSS Property Basics

There are many, many different CSS formatting properties you can use to style HTML elements. All properties are specified using the name: value; syntax described above. The task is then to determine which property produces the appearance you want, and provide a valid value for that property. Making beautiful webpages involves combining lots of different properties to style lots of different HTML elements.

Pro Tip: modern editors such as VS Code will provide auto-complete suggestions for valid property names and values. Look carefully at those options to discover more!

Discussing every single CSS property is beyond the scope of this book (especially as more are introduced every year!) Later chapters will discuss some common or important properties in more detail. For now, below is a short list of common styling properties you might specify in CSS to get you started:

  • font-family: the “font” of the text (e.g., 'Comic Sans'). Font names containing white space must be put in quotes (single or double), and it’s common practice to quote any specific font name as well (e.g., 'Arial').

    Note that the value for the font-family property can also be a comma-separated list of fonts, with the browser picking the first item that is available on that computer:

    /* pick Helvetic Nue if exists, else Helvetica, else Arial, else the generic
       sans-serif font */
    p {
      font-family: 'Helvetica Nue', 'Helvetica', 'Arial', sans-serif;
    }

    See Fonts for more about working with fonts.

  • font-size: the size of the text (e.g., 12px to be 12 pixels tall). The value must include units (so 12px, not 12). See CSS Units for more details on units and sizes.

  • color: text color (e.g., either a named color like red or a hex value like #4b2e83. See CSS Colors for more details on colors in CSS. The background-color property specifies the background color for the element.

  • width: the width of a block element. The value is usually defined in px units (e.g., 300px). This proeprty can be useful especially for sizing image elements. There is also a similar height property.

  • border: a border for the element (see “Box Model”). Note that border is a short-hand property which actually sets multiple related properties at once. The value is thus an ordered list of values separated by spaces:

    /* border-width should be 3px, border-style should be dashed, and border-color
       should be red */
    p {
      border: 3px dashed red;
    }

Read the documentation for any individual property to determine what values it can take!

Note that not all properties or values will be effectively or correctly supported by all browsers. Be sure and check the browser compatibility listings when using the latest CSS features!

4.4 The Cascade

CSS is called Cascading Style Sheets because multiple rules can apply to the same element (in a “cascade” of style!)

CSS rules are additive—if multiple rules select the same element, the browser will combine all of the style properties of those rules when rendering the content:

/* CSS */
p { /* applies to all <p> elements */
  font-family: 'Helvetica'
}

.alert { /* applies to all elements with class="alert" */
  font-size: larger;
}

.success { /* applies to all elements with class="success" */
  color: #28a745; /* a pleasant green */
}
<!-- HTML -->
<p class="alert success">
  This paragraph will be in Helvetica font, a larger font-size, and green color,
  because all 3 of the above rules apply to it.
</p>

This means you can write one rule that applies to a type of element in general (e.g., “all paragraphs have Helvetica font”), and then write another rule that adjusts or customizes specific elements (e.g., “this one paragraph is (also) green”). Combining rules in this way allows you to make very complex styling out of simple and understandable parts. It also allows you to easily reuse such styling across multiple pages or contexts.

In addition to multiple rules specifying property values for an element, an element can also inherit property values from its parent.

/* CSS */
ul { /* applies to all <ul> elements */
  font-size: larger;
}

li { /* applies to all <li> elements */
  color: red;
}
<!-- HTML -->
<ul>
  <li>
    This element's content will be in a larger font size (inherited from the
    parent), as well as red (specified directly).
  </li>
  <li>(So will this element's content because the same rules apply)</li>
</ul>

In the above example, the <li> elements will be in a larger font size even though a rule doesn’t specifically apply that property to them—the parent <ul> element gets that property value, and then the child <li> elements inherit it.

Property inheritance means that rules can apply to entire “sections” of the page, and all the content within that section will be styled in the same way. Indeed, you can even use inheritance to apply formatting rules to the entire page by selecting the <body> element. (Note that for clarity/speed purposes, you generally do not apply formatting to the <html> element).

Note that if a rule explicitly specifies a property’s value, that value will be used instead of any inherited value:

/* CSS */
p { /* applies to all <ul> elements */
  color: purple;
}

em { /* applies to all <em> elements */
  color: gold
}
<!-- HTML -->
<p>
  This text will be purple <em>(but this text will be gold; it does not
  inherit the purple)</em>
</p>

Property values can be inherited, but styling classes are not. An element is not considered to have a particular class just because its parent does (though it may inherit the properties that are applied to the parent by having that class).

Because multiple rules may apply to a single element, it’s possible that multiple values for the same property will be specified for that element:

/* css */
p { color: blue; }
.alert { color: red; }
<!-- html -->
<p class="alert">
  This element is styled by both rules, both of change its `color`. The text
  will be red, because the `.alert` selector has higher specificity. The `p`
  rule's property will be overridden.
</p>

If multiple rules try to specify the same property, then the value from the more specific rule is applied. Class selectors are considered more specific than type selectors, so the rule with the class selector “wins”. If two rules are equally specific (e.g., two class selector rules), then the last declared rule is applied. See Selector Specificity for more details. The property value from the less specific rule is said to be overridden. Note that property values that don’t conflict will still be applied even if one value is overrriden.

The cascade of rules enabled by CSS makes it powerful and expressive, allowing you to write flexible, reuseable rules that can be combined to produce amazing visual appearances. It’s like making your own Lego bricks!

That said, the cascade of rules does create a trade-off: you can have lots of reusable rules that apply to multiple elements, but these rules may combine in a scattered way that makes it difficult to intuit about what the page may look like just from considering the code. Having reusable components increases complexity—sometimes too much. Managing this complexity—determining when rules should apply to which elements and when to combine them—is part of the “skill” in working with CSS. Heuristics for designing rules are discussed more in a later chapter; you will also develop a sense for working with CSS as you practice over time.

My best advice for managing the large number of CSS rules used in web pages is to follow the KISS principle: “Keep it simple, silly!”. CSS rules don’t need to be complex; think about rules that apply to general elements or to “sections” of a page. Many elements may only have one or two rules applying to them—and that’s okay!

Resources

Some useful references and documentation for CSS (in general); more specific resources will be included in later chapters.