Ian Marshall


Lesson 1: HTML and CSS

HTML predates CSS, and each was developed independently. That means for the first couple of years that HTML was being tested and then used, there was no styling. Even after CSS was released in 1996, the dominant browsers of the time, Netscape Navigator and Internet Explorer, didn't reliably adopt consistent implementation. So the first HTML pages were not colorful, thoughtfully laid out web applications like they are today. Instead, they looked a lot like this:


There is no CSS on Mr. Longman's page: it's very black and white, the text spans one side of the browser to the other, the lines of text are close together and difficult to read, and there's very little stylistic variation. Font sizing is dependent on the HTML elements used. The <h1> has the largest font size and the <small> has the smallest.

Would it surprise you to know that this was the original intention? HTML was not invented for the Internet as we know it now. The World Wide Web was meant for more easily distributing academic and scientific research, reports, and white papers. None of those documents, aside from a few images and graphs, needed any special design attention. HTML was originally created to present headlines and blocks of text. It's still very good at it.

The above site is a fascinating read, by the way, and provides a lot of historical background for what we're learning here. Definitely, you should read it when you get the chance.


Defaults and Overrides

Even without CSS styling, HTML still displays in browsers. That's because browsers have an HTML rendering engine that pairs HTML data (text and images, mostly) with styling information. When no styling information is provided, the engine relies on default values: predetermined colors, font sizes, layout, etc. Once data and styling are successfully paired, the engine can then render, or display, the styled data in the viewport, or the portion of the computer screen dedicated to the browser.

Since HTML was developed without CSS, the default styling used to be the only styling available. Understanding styling defaults will greatly improve your CSS knowledge. Some defaults are obvious. For example, all text is black by default. That is, except for hyperlinks, or anchors, or <a> tags, which are blue and underlined so to stand out prominently within black text. Other defaults are less obvious. I'll discuss them as they come up.

CSS gave us the ability to override these default settings and replace them with our own, custom styling values. We could, for example, override the default black text and turn it bright pink, or portions of it, if we wanted. We could override hyperlink styling and remove the underline, or change many other styling rules.

This is what CSS does. It provides the syntax for overriding styling rules with other styling rules. As we'll see, some of the things we can do with CSS are quite stunning because we're also given the tools for animation. That is, we can change styling over time, not just set CSS rules statically. We'll get to the good stuff soon enough!


To apply any styling, we need to identify which HTML element or elements we want to alter. The first part of CSS syntax, or grammar rules, is the selector, or the identifier of the HTML element to be affected.

Tag Name Selectors

The easiest selectors are by HTML tag name. For instance, if you want to change the font size of all paragraph <p> tags, your selector would simply be p. Any HTML element can have its styling changed by using its tag name as a selector. For example:

HTML ElementCSS Selector

The one consideration is that all HTML elements with that tag name will be affected. If you want to target some paragraphs but not others you'll need a different kind of selector.

Attribute Selectors

HTML provides two attributes that carry no meaning on their own, but rather are reserved for use by the web developer. These are id and class. It is possible to use CSS to select elements based on either of these attributes. For example:

HTML ElementCSS Selector
<p id="uniqueParagraph">#uniqueParagraph
<p class="specialParagraph">.specialParagraph

Sometimes, id and class attributes aren't present; CSS can still target these elements. Using square brackets in the selector allows any attribute to be the identifier for an HTML element. For example:

CSS SelectorTargeted HTML Elements
[id]any element with an id attribute
h1[class]any <h1> with a class attribute (regardless of its value)
a[href="/home"]any <a> with an href attribute with a value of /home
img[src*="http"]any <img> with a src attribute with a value containing http

There are a few others, but I personally use them very rarely. And this course is not meant to be an exhaustive manual; I intend to cover the CSS that you must know.

DOM Selectors

What if you wanted to change the styling of the paragraphs in every article, but not elsewhere? A simple selector isn't enough; you don't want to change all paragraphs everywhere. And it's poor practice to give an identifying attribute to each paragraph you do want to change. (Why? See below, "Best Practices.") Luckily, there's an easy CSS solution.

DOM tree By Birger Eriksson - Own work, CC BY-SA 3.0, Link

HTML elements sit in a tree data structure called the DOM (Document Object Model). It's very much like a document outline or nested lists: parent elements contain child elements, which themselves may contain child elements, and so on. In an HTML document, every element is a child of another element—except for the <html> element itself (the root element), which contains everything. The children of <html> are <head> and <body>, which themselves contain children. CSS has selectors that can pick elements based on their position in the DOM tree. For example:

