Sample Video Frame

Created by Zed A. Shaw Updated 2024-12-10 18:57:40

03: Rule Sets

There is a mistake in this exercise's video. At 2:00 in the video I say the div > a selector only works on the "first child" when I should have said "immediate descendents". The second mistake is I claim that div > a has higher specificity than div a but it doesn't, it's simply order dependent. If you reorder those lines you should get the same effect. The important part of the video is that div > a solves situations where div a is too aggressive and applies to all a tags when you only want the immediate children.

Now I start to show you the most basic element of CSS and how it interacts with the HTML Document Object Model (DOM). When we talked about the DOM in the HTML module I showed you how the HTML tags are structured into a tree, with parents and children. The problem with trees is they're difficult to process without some kind of "rules engine". A rules engine is a way to specify what should happen when certain things are encountered in a tree. You give the browser a set of rules, the browser "walks" the DOM, and if any part of the DOM matches your rule, then the browser runs it.

Your Files Are a Tree

A simpler way to explain trees is to use what you know about the command line from the First Steps module. If you remember you can use a command like this:

cd Projects/ljsthw/ex1

This command does a "change directory" to the Projects, then ljsthw, then ex1 directory. We call this a "path", and you can use that path to specify an exact location for commands. For example, you could copy a file there without using cd first:

cp mycode.js Projects/ljsthw/ex1/

I can also write this path out as a tree, and even include any other directories. Imagine I had this structure:

If I want to access ex2 of my ljsthw project I use Projects/ljsthw/ex2. If I want to access the docs of the buttons.computer project, I do Projects/ButtonsComputer/docs. We can call this process "flattening the tree" or "building a path".

Rules for File Paths

What if you have a huge directory structure and you want to do a command repeatedly to only certain files? Imagine in your Projects directory you had thousands of files ending in .txt and you needed to list them all? Are you seriously going to write out every path to every file, or even to every subdirectory? No way. You want to write one command that processes all files ending in .txt without you telling it to go into every directory.

The first thing you'd use is the "regex" that says "all the .txt files". You then combine this with the find command like so:

find . -name "*.txt" -print

The format for the find comand is:

  1. find
  2. . the start of the search (. means "the current directory")
  3. -name to say you want to select files by their names
  4. "*.txt" the regex pattern for "anything ending in .txt"
  5. -print what find should do when a file matches your pattern

The find command takes a sequence of "selectors" and traverse your file tree. When any file or directory matches your selector it runs a command, in this case it prints it out. You can get even more complicated with find:

find . -name "ex*" -a -type d -print

This will find every directory beginning with "ex". The -a tells find to use "and" logic, -type d means a directory type of file, so the selector -name "ex*" -a -type d means "every file named ex* and type directory".

Paths in CSS

Imagine I decided to do something weird and translate a simple HTML document into a set of files and directories like this:

mkdir html
cd html
mkdir body
cd body
mkdir p
cd p
pwd
echo "Hello world" > text.txt
cat text.txt

Try this and you'll see its structure is this:

I can translate that set of directories into this HTML:

<html>
  <body>
    <p>Hellow world</p>
  </body>
</html>

If I can use the find command on the "HTML as directories", then how do I do the same thing to the actual HTML? I use CSS selectors (or XPath, but that's a more complicated story). Imagine I want to find all of the directories named "p" using find:

find -name "p" -a -type d -print

In CSS I do the same thing with this:

p {
  /* do stuff */
}

That first line where you see the p is the selector, and it tells CSS that any tag with the name "p" should get this rule applied. I only have a comment there for now, but this is similar to the find command above. CSS assumes I mean tags and names, so I only have to give the p tag name in the selector and I'm done.

CSS can also do a complete path to a tag. Looking at our "HTML as directories", I could list all the files in the "p tag" like this:

ls html/body/p

In CSS I can do the same thing like this:

html body p {
  /* do stuff */
}

I don't have to give a / (slash) character since CSS assumes I mean a path of DOM tag elements.

Full Rule Set Format

Now that you know how CSS traverses "paths" in your DOM I can describe the full format for CSS. Here's a simple example that only changes the p tags to blue:

p {
  color: blue;
}

Breaking this down we can show the full syntax before explaining what each part does:

  1. You see the p selector, and this can get very complex, but you'll focus on using this style with a few modifications as you advance.
  2. { starts the rule set definition and must be matched with the } at the end.
  3. color defines what property of the element's display you want to set. These are similar to the attributes in HTML, but CSS has mostly new names for the same things (because the CSS designers hated HTML).
  4. : the colon sets the end of the property and the start of the value. It's similar to the = in HTML attributes such as color="".
  5. blue is the value you want for this property, and it's similar to the part of HTML attributes that is in the quotes as in color="blue".
  6. ; ends this property. You can then start a new one on the next line.
  7. } ends the rule set.

