Skip to content

Stylesheet

the file that holds a page's CSS

What it is

A stylesheet, spelled style sheet in the specifications and often just called the CSS file, is one collection of CSS rules that a browser fetches, parses and applies to a document as a single unit.

A page can receive stylesheets in four ways. A link element with rel="stylesheet" points at a separate file. A style element carries rules inside the HTML itself. An @import rule at the top of one stylesheet pulls in another. And a script can build one with new CSSStyleSheet() and attach it to a document or a shadow root through adoptedStyleSheets. The first two look like this in the head of a page:

<link rel="stylesheet" href="/css/site.css" />
<link rel="stylesheet" href="/css/print.css" media="print" />
<style>
    .banner {
        border-block-end: 2px solid;
    }
</style>

The author’s sheets are not the only ones in play. Every browser ships a default stylesheet, which is why an unstyled heading still comes out large and bold, and a reader can add a user stylesheet of their own; the cascade weighs the three origins against each other. The style attribute on one element is a different thing: declarations with no selector, belonging to that element and to no sheet.

Once loaded, a stylesheet becomes a CSSStyleSheet object in the CSS Object Model, the specification this term’s definition comes from. The object records the element that owns the sheet, the media it applies to, its title, whether it is disabled, and its list of rules, and document.styleSheets lists a page’s sheets in order. One flag exists for privacy: a sheet fetched from another origin without CORS is not origin-clean, and a script that asks for its rules gets a security error instead.

The media attribute limits a sheet to an environment. A sheet marked media="print" is still downloaded, but the browser waits only for sheets whose media matches, so a print sheet does not hold up the screen.

Why it matters

A stylesheet decides what a browser has to fetch before it shows anything. The HTML standard treats a stylesheet link the parser finds as render-blocking whenever its media matches, so the first paint waits until that file has arrived and been parsed, instead of drawing the page unstyled and redrawing it in front of the reader. Its size, its host and the round trips needed to discover it all sit between a click and the first thing a reader sees.

The number of files is the first decision. One bundle shared by every page is fetched once and then served from the cache; a stylesheet per page sends less up front and more as a reader moves around. Rules inlined in a style element cost no extra request, but they travel again with every page, because they cannot be cached apart from the document that carries them. Each @import adds a full round trip, because the browser learns the imported address only after the importing file has arrived.

Caching is the second. A stylesheet cached for a year makes every later page faster, but a change then has to reach readers holding the old copy. The usual answer is a fingerprinted file name, with a hash of the content inside the name, so a changed file is a new address and an old one can stay cached for as long as anyone likes, while the HTML pointing at it is kept short-lived.

Trust is the third. A stylesheet decides what is visible and where; it can hide a warning, restyle a button, or request images from any address its rules name. Loading one from a third party hands that power to the third party, along with a host the page cannot render without. Subresource Integrity lets the page state a cryptographic hash for the file, and the browser refuses any file whose bytes differ.

What it looks like in practice

The site’s own stylesheet bundle shows how far one inlined asset can tip the balance. At 87 KB it was, for a document site with no framework, most of a page’s weight spent before any content arrived, and two thirds of the largest stylesheet turned out to be one base64-encoded font: about 56 KB of text encoding about 42 KB of font. It had been inlined for a first-paint benefit that a preloaded external file gets anyway, and every page paid for it whether the weight was needed or not. Taking it out took that file from 84 KB to 28 and the whole bundle from 87 KB to 31 KB, with the font weights preloaded instead. Base64 writes every three bytes as four characters, so a binary file inlined into a stylesheet always weighs about a third more than the file served on its own.

Paper is a medium a stylesheet has to serve too. The site’s case studies are what someone prints and takes into a meeting, so one 637-line stylesheet with a single page rule and one print block makes them print on unknown paper in both colour themes. The page rule sets a margin and deliberately no paper size, and a measure of about seventy-two characters keeps a landscape sheet from running a line to about a hundred and ten. Four engine behaviours are written down as traps, each having caused a real defect: viewport units measured against the page box by one engine but against the window by another, which cropped a third of every line; fixed-position elements that paint on every sheet; a palette defaulting to white text for the dark theme, which printed four section labels white on white; and a flex or grid box that paged media cannot split, which produced a blank first sheet. A six-part check covers six paper and orientation combinations, both colour-scheme preferences and the real page count, and it was that check which noticed when a stray comment terminator made the CSS linter swallow a whole rule and pass clean twice.

The site’s security page lists Subresource Integrity on the stylesheet: the bundle is fingerprinted and hashed at build time, so a reader’s browser can verify that it arrived unmodified. The same page asks the reader to count the hosts in the browser’s network panel and gives the answer as one, the site’s own.

Where it goes wrong

A stylesheet the browser learns about late arrives late. A link at the end of the body, a sheet injected by a script, or a chain of @import rules each lets the page paint before the styles land, so raw text jumps into a layout, or a finished layout shifts. An import chain looks harmless in the source:

/* site.css: the browser finds base.css only after this file arrives */
@import url("base.css");
@import url("theme.css");

Put stylesheet links in the head and flatten imports into the bundle at build time.

A deploy can pair new markup with old rules. Readers whose cache holds the previous stylesheet get HTML expecting classes the old file has never heard of, and their layout breaks until the cache expires, while anyone testing with a fresh cache sees a working site. The cause is a stylesheet name that never changes combined with a long cache lifetime, and a fingerprinted name removes it.

A stylesheet served with the wrong content type is ignored. The HTML standard applies a linked stylesheet only when it arrives as text/css, with one exception for a same-origin file on a page in quirks mode. A server that labels .css files as text/plain, or answers a missing file with an HTML page and a success status, leaves the page unstyled while the network panel shows a request that apparently worked. Check the header on the deployed file.

An integrity hash goes stale when the file changes after hashing: a hand edit on the server, a CDN that minifies on the way through, or a build that hashes before its last rewrite. The browser treats the mismatch as a network error and renders without the sheet. On another origin, integrity also needs CORS: a crossorigin attribute on the link and a matching header from the server. Compute the hash in the step that writes the final file.

A framework stylesheet on a public CDN used to be justified by readers already having it cached from other sites. The Fetch standard partitions the HTTP cache by top-level site, so each site’s copy is fetched and stored separately, and what remains is a second host on the critical path under someone else’s control. Serving the file from the site’s own host costs the same bytes without the extra connection.

Further reading

Glossary