Cascading Style Sheets (CSS) allow you to transform the look of your webpages. From fonts and colors to spacing and overall layout, all manner of design tools are at your fingertips. Although CSS is a complicated language in its entirety, there are only two basic concepts you need to understand to begin.

It all starts with identifying exactly which part of a page you want to style.

CSS = Selectors + Declarations

A CSS file contains a series of rules describing how an HTML file should be formatted. Each rule consists of two parts: what to style, and how to style it. The first part is controlled using a series of terms known as “selectors.”

An example CSS rule

Examples in this article include style declarations, but they are not the focus: the selectors themselves are.

Historically, there were three CSS selector levels (or versions) with varying degrees of browser support. In 2020, according to Can I Use, these are all available to over 99 percent of users around the world.

Level 1 Selectors

Level 1 introduced the four fundamental types of selectors that cover a huge number of cases, even today.

Pattern

Matches

E

all E elements

.c

all elements with class="c"

#myid

the element with id="myid"

E F

an F element inside an E element

Pseudo-classes

E:link

a hyperlink to a page that has not previously been visited

E:visited

a hyperlink to a page that has already been visited

E:active

a hyperlink that is currently selected

Pseudo-elements

E::first-line

the first formatted line of an E element

E::first-letter

the first formatted letter of an E element

Type Selector

The very simplest selector is the “type selector”. It targets all instances of an element, such as a paragraph or bold text:

        p { margin-bottom: 0; }
b { font-family: sans-serif; }

Class Selector

The class attribute allows further semantics to be added to an HTML element, such as a specific type of paragraph. Such elements can be selected in CSS as follows:

        .intro { font-weight: bold; }
    

This selector would match:

        <p class="intro">&hellip;</p>
    

But note that it would also match:

        <ul class="intro">&hellip;</ul>
    

If you only want it to apply to intro paragraphs, you can combine the type selector and class selector:

        p.intro { font-weight: bold; }
    

ID Selector

The HTML id attribute should be unique within a document, e.g. if you have:

        <div id="contents">&hellip;</div>
    

That should be the only element with a “contents” id. An ID selector allows you to target that specific element in a document:

        #contents { color: #333; }
    

Descendant Selector

Strictly, a “combinator”, because this selector is all about the space in between two others. HTML is hierarchical, as explained in our overview of the DOM. A descendant selector allows an element to be identified by its context inside another element:

        table b { font-weight: normal; }
    

Pseudo Classes and Elements

Pseudo selectors target classes or elements that don’t explicitly exist but are made available anyway. Think of them as special content bonuses:

        p::first-line { text-transform: uppercase; }
    

Selector Lists

Use a comma to combine selectors into a list if you want to apply the same set of rules to each. Instead of:

        th { padding: 1em; }
td { padding: 1em; }

You can write:

        th, td { padding: 1em; }
    

Specificity

A style sheet is a series of rules which use a selector to match an element, but what happens when more than one rule matches a given element? The resulting behavior is governed by “specificity” which defines which rule is used in a case such as:

        p.intro { color: black; }
p { color: gray; }

