CSS continued

In this reading, we'll learn more CSS. We'll learn about some powerful selection tools, how to handle colors and transparency, and (optionally) how to expand our repertoire of fonts.

Ottergram Example

As before, let's start with an example. The following three pages are all Ottergram, and the HTML is mostly unchanged. The appearance of the latter two are subtly different, mostly in fonts and colors. There's also a difference in the HTML and CSS.

I suggest opening the latter two each in a separate window, placing them side by side and scrolling up and down a bit. Also:

  • View Source, to see the changes to the HTML
  • Inspect an element and see the CSS rule(s) that are applied

Structural Selectors

If you view the source, you'll see that the HTML has been simplified, removing much of the clutter of all those class attributes that we added last time in order to style various elements. Instead, we are using structural selectors, which are built out of combinations of the basic selectors.

Basic Selectors

Recall that we have (so far), three kinds of basic selectors in CSS:

  • tag: style every such tag
  • classes: style every element with that class.
  • id: style the (unique) element with that ID.

Descendant Selectors

As you know, HTML defines the structure of the page. In particular, it defines parent/child relationships, which we can generalize to ancestor/descendant relationships.

So, the following snippet of HTML from Ottergram

      <ul class="thumbnail-list">
        <li class="thumbnail-item">
          <a href="imgs/otter1.jpg">
            <img src="imgs/otter1.jpg" alt="Barry the Otter">
            <span>Barry</span>
          </a>
        </li>

means that the ul is a parent of the li, which is a parent of the a, which is the parent of both the img and the span. If we look at the img element in the Firefox developer tools, we see the following:

developer tools shows ancestry of IMG tag

This shows the ancestry of the img element. In fact, the ancestry is shown at the bottom of the screenshot, like this:

html > body > main > ul.thumbnail-list > li.thumbnail-item > a > img

We can use the parent/child and ancestor/descendant relationships to select a set of elements.

To select any hyperlink (a) that is a child of a .thumbnail-item, we can use the following selector:

.thumbnail-item > a

For example, to remove the underline on the hyperlinks in every thumbnail-item, we could do:

.thumnail-item > a {
    text-decoration: none;
}

This selector is built out of two basic selectors: a class (.thumbnail-item) and a tag (a). It matches elements based on their structural relationships. In general, the selector is:

parent-selector > child-selector

where both parent and child selectors are drawn from our basic set (tag, classes and ids).

Descendants

Descendants are exactly what you'd think they are: the extension of the parent/child relationship. (Click that link if you're not sure about the meaning of "descendant"). In the following, the em is a descendant of the ul, though it is not a child of ul:

<ul>
   <li>harry</li>
   <li>ron</li>
   <li>hermione</li>
   <li>malfoy, who is a <em>pureblood</em></li>
</ul>

Thus, a descendant selector, A B, is always a superset of a child selector, A > B.

Siblings

Similarly, an li is a child of the ol or ul that it's a list item of. But lists also create a bunch of siblings. Here's a list with three children, all siblings:

<ul>
   <li>harry</li>
   <li>ron</li>
   <li>hermione</li>
</ul>

In the list above, ron and hermione have an earlier sibling (harry is earlier than ron, and ron is earlier than hermione), but harry does not have an earlier sibling.

Note that there are lots of sibling relationships; they can even be different tags. The h2, p and ul in this div are all siblings:

<div>
    <h2>stuff</h2>
    <p>introduction</p>
    <ul>
        <li>first</li>
        <li>second</li>
    </ul>
</div>

More Structural Selectors

If A and B are both basic selectors, then:

  • A > B selects every B that is a child of any A
  • A B selects every B that is a descendant of any A
  • A + B selects every B that immediately follows (is the next sibling of) any A
  • A ~ B selects every B that follows (is a later sibling of) any A. That symbol is a tilde (probably next to the exclamation point on your keyboard)

Notice that it's always the second thing, the B, that is selected. So if B is, say, em, then everything that is selected will be an em element; the only question is what its ancestor relationship is.