The format for CSS is not that complicated, and it's similar to a syntax in JavaScript. What makes CSS complicated is all of the varying properties you can set and how they're processed.

Selectors

You'll receive plenty of practice in using selectors as this module continues, but I should give you a small list of the most common selectors you'll need:

This is definitely not an exhaustive list of selectors, and many of these can be combined to make them more specific. I recommend you keep things simple with only tag selectors and a little bit of class or id. Then add others as needed.

Properties and Value

Once CSS has figured out what part of your HTML this rule applies to it then must know how to change the display of the HTML. The properties and values do that. This is a list of the most common properties you'll use in CSS.

Many of these have additional more specific variants. Such as font has font-size, font-family, and more. You should review the MDN documentation on CSS properties to find more.

Specificity

What happens if I have two competing rules that want to change the same property? To make this discussion clear I will use this HTML:

<span>
  <h1 class="red-title">I Am Title</h1>
</span>

Nothing fancy, just two tags. Next, let's create some CSS with conflicting selectors and the same properties:

h1.red-title {
  color: red;
}

span h1 {
  color: blue;
}

Take some time to get this all working in an HTML file using a <style></style> tag based on this clue:

<html>
  <head>
    <style>
    </style>
  </head>
<body>
</body>

Once you get the CSS loaded you'll see that the h1 tag is red, even though you said span h1 should turn it blue after. Both CSS rules want to set the color property, so the browser has to determine which one is the winner. It does this using something called "specificity" which I touched on in lesson 1. Specificity says that the class rule h1.red-title beats the tag rule h1.

The simplest way to remember the specificity rules is "tag < class < id". If I say this out in English it's "tag is less priority than class which is less priority than id". This means that tags are even lower priority than id. Another way to put this is, "id beats class which beats tag".

The !important Exception

There are more to the rules, but most of the exceptions are either very rare, or simply override this hierarchy. You can add a specifier to a rule called !important which makes that rule win against anything else:

span h1 {
  color: blue !important;
}

If you add this to the example then the title will be blue. This is a good debugging tool to see if maybe it's just specificity that is causing a rule to fail rather than how you wrote it. If you add !important and the rule works then you have some other rule with higher importance blocking this rule. If you add !important and the rule is still broken then it's not specificity and it's actually how you wrote the rule. Check things like spelling or the selector.

Previously I had p h1 with a p tag containing an h1 tag. Apparenlty, according to MDN this actually doesn't work because browsers--for some insane reason--will pull the h1 tag out of the p tag and then invent an entirely new structure. Why? Because it's in the standard. Why? I just told you, it's in the standard. Are you daring to question the all mighty standard?! This is the web my friends. Perfectly logical things like one tag inside another will have a random footgun exception for no other reason than someone in 1996 couldn't make it work on a NeXT cube so they "standardized" it rather than clean it up.

The style="" Exception

Another option for debugging your CSS is to use the style="" attribute on the tag like this:

<span>
  <h1 style="color: blue">I am Blue</h1>
</span>

This wins all the time, so like !important you can use it to see if your CSS rule is written correctly, or if there's something else that's blocking in in your CSS.

Specificity Hell

If you look at the HTML from Bootstrap you'll see this style:

