018: What CSS Is
Learning outcomes
By the end of this lesson, you can:
- explain how HTML supplies meaning and CSS supplies presentation;
- link one external stylesheet to an HTML document;
- identify a rule, selector, declaration block, property, and value;
- write simple type and class rules and verify that they apply; and
- use browser DevTools to distinguish a loading problem from an invalid declaration.
Prerequisites and retrieval
You need the 013 portfolio, a code editor, and a browser. First refresh your memory of the <head>, <body>, heading hierarchy, links, and sections in the HTML. Then ask yourself what would still communicate meaning if every visual style vanished. That boundary is the practical difference between HTML and CSS.
Terminology
- CSS: “CSS (Cascading Style Sheets) is the language used to describe the presentation of a document written in HTML.” — Source: MDN: What is CSS?
- Stylesheet: A collection of CSS rules, usually delivered as an external .css file via link. — Source: CSSWG: CSS Syntax Level 3
- Rule: A selector followed by its declaration block. — Source: CSSWG: Style rules
- Selector: A pattern that matches elements to which declarations apply. — Source: CSSWG: Selectors Level 4
- Declaration: A property/value pair such as color: navy, ending with a semicolon inside a block. — Source: CSSWG: Declarations
- Property: The stylistic feature being set (color, margin, font-size). — Source: CSSWG: Declarations
- Value: The setting assigned to a property; may be a keyword, number, function result, or list. — Source: CSSWG: Declarations
- User agent stylesheet: The browser’s built-in stylesheet supplying readable defaults before author CSS loads. — Source: CSSWG: Cascading and Inheritance Level 5
- DOM: The browser’s live tree representation of the document that selectors match against. — Source: MDN: Document Object Model
- Declaration block (official): "A declaration block is a (possibly empty) sequence of declarations and at-rules." — Source: CSS Syntax Module Level 3: Declaration blocks
- At-rule: "An at-rule starts with @, such as @media or @import." — Source: CSS Syntax Module Level 3: At-rules
Mental model: content, instructions, rendering
Think of HTML as a labeled document. A heading is still a heading whether it is large, small, blue, or completely unstyled. CSS is a separate set of presentation instructions: find elements matching a pattern, then try to apply these declarations. The browser parses the HTML into the DOM, downloads linked CSS, matches rules to elements, resolves conflicts, and paints the result.
CSS is fault tolerant. When one declaration is invalid, the browser normally ignores that declaration and carries on. That behavior helps the web keep working as it changes, but it also lets a typo fail silently. Inspecting the result with DevTools is part of writing CSS, not merely something to try after everything goes wrong.
Consider:
.intro {
color: rgb(31 41 55);
background-color: rgb(239 246 255);
}
.intro is the selector. The content between { } is the declaration block. color and background-color are properties, and the rgb(...) expressions are their values. Each declaration ends with a semicolon. Modern rgb() notation separates its channels with spaces; later, you will use / when adding alpha.
An external stylesheet is usually the sensible default: one file can serve several pages, the HTML remains easier to read, and the browser can cache the CSS. Inline style attributes combine content with presentation and become difficult to maintain. A <style> element is fine for a small isolated demonstration, but this portfolio will use an external file.
Beginner example: connect the portfolio
Create styles.css next to index.html. If your 013 file is different, use this reduced portfolio as a reference:
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Asha Rao | Portfolio</title>
<link rel="stylesheet" href="styles.css">
</head>
<body>
<header>
<h1>Asha Rao</h1>
<p class="intro">Building clear, useful websites with semantic HTML.</p>
</header>
<main>
<section aria-labelledby="projects-title">
<h2 id="projects-title">Projects</h2>
<article class="project">
<h3>Local library page</h3>
<p>A semantic information page made with HTML.</p>
<a href="https://example.com">View project</a>
</article>
</section>
</main>
<footer><p>Contact: <a href="mailto:asha@example.com">asha@example.com</a></p></footer>
</body>
</html>
Put this in styles.css:
body {
color: rgb(31 41 55);
background-color: rgb(248 250 252);
font-family: system-ui, sans-serif;
}
h1,
h2,
h3 {
color: rgb(30 64 175);
}
.intro {
font-size: 1.125rem;
}
.project {
background-color: white;
border: 1px solid rgb(203 213 225);
padding: 1rem;
}
a {
color: rgb(29 78 216);
}
Save both files and reload the page. Make one change at a time so the cause of each result stays clear. body matches the body element, and its text color is inherited by much of the page. The heading list uses one shared declaration block. .intro matches the element whose class contains intro. .project makes a visible panel without trying to arrange the page layout.
Change the heading color, save, and reload. Add a second .project article and see that the existing class rule styles it too. Then temporarily change href="styles.css" to href="missing.css"; the browser falls back to its defaults. Restore the original path. You have now separated two stages that are easy to conflate: loading the stylesheet and applying its rules.
Intermediate example: separate broad defaults from component styles
A small stylesheet is easier to maintain when broad defaults establish the page baseline and reusable component rules handle local details:
html {
box-sizing: border-box;
}
*,
*::before,
*::after {
box-sizing: inherit;
}
body {
margin: 0;
color: rgb(30 41 59);
background: rgb(248 250 252);
font-family: system-ui, sans-serif;
line-height: 1.6;
}
header,
main,
footer {
padding: 1rem;
}
.project {
margin-block: 1rem;
padding: 1rem;
border: 1px solid rgb(203 213 225);
border-radius: 0.5rem;
background: white;
}
.project a {
font-weight: 700;
text-underline-offset: 0.2em;
}
.project a:focus-visible {
outline: 3px solid rgb(245 158 11);
outline-offset: 3px;
}
The box-sizing setup makes future sizing more predictable because declared widths include padding and borders. margin-block follows the writing mode’s block direction instead of hard-coding top and bottom. The descendant selector confines bold links to projects, and :focus-visible supplies a clear keyboard focus indicator. You do not need to memorize these declarations yet. For now, practice identifying each selector, property, and value.
Optional advanced example: one stylesheet, two pages
Add about.html with the same <link> and a class on its body:
<body class="about-page">
<main><h1>About Asha</h1><p class="intro">I learn by building.</p></main>
</body>
Then add this rule:
.about-page .intro {
border-inline-start: 0.25rem solid rgb(30 64 175);
padding-inline-start: 1rem;
}
The shared defaults work on both pages. The new rule is narrower: it reaches only an .intro inside .about-page. That is reuse with a page-specific variation, without copying styles into every HTML file.
Mistakes, debugging, and DevTools
- Wrong path:
hrefis relative to the HTML file. Use the Network panel; a404means the stylesheet was not found. - Wrong element:
<link>belongs in<head>, usesrel="stylesheet", and has no closing tag. - HTML syntax in CSS: write
.intro, notclass="intro". - Missing punctuation: check braces, the colon between property and value, and semicolons between declarations.
- Unsupported or misspelled value: DevTools crosses out or warns about invalid declarations.
- Stale page: save files, reload, and check that DevTools Sources shows the current CSS.
- Editing the wrong rule: inspect the element, then use the Styles pane to see matched rules. Toggle a declaration’s checkbox and watch the page.
In DevTools, select the <h1>. The Styles pane shows both author rules and browser defaults, while the Computed pane shows the final color. If your rule is missing entirely, either the stylesheet did not load or the selector did not match. If the rule is present but crossed out, another declaration won; lesson 020 explains why.
Rendering diagnosis: layout, paint, and compositing
Not every style change makes the browser do the same amount of work. A geometry change such as width, margin, or font size can invalidate layout for the element and possibly its neighbors; the browser may then repaint affected pixels. A color or shadow often needs paint without changing geometry. A transform or opacity animation can sometimes run through a compositor layer without repeated layout, but layer promotion is an optimization, not a guarantee. “Reflow” and “repaint” are useful shorthand in interviews, not evidence that every CSS rule is slow.
When you investigate jank, reproduce the interaction and record a Performance trace. Inspect long tasks and layout/paint events. Check whether a script changes styles and immediately reads geometry, forcing synchronous layout; whether a large region is repainting; and whether image decoding or JavaScript is the real cost. For an optional movement animation, prefer transform, measure the result, and honor prefers-reduced-motion. Do not use transforms to conceal an overflow or source-order defect.
Accessibility and performance
CSS should enhance semantic HTML, not stand in for it. Do not select heading elements because their defaults happen to be the right size; keep the logical HTML hierarchy and style the headings instead. Keep link underlines unless another persistent visual cue identifies links, and never remove focus outlines without a strong replacement. Text and background colors need adequate contrast: WCAG 2.2 requires at least 4.5:1 for normal text and 3:1 for large text.
External CSS can be cached across pages. Keep the <link> in <head> so the browser discovers the styling early. Avoid using large background images for decoration when a color or gradient is sufficient, and do not break a tiny site into many blocking stylesheets. These architectural choices generally matter more than micro-optimizing selector syntax.
Deep dive: CSS syntax, parsing, and the three ways to attach styles
A CSS rule is more precise than the shorthand “selector plus styles” suggests:
.card {
color: #1f2937;
background-color: white;
padding: 1rem;
}
Read the rule one layer at a time:
.cardis the selector. It decides which elements are candidates.{ ... }is the declaration block.color: #1f2937is one declaration.coloris the property.#1f2937is the value.
A declaration normally ends in a semicolon. The final semicolon in a block is technically optional, but retaining it prevents a small edit from breaking the previous declaration when you append another one.
Browsers are built to recover from CSS they do not understand. If one declaration is invalid, the browser usually ignores that declaration and continues parsing the later ones:
.card {
color: navy;
invented-property: 42magic; /* ignored */
padding: 1rem; /* still applied */
}
That recovery is useful, but it can also conceal an error. DevTools is consequently part of normal CSS authoring, not an optional tool reserved for emergencies.
External CSS: the normal project choice
<head>
<link rel="stylesheet" href="styles.css">
</head>
/* styles.css */
body {
font-family: system-ui, sans-serif;
}
External stylesheets can be shared across pages, cached by the browser, and maintained in one place.
Internal CSS: useful for isolated documents and experiments
<head>
<style>
.notice {
border-inline-start: 4px solid royalblue;
padding: 1rem;
}
</style>
</head>
Internal CSS lives in a <style> element, normally inside <head>, and affects only that document.
Inline CSS: highest maintenance cost
<p style="color: crimson; font-weight: 700;">Important</p>
Inline styles have legitimate uses for generated one-off values, email HTML, and tightly constrained integrations. They also mix content with presentation, make reuse difficult, and are harder to override. Their place in the cascade differs from ordinary selector rules, a distinction that becomes important in 020.
Comments
CSS comments use /* ... */:
/* Component: pricing card */
.price-card {
padding: 1.5rem;
}
Do not write // comments in plain CSS. Some preprocessors support that syntax, but a browser parsing regular CSS does not.
Worked example: diagnose a stylesheet that “does not work”
Suppose the HTML is:
<link rel="stylesheet" href="./styles/site.css">
<article class="profile-card">
<h2>Ravi Kumar</h2>
<p>Frontend engineer</p>
</article>
and styles/site.css contains:
.profile-card {
background: #ffffff;
padding 1.5rem;
border: 1px solid #d1d5db;
}
.profile-card h2 {
colour: #111827;
}
Two declarations are invalid:
padding 1.5remis missing:.colouris not the CSS property name; the standard property iscolor.
Correct version:
.profile-card {
background: #ffffff;
padding: 1.5rem;
border: 1px solid #d1d5db;
}
.profile-card h2 {
color: #111827;
}
A reliable debugging sequence is:
- Confirm the stylesheet request succeeds in the Network panel.
- Inspect the element.
- Check whether the rule appears in the Styles panel.
- Look for crossed-out declarations, warning icons, or invalid values.
- Check the Computed panel to find the final value.
- Only then change code.
If the complete rule is absent from DevTools, investigate the file path, selector matching, and whether the CSS file loaded. If the rule exists but one declaration is crossed out, investigate cascade and specificity. If the declaration is present but nothing visible changes, investigate layout, inherited values, and whether you are styling the box you think you are styling.
Worked example: same HTML, three styling approaches
Start with:
<button class="save-button">Save profile</button>
External:
.save-button {
padding: 0.75rem 1rem;
border: 0;
border-radius: 0.5rem;
background: #2563eb;
color: white;
}
Internal:
<style>
.save-button {
padding: 0.75rem 1rem;
background: #2563eb;
color: white;
}
</style>
Inline:
<button
class="save-button"
style="padding: .75rem 1rem; background: #2563eb; color: white"
>
Save profile
</button>
All three approaches can produce a similar button. The meaningful difference is architectural. External CSS gives .save-button a reusable meaning, whereas inline CSS repeats the presentation each time another button appears.
Deeper mental model: specified, computed, used, and actual values
When you write:
.card {
width: 60%;
color: inherit;
}
the browser does not turn those tokens straight into pixels and RGB values. Conceptually, it resolves them through several stages:
- specified value: what the cascade selected;
- computed value: after inheritance and relative-value processing that can happen at that stage;
- used value: after layout knows dimensions and context;
- actual value: what the device can finally render.
That model explains why DevTools may show width: 60% for a declaration while the Layout panel reports a pixel width. CSS describes constraints, and the surrounding document and viewport help determine the final result.
Tiered exercises
Before starting the exercise, do one last retrieval check. Close DevTools, choose a rule, and state its selector and every declaration. Reopen DevTools and test your prediction. Predicting before inspecting turns browser tools into evidence instead of a place for random edits.
Foundation: Link portfolio.css to the 013 portfolio. Set a readable system font, text color, page background, and distinct heading color.
Core: Add .project to at least two articles and style both with one rule. Style links and provide a visible :focus-visible outline.
Stretch: Add a second HTML page that reuses the stylesheet. Give its <body> a page class and add one page-specific rule without inline styles.
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Portfolio</title>
<link rel="stylesheet" href="portfolio.css">
</head>
html { box-sizing: border-box; }
*, *::before, *::after { box-sizing: inherit; }
body {
margin: 0;
color: rgb(30 41 59);
background-color: rgb(248 250 252);
font-family: system-ui, sans-serif;
line-height: 1.6;
}
header, main, footer { padding: 1rem; }
h1, h2, h3 { color: rgb(30 64 175); }
.project {
margin-block: 1rem;
padding: 1rem;
border: 1px solid rgb(203 213 225);
background-color: white;
}
a { color: rgb(29 78 216); text-underline-offset: 0.2em; }
a:focus-visible { outline: 3px solid rgb(245 158 11); outline-offset: 3px; }
.about-page .intro { border-inline-start: 4px solid rgb(30 64 175); padding-inline-start: 1rem; }
Recap and exit questions
CSS matches rules to HTML and changes presentation while HTML keeps its meaning. External stylesheets separate those concerns and make reuse possible. A rule consists of a selector and declarations; each declaration pairs a property with a value.
- What is the difference between a selector and a property?
- Why can an unstyled HTML page still be readable?
- What three things would you check if no CSS appears?
- Why is an external stylesheet preferable for a multi-page portfolio?
- Can you explain every part of
.project { padding: 1rem; }? - Which changes are likely to affect geometry, and how would you prove the cause of a slow interaction?