In such cases, the rule taking priority is defined by its specificity, as follows:

  1. ID selectors (#contents) are the most specific.
  2. Class selectors (.author) are less specific.
  3. Type selectors (p) are the least specific.

When calculating specificity, each level is only considered if two selectors have the same score at the higher level, so “#contents” is more specific than “article.news p.author.special” because the former “wins” on ID selectors.

Level 2 Selectors

The next revision of CSS selectors introduced attribute selectors, expanded on pseudo-classes & pseudo-elements, and added two new combinators.

Pattern

Matches

*

any element

E > F

an F element child of an E element

E + F

an F element immediately preceded by an E element

Attribute selectors

E[foo]

an E element with a "foo" attribute

E[foo="bar"]

an E element whose "foo" attribute is exactly "bar"

E[foo~="bar"]

an E element whose "foo" attribute is a list of whitespace-separated values, one of which is "bar"

E[foo|="en"]

an E element whose "foo" attribute has a hyphen-separated list of values beginning with "en"

Pseudo-classes

E:first-child

an E element, first child of its parent

E:lang(fr)

an element of type E in language "fr"

Pseudo-elements

E::before

generated content before an E element's content

E::after

generated content after an E element's content

Universal Selector

The “*” matches any element. It’s not often that useful, but if you want to reset any default margins, for example, you can do so:

        * { margin: 0; }
    

Attribute Selectors

Attribute selectors allow styles to be targeted very specifically, filtered by an element’s attribute:

        a[title] { text-decoration: underline dotted; }
    

Child Combinator: An Element Immediately Inside Another

Similar to the descendant combinator, but this one only matches immediate children, not descendants any lower down the tree. For example, “ul > li” will match only the “Section 1” text here, and not “Section 1.1”:

        <ul>
    <li>
        Section 1
        <ul>
            <li>Section 1.1</li>
            <li>Section 1.2</li>
        </ul>
    <li>
</ul>

Adjacent Sibling Combinator: The Next Sibling

        h1 + p { font-weight: bold; }
    

Often useful for controlling margins, or an introductory paragraph without a specific class, this selector matches one element only if it immediately follows another. In the example, only the first paragraph here will be matched, not the second:

        <h1>Contents</h1>
some extra text
<p>Introductory paragraph</p>
<p>Following paragraph</p>

Note that this selector only considers elements—not text—when deciding what the next sibling is.

Inheritance

Some CSS properties inherit their value from an ancestor element. In practice, this means—for example—that setting the font face of the “body” element means that every paragraph, table, etc. also uses that same font face.

Of course, this is exactly what you’d expect, but consider a property that doesn’t inherit: “margin”, for example. You wouldn’t want every individual paragraph or bit of bold text to have the same margin as the whole document.

Related: Simple CSS Code Examples You Can Learn in 10 Minutes

A good rule of thumb is to target elements as generally as makes sense—don’t target every individual element when a simple “body” selector will do.

Level 3 Selectors

Many more pseudo-classes were added in this level, alongside some attribute selectors and a new combinator.

Pattern

Matches

E ~ F

an F element preceded by an E element

Attribute selectors

E[foo^="bar"]

an E element whose "foo" attribute begins with the string "bar"

E[foo$="bar"]

an E element whose "foo" attribute ends with the string "bar"

E[foo*="bar"]

an E element whose "foo" attribute contains the substring "bar"

Pseudo-classes

E:root

an E element, root of the document

E:nth-child(n)

an E element, the n-th child of its parent

E:nth-last-child(n)

an E element, the n-th child of its parent, counting from the last one

E:nth-of-type(n)

an E element, the n-th sibling of its type

E:nth-last-of-type(n)

an E element, the n-th sibling of its type, counting from the last one

E:last-child

an E element, last child of its parent

E:first-of-type

an E element, first sibling of its type

E:last-of-type

an E element, last sibling of its type

E:only-child

an E element, only child of its parent

E:only-of-type

an E element, only sibling of its type

E:empty

an E element that has no children (including text nodes)

E:target

an E element being the target of the referring URI

E:enabled

a user interface element E that is enabled

E:disabled

a user interface element E that is disabled

E:checked

a user interface element E that is checked

E:not(s)

an E element that does not match simple selector s

Attribute Selectors

You can select elements with an attribute that starts with a given value: a[href^="https:"], ends with a given value: img[src$=".gif"], or contains a value: a[*="value"].

Pseudo Classes

Additional pseudo-classes include “:last-child”, “:empty” (to match an element with no content), and “:checked” which matches an element like a checkbox input, but only when it’s checked.

General Sibling Combinator: A Following Sibling

Similar to the Adjacent Sibling Combinator from Level 2, this matches any sibling that comes after another, not just the next one:

        h1 ~ p { font-size: 110%; }
    

CSS Selectors and How to Use Them

Now you know just about everything there is to know about how to select part of a webpage using CSS. You’re now ready to style your pages with the great variety of CSS properties that cover everything from colors to layout.

Image Credit: Pankaj Patel/Unsplash