Skip to content
EzzyWeb

Level 2, Styling & Logic, Chapter 4 of 24

CSS Basics — Selectors, Colors & the Box Model

Why this matters

Every website you admire got its structure from HTML, but its personality from CSS. The fonts, colors, spacing, and layout choices that make a page feel calm, energetic, playful, or professional all come from CSS rules layered on top of plain HTML. Once you can write CSS, the same HTML skeleton you built in earlier chapters can look like a dozen completely different products.

Think about two recipe websites built from the exact same HTML: headings, a list of ingredients, an ordered list of steps, and an image. One could look cramped and gray, the other spacious and colorful, purely because of CSS. In this chapter you will learn how to target the elements you already know how to write with a full range of selectors, control the space around and inside them using the box model, shape their text, and understand why some rules seem to "win" over others.

The lesson

What CSS actually does

CSS stands for Cascading Style Sheets. Its job is to describe how HTML elements should look. HTML gives a page meaning and structure: this is a heading, this is a paragraph, this is a list. CSS gives that structure appearance: what color, what size, what spacing. You already know how to write semantic HTML with headings, paragraphs, lists, links, images, and forms. CSS lets you take that same markup and decide exactly how each piece is presented, without changing what the content means. This separation is powerful because you can redesign a page's entire look by editing CSS alone, leaving the HTML untouched.

