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).
| Selector | Specificity | Score |
|---|---|---|
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-titlebeats.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:
| Unit | Use for | Base |
|---|---|---|
px | Borders, shadows, fine details | Absolute |
rem | Font sizes, spacing, widths | Root font size (usually 16px) |
em | Padding/margin relative to current font | Current element's font size |
% | Widths relative to parent | Parent dimension |
vw / vh | Viewport-relative sizing | Viewport width/height |
dvh | Full-height layouts on mobile | Dynamic 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.