In the last post we discovered why CSS came to be, what it is, and how it works. Now we’re going focus more on how we can style our website HTML with CSS rules. This is the magical part – CSS can style almost everything in almost any way. CSS Zen Garden was one of the first proof of concept sites to show just how amazing CSS can be, by presenting not just the same content but exactly the same HTML document in drastically varying ways, and that’s really cool!
Recall that CSS rules are written in the following form:
selector {
property1 : value1;
property2 : value2;
...
}
Note that whitespaces don’t matter. A rule can be written mashed together on one line or spanned across several. It’s all the same to the CSS parser as long as there is a selector followed by property styles between curly braces.
p a {
color: red;
font-weight: bold;
}
is the same as
p a{color:red;font-weight:bold;}
In practice I personally tend to favour spreading the CSS rules across multiple lines as I find it more consistent for readability even with larger rules.
Selectors
To style HTML with CSS rules, the rule has to select which HTML elements it affects. At the very minimum, a selector can be as simple as a single element. Suppose that we wanted to style all paragraph text to be dark but not black, and slightly larger than the default font size. We would right a rule that selects all paragraphs and styles their color & font-size properties.
p { color: #444; font-size: 1.125em; }
I’ve chosen the HEX color #444, which is a dark gray, and made the font 1.125 times bigger than its current size. The Em is a relative unit. For now, don’t worry about memorizing the properties at the moment; some of the more common properties will be introduced in detail later on and will become familiar. Let the focus be on writing CSS and selecting the correct elements to be styled.
Now suppose that we want to be a bit fancy and style links that are within paragraphs which are themselves contained within an article. We would write
article p a { ... }
Why is the selector written in that order? Consider the HTML structure for that example:
<article>
<p>This paragraph text is <a href="another.html">linked</a> to another page.</p>
</article>
The anchor element is ultimately the element of interest and we encounter those HTML elements in that order as we work our way to the <a>
. I find that understanding what the CSS selector is selecting makes more sense if read from right to left. Read from right to left we know immediately what the target is and progressively hone in on its context.
This rule styles all anchor elements that are within a paragraph that is within an article!
Reading the selector from left to right, the target isn’t known until the very end and each component encountered adds context to something that we haven’t yet encountered. Try reading the example selector out aloud while making a phrase to explain what is being selected, and you tell me which direction feels more natural.
Classes and IDs
So far we’ve only looked at selectors which target HTML elements. This is fine and dandy, but suppose that we want to only target a certain subset of elements that exist within the same scope but not all of them? We need a more refined way of referring to these specific elements. Fortunately, good guy HTML offers some utility for “labelling” elements, and CSS is able to leverage this to our advantage. We can identify HTML elements more specifically by class and ID.
The important distinction here is that classes add descriptors to an element while IDs give elements a proper name. Using a human for example, I would be classified as caucasian, medium height, and clean-shaven, and given the ID Jonathan Weatherhead.
Classes are used to classify an element in a more detailed way than by its generic type. For example, <p class="prominent">...</p>
for drawing attention to certain paragraphs. We can use the class in a CSS rule by prefixing a dot to the name. Lets go ahead and style all elements that are marked with the prominent class to have a light gray background and slightly smaller text than their respective parent elements.
.prominent {
background-color: lightgray;
font-size: .85em;
}
IDs are similar creatures. The big difference here lies in IDs being unique. While a class can and should be reused on multiple elements, an ID should only be used once per HTML document. As such, the role of an ID is to uniquely identify a single element. For example, <p id="about-author">...</p>
for identifying a particular paragraph as a specific blurb about the author. We can use the ID in a CSS rule by prefixing a pound to the name. Lets go ahead and style the about-author element to have a black top border.
#about-author {
border: 4px solid black;
}
It’s good to come up with a name that captures the intended semantic as this makes it easy to understand the structure of the HTML structure and clarifies the CSS rules. <p class="abc"></p>
is perfectly valid but in a month from now it might be a bit of a puzzlement trying to remember what exactly was the intent of abc.
Combinators
CSS combinators are the glue, the adjectives of our selectors. They describe where each element is with respect to the previous one in the context of a rule. So far we’ve worked mostly with simple one-element selectors but CSS can do much more than that. We can select elements based on the context of how they are contained within other elements. CSS also allows elements to be selected on various properties but that’s not important for now. Lets meet some of the most commonly used combinators now.
Descendants
Lets start with the descendant combinators as we’ve already been informally introduced. It means that the elements we are selecting are contained somewhere within the ancestor element – it is the ancestor’s descendants. This combinator is a space between two components in a selector. Recall our article p a { ... }
rule! All links that are within paragraphs which are themselves contained within an article.
article p a {
font-weight: bold;
}
<article>
<p><a href="#">I'm bold!</a></p>
</article>
<article>
<section>
<div>
<p>
<span><a href="#">I'm bold, even under all of these elements!</a></span>
</p>
</div>
<section>
</article>
The second link qualifies as it is still a descendant of a paragraph which is itself a descendant of the article.
Children
Next on our list of meet & greet is the child combinator. This one is more specific and only applies to elements that are immediate descendants of the containing element – the containing element’s children. This combinator is a > between two components in a rule. Let’s continue with our bold links example, but with a little twist this time: article p > a { ... }
article p > a {
font-weight: bold;
}
<article>
<p><a href="#">I'm bold!</a></p>
</article>
<article>
<p>
<span><a href="#">I'm not bold :(</a></span>
</p>
</article>
This time, the 2nd link doesn’t qualify as it is not a child (immediate descendant) of the paragraph element. Shucks.
Adjacent Sibling
Another combinator up CSS’ toolkit is the adjacent sibling combinator. This combinator selects an element that is immediately adjacent to the previous one. This combinator is a + between two components in a rule. Supposed that we wanted to style the first paragraphs following an h1 heading for some emphasis effect. We could write article h1 + p { ... }
article h1 + p {
font-size: 1.125em;
}
<article>
<h1>Meet the Adjacency combinator</h1>
<p>I'm the first paragraph following a first level heading. Naturally I get preferred treatment.</p>
<p>I'm just another paragraph. Nothing special here.</p>
</article>
Author’s Note: It’s important to understand that the adjacency combinator only applies to the very first adjacent element. Read our example selector as “paragraphs that are immediately adjacent to h1 headings.”
General Siblings
The general siblings combinator behaves similarly to the adjacent sibling combinator but selects all elements that follow the previous one, at the same nested level. This combinator is a ~ between two components in a rule. Supposed that we wanted to indent all h2 headings following an h1 heading for some emphasis effect. We could write article h1 ~ h2 { ... }
article h1 ~ h2 {
padding-left: 1m;
}
<article>
<h1>Meet the Adjacency combinator</h1>
<p>I'm the first paragraph following a first level heading.</p>
<h2>I'm a sub-heading following a first level heading that will be indented.</p>
</article>
Summary
- Selectors are usually more easily understood when read from right to left.
- Classes describe semantics of an element. IDs give elements proper names.
- Descendant combinator selects elements that are descendants of the containing element at any level.
- Child combinator selects elements that are immediate children of the containing element.
- Adjacent Sibling combinator selects elements that are immediately adjacent to the context element.
- General Sibling combinators selects elements that follow the context element within the same nesting.