CSS SelectorTargeted HTML Elements
article pany <p> element that is a descendant of an <article>

The single space between the <article> and the <p> has specific meaning in a CSS selector: it identifies an ancestor–descendant relationship. If an <article> contains a <p> anywhere between its opening and closing tags, that paragraph will have its styling changed.

No matter how complicated the CSS DOM selector, it's the final element that will have its styling changed.

The selector article p will change the p, not the article.

DOM selectors target elements based on their position in the DOM tree and their relationship to other elements around them. Here's the complete list:

CSS SelectorTargeted HTML Elements
article pany <p> element that is a descendant of an <article>
article > pany <p> element that is a direct child of an <article> (no grandchildren or other descendants are affected)
article + pany <p> element that immediately follows an <article>, and both have the same parent element (they're siblings)
article ~ pany <p> element that follows an <article>, but not necessarily immediately after, and both have the same parent element (they're siblings)

Pseudo-class Selectors

CSS also allows some very special selector distinctions for properties that would otherwise be difficult to target, even with an attribute. For example:

CSS SelectorTargeted HTML Elements
:first-childany element that is the first child of its parent
:first-of-typeany element that is the first of its type among siblings
:last-childany element that is the last child of its parent
:last-of-typeany element that is the last of its type among siblings
:not()any element that does not match the selector in the parentheses
:nth-child()any element that has a position among siblings that matches the equation in the parentheses
:nth-last-child()same as :nth-child, but counting from the end
:nth-last-of-type()same as :nth-of-type, but counting from the end
:nth-of-type()any element that has a position among same-type siblings that matches the equation in the parentheses

Again, there are others; this is not a complete list. But these are definitely the most significant of the pseudo-class selectors. That is, except for the event-based pseudo-class selectors like :hover, but I'll discuss those in Lesson 8: Events and Effects.

The selectors with parentheses require something in those parentheses. :not requires another selector, any selector, so it can find elements that are not selected by it. The others require an algorithm, which could be a simple number starting with 1, some repeating number pattern, or a special keyword: even or odd. I hope some examples below will help explain these because they're definitely not as straightforward as the others.

Wildcard Selector

Use an asterisk (*) for a generic, match-anything selector. It can be used in combination with any other selector.

Multiple Selectors

If you want to apply the same CSS styling to more than one selection, just put a comma between selectors. The comma acts as an "and," creating a list of selected elements. For example:

CSS SelectorTargeted HTML Elements
h1, h2, h3all <h1>, <h2>, and <h3> elements

Selector Practice

<h1>Primary Headline</h1>

<h2>Secondary Headline</h2>

<h3 class="special">Tertiary Headline</h3>

<section id="top_section" class="special">

<h3>Tertiary Headline</h3>

  • <li>First list item</li>
  • <li>Second list item</li>
  • <li>Third list item</li>
  • </ul>
<section id="middle_section" class="special">

<h3>Tertiary Headline</h3>

<p class="special">Paragraph one</p>

<p>Paragraph two</p>

<p>Paragraph three</p>

<section id="bottom_section">

<h3>Tertiary Headline</h3>

    <ul class="special">
  • <li lang="ka">Georgian list item</li>
  • <li lang="ko">Korean list item</li>
  • <li lang="no">Norwegian list item</li>
  • </ul>

Tag name selectors:

Attribute selectors:

DOM selectors:

Pseudo class selectors:

Wildcard selector:

Multiple selectors

The Rule Set

After the selector comes a pair of curly braces ({}). These surround one or more CSS rules that are applied to whatever is selected by the selector. Everything in those curly braces is one rule set.

The three rules on lines 2–4 comprise one set, and each is applied to the selector HTML element. Each rule is composed of a name–value pair separated by a colon (:). The name is the property to alter and the value is the new setting for that property. For example:

For every h1 in the header, the rendering engine will ensure that the text will be sans-serif, colored cyan, and 30 pixels in height. The three rules, each a name–value pair, are gathered into one rule set that applies to the selector.

Every rule must be followed by a semicolon (;) but not necessarily a line break. For example, the above CSS could be written like this:

The rendering engine doesn't care about spacing except for the space between the header and h1, which important syntax for a DOM selector. There are some other circumstances in which spacing is important, and we'll address them soon. But know that the computer doesn't care about the prettiness of the text. It's human readability that matters in most cases. Will you be able to easily make changes six months from now? Or will it be a slog to have to read through miles of CSS code like the above to find exactly the property to alter? Code formatting is highly subjective and everyone has a preference, including me. I hope eventually you will, too!


Even though we can select any and every single HTML element, there are times when such efforts are completely unnecessary. For example, if we wanted to override our font weight and make everything bold, we could use CSS to select every headline and paragraph independently to make that change. But we don't have to. All elements that contain text, if the font-weight is not set, will attempt to inherit the style from its parent element. And if the style is not set on its parent, it'll look for the style on its parent's parent, and so on. If we wanted all text in the HTML body to be bold, we could set that rule once, in the body:

This inheritance system allows style rules to trickle down to children elements, in a cascade, if you will. In fact, it's this concept that gives CSS its very name: cascading style sheets.


Every HTML element will have a default style, either one that it has as an initial value, or one it inherits from a parent's initial value. But the fact that you could override these defaults means that in a conflict situation in which two or more incompatible styles—including defaults—are targeting the same HTML element there can be only one winner. Any user-defined styling will override default values, but can one user-defined rule override another one? There has to be a system for this because many CSS rules are mutually exclusive. A bit of text, for example, cannot be both bold and not bold. So which rule wins in the case of conflict?

A rule will have precedence over another depending on its selector specificity. A wildcard selector, for example, is generic, and has no specificity at all. Any rule set by a wildcard selector will be overridden by any other selector at all. For example:

An HTML page with the above styling will set every element's font size to twenty pixels. However, primary headlines will all be eight pixels larger—except, however, for primary headlines that are direct children of the footer; those will be four pixels smaller. The selector on line two above has more specificity than line one and so overrides it. Line three has more specificity than line two and so will override both previous rules.

A numerical specificity score is given to each selector, and higher scores will override lesser scores. This is how they are calculated:

CSS SelectorAdd to Specificity Score
Modifiers (*, >, +, ~)+0 (these do not affect score)
Tag Name Selector (p, h1, footer)+1 for each
Class (.myClass), Attribute ([href]), and Pseudo-class (:first-child) Selectors1+10 for each
Id (#myId) Selectors+100 for each

1 The :not() pseudo-class selector adds nothing to the specificity score, but its selector in the parentheses does.

What about a tie? As a CSS rule is loaded into memory, if it both conflicts with a previously declared style and has an identical specificity score, it will override the existing rule. Since each rule is read one at a time from top to bottom, a rule that is literally lower in the CSS or appears in a later-loaded document will override a previous rule in the case of a tie.

The above two rules have the same specificity score. But paragraph text will be midnightblue because that rule comes after the seagreen one.

There are other ways to affect the specificity score, such as using inline CSS or the !important keyword. Both, however, are discouraged for normal page styling, so I knowingly omit them from a must-know CSS course.

Working with the HTML Document

If you've read and worked through my Getting Started with HTML and CSS course (which perhaps you should, if you haven't), then this should look familiar:

The above is a very, very basic HTML starting template, one which I use at the beginning of some of my custom web work. How do we use it? And how do we add CSS?

In many situations (single-page sites or applications and rapid prototyping, for example) a simple approach works well: include the CSS directly in the HTML document itself. Here's a simple HTML page (from chapter 4 of my Getting Started with HTML and CSS course) that includes CSS and creates an empty, but hot pink web page.

This is an embedded style sheet. The <style> tag is HTML and follows HTML syntax. However, its contents are CSS and follow CSS syntax. It's sometimes very confusing to have to switch syntax patterns in the middle of the same document. For this reason—and other reasons involving more complicated web sites—styling may be created within a separate document. This keeps all the HTML and its syntax in one HTML document and all the CSS and its syntax in one CSS document. For example:



The link tag on line 7 attaches the style.css document to the hotpink.html document. It links them together so that all styling in the external style sheet will be applied to the HTML page.

For convenience, I'll continue using embedded style sheets in this course, but know that any CSS could be extracted into an external document and linked back to the HTML.

CSS Resets

One relic of the first and second Browser Wars is that rendering engines among all the various browsers are not identical. That is, the defaults for each browser may be slightly different from one another. The result is that a web page you meticulously build to be pixel-perfect on your browser may appear very off when rendered on another one. This rendering inconsistency has narrowed in recent years, but still exists and in many cases needs to be resolved by the web developer.

One way this has been accomplished in the past is to use a CSS reset style sheet. This is an external style sheet, often freely available online, that overrides all key browser defaults to the exact same sets of values. A developer using a reset could start designing and styling from a custom, but consistent, set of defaults. The end result is that a website will render identically across all browsers, which is a very good thing.

There is a drawback, however. By resetting all defaults, it falls on the developer to assess each and every style choice: each margin, position, font size, etc. While cross-browser compatibility and consistency is very important, using a CSS reset increases the amount of styling work that will be needed. For many, it's worth it. For me, I pick the projects that could benefit from a reset; I don't always use a reset just out of habit.

One reset I have used many times in the past is Eric Meyer's CSS reset. It's thorough and comprehensive. You also don't even need to copy–paste or download anything; he provides a direct link: https://meyerweb.com/eric/tools/css/reset/reset.css. If you're so inclined, you could incorporate it directly into your basic HTML template:

Line 7 will automatically load Mr. Meyer's CSS reset into your HTML page. You needn't do anything else. Make sure, however, that your own custom styling happens after that line 7, otherwise you may run into precedence issues (see "Precedence" above).


It's often useful to provide some additional information to human readers (like yourself!). CSS documents can get long and dense, so breaking up portions into sections with a comment can make a big difference in long-term maintenance.

Line 1 and line 10 are comments. Those lines will be ignored by the rendering engine. They only exist to help clarify portions of code for a human reader.

Best Practices

Okay, we're finally here: advice. Everyone has some, including me. But what one professional developer considers to be a "best practice," another one scoffs and derides as either a non-standard idiosyncrasy or a mindless conformity. In other words, I invite you to freely accept or ignore what I say here because I assure you there will be others elsewhere who strongly disagree with me.

#1. Readability

To me, this is absolutely the most important consideration for any coding project: your code must be human readable. It's more important than having your code work because if it's readable someone else can help you debug.

Readability includes several things. The CSS document needs to be well formatted and organized, consistent, and use helpful comments wherever needed. As you work through this CSS course I'll use formatting, organization, and comments in what I feel is a readable way. Again, I hope you'll develop your own style—as long as it's readable.

#2. Write your HTML first

Remember that "History of HTML" site at the top of this very long page? It had no CSS at all. It's possible to turn off CSS in browsers, which means it's possible your site will render as HTML without any CSS. This is actually a good thing because if you take it seriously, it'll keep your HTML clean, succinct, and organized exactly in the order of importance it needs.

Only after that's completed should you start writing CSS. From lesson 6 of my Getting Started Course I explained the importance of keeping your HTML simple, shallow, and semantic. That must be accomplished before any styling.

#3. DRY: Don't repeat yourself

There's nothing worse than making a change to the CSS and…it doesn't work. In my experience, this is often because the styling has been altered somewhere else, in another rule set. Keeping your CSS document organized (see #1 above) helps, but as you're styling it's best to ask yourself if what you're writing is undoing something you've already written.

#4. Avoid excessive dependency on class and id attributes

I can already hear the yelps of "What are you talking about?!" from developers who have been doing exactly this for their entire careers. The class and id HTML attributes are useful, but their overuse can severely hamper readability (see #1 above) and interferes with focusing on HTML first (see #2 above).

There's another issue, which has to do with general computer science best practices: separate your data from your presentation. By this, I mean that the HTML document is supposed to be a data structure. The style tag (not to be confused with the style attribute) is an important part of that data and it belongs there. But using class and id attributes to identify, for example, what color something should be or in which column it should render is polluting the data space with presentation metadata. Any good computer scientist will see that and want it undone posthaste.

#5. Be wary of any library or framework that "makes it easier"

We all appreciate ease. But building websites requires a lot of work and if it's not you doing it then someone else is. By using someone else's library, framework, or site-building application you'd be relinquishing control and partial authorship of the project in favor of speed and, well, ease. But you never relinquish responsibility. This means you'd be accepting responsibility for someone else's code and left to assume it's not just effective but also good, well written, and standards compliant.

If you are going to use a library or framework, I strongly urge you to read through it and make sure you understand what you're getting you and your project into. You likely won't need everything that comes with it; are you able to pick and choose only the parts you want? You likely will want to customize; are you able to manually override its default styling? And you likely will someday find it outdated, out of style, insecure, or technologically obsolete; are you able to remove it without too much hassle?

This above all: do not allow any library or framework to become a crutch. It's bad enough if your project has dependencies; you shouldn't also.


  1. There are many ways to target one or more HTML elements for styling. Craft your selectors carefully to get all the HTML you want for each rule set.
  2. The rules themselves come in name–value pairs. We'll explore what you can do with them in every other lesson of this course.
  3. There is a system for inheritance (an HTML element may inherit the styling of its parent element) and precedence (resolution of conflicting styles).
  4. CSS can be embedded in an HTML document, or linked into it from an external style sheet. And a reset can be used to change all the significant defaults for cross-browser consistency.
  5. Use comments, good organization, and consistent syntax to keep your CSS human readable.