Cecilia Baldoni
  • About
  • Projects
  • Blog
  • Contact

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.

  1. What CSS is
  2. Anatomy of a rule: selector, property, value
  3. Selectors: type, class, id and friends
  4. Values you will use all the time
  5. The box model
  6. Inheritance and the cascade
  7. Where a box goes, with position
  8. SCSS
  9. 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

Chickpea Sprouts

A newsletter that explains science through stories.

Read the latest issue

HTML only, browser defaults

Part 1 · What CSS is

Chickpea Sprouts

A newsletter that explains science through stories.

Read the latest issue
h4 {
  color: #a04a0d;
}
a {
  color: #105b09;
}

Part 1 · What CSS is

Chickpea Sprouts

A newsletter that explains science through stories.

Read the latest issue
.sprout-card {
  background: #f3dbc1;
  padding: 1.5rem;
  border-radius: 12px;
}

Part 1 · What CSS is

Chickpea Sprouts

A newsletter that explains science through stories.

Read the latest issue
a {
  background: #f8ca5d;
  padding: 0.4rem 0.9rem;
  border-radius: 6px;
  text-decoration: none;
  font-weight: 700;
}

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

h4selector
what to style
{
colorproperty
what to change
:
#a04a0dvalue
what to change it to
;
}

Part 2 · Anatomy of a rule

h4selector
what to style
{
colorproperty
what to change
:
#a04a0dvalue
what to change it to
;
}

Part 2 · Anatomy of a rule

h4selector
what to style
{
colorproperty
what to change
:
#a04a0dvalue
what to change it to
;
}

Part 2 · Anatomy of a rule

h4selector
what to style
{
colorproperty
what to change
:
#a04a0dvalue
what to change it to
;
}

Part 2 · Anatomy of a rule

h4selector
what to style
{
colorproperty
what to change
:
#a04a0dvalue
what to change it to
;
}

Part 2 · Anatomy of a rule

h4selector
what to style
{
colorproperty
what to change
:
#a04a0dvalue
what to change it to
;
}

Part 2 · Anatomy of a rule

/* Style every level-4 heading */
h4 {
  color: #a04a0d;
  font-size: 1.4rem;
  margin: 0;
}

Part 2 · Anatomy of a rule

.sprout-cardselector
anything with this class
{
paddingproperty
space inside the box
:
1.5remvalue
a length
;
}

Part 2 · Anatomy of a rule

a:hoverselector
links under the mouse
{
text-decorationproperty
underline or not
:
nonevalue
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

Chickpea Sprouts

New issue every month.

Science, told as stories.

Read the latest issue

Nothing is lit up yet.

HTML

<div class="card">
  <h4 id="title">Chickpea Sprouts</h4>
  <p class="note">New issue every month.</p>
  <p>Science, told as <a href="#">stories</a>.</p>
  <a href="#" class="note">Read the latest issue</a>
</div>

CSS

/* no CSS yet */

Part 3 · Selectors

Chickpea Sprouts

New issue every month.

Science, told as stories.

Read the latest issue

Lit up: both paragraphs. Not the links, not the heading.

HTML

<div class="card">
  <h4 id="title">Chickpea Sprouts</h4>
  <p class="note">New issue every month.</p>
  <p>Science, told as <a href="#">stories</a>.</p>
  <a href="#" class="note">Read the latest issue</a>
</div>

CSS

p {
  background: #f8ca5d;
}

Part 3 · Selectors

Chickpea Sprouts

New issue every month.

Science, told as stories.

Read the latest issue

Lit up: the first paragraph and the last link, because both have class="note".

HTML

<div class="card">
  <h4 id="title">Chickpea Sprouts</h4>
  <p class="note">New issue every month.</p>
  <p>Science, told as <a href="#">stories</a>.</p>
  <a href="#" class="note">Read the latest issue</a>
</div>

CSS

.note {
  background: #f8ca5d;
}

Part 3 · Selectors

Chickpea Sprouts

New issue every month.

Science, told as stories.

Read the latest issue

Lit up: only the heading, the one element with id="title".

HTML

<div class="card">
  <h4 id="title">Chickpea Sprouts</h4>
  <p class="note">New issue every month.</p>
  <p>Science, told as <a href="#">stories</a>.</p>
  <a href="#" class="note">Read the latest issue</a>
</div>

CSS

#title {
  background: #f8ca5d;
}

Part 3 · Selectors

Chickpea Sprouts

New issue every month.

Science, told as stories.

Read the latest issue

Lit up: the heading and both links.

HTML

<div class="card">
  <h4 id="title">Chickpea Sprouts</h4>
  <p class="note">New issue every month.</p>
  <p>Science, told as <a href="#">stories</a>.</p>
  <a href="#" class="note">Read the latest issue</a>
</div>

CSS

h4,
a {
  background: #f8ca5d;
}

Part 3 · Selectors

Chickpea Sprouts

New issue every month.

Science, told as stories.

Read the latest issue

Lit up: only the link inside the second paragraph.

HTML

<div class="card">
  <h4 id="title">Chickpea Sprouts</h4>
  <p class="note">New issue every month.</p>
  <p>Science, told as <a href="#">stories</a>.</p>
  <a href="#" class="note">Read the latest issue</a>
</div>

CSS

p a {
  background: #f8ca5d;
}

Part 3 · Selectors

Chickpea Sprouts

New issue every month.

Science, told as stories.

Read the latest issue

Lit up: whichever link the mouse is over.

HTML

<div class="card">
  <h4 id="title">Chickpea Sprouts</h4>
  <p class="note">New issue every month.</p>
  <p>Science, told as <a href="#">stories</a>.</p>
  <a href="#" class="note">Read the latest issue</a>
</div>

CSS

a:hover {
  background: #f8ca5d;
}

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

red
a name
#a04a0d
a hex code
rgb(16 91 9)
rgb numbers
color: red;
color: #a04a0d;
color: rgb(16 91 9);

Part 4 · Values

100px
6rem
50%
width: 100px;
width: 6rem;
width: 50%;

Part 4 · Values

left

center

right

text-align: left;
text-align: center;
text-align: right;

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

margin
border
padding
content
/* the numbers in the drawing are approximate */
.box {
  width: 8rem;
  padding: 28px;
  border: 8px solid #112909;
  margin: 28px;
}

Part 5 · The box model

margin
border
padding
content
/* the numbers in the drawing are approximate */
.box {
  width: 8rem;
  padding: 28px;
  border: 8px solid #112909;
  margin: 28px;
}

Part 5 · The box model

margin
border
padding
content
/* the numbers in the drawing are approximate */
.box {
  width: 8rem;
  padding: 28px;
  border: 8px solid #112909;
  margin: 28px;
}

Part 5 · The box model

margin
border
padding
content
/* the numbers in the drawing are approximate */
.box {
  width: 8rem;
  padding: 28px;
  border: 8px solid #112909;
  margin: 28px;
}

Part 5 · The box model

margin
border
padding
content
/* the numbers in the drawing are approximate */
.box {
  width: 8rem;
  padding: 28px;
  border: 8px solid #112909;
  margin: 28px;
}

Part 5 · The box model

* {
  box-sizing: border-box;
}

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

Parent

Child: a paragraph inside the parent.

.parent {
  color: #a04a0d;
}

Part 6 · Inheritance and the cascade

Parent

Child: a paragraph inside the parent.

.parent {
  color: #a04a0d;
  border: 2px solid #112909;
  padding: 0.75rem;
}

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?

p {
  color: #105b09;
}
p {
  color: #a04a0d;
}

Winner: the later rule, brown.

Part 6 · Inheritance and the cascade

Which colour will I be?