(I'll note that I use child and descendant selectors all the time; the sibling selectors are relatively rare. We'll use + in Ottergram, but we won't use ~ at all.)

Interactive Demonstration

You might re-visit the interactive selector display to try some of these structural selectors. First, though, you might refresh your memory about what the HTML looks like:

    <h2>this is an h2 header</h2>
    <p id="par1">This paragraph has ID <code>par1</code></p>
    <h2>this is another h2 header</h2>
    <p>this paragraph precedes the list of fruits and veggies</p>
    <ol id="list1">
      <li class="fruit">apple</li>
      <li class="veg">broccoli</li>
      <li class="fruit">coconut. this is tough</li>
      <li class="fruit">date</li>
      <li class="veg">endive</li>
      <li class="veg">fennel. this is yucky</li>
      <li class="fruit">"g" fruits
        <ul>
          <li>gooseberry</li>
          <li>grape</li>
          <li>grapefruit</li>
          <li>guava</li>
        </ul>
      </li>
      <li class="veg">horseradish</li>
      <li>tomato</li>
    </ol>
    <p>this paragraph follows the list of fruits and veggies</p>

Notice the parent/child, sibling and ancestor/descendant relationships. Then try the structural selectors below.

Revised Ottergram HTML

With this in mind, we can omit a lot of the class attributes in Ottergram's HTML and plan instead to use descendant selectors. So instead of putting class="thumbnail-item-a" on the A tag that is a descendant of the li.thumbnail-item, we can just use .thumbnail-item a as the selector.

Here's the body of the revised Ottergram page.

  <body>
    <header>
      <h1 class="logo-text">ottergram</h1>
    </header>
    <main>
      <ul class="thumbnail-list">
        <li class="thumbnail-item">
          <a href="imgs/otter1.jpg">
            <img src="imgs/otter1.jpg" alt="Barry the Otter">
            <span>Barry</span>
          </a>
        </li>
        <li class="thumbnail-item">
          <a href="imgs/otter2.jpg">
            <img src="imgs/otter2.jpg" alt="Robin the Otter">
            <span>Robin</span>
          </a>
        </li>
        <li class="thumbnail-item">
          <a href="imgs/otter3.jpg">
            <img src="imgs/otter3.jpg" alt="Maurice the Otter">
            <span>Maurice</span>
          </a>
        </li>
        <li class="thumbnail-item">
          <a href="imgs/otter4.jpg">
            <img src="imgs/otter4.jpg" alt="Lesley the Otter">
            <span>Lesley</span>
          </a>
        </li>
        <li class="thumbnail-item">
          <a href="imgs/otter5.jpg">
            <img src="imgs/otter5.jpg" alt="Barbara the Otter">
            <span>Barbara</span>
          </a>
        </li>
      </ul>
    </main>
  </body>

That's much simpler and less cluttered! (For comparison, here's the original Ottergram and the previous Ottergram (css1). Do a "view source" to see the source.

Revised Ottergram CSS

I won't go through the CSS rules one by one; at this point, I just want you to look at the selectors.

body {
  font-size: 62.5%;
  background: rgb(149, 194, 215);
}

.logo-text {
  background: white;
  text-align: center;
  text-transform: uppercase;
  font-family: lakeshore;
  font-size: 3.7em;
}

.thumbnail-list {
  padding: 0;
}

.thumbnail-item {
  display: inline-block;
  border: 2px solid rgb(100%, 100%, 100%);
  border: 2px solid rgba(100%, 100%, 100%, 0.8);
}

.thumbnail-item:hover {
    border: 2px solid mediumblue;
}

.thumbnail-item + .thumbnail-item {
  margin-top: 1em;
}

.thumbnail-item a {
  text-decoration: none;
  display: block;
  border: 1px solid rgb(100%, 100%, 100%);
  border: 1px solid rgba(100%, 100%, 100%, 0.8);
}

.thumbnail-item img {
  display: block;
  width: 100%;
}

.thumbnail-item span {
  display: block;
  margin: 0;
  padding: 0.4em 1em;
  background: rgb(96, 125, 139);
  color: rgb(202, 238, 255);
  font-family: airstreamregular;
  font-size: 1.8em;
}


You can compare them with the prior CSS file.

Pseudoclasses

You may have noticed a new rule:

.thumbnail-item:hover {
    border: 2px solid mediumblue;
}

The :hover notation is a pseudo-class. In this case, the rule applies to selected elements that also have the mouse hovering on them. For example, p:hover would match the paragraph the mouse is on.

Of course, :hover isn't useful on touch devices, since the device can't tell where your finger is hovering.

Spacing

To put some vertical space between the thumbnails, we can add some margin-top to each thumbnail except the first one. (We don't want to add a margin-top to the first one because there's nothing above it that we want separation from.) We can do that by using a sibling selector:

.thumbnail-item + .thumbnail-item {
  margin-top: 1em;
}

This rule will apply to all the thumbnail items except the first one, because the first one doesn't have a prior sibling. That's exactly what we want.

Colors

You probably noticed some other changes in the rules, namely replacing color names with an rgb(red, green, blue) syntax. For example,

body {
  font-size: 62.5%;
  background: skyblue;
}

became

body {
  font-size: 62.5%;
  background: rgb(149, 194, 215);
}

So, let's talk about color in CSS. There are many named colors in the CSS language. But with all those named colors, it's only a small fraction of the over 16 million colors that are possible on modern screens. (224 is 16,777,216) If you want a color that's not on the list of names, you have to specify it using the amount of red, green and blue (henceforth, RGB). This isn't the place for a tutorial in RGB color theory; though I've written those in the past, so feel free to ask.

The CSS language allows a variety of ways to specify colors. Just using RGB (there are other color models as well), we have several notations. Consider a shade of chartreuse. You can give it as:

  • a name: background-color:chartreuse
  • an RGB triple of values from 0-255: background-color:rgb(127,255,0)
  • an RGB triple of percentages: background-color:rgb(50%,100%,0%)
  • a hex value: background-color:#7fff00

Let's focus on the second option. So, the background color we saw above for the body tag, rgb(149, 194, 215) is 149 units of red (on a scale from 0 to 255), 194 units of green, and 215 units of blue. I like to think of these as "knobs" you can turn to adjust the amount of each primary color.

The result is like this.

If you want to design your own color, you can use this color calculator

But wait, there's more.

Transparency

Most browsers also support a four-dimensional system of colors, where the fourth dimension is transparency, also called alpha. So the system is RGBA. Alpha is measured on a scale from 0 (perfectly transparent) to 1 (completely opaque). If you want to use transparency, though, your choices of notation are more limited:

  • RGBA percentages: background-color:rgba(50%,100%,0%,0.5)
  • RGBA values: background-color:rgba(127,255,0,0.5)

Here they are in action. Here are two overlapping squares, one solid red background-color: rgb(100%,0%,0%) and the other 50% transparent blue background-color: rgba(0%,0%,100%,0.5):

(By mixing solid red with blue at 50% transparency, we get a purplish result because red+blue is purple. Note that the blue at the right looks different from solid blue because the white background of the page is showing through. We'll learn how to have overlapping elements in a future reading.)

Since not all browsers support transparency, it's good to have a backup option. Strangely, we put the backup option first. That's because the second value will override the first value, but if the browser doesn't understand the second value because the second value uses transparency, the browser will use the first value.

Thus, if we want to have a border around our thumbnail items that is white but 80% transparent, with solid white as the backup, we do this:

.thumbnail-item {
  display: inline-block;
  border: 2px solid rgb(100%, 100%, 100%);
  border: 2px solid rgba(100%, 100%, 100%, 0.8);
}

You get white if you turn all the knobs up to 100% (or 255). I could have used the color name "white" on the first one, but I wanted to show the RGB and RGBA notations.

Multiple Selectors

If you have a declaration block that you'd like to have apply to more than one set of elements, just list all the selectors before the block, separated by commas:

A, B {
    prop1: val1;
    prop2: val2;
}

For example, the following turns off padding for both kinds of lists:

OL, UL { 
   padding: 0
}

Style Inheritance

Some CSS declarations (property-value pairs) are inherited, which means they apply to all descendants of the selected elements. This is usually pretty intuitive: font-size and background color are inherited, but border is not.

Fonts

Most of the time, we can stick to built-in fonts that browsers provide, though if a user doesn't have that font, they may see something slightly different from what you intended.

Some built-in values for font-family include:

  • verdana; the quick brown fox jumps over the lazy dog
  • arial; the quick brown fox jumps over the lazy dog
  • Times New Roman; the quick brown fox jumps over the lazy dog
  • Georgia; the quick brown fox jumps over the lazy dog
  • fantasy; the quick brown fox jumps over the lazy dog

If you'd like to learn more about custom fonts, you can read that at the given link.

I put the font definitions in a second CSS file fonts.css and modified the head of the HTML file to load it:

    <link rel="stylesheet" href="stylesheets/fonts.css">
    <link rel="stylesheet" href="stylesheets/styles.css">

The fonts are referred to in the styles.css file, so we load the fonts.css file first.

Summary

  • CSS selectors can be structural
  • We learned four kinds:
    • A > B which is a child selector, selecting B elements that are children of A elements
    • A B which is a descendant selector, selecting B elements that are descendants of A elements
    • A + B which is a next sibling selector, selecting B elements that immediately follow A elements
    • A ~ B which is a sibling selector, selecting B elements that follow A elements
  • We learned about RGB color and the rgb(val, val, val) notation
  • We learned about transparency and the rgba(val, val, val, val) notation