Module 1: Introduction & Fundamentals
Colors and Units: What Each One Is Relative To
Hex, RGB, HSL and modern color syntax, plus the real behavior of px, %, em, rem and viewport units, including the traps.
Colors and units look like trivia, but picking the wrong unit is what makes a layout break when someone changes their browser font size, and picking the wrong color format is what makes a theme painful to maintain.
Color formats#
You can write the same blue five ways (the last one is a close match, not an exact conversion).
.a { color: royalblue; } /* named color */
.b { color: #4169e1; } /* hex: red, green, blue as 00-ff */
.c { color: rgb(65 105 225); } /* same channels, in decimal */
.d { color: hsl(225 73% 57%); } /* hue, saturation, lightness */
.e { color: oklch(0.55 0.2 265); } /* perceptual lightness, chroma, hue */
Hex is what most design tools export. #fff is shorthand for #ffffff, and an extra pair adds alpha: #4169e180 is the blue at 50% opacity.
RGB is the same information in decimal. The modern syntax uses spaces and a slash for alpha: rgb(65 105 225 / 50%). The older comma form rgba(65, 105, 225, 0.5) still works everywhere and you will see it in a lot of existing code.
HSL is the one worth learning for hand-written CSS. Hue is a position on the color wheel from 0 to 360, saturation is how vivid it is, lightness is how bright. Making a hover state is easy because you change one number:
.btn { background: hsl(225 73% 50%); }
.btn:hover { background: hsl(225 73% 42%); } /* same color, darker */
With hex you would need a color picker to do that.
OKLCH is newer and has been supported in all major browsers since 2023. Its lightness matches how humans perceive brightness, so two colors with the same L actually look equally bright. That is not true in HSL, where a yellow and a blue at 50% lightness look nothing alike. It is increasingly used for design systems.
Two special values you will use constantly:
transparentis fully see-through.currentColortakes the element's currentcolor. Borders, SVG icons and shadows can follow the text color without repeating it.
.icon-btn {
color: #0b5fff;
border: 2px solid currentColor;
}
Contrast is not optional#
Text needs enough contrast against its background to be readable. WCAG AA asks for a ratio of 4.5:1 for normal text and 3:1 for large text (roughly 24px regular or 19px bold). Light gray on white is the most common failure, and it looks fine on your good monitor and unreadable in sunlight. DevTools shows the contrast ratio in the color picker.
Units#
px#
A CSS pixel is not a screen pixel. On a high-density display, one CSS pixel can be two or three device pixels. The browser handles that for you, which is why a 16px font looks about the same size on a phone and a desktop monitor. Pixels are fine for borders, shadows and anything that should not scale with text.
%
A percentage is always relative to something, and what it is relative to depends on the property.
width: 50%is half of the containing block's width.font-size: 120%is 120% of the parent's font size.padding-top: 10%is 10% of the containing block's width, not height. This is a feature. It is how aspect-ratio hacks used to work.height: 50%only works if the parent has an explicit height. Otherwise it behaves likeauto.
em#
On most properties, em is relative to the element's own font size. On font-size itself it is relative to the parent's, because the element's own size is the thing being calculated.
That second rule is where compounding comes from:
.card { font-size: 1.25em; }
.card .note { font-size: 1.25em; }
html 16px
└ .card 1.25em → 20px
└ .note 1.25em → 25px (1.25 × 20, not 1.25 × 16)
└ .tiny 1.25em → 31.25px and so on, every level multiplies
For padding and margins, using em is useful because spacing scales with the component's own text size:
.btn { font-size: 1rem; padding: 0.6em 1.2em; }
.btn.large { font-size: 1.25rem; } /* padding grows with it automatically */
rem#
rem is relative to the root element's font size (html), so it never compounds. The browser default is 16px, so 1rem is 16px unless the user changed it.
That last part matters. People with low vision raise their browser's default font size, and rem units respect it. px units for text do not scale with that setting in most browsers.
Do not set html { font-size: 10px } to make math easier. It overrides the user's preference. If you want easy math, keep the root alone and use a custom property for your scale (Module 8).
Viewport units#
vwandvhare 1% of the viewport width and height.vminandvmaxare 1% of the smaller or larger dimension.svh,lvhanddvhare the small, large and dynamic viewport height. On phones the address bar slides away while scrolling, and100vhis based on the largest case. A box withheight: 100vhends up taller than the visible screen at first.100dvhfollows the real visible height.
.hero { min-height: 100dvh; }
Another trap: 100vw includes the width of the vertical scrollbar, so width: 100vw on a page that scrolls makes a horizontal scrollbar appear. Use 100% for widths unless you truly need the viewport.
Others you will see#
chis the width of the "0" character, which makesmax-width: 65cha good way to limit line length.lhis one line-height, andfris a fraction of free grid space (Module 5).
Common mistakes#
- Using
pxfor font sizes everywhere. It ignores user preferences. Userem. - Nesting
emfont sizes and wondering why the text keeps growing. height: 100%with no parent height, then deciding CSS is broken.100vwcausing a horizontal scrollbar.100vhon mobile hiding content under the browser toolbar.- Choosing colors by eye and shipping text with weak contrast.
Practice#
- Build three nested
divs, each withfont-size: 1.2em. Predict the computed size of the innermost one, then verify it in the Computed tab. Repeat withremand compare. - Make a button using
empadding. Create.small,.normaland.largevariants by changing onlyfont-size. - Pick a brand color in HSL and generate a hover and active state by changing only lightness. Check the contrast ratio of white text on each.
- Create a full-screen hero with
100vh, then change it to100dvh. Open it in your browser's device toolbar in mobile mode and describe any difference.