<div id="carouselExampleSlidesOnly" class="carousel slide" data-ride="carousel">
  <div class="carousel-inner">
    <div class="carousel-item active">
      <img class="d-block w-100" src="..." alt="First slide">
    </div>
    <div class="carousel-item">
      <img class="d-block w-100" src="..." alt="Second slide">
    </div>
    <div class="carousel-item">
      <img class="d-block w-100" src="..." alt="Third slide">
    </div>
  </div>
</div>

In this example from Bootstrap's Carousel you can see that it relies entirely <div class=""> for everything. Is this necessary? No, not at all. Here's my replica of that same example:

  <carousel>
      <figure class="active">
        <img alt="Stock photo" src=""> <figcaption>Image 1</figcaption>
      </figure>
      <figure>
        <img alt="Stock photo" src=""> <figcaption>Image 2</figcaption>
      </figure>
      <figure>
        <img alt="Stock photo" src=""> <figcaption>Image 3</figcaption>
      </figure>
    <prev></prev>
    <next></next>
  </carousel>

You can do everything Bootstrap does with simple simple custom HTML tags, so it's not necessary for to rely on classes. Bootstrap isn't the only project to do this either. Nearly every CSS framework uses this div.class style, but they seem to not know why. They just do it because other people did it, but there is an actual reason as explained by Jeffrey Zeldman in his book, Designing with Web Standards:

...in most browsers, without CSS, the <h1> heading would be big and bold with vertial margins (whitespace) above and below.

CSS lets you change that. With CSS, <h1> can be small, italic, and margin-free if it suits the designer to make it so. Alas, not in Netscape 4, which adds its default legacy renderings to any CSS rule the designer specifies. If the CSS says there shall be no whitespace below the headling, Netscape 4 will go ahead and stick whitespace down there anyway.

When designers applied CSS to standard HTML markup, they quickly discovered that IE4 mainly did what they asked it to do, wheras Netscape 4 made a mess of their layouts.

Some designers abandoned CSS. Others (sadly including me) initially worked around the problem by eschewing structural markup, using constructions like <div class="headline1"> instead of <h1>. This solved the display problem at the expense of document structure and semantics, thereby placing short-term gain ahead of long-term viability and leading to numerous problems down the road. Said problems have now come to roost.

This means everyone uses div.class style because Netscape Navigator version 4 didn't let you change the browser stylesheet. Netscape 4 was released in...1997 and it hasn't been used since at least 2004, but it fixed this problem in at least 2000. That means people have been using this style of CSS to support a browser that died 21 years ago. There is then no reason to use div.class and in fact, if you're forced to use this style then the browser is not following the standards.

The Impact of div.class

The first problem with the div..class style is readability. Take this snippet from the bootstrap example to see what I mean:

<div class="carousel-item">
  <img class="d-block w-100" src="..." alt="Third slide">
</div>

This could be rewritten to simply be:

<carousel-item>
  <img src="..." alt="Third slide">
</carousel-item>

Which also solves the second problem of class specificity hacking. Because Bootstrap uses .class everywhere it completely dominates your own CSS and makes determining the specificity of any CSS far more difficult. If Bootstrap simply used a set of predefined tags your own CSS would override their style without any need to hack specificity with !important or resort to using id.

Keeping it Simple

The solution to specificity hell is to simply reject the <div class="tagname"> style and get back to the original intent of CSS specificity:

  1. Use mostly tags in your CSS with full (or mostly complete) DOM paths for the selectors.
  2. Use .class for different variants or states of those tags.
  3. Use .id for one time localized changes for a single view.

This model is not only actually semantic but you almost never need to calculate specificity. The CSS applies to tags and exact structures, classes then create variations on those rules, and ids are used in your local HTML and CSS for page specific clarifications. Doing this has removed about 90% of my specificity problems and results in cleaner CSS and HTML that's easy to maintain.

Previous Lesson Next Lesson

Register for Learn JavaScript the Hard Way

Register today for the course and get the all currently available videos and lessons, plus all future modules for no extra charge.