CSS and SCSS for beginners
A scroll-through guide: from your first selector to your first mixin
This guide explains CSS and SCSS from scratch. You don’t need any experience. Each part builds on the one before, so it works best in order. Scroll slowly: the explanation is on the left, and the picture on the right changes as you go. Where a picture is interactive, the text will say so.
- What CSS is
- Anatomy of a rule: selector, property, value
- Selectors: type, class, id and friends
- Values you will use all the time
- The box model
- Inheritance and the cascade
- Where a box goes, with
position - SCSS
- Glossary and where to learn more
Part 1: What CSS is
Web pages are built from two languages. HTML describes what is on the page: a heading, a paragraph, a link. This card is HTML and nothing else. It still looks like something, because your browser has built-in default styles. That’s why a link is blue and underlined, and a heading is bold.
CSS (Cascading Style Sheets) is the second language. It describes how those things should look, as a list of rules. Here are two: one turns headings brown, the other turns links green.
A third rule gives the whole card a background, some room inside it (padding) and rounded corners. The dot in .sprout-card matters. Part 3 explains it.
One more rule and the link looks like a button. Notice that the HTML never changed. CSS only changes appearance. That is the whole idea: HTML for content, CSS for looks. The next parts unpack how those rules are written.
Part 1 · What CSS is
HTML only, browser defaults
Part 1 · What CSS is
Part 1 · What CSS is
Part 1 · What CSS is
Part 2: Anatomy of a rule
Everything in CSS is written as rules. A rule has two halves: a pointer that says what to style, and instructions that say how it should look.
The selector is the pointer. It says which elements the rule is for. h4 means “every level-4 heading”.
The declaration block is everything between the curly braces { }. It holds the instructions.
A property is what you want to change, such as color, font-size or margin. CSS has hundreds of them.
A value says what to change it to. Which values are allowed depends on the property: color wants a colour, font-size wants a size.
A property and its value, joined by a colon and ended with a semicolon, make a declaration. Forgetting the semicolon is the classic beginner slip.
A block can hold as many declarations as you like. Text between /* and */ is a comment: the browser skips it, it is a note for humans.
Same shape, different rule. .sprout-card points at anything with that class. padding is the space inside a box. 1.5rem is a length.
And another. a:hover points at links, but only while the mouse is over them. none is a keyword: a word CSS already knows.
Part 2 · Anatomy of a rule
what to style
what to change
what to change it to
Part 2 · Anatomy of a rule
what to style
what to change
what to change it to
Part 2 · Anatomy of a rule
what to style
what to change
what to change it to
Part 2 · Anatomy of a rule
what to style
what to change
what to change it to
Part 2 · Anatomy of a rule
what to style
what to change
what to change it to
Part 2 · Anatomy of a rule
what to style
what to change
what to change it to
Part 2 · Anatomy of a rule
Part 2 · Anatomy of a rule
anything with this class
space inside the box
a length
Part 2 · Anatomy of a rule
links under the mouse
underline or not
a keyword
Part 3: Selectors
Selectors are how CSS decides which elements a rule applies to. To practise, here is a small piece of HTML and how it looks with no CSS. It has a heading, two paragraphs and two links. Some carry extra labels, called id and class. We’ll use them in a moment.
The simplest selector is the type selector, also called the element selector. It is just an HTML tag name. p means every paragraph.
A class is a label you make up and attach in the HTML, as in class="note". In CSS you select it with a dot: .note. Classes are reusable. Here a paragraph and a link share one, while the plain paragraph doesn’t have it. An element can also carry several classes, separated by spaces.
An id is a label that must be unique: only one element per page can have it. Select it with a hash: #title. For styling, classes are the everyday tool. Ids are better kept for one-off jobs such as link targets.
A selector list uses commas to give one rule to several selectors at once: h4, a means “headings and links”.
A space between two selectors means “inside”. p a reads as “any link inside a paragraph”. The last link is not inside a paragraph, so it is left alone.
A pseudo-class starts with a colon and describes a state. a:hover means “a link while the mouse is over it”. Try it: hover over the links on the right.
A cheat sheet for what you just met.
Part 3 · Selectors
Nothing is lit up yet.
Part 3 · Selectors
Lit up: both paragraphs. Not the links, not the heading.
Part 3 · Selectors
Lit up: the first paragraph and the last link, because both have class="note".
Part 3 · Selectors
Lit up: only the heading, the one element with id="title".
Part 3 · Selectors
Lit up: the heading and both links.
Part 3 · Selectors
Lit up: only the link inside the second paragraph.
Part 3 · Selectors
Lit up: whichever link the mouse is over.
Part 3 · Selectors
| Kind | You write | It matches |
|---|---|---|
| Type | p |
every <p> |
| Class | .note |
every element with class="note" |
| Id | #title |
the one element with id="title" |
| List | h4, a |
headings and links |
| Descendant | p a |
links inside paragraphs |
| Pseudo-class | a:hover |
links under the mouse |
| Universal | * |
everything |
Part 4: Values you will use all the time
Selectors say what. Declarations say how, and the values in them come in a few kinds. First, colours. You can write them as a name (red), as a hex code (#a04a0d) or as rgb() numbers. All three are the same idea: a mix of red, green and blue. Hex codes are the most common, and any colour picker will give you one.
Lengths are a number plus a unit. px is a fixed number of pixels. rem scales with the page’s base font size, so it grows if a reader enlarges their text. % is a share of the parent’s width. The dashed box below is the parent.
Some values are plain keywords: words CSS already knows, like center, bold or none. Here text-align moves text within its box.
A starter kit of properties, and the kind of value each one takes.
Part 4 · Values
a name
a hex code
rgb numbers
Part 4 · Values
| Property | What it changes | Example value |
|---|---|---|
color |
text colour | #a04a0d |
background |
background colour | #f8ca5d |
font-size |
text size | 1.2rem |
font-family |
typeface | "Roboto Slab", serif |
text-align |
text position in its box | center |
width |
width of a box | 50% |
padding |
space inside a box | 1.5rem |
margin |
space outside a box | 1rem |
border |
a line around a box | 2px solid #112909 |
Part 5: The box model
Every element on a page is a rectangular box, and every box has four layers. Learning them is the key to spacing things out. Here is a box from the inside out.
At the centre is the content: your text or image. By default, width and height set the size of this part only.
Padding is space inside the box, between the content and the edge. Add padding to give text room to breathe.
The border is a line around the padding. You choose its thickness, style and colour in one go: border: 8px solid #112909;.
Margin is space outside the border. It pushes neighbouring boxes away. An easy way to remember: padding is inside, margin is outside.
A common surprise: because width covers only the content, padding and border make the box wider than you asked for. This one line changes that, so width includes padding and border. Many stylesheets start with it.
Part 5 · The box model
Part 5 · The box model
Part 5 · The box model
Part 5 · The box model
Part 5 · The box model
Part 6: Inheritance and the cascade
Some properties are inherited: set them on a parent and the children pick them up. Text properties such as color and font-family work like this. The paragraph never got a colour rule of its own. It took its parent’s.
Others are not inherited. border, margin and padding apply only to the element you set them on. Otherwise every child would grow its own border too.
Now a bigger question. What if two rules point at the same element and disagree? The browser settles it in order. First, specificity: more specific selectors win. As a rough scoring guide, an id is worth 100 points, a class 10 and a type selector 1.
If two rules are equally specific, the later one wins. That is the “cascading” in Cascading Style Sheets. Both rules below are a type selector, so the second one takes it.
Specificity beats order. .note comes first but is worth 10 points against p’s 1, so the class wins and the paragraph is green.
And an id beats a class. The paragraph has both id="x" and class="note", so #x wins whatever the order.
When a rule seems to do nothing, ask two questions. Is my selector really matching the element? Is a more specific rule overriding it? Your browser’s developer tools (right-click, then Inspect) show which rules apply to an element and cross out the ones that lost.
Part 6 · Inheritance and the cascade
Child: a paragraph inside the parent.
Part 6 · Inheritance and the cascade
Child: a paragraph inside the parent.
Part 6 · Inheritance and the cascade
| Selector | Kind | Roughly worth |
|---|---|---|
#x |
id | 100 |
.note |
class | 10 |
p |
type | 1 |
Part 6 · Inheritance and the cascade
Which colour will I be?
Winner: the later rule, brown.
Part 6 · Inheritance and the cascade
Which colour will I be?
Winner: .note, green, even though it is earlier.
Part 7: Where a box goes, with position
Browsers lay boxes out in normal flow: one after another, top to bottom. The position property lets a box break that pattern. Here are three squares inside a parent box. With position: static, the default, they simply stack in order.
position: relative keeps C in the flow but lets you nudge it away from its normal spot with top, left, right or bottom. Its old spot stays reserved (the dashed outline), so B does not move up.
position: absolute takes C out of the flow completely. B moves up into the gap, and C is placed against its nearest positioned ancestor, meaning a parent whose own position is not static. Here that is the dashed box, which has position: relative. With no positioned ancestor, C would use the whole page.
position: fixed pins C to the browser window (the viewport). Scroll the page and C stays exactly where it is, like a chat button in the corner of a site.
position: sticky is a mix. C scrolls with the page like normal until it reaches the offset you set (top: 0), then it sticks there. It is how a table header can stay visible while you scroll.
Part 7 · position
C moved, but its old spot is still reserved.
Part 7 · position
C left the flow, so B moved up.
Part 7 · position
Illustration: the page moves, C does not.
Part 8: SCSS
Real stylesheets repeat themselves. Here the same cinnamon brown is written in four places. To try a different colour, you have to hunt down every copy.
Plain CSS can now fix this itself, with custom properties, also called CSS variables. Name a value once with two dashes, then use it with var(). The browser reads them live. Good to know, and it is often all you need.
SCSS goes further. It is an extension of CSS: every valid CSS file is already valid SCSS, and it adds extra features on top. Browsers can’t read SCSS, though. A tool called Sass translates your .scss file into ordinary .css first, and the browser only ever sees the result.
SCSS feature one: variables. They start with $. Name a value once and use it anywhere. The difference from --cinnamon is that Sass swaps $cinnamon for the real value while compiling, before the browser sees anything. Left is what you write, right is what comes out.
Feature two: nesting. Write the rules for things inside .scss-card inside the .scss-card block, instead of repeating the prefix each time. The & means “the thing I’m nested in”. Hover over the link. Don’t nest too deep, or your selectors get long-winded.
Feature three: mixins. A mixin is a recipe you write once with @mixin and reuse with @include, with ingredients you can swap. Two different pills, one recipe.
Feature four: splitting into files. As a stylesheet grows you can keep colours in their own file and load it with @use. A file whose name starts with an underscore, like _colours.scss, is a partial: it is only there to be loaded by other files.
So, CSS or SCSS? Here is the difference in one table. Start with plain CSS. Reach for SCSS once your stylesheet grows and you keep repeating yourself. Since all CSS is valid SCSS, you can rename a .css file to .scss and adopt the extras a bit at a time. You can practise in the Sass playground.
Part 8 · SCSS
Part 8 · SCSS
Part 8 · SCSS
Part 8 · SCSS
SCSS you write
Result
Chickpea Sprouts
A newsletter that explains science through stories.
Part 8 · SCSS
SCSS you write
Result
Part 8 · SCSS
SCSS you write
Result
Part 8 · SCSS
Part 8 · SCSS
| CSS | SCSS | |
|---|---|---|
| Browser reads it directly | Yes | No, compiled to CSS first |
| Variables | --name, live in the browser |
$name, replaced at compile time |
| Nesting | Yes, in modern browsers | Yes |
| Mixins | No | Yes |
| Splitting into files | @import (loads at runtime) |
@use (combined at compile time) |
| Comments | /* */ |
/* */ and // |
| Good for | Small stylesheets | Big ones that repeat themselves |
Glossary and where to learn more
| Term | In one line |
|---|---|
| Rule | A selector plus a block of declarations. |
| Selector | Says which elements a rule applies to (p, .note, #title). |
| Declaration block | The { } part holding the instructions. |
| Declaration | One property: value; pair. |
| Property | What to change (color, margin). |
| Value | What to change it to (#a04a0d, 1.5rem, center). |
| Class | A reusable label on HTML elements, selected with a dot. |
| Id | A unique label on one element, selected with a hash. |
| Pseudo-class | A state, selected with a colon (:hover). |
| Box model | The layers of every box: content, padding, border, margin. |
| Inheritance | Some properties (like color) pass from parent to child. |
| Specificity | How the browser ranks conflicting rules: id, then class, then type. |
| Cascade | If specificity ties, the later rule wins. |
| Normal flow | The default top-to-bottom layout that position can break. |
| CSS variable | --name and var(--name): a live value in the browser. |
| SCSS / Sass | CSS with extras (variables, nesting, mixins) that gets compiled to CSS. |
Further reading, from the sources this guide draws on: