Skip to main content
HTML & CSS Foundations·Lesson 2 of 5

CSS Fundamentals

CSS controls how HTML elements look on screen. Before diving into layouts, you need a solid understanding of how styles are selected, calculated, and applied.

Selectors

Selectors target the elements you want to style. Here are the ones you will use most:

/* Element selector */
p {
  color: #333;
}

/* Class selector */
.card {
  border: 1px solid #ddd;
  border-radius: 8px;
}

/* ID selector (use sparingly) */
#hero {
  min-height: 100vh;
}

/* Descendant selector — any <a> inside .nav */
.nav a {
  text-decoration: none;
}

/* Direct child selector — only immediate children */
.list > li {
  margin-bottom: 0.5rem;
}

/* Attribute selector */
input[type="email"] {
  border-color: blue;
}

/* Pseudo-classes */
a:hover {
  color: #E21B1B;
}

button:focus-visible {
  outline: 2px solid blue;
  outline-offset: 2px;
}

/* Pseudo-elements */
.quote::before {
  content: "\201C"; /* left double quotation mark */
  font-size: 2rem;
}

Specificity

When multiple rules target the same element, specificity determines which one wins. Think of it as a three-part score: (ID, Class, Element).

SelectorSpecificityScore
p(0, 0, 1)1
.card(0, 1, 0)10
#hero(1, 0, 0)100
.card p(0, 1, 1)11
#hero .title(1, 1, 0)110

Practical rules:

  • Prefer classes over IDs for styling.
  • Avoid !important — it breaks the cascade and makes debugging painful.
  • Keep selectors short. .card-title beats .sidebar .card .card-body .card-title.

The Box Model

Every element is a rectangular box with four layers:

┌─────────── margin ───────────┐
 ┌──────── border ──────────┐ 
  ┌────── padding ───────┐  
                          
        content           
                          
  └──────────────────────┘  
 └──────────────────────────┘ 
└──────────────────────────────┘

By default, width sets the content width, and padding/border are added on top. This is confusing, so always use border-box:

*,
*::before,
*::after {
  box-sizing: border-box;
}

With border-box, a width: 300px element stays 300px total, including padding and border.

.card {
  width: 300px;
  padding: 1rem;     /* included in the 300px */
  border: 2px solid; /* included in the 300px */
  margin: 1rem;      /* outside the 300px */
}

Units

Choose the right unit for the job:

UnitUse forBase
pxBorders, shadows, fine detailsAbsolute
remFont sizes, spacing, widthsRoot font size (usually 16px)
emPadding/margin relative to current fontCurrent element's font size
%Widths relative to parentParent dimension
vw / vhViewport-relative sizingViewport width/height
dvhFull-height layouts on mobileDynamic viewport height
:root {
  font-size: 16px; /* 1rem = 16px */
}

body {
  font-size: 1rem;       /* 16px */
  line-height: 1.6;      /* unitless — scales with font-size */
  padding: 0 1rem;       /* 16px horizontal padding */
}

h1 {
  font-size: 2.5rem;     /* 40px */
  margin-bottom: 0.5em;  /* 20px — relative to the h1's own font-size */
}

.hero {
  min-height: 100dvh;    /* full viewport height, accounts for mobile browser chrome */
}

Tip: Use rem for most spacing. It scales consistently and makes responsive adjustments easy — change the root font size, and everything updates.

Custom Properties (CSS Variables)

Custom properties let you define reusable values and swap them dynamically:

:root {
  --color-primary: #E21B1B;
  --color-text: #1a1a1a;
  --color-bg: #ffffff;
  --radius: 8px;
  --spacing: 1rem;
}

/* Dark mode override */
.dark {
  --color-text: #e5e5e5;
  --color-bg: #0a0a0a;
}

.card {
  background: var(--color-bg);
  color: var(--color-text);
  border-radius: var(--radius);
  padding: var(--spacing);
}

.button {
  background: var(--color-primary);
  border-radius: var(--radius);
  /* Fallback if the variable is undefined */
  padding: var(--button-padding, 0.75rem 1.5rem);
}

Custom properties cascade and inherit, making them perfect for theming. Change the variable in one place, and every element using it updates.

Practical Reset

A minimal reset gives you a consistent starting point:

*,
*::before,
*::after {
  box-sizing: border-box;
  margin: 0;
}

body {
  font-family: system-ui, sans-serif;
  line-height: 1.6;
  -webkit-font-smoothing: antialiased;
}

img,
video,
svg {
  display: block;
  max-width: 100%;
}

input,
button,
textarea,
select {
  font: inherit;
}

This reset eliminates default margins, makes images responsive, and ensures form elements inherit font styles.