Troubleshooters.Com® and Web Workmanship Present:
CSS Primer
Copyright © 2022 by Steve Litt
See the Troubleshooters.Com Bookstore.
CONTENTS
CSS stands for Cascading StyleSheets. It's one of the best technologies I've ever seen. It's simple, powerful, easy to learn and remember, and it usually works as expected, and if not, you can get it to work as expected. As explained in Content, Styles and Appearances, CSS is used to map appearances to content.
A style sheet consists of a list of styles. The word "rule" is sometimes used instead of the word "style", but in this document we'll use the word "style". A style defines how an element or class of elements look. Each style consists of one or more selectors and exactly one declaration block. The following example is a very simple example of a style:
h1{background-color: blue; border-style: solid;}
In the preceding, the one and only selector is "h1", and the declaration block is "{background-color: blue; border-style: solid;}". The declaration block contains two declarations, background-color: blue;" and "border-style: solid;". Each declaration contains a property name, then a colon, then a property value, then a semicolon. So this style gives every h1 element in the document a blue background and a border.
The reason CSS is called Cascading Style Sheets is that later styles, for a given element or class of the element, override earlier styles for that element or class. This gives the person applying appearances to styles (let's call that person an appearancer) several great powers related to adding a style below the style's current definition:
A CSS statement contains a selector (style name), optionally followed by a pound sign (#) and a name, or a dot (.) and a name. The pound sign indicates a specific element ID that can appear only once in the page's body, while the dot indicates a class name that can be used multiple times in the body.
So, as you read the rest of this document to get the specifics, remember the following:
The rest of this document will either detail or add to what's been said in this introduction.
I'm covering declaration blocks first because declaration blocks are much, much simpler than selectors. A declaration block is simply a list of key=>value pairs (name=>value pairs in CSS speak) enclosed in a set of curly braces. That's it. The key=>value pairs can be on the same line, or different lines.
If you don't know what a key=>value pair is, consider a list of grades, on the latest test, for Mary, Barry and Gary, the only kids in the class, who got grades of 100%, 90% and 80% respectively. Each kid's name is a key, and the percentage is a value. If we were to put this list into declaration block format, it would look like the following:
{Mary: 100%; Barry: 90%; Gary: 80%;}
It could also be expressed line by line:
{ Mary: 100%; Barry: 90%; Gary: 80%; }
CSS declarations don't care about newlines or indentation: Using a colon to separate a key from a value, and using semicolons to separate key=>value pairs is both necessary and sufficient to write well formed CSS.
In normal, valid HTML, each CSS declaration key is either an attribute defined in HTML, including the following:
Always remember to use a colon between each key and its value, and use semicolons between key=>value pairs. Failure to do so gives you silent errors that are murder to troubleshoot. Although it's not required to use a semicolon after the final key=>value pair, doing so is not forbidden, and doing so helps prevent later errors when you add more key=>value pairs. I always end the final key=>value pair with a semicolon.
Using the W3C validators to check your CSS is covered in Web Workmanship document Validating and Debugging HTML, CSS and Javascript, but don't depend exclusively on that. Catch your errors early by inspecting your CSS.
There exist CSS preprocessors to make CSS even easier to use than it already is. Some popular CSS preprocessors include:
I don't use CSS preprocessors personally, but where I envision them being a big help is when you want to make a style that's substantially the same as a style in the other peoples' CSS, but has a small difference, and you're going to need both styles.
If you're using other peoples' CSS, my first recommendation is to evaluate whether you really need their CSS, hook, line and sinker. If not, just roll your own subset. But if you really need the whole thing, and yet want to create derivative styles, then a CSS preprocessor just might be what you need.
Note:
CSS preprocessors can do some other things I don't even understand. Given that I've never personally run across an appearance I couldn't achieve with regular CSS, I'm not going to research those things at this time.
Selectors are called "selectors" because they select which elements they apply to. I found this confusing when I started out with CSS. I suppose they could also be called "identifiers" because they identify which elements they apply to. However, the official word is "selector", so that's the word used in this document and all of the Web Workmanship subsite of Troubleshooters.Com.
This section, the selectors section, consists of a series of examples, and below each example is text explaining exactly what's happening in the example. Each example consists of a complete style, with each example's declaration block consisting of {color: brown;}. The selector will be everything except that declaration block.
h1{color: brown;}
The preceding selector selects any and all h1 elements for styling.
h1#dogs{color: brown;}
The preceding selector selects only the h1 element whose id is "dogs" for styling. The HTML standard requires every element to have its own unique id, so in a properly written document, #dogs would suffice. The advantage of including the "h1" is documentation: You know to look only in documents formatted as "h1".
h1.cats{color: brown;}
The preceding selector selects only h1 elements whose class attribute is "cats" for styling. The HTML standard allows multiple elements with the same class name.
h1.cats,h1.birds{color: brown;}
The preceding selector selects only h1 elements whose class attribute is either "cats" or "birds", but not h1 elements with other class attributes such as "reptiles", nor h1 elements with no class attribute at all. You can use spaces before or after the comma, or both. However, if you use a space without a comma, it means something completely different.
The comma delimited selector list can be done with id as well as class, as shown in the following example:
div#membership,div#dues,h1#bylaws{color: blue;}
Note that to take up less width, the preceding could have been written as follows:
div#membership, div#dues, h1#bylaws{color: blue;}
The preceding could be narrowed even more by placing each (in this case only one) declaration on its own line:
div#membership, div#dues, h1#bylaws{ color: blue; }
When a declaration block has a lot of selectors, putting each selector on its own line increases readability.
From this point on, we won't speak of "h1 elements whose class attribute is cats", but instead use the shorter h1.cats.
div.note p.title{color: brown;}
The preceding selector selects only p.title elements that are contained directly or indirectly in a div.note. In other words, the p.title could be a child, grandchild, great-grandchild, etc, of the div.note.
div.note>p.title{color: brown;}
The preceding selector selects only p.title elements that are directly contained in a div.note. By placing the angle bracket where the space was in the example before this, we match only direct children, not grandchildren, great grandchildren, etc.
Pseudo-classes begin with a single colon (:). Pseudo-elements begin with a double colon (::). These can bestow unexpected power to a master of CSS. Just as an example, following two lines cause every paragraph to be indented except the first of a series of paragraphs:
p::first-letter{margin-left: 3em;} :not(p) + p::first-letter{margin-left:0.0001em;}
The rest of this subsection discusses less challenging work with pseudo-classes and pseudo-elements.
a:hover{background-color: cyan;}
The preceding CSS makes the all hyperlinks, which of course are a elements, appear inside a cyan square background. This is important for giving users feedback that they're hovering a link. If you think this CSS has ugly results, by all means change the declaration block.
a:active{ background-color: blue; foreground-color: yellow; font-size: 150%; }
The preceding CSS grows the size of the hyperlink text, and therefore its square background, when the hyperlink is clicked. It's a way of telling users "I heard your click". There's a special place in the devil's playground for web pages that give no feedback, leaving users wondering whether the linked page is a slow loader, there's something wrong, or they missed when clicking. The preceding CSS also changes the background to blue and the foreground to yellow. If you think this CSS has ugly results, by all means change the declaration block.
Note:
In reality it's a bad idea to change the size of something when it gets clicked, because it pushes other elements down and it's confusing for the visitor. I grew the element on click only as a demonstration.
input:disabled{background-color: #999999;}
The preceding is how you "gray out" a disabled input element.
input:read-only{background-color: #550000;}
The preceding is how you give a specific appearance to all read-only input field elements.
p:first-child{color: brown;}
This one's tricky and a little unexpected, so be careful. It is from the child's point of view, not the parent's. If Jim has five younger siblings, Jim would say "I am the first child", and that is how this pseudo-class is used. For argument's sake, his parents could say "I'm going to paint my first child green." This would be perfectly good English, but it is not how the first-child pseudo-class is used. Always remember, first-child is always from the child's point of view. And it means "first child of my parent", not "first child since another kind of element was shown.
Note that there's also a last-child and nth-child and nth-last-child, as well as a only-child. Powerful stuff.
p:first-of-type{color: brown;}
first-of-type is like first-child only subtly different, and that's all I'll say about that.
Pseudo-Elements begin with a double colon, e.g. ::first-letter are relatively new and not implemented in all browsers and other software. But when you need them, they can make amazing things possible.
p::first-letter{color: brown;}
The preceding turns the first letter of each paragraph brown. The ::first-letter pseudo-element is a big part of how you implement drop-caps on first paragraphs.
Don't let a fancy word like "combinator" intimidate you. It simply means that you specify elements that match the selector by a relationship between two or more element or class types. There are four of these relationships, each with its own syntax:
Even the exotic combinators, like + and ~ can be handy at times. Consider the following:
:not(p) + p::first-letter{font-size: 130%;}
The preceding takes the next sibling of everything not a paragraph, and if that next sibling is a paragraph, makes its first character larger. This is how you do drop caps, among other things. However, it will miss some paragraphs you wanted to match, specifically the first paragraph in a container such as a div if that paragraph is the first child of the div. If you want to match such top-of-div paragraphs, you need to add one more piece of CSS:
p:first-child":first-letter{font-size: 130%;}
In some circles it's popular to indent the first line of every paragraph except the first paragraph in a series of paragraphs. The following is the CSS to accomplish just that:
p:first-letter{margin-left: 3em;} :not(p) + p::first-letter{margin-left:0.0001em;} p:first-child::first-letter{margin-left: 0.0001em;}
In the preceding CSS, the first line indents the first line of every paragraph in the document. The second line eliminates that indent from every paragraph directly following a non-paragraph sibling. The third line eliminates that indent on the first child of any container (such as <div;>, if and only if that first child is a paragraph. Between those three lines, you've accomplished pretty much everything you want if you're a "don't indent the first paragraph of a series" type of person.
Selector specificity is used to determine what can and what cannot override earlier selectors. Specificity is half cool and half a pain in the posterior. Specificity is often the cause of correct looking CSS not applying its appearances. Because of specificity, the following two CSS snippets have identical results:
p{background-color: #ff0000;} p.myspecial{background-color: #0000ff;} }
and
p.myspecial{background-color: #0000ff;} p{background-color: #ff0000;} }
In the preceding, p.myspecial is more specific than p, so regardless of order, the p.myspecial prevails.
This subsection scratches the surface of specificity, which can get very complicated. This subsection is an approximation, and parts of it might be in error, but it's a good starting point. Learn what this section has to offer. Then, if you need to know more, or if your style's appearances aren't being applied the way you think they should, do a web search on CSS specificity. Now for the approximate explanation...
WARNING:
This explanation does not take into accounts of the !important property, which in my opinion should not be used, nor the @layer at-rule, which I recommend if things get really complicated and there's no sane way to simplify them.
Note:
Any declaration declared inline, e.g.
<h1 style="color:red;">My H1</h1>
overrides any declarations, of the same name and class(if any), from any other CSS. I don't recommend using inline styles unless absolutely necessary, so the rest of this subsection assumes there are no inline styles.
Every selector has a three digit number. The higher the number, the more specific it is. In a contest between two selectors, regardless of order of declaration, the one with the higher specificity number "wins". Only in cases where this number is equal for two styles does order matter, in which case the latter selector "wins".
Note:
Technically, rather than a three digit number, the "number" I've been talking about is really three sequential numbers, because any digit can exceed 9. For instance, (4,13,11). But it is the same type of comparison: Only if the first number, for two different styles, is a tie, is the second number considered, only if the first and second number are ties is the third number considered. Only if all three numbers are identical is order considered.
It takes a pretty complex selector, perhaps too complex, to score more than 9 in any one digit. Therefore, thinking of it as a 3 digit number is probably more intuitive, and accurate in most practical cases.
Here's how you make the specificity number...
As mentioned, we're representing the specificity number is a three digit number:
Keeping in mind to the preceding list and the paragraphs before it, look at the following examples of selector, number, and explanation:
Please always remember the following two principles:
As I said earlier in this section, my description of how CSS specificity works is an approximation. For the authoritative details, go to https://developer.mozilla.org/en-US/docs/Web/CSS/Specificity
The CSS spec includes a declaration value modifier called !important. You'll see it a lot on gigantic pre-prepared CSS files. I'd strongly advise you not to use !important in your CSS.
The !important has two effects:
!important is an anti-pattern. Unless you know and remember everywhere it's been used, it will screw you up. It may make sensible CSS that you need to write exceedingly difficult. People override !important declarations with yet another !important, adding more crazy.
I know, I know, you need !important to write one of those 1000 line CSS files that gives the user everything and covers every edge case and corner case. My question is, why write such files and why use such files? The CSS you've learned in this document gives you the power to get the same results, with your own CSS, without the complexity and craziness. Those one size fits all CSS files carry complexity costs, which translate to dollar costs, during web page maintenance and creation. The 10037 line Bootstrap behemoth, containing 1031 declarations marked !important, won't be necessary after you master what's in the Web Workmanship project.
Note.
I don't mean to keep picking on Bootstrap. There are many such vendors of gratuitously complexified CSS. I used Bootstrap as an example because it's the best known of the behemoths. My line count on bootstrap.css comes from the un-minimized 4.3.1 version bootstrap.css.
I'm not a fan of complex CSS, but if you have to go complex, I'd recommend the @layer at-command. The @layer at-command lets you control overrides in complex CSS, in a sane way (unlike !important).
In the most generic terms, you declare layers early in your <style></style> container element, then later assign various CSS styles to each layer, so that later layers take precedence. If you're forced to use one of these one size fits all gigantic CSS files, you can declare your layers, then @import the CSS giant into the earliest layers, and then have some hope of your styles modifying the behemoth's styles.
I'm no authority on @layer. If you need @layer, see https://developer.mozilla.org/en-US/docs/Web/CSS/@layer for the nitty-gritty details. But remember, it's usually better to get rid of complexity than to work around it.
Grid layouts are a huge subject. They can be used in responsive and non-responsive layouts, so grid layouts are discussed in full in the Designing For Mobile and Desktop Via Responsive Web Design Web Workmanship document.
CSS is like spice: You need only a small amount if you're doing things right. If you take a look at this web page, you'll see that it incorporates about 160 lines of web_workmanship.css, 20 lines from monthfea.css, and about 15 lines of CSS internal to this web page. This is enough CSS to yield a very readable, eminently responsive page with styles for source code, terminology, and although I don't use it on this particular page, foldunder columns. If I wanted one of these oh so modern, tragically hip pages that acts like a PDF and jumps whole screens as you scroll, I could probably do that in an additional couple hundred lines.
So let me ask you: Why does the un-minimized bootstrap.css version 4.3.1 contain 10037 lines? How is one supposed to debug a page using such a monstrosity? Bootstrap isn't the only one. Many "pre-built CSS files are huge, because they're built to cover every possible use case, taking care of every edge case and corner case. Leaving you to memorize all sorts of incantations to alter their default look.
Don't subject yourself to albatross sized CSS files. Make CSS files that suit your website. Be sure to use relatively simple selectors. If you read and then reference the Web Workmanship project, you'll find it simple to get our documents' appearance and responsiveness exactly how you want it, with a reasonable amount of CSS.
If you want to make a hip, ultra-modern website, my advice would be to find a hip, ultra-modern website to imitate, and then use your CSS (and perhaps Javascript if you want to do that) prowess to mimic the look of that website.
CSS is how you apply appearances to text and other elements. A CSS style sometimes called a rule) consists of a selector and a declaration block. The declaration block consists of 0 or more declarations, each of which consists of a name and a value separated by a colon. Individual declarations are separated by semicolons, and best practice is to put a semicolon after the final declaration in the declaration block so if you add a declaration after it, you won't be missing a semicolon.
The selector selects elements it applies to you, so that matched elements have the selector's declarations applied to them. Selectors are a big and somewhat complex subject that were covered in the Selectors and Selector Specificity sections of this document.
CSS stands for Cascading Style Sheets. What cascading means is that the last change to something takes precedence.
Declaration blocks are fairly simple. A declaration block has zero or more declarations, separated by semicolons, and you really should put a semicolon after the last declaration so appending declarations doesn't inadvertently cause an error. Spaces are allowed before and/or after each semicolon. Each declaration is a name=>value pair, with the name and value separated by a colon. Spaces are allowed before and/or after the colon. Each declaration name is an HTML attribute such as:
Don't use !important because it will come back to haunt you or somebody else who tries to maintain your CSS. Use @layer in special cases in which you need to override normal CSS order/specificity in large chunks of CSS, or when you need to be able to override CSS from a third party. CSS is like a spice: Best used in moderation. You can do almost anything with a couple hundred lines of CSS, you can do a lot of things with 40 CSS lines, so resist the temptation to make or use these one-size-fits-all 10K line CSS behemoths.
Now that you understand HTML, HTML as well formed XML, and basic CSS, your next step is to read Designing For Mobile and Desktop Via Responsive Web Design, which has even more CSS tips and tricks, and introduces you to responsive web page techniques.