.note {
  color: #105b09;
}
p {
  color: #a04a0d;
}

Winner: .note, green, even though it is earlier.

Part 6 · Inheritance and the cascade

Which colour will I be?

#x {
  color: #a04a0d;
}
.note {
  color: #105b09;
}

Winner: #x, brown.

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

A
C
B

Normal flow: A, C, B in order.

.c {
  position: static; /* the default */
}

Part 7 · position

A
C
B

C moved, but its old spot is still reserved.

.c {
  position: relative;
  top: 20px;
  left: 30px;
}

Part 7 · position

A
C
B

C left the flow, so B moved up.

.parent {
  position: relative;
}
.c {
  position: absolute;
  top: 10px;
  right: 10px;
}

Part 7 · position

C
Before scrolling
C
After scrolling

Illustration: the page moves, C does not.

.c {
  position: fixed;
  top: 10px;
  right: 10px;
}

Part 7 · position

C
Before scrolling
C
After scrolling

Illustration: C scrolls until it reaches the top, then stays.

.c {
  position: sticky;
  top: 0;
}

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

h4 { color: #a04a0d; }
a:hover { color: #a04a0d; }
.sprout-card { border: 2px solid #a04a0d; }
blockquote { border-left: 4px solid #a04a0d; }

Part 8 · SCSS

:root {
  --cinnamon: #a04a0d;
}
h4 {
  color: var(--cinnamon);
}
a:hover {
  color: var(--cinnamon);
}

Part 8 · SCSS

CSS
styles.css
→
Browser
SCSS
styles.scss
→ Sass →
styles.css
→
Browser

Part 8 · SCSS

SCSS you write

$cinnamon: #a04a0d;

.scss-card {
  border: 2px solid $cinnamon;
}
.scss-card h4 {
  color: $cinnamon;
}

CSS you get

.scss-card {
  border: 2px solid #a04a0d;
}
.scss-card h4 {
  color: #a04a0d;
}

Result

Chickpea Sprouts

A newsletter that explains science through stories.

Part 8 · SCSS

SCSS you write

.scss-card {
  border: 2px solid $cinnamon;
  h4 { color: $cinnamon; }
  a {
    background: #f8ca5d;
    &:hover {
      background: $cinnamon;
      color: white;
    }
  }
}

CSS you get

.scss-card {
  border: 2px solid #a04a0d;
}
.scss-card h4 {
  color: #a04a0d;
}
.scss-card a {
  background: #f8ca5d;
}
.scss-card a:hover {
  background: #a04a0d;
  color: white;
}

Result

Chickpea Sprouts

A newsletter that explains science through stories.

Read the latest issue

Part 8 · SCSS

SCSS you write

$chickpea: #f8ca5d;

@mixin pill($bg) {
  background: $bg;
  padding: 0.4rem 0.9rem;
  border-radius: 999px;
}
.cta {
  @include pill($chickpea);
}
.tag {
  @include pill($cinnamon);
  color: white;
}

CSS you get

.cta {
  background: #f8ca5d;
  padding: 0.4rem 0.9rem;
  border-radius: 999px;
}
.tag {
  background: #a04a0d;
  padding: 0.4rem 0.9rem;
  border-radius: 999px;
  color: white;
}

Result

Read more science

Part 8 · SCSS

_colours.scss

$cinnamon: #a04a0d;

styles.scss

@use "colours";

h4 {
  color: colours.$cinnamon;
}

CSS you get

h4 {
  color: #a04a0d;
}

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:

  • MDN: What is CSS?
  • MDN: Basic CSS selectors
  • MDN: The box model
  • MDN: Handling conflicts (cascade, specificity, inheritance)
  • MDN: the position property
  • MDN: CSS custom properties
  • CSS-Tricks: How CSS selectors work
  • Sass: the official guide and playground
  • Closeread, the extension behind this page
© Copyright 2026, Cecilia Baldoni
 

Built with Quarto