Lesson 02 / 26
Linking the Stylesheet
Attaching a set of styles to a document in three ways — inline, internal, and external — and how these three ways differ in request count, reuse, and conflict resolution.
Contents
The previous lesson established that a rule consists of a selector and a declaration block, and wrote the station page’s first three rules. Rules have to live somewhere, and where they live determines both maintenance cost and when the page’s first render begins.
This lesson defines the three ways of attaching a stylesheet, shows the differences between them with a measurable example, and revisits, from the stylesheet’s side, the render-blocking behavior established in the Web Fundamentals and HTML course’s resource-loading-order lesson.
Three Ways
Inline style is written into an element’s style attribute. There is no selector: the
declarations attach directly to that one element.
<h1 style="color: #8a1c1c; font-size: 2rem">North Slope Measurement Station</h1>
Internal style sheet is written inside a style element in the document’s head. The
rules are fully formed; the selectors only look at that one document.
<head> <meta charset="utf-8"> <title>North Slope Measurement Station</title> <style> h1 { font-size: 2rem; } </style> </head>
External style sheet is a separate .css file attached to the document with a link
element. rel="stylesheet" states the kind of the link; href gives the resource’s
address.
<link rel="stylesheet" href="/station.css">
There is a fourth way: writing @import url("…") inside a style file to pull in another
file. This notation works, but it introduces a sequential dependency: the address of the
imported file cannot be known until the file that calls it has been downloaded and parsed.
This is why an @import chain finishes later than the same number of link elements
would.
The External Style Is a Separate Resource
That an external style file is a resource separate from the document is measurable. The following server serves two addresses separately and logs every request it receives.
// style-server.mjs — serves two resources separately and logs every request received import { createServer } from "node:http"; const document = `<!doctype html> <html lang="en"> <head> <meta charset="utf-8"> <title>North Slope Measurement Station</title> <link rel="stylesheet" href="/station.css"> <style>h1 { font-size: 2rem }</style> </head> <body> <h1 style="color: #8a1c1c">North Slope Measurement Station</h1> </body> </html> `; const style = `body { font-family: system-ui; line-height: 1.5 } h1 { color: #143a52 } `; const server = createServer((request, response) => { console.log(`request: ${request.method} ${request.url}`); if (request.url === "/station.css") { response.writeHead(200, { "content-type": "text/css; charset=utf-8" }); response.end(style); } else { response.writeHead(200, { "content-type": "text/html; charset=utf-8" }); response.end(document); } }); server.listen(8123, "127.0.0.1", () => { console.log("listening: 127.0.0.1:8123"); });
The server is started in the background and the two addresses are requested separately:
node style-server.mjs > server.log 2>&1 & sleep 1 curl -s -o /dev/null -w "%{http_code} %{content_type}\n" http://127.0.0.1:8123/ curl -s -o /dev/null -w "%{http_code} %{content_type}\n" http://127.0.0.1:8123/station.css cat server.log
200 text/html; charset=utf-8 200 text/css; charset=utf-8 listening: 127.0.0.1:8123 request: GET / request: GET /station.css
The two lines in the log show that the external style file does not arrive with the document: a second request is made. This has three consequences.
First, the file is reusable. If ten pages attach to the same station.css file, the
file is downloaded once; later pages read it from cache. The conditional-request and
time-to-live concepts defined in the How the Internet Works course apply here.
Second, the file can change independently of the document. The document’s bytes can stay unchanged while the style is updated, and the reverse is also true.
Third, the second request is a cost. The link element found while the document is
parsed may require a new connection, and that delays the start of the first render.
Rendering Does Not Start Until the Style Resolves
The Web Fundamentals and HTML course’s resource-loading-order lesson described how scripts block parsing. A style file does not block parsing — the document tree keeps being built — but it blocks rendering.
The reason becomes visible when looking at what not blocking would cause. If rendering happened before the style resolved, the page would first render with the default style, then render again once the file arrived. The user would see two different pages. This is why the defined behavior is to wait until the style resolves.
This rule produces three practical consequences:
- The style file is attached in the document’s head. If it is placed at the end of the body, rendering still waits, but the wait starts later; the total time grows longer.
- The file is kept small; rules unused on a given page delay that page’s rendering.
- If a link is only needed under a specific condition, it is conditioned with the
mediaattribute. A link whose condition is not met still has its file downloaded, but it does not block rendering:
<link rel="stylesheet" href="/station.css"> <link rel="stylesheet" href="/print.css" media="print">
The second file carries rules written for print output; it does not wait for the start of screen rendering.
Comparing the Three Ways
| Criterion | Inline | Internal | External |
|---|---|---|---|
| Selector can be written | No | Yes | Yes |
| Usable across multiple documents | No | No | Yes |
| Separate request | No | No | Yes |
| Cacheable | With the document | With the document | Independently |
| Priority in a conflict | Highest | By source order | By source order |
| Pseudo-class can be written | No | Yes | Yes |
Two rows in the table call for further explanation.
There is no selector in inline style, so a pseudo-class cannot be written either. A
declaration that should change when an element is hovered over or focused cannot be
written into the style attribute; that requires a rule.
Inline style has the highest priority. The specificity calculation covered in a later section does not cover inline declarations; they sit above the calculation, outside of it. This makes it effectively impossible to override an inline declaration with a rule through ordinary means. The result is that inline style is the most expensive way to maintain, and it is justified only in cases where the value is computed while the document is being produced.
Relative Addresses Resolve Against the File Itself
A commonly overlooked consequence of an external style file being a separate resource is that the relative addresses inside that file resolve not against the document, but against the style file’s own address.
Suppose the document is at /products/station.html and the style file is at
/styles/station.css. If background-image: url("pattern.png") is written in the style
file, the address requested is /styles/pattern.png — not /products/pattern.png. The
same notation written in the document’s style attribute would resolve against the
document, requesting /products/pattern.png.
This distinction is exactly what makes a style file reusable: the file refers to assets next to it through a fixed path and is unaffected by which document called it. Keeping image files next to the style file is, for this reason, more than a matter of organizational preference.
Attaching the Station Page
The course center uses a single external file. The document’s head becomes:
<head> <meta charset="utf-8"> <meta name="viewport" content="width=device-width, initial-scale=1"> <title>North Slope Measurement Station</title> <link rel="stylesheet" href="/station.css"> </head>
The station.css file starts with the previous lesson’s three rules and grows through the
course. Attaching to the document from a single point means that changing a rule in later
lessons can be done without touching the document — this is the file-level counterpart of
the structure-presentation distinction.
Summary
- A set of styles attaches to a document in three ways: an element’s
styleattribute (inline), astyleelement in the head (internal), and a separate file called with alinkelement (external). - An external style file is a separate HTTP request; it is independently cacheable and reusable across multiple documents, but it comes with a request cost.
- A style file does not block parsing, it blocks rendering; this is a behavior defined to keep the page from appearing twice, in two different states.
- A link conditioned with the
mediaattribute still has its file downloaded, but it does not block rendering if its condition is not met. - Inline style has no selector, so a status style cannot be written with it; its priority also sits above the specificity calculation, which makes it hard to override.
Next Step
Rules now reach the document, but which elements they reach is still limited to a single
example. Writing h1 selects every top-level heading; what if only the missing values in
the measurement table need to be selected? The next lesson defines the four basic kinds of
selector — type, class, id, and universal — and measures, with a runnable matcher, which
set each one returns from the document tree.
To keep your progress and take notes, Log in
My notes
Log in to take notes.