Skip to content
academia.sh

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 media attribute. 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 style attribute (inline), a style element in the head (internal), and a separate file called with a link element (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 media attribute 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.

Start typing to search.

↑↓ Esc navigate · open · close