CSS rules are made of a selector and a declaration block. The selector says which elements to style, and the declaration block, wrapped in curly braces, lists property-value pairs separated by semicolons. For example, a rule targeting every <p> element might set its color and font size. Selectors can target elements by tag name (like p or h1), by class (using a dot, like .card), or by id (using a hash, like #header). Classes are the most commonly used selector in real projects because you can reuse the same class name on many elements, while an id should be unique to a single element on the page.

Basic selectors and declarations
CSS
1/* tag selector: every paragraph */2p {3  color: #333333;4  font-size: 16px;5}67/* class selector: any element with class="highlight" */8.highlight {9  background-color: yellow;10}1112/* id selector: the one element with id="site-title" */13#site-title {14  text-transform: uppercase;15}
Tag, class, and id selectors each target elements differently. Classes are reusable, ids are unique.

To connect CSS to your HTML, you can add a <link> element inside the <head> that points to a separate .css file, or place rules inside a <style> element in the head for quick experiments. Linking an external stylesheet is the standard approach for real projects because it keeps content and presentation cleanly separated, and lets one stylesheet control many pages at once. You will use this pattern for the rest of the course: one HTML file, one linked CSS file.

Linking a stylesheet
HTML
1<head>2  <meta charset="UTF-8" />3  <title>My Page</title>4  <link rel="stylesheet" href="styles.css" />5</head>
The href attribute points to the CSS file relative to the HTML file.

More ways to select elements

Beyond tags, classes, and ids, CSS gives you several other selectors that come up constantly in real projects. The universal selector * matches every element on the page, which is why it's often used for resets like box-sizing. Attribute selectors, written in square brackets like [type="text"] or [required], target elements based on an HTML attribute they carry, which is especially useful for styling form inputs without adding extra classes. Combinators describe relationships between elements: a space between two selectors means "descendant" (any element nested inside, at any depth), a > means "direct child only" (one level down), and a + means "adjacent sibling" (the element immediately after another, at the same level).

  • * — the universal selector, matches every element.
  • [type="text"] — attribute selector, matches elements with that exact attribute value.
  • [required] — attribute selector with no value, matches any element that has the attribute at all.
  • .card p — descendant combinator, matches any <p> nested anywhere inside .card.
  • .card > p — child combinator, matches only <p> elements that are direct children of .card.
  • h2 + p — adjacent sibling combinator, matches a <p> that comes immediately after an <h2>.
Attribute selectors and combinators together
CSS
1/* every element, useful for resets */2* {3  box-sizing: border-box;4}56/* inputs of type text, styled without extra classes */7input[type="text"] {8  border: 1px solid #999;9}1011/* required fields get a colored left edge */12input[required] {13  border-left: 3px solid #d9480f;14}1516/* only paragraphs directly inside .card, not nested deeper */17.card > p {18  margin-top: 0;19}2021/* a paragraph immediately following a heading */22h2 + p {23  font-weight: bold;24}
Attribute selectors reach into HTML attributes; combinators describe how elements relate to each other.

Shaping text

Beyond color and font-size, several properties shape how text reads on the page. line-height controls the vertical space a line of text occupies; giving it a unitless number like 1.5 scales relative to the element's own font size, which is why unitless values are recommended over fixed pixel heights, they stay proportional if the font size changes. letter-spacing adds or removes space between characters, often used to open up small uppercase headings. text-align controls horizontal alignment (left, center, right, justify). text-decoration adds or removes lines like underlines, commonly used to strip the default underline from links. text-transform changes the casing shown on screen (uppercase, lowercase, capitalize) without altering the actual HTML text. font-style toggles italics.

A full set of text properties
CSS
1.eyebrow {2  text-transform: uppercase;3  letter-spacing: 1.5px;4  font-size: 13px;5  color: #6b7280;6}78.article p {9  line-height: 1.6;10  text-align: left;11}1213.quiet-link {14  text-decoration: none;15  font-style: italic;16}
line-height at 1.6 gives paragraphs comfortable breathing room; text-transform changes display casing only.

The box model

Spacing is controlled by the box model, which treats every HTML element as a rectangular box made of four layers, from the inside out: content, padding, border, and margin.

  • Content: the actual text or image inside the element.
  • Padding: space between the content and the border, still part of the element's background.
  • Border: a visible or invisible line that wraps the padding and content.
  • Margin: space outside the border that pushes other elements away.
The box model in action
CSS
1.card {2  padding: 16px;3  border: 2px solid #cccccc;4  margin: 24px;5  background-color: #f9f9f9;6}
Padding adds breathing room inside the box; margin pushes neighboring elements away from the outside.

Shorthand versus longhand

Many box model properties have a shorthand form that sets all four sides at once, following clock-face order: top, right, bottom, left. Writing margin: 10px 20px is a two-value shorthand meaning 10px top and bottom, 20px left and right. You can also write each side individually with longhand properties like margin-top, margin-right, margin-bottom, and margin-left when only one side needs a different value. The exact same pattern applies to padding and to border, where the shorthand border: 1px solid #333 sets width, style, and color together, while border-bottom lets you style just one edge.

Shorthand vs longhand
CSS
1.banner {2  /* shorthand: top/bottom 10px, left/right 20px */3  margin: 10px 20px;4  padding: 8px 16px 8px 16px; /* top right bottom left */5}67.banner {8  /* longhand: override just one side */9  margin-bottom: 30px;10}1112.divider {13  border-bottom: 1px solid #e5e5e5;14}
Shorthand is faster to write; longhand is useful when only one side needs to differ from the rest.

Making sizing predictable with box-sizing

By default, an element's declared width and height apply only to its content area, so adding padding or a border makes the box visibly bigger than the number you typed. If you set width: 200px and then add padding: 20px and a 2px border, the box actually renders at 244px wide, which quickly becomes confusing when you're trying to line elements up. Setting box-sizing: border-box changes the math so that padding and border are included inside the declared width instead of added on top, meaning a 200px box stays 200px no matter how much padding or border you add. This is so useful that nearly every real project applies it globally with a rule targeting *, *::before, and *::after, so every element on the page, including generated pseudo-elements, gets predictable sizing from the start.

Applying box-sizing globally
CSS
1*,2*::before,3*::after {4  box-sizing: border-box;5}67.button {8  width: 200px;9  padding: 12px;10  border: 1px solid #333;11  /* total rendered width stays 200px */12}
This three-selector rule is one of the most common lines in real-world CSS files.

The cascade and specificity

Sometimes two CSS rules target the same element and disagree about a property, and the browser has to decide which one wins. This decision is called the cascade, and it's based partly on specificity: how precise a selector is. As a rough rule, a tag selector like p is the least specific, a class selector like .highlight is more specific, and an id selector like #header is the most specific of the three. When rules conflict, the more specific selector wins regardless of the order the rules were written in. This explains a very common beginner frustration: a CSS rule that looks correct but "isn't applying" is usually being overridden by a more specific rule elsewhere in the stylesheet.

Specificity deciding a conflict
CSS
1p {2  color: black;3}45.note {6  color: blue;7}89#special {10  color: red;11}1213/* An element with all three: <p class="note" id="special">14   renders red, because the id selector is the most specific. */
When rules conflict, the browser applies the one with higher specificity, not the one written last.

Combining selectors with your HTML

Because you already know how to structure content with headings, lists, links, and forms, you can now apply classes directly to those familiar elements. For example, you could give a <ul> used for navigation a class of nav-list and style just that list differently from a list of ingredients elsewhere on the page. This is the core workflow you will use throughout the rest of the course: write meaningful HTML first, then add classes where you need distinct styling, then write CSS rules that target those classes.

Try it yourself

Style the recipe card below: give the heading a color, add padding and a border to the card, style the ingredients list using an attribute-style approach, and try the descendant combinator on the card's paragraphs.

Live preview

Chapter boss project

Boss Project: Style a Profile Card

Take an HTML profile card containing a heading, a paragraph bio, and a list of skills, and give it a complete visual identity using only CSS you've learned in this chapter.

Your card should have a clear border or background color that separates it from the page, comfortable padding so the text doesn't touch the edges, at least one attribute or combinator selector in use, and thoughtful text styling using properties like line-height and letter-spacing.

Focus on making deliberate choices: pick two or three colors that work together, use shorthand where it's convenient and longhand where you need to override one side, and set box-sizing: border-box globally so padding never surprises you.

  • Start by writing the HTML structure first if you don't already have it, using elements from earlier chapters.
  • Add a class to the outer container so you can target the whole card with one selector.
  • Use a `*, *::before, *::after { box-sizing: border-box; }` reset at the top of your stylesheet.
  • Try a combinator like `.card > p` to target only a direct child paragraph, and compare it to `.card p`.
  • If a rule doesn't seem to apply, check whether a more specific selector elsewhere is winning the cascade.

Level-up checklist

Tick these off once each one is true for you.