Files, folders and paths for CSS
Your stylesheet is perfect. Every selector is right, every brace is closed, and the page renders as plain black text on white as though the file did not exist.
It probably does not — not at the address you gave. A stylesheet that never arrives fails in complete silence: no console error you would notice, no warning, no half-styled page. Just the browser's own defaults, which look exactly like "my CSS is broken".
This lesson is about the twenty minutes that costs, and how to make it thirty seconds.
The link element
<head>
<link rel="stylesheet" href="styles/main.css">
</head>
Three parts, and each of them is a place people go wrong.
<link>goes in<head>, not<body>. Putting it in the body works in most browsers, which is worse than it failing, because it works until it does not.rel="stylesheet"tells the browser what the file is. Leave it out and the file is fetched and then ignored — a 200 in the Network tab and no styling on screen, which is the single most confusing version of this bug.hrefis the path, and it follows exactly the rules in the HTML paths lesson.
<link> is a void element: no closing tag, and nothing goes between anything.
You will also see the older type="text/css". It has been the default since
HTML5 and is no longer needed.
Relative paths are resolved from the HTML file
This is the sentence that fixes most of these bugs, so it is worth reading
twice: the path in href is resolved from the location of the HTML page, not
from where the CSS file lives, and not from the top of your site.
Take this folder:
site/
index.html
styles/
main.css
about/
team.html
From index.html, the stylesheet is one folder down:
<link rel="stylesheet" href="styles/main.css">
From about/team.html, that same path asks for site/about/styles/main.css,
which does not exist. You have to climb out of about first:
<link rel="stylesheet" href="../styles/main.css">
Two pages, two different paths, one stylesheet. That is the part that feels wrong and is simply how relative paths work.
Root-relative paths stop the counting
A path starting with a single / is resolved from the root of the site,
wherever the page happens to sit:
<link rel="stylesheet" href="/styles/main.css">
That one line is correct in index.html, in about/team.html, and in a page
five folders deep. For a stylesheet every page shares, that is obviously the
better answer — you never recount ../ again, and moving a page does not break
its styling.
The catch is the one that sends people in circles. Root-relative paths need a
web server. Double-click an HTML file and the address is
file:///D:/Projects/site/about/team.html; the browser resolves
/styles/main.css against the root of your hard disk and asks for
D:/styles/main.css. Nothing is there.
So the symptom is backwards from what you expect: the path that is more
correct fails locally and works once deployed, while ../styles/main.css works
in both. If a stylesheet loads on the live site and not on your machine, this
is almost always why.
The fix is to stop double-clicking. Serve the folder:
python -m http.server
Then open http://localhost:8000/ instead. Most editors have a one-click
equivalent. Once you are on http://, root-relative paths behave the same
locally as they do in production, and you can use them everywhere.
Reading the Network tab
Stop guessing. When styling does not apply, open developer tools, go to Network, tick Disable cache, and reload.
Find your CSS file in the list. There are only a few things it can be telling you:
- 404, red. The file is not where you said. Look at the URL the browser
actually requested — it is written right there, and it is the truth. If it
contains a folder name twice (
site/about/styles/...), your relative path needed a../. If it points at your hard disk root, you have a root-relative path and no server. - 200, but nothing applied. The file arrived. Either
rel="stylesheet"is missing, or the CSS itself is not matching — a selector problem, not a path problem, and combinators is where to go. - Not in the list at all. The
<link>is not in the HTML the browser received. Check you saved the file, and check View Source rather than the Elements panel. - 200 (from disk cache), and your changes are missing. The browser is serving an old copy. That is the next section.
Thirty seconds in the Network tab beats twenty minutes of re-reading a selector that was never the problem.
The capital letters that only break in production
This one ships. It passes every test you run, and fails for everybody else.
Your file is called Main.css. Your HTML says main.css. On Windows and on
macOS this works, because both use case-insensitive file systems by default —
ask for main.css and you are handed Main.css, because as far as those
systems are concerned it is the same name.
Almost every web server runs Linux, and Linux file systems are
case-sensitive. Main.css and main.css are two different files, in
exactly the way Ali and ali are two different words. The server looks for
main.css, finds nothing, returns 404, and your live site renders unstyled
while your local copy looks perfect.
It is worse than the equivalent image bug, because a missing image leaves a broken icon behind and a missing stylesheet leaves a page that just looks like bad CSS.
The habit, not a setting:
- Lower case everything — file names, folder names, extensions.
- Hyphens, never spaces.
main-nav.css, notmain nav.css. A space in a URL becomes%20, which is legal and a nuisance. - Copy the file name, do not type it. Every one of these bugs was typed from memory by somebody who was certain.
Git makes this harder to spot, not easier: rename Main.css to main.css on
Windows and Git often does not register a change at all, because it is asking
the same case-insensitive file system whether anything is different. You
commit, you deploy, and the server still has the old name.
When the browser serves you an old file
You change a colour, reload, and nothing happens. You change it again. Still nothing.
Browsers cache stylesheets aggressively — that is the point of them — and a hard reload does not always clear it. While developing, tick Disable cache in the Network tab and leave devtools open; that setting only applies while they are.
For a live site, the fix is to change the URL when the file changes:
<link rel="stylesheet" href="/styles/main.css?v=7">
The query string is ignored by the server and treated as a different address by
the cache, so bumping the number forces a fresh download. Real projects
automate this — the file ends up named main.a3f9c1.css, with the hash
regenerated whenever the contents change — but a hand-bumped ?v= is perfectly
adequate for a small site.
If a colleague reports the old design and you see the new one, this is nearly always the answer.
More than one stylesheet
You may link several, and order matters — later files win ties, exactly as later rules do within one file:
<link rel="stylesheet" href="/styles/reset.css">
<link rel="stylesheet" href="/styles/main.css">
<link rel="stylesheet" href="/styles/print.css" media="print">
reset.css first, so your own rules override it. media="print" restricts a
sheet to printing, and the browser still downloads it but gives it low
priority.
@import inside a CSS file does the same job and is worth avoiding: an
imported file cannot start downloading until the file importing it has arrived,
so imports load in series where <link> elements load in parallel.
A checklist for "my CSS is not working"
In the order that finds it fastest:
- Network tab. Is the file 404ing? Read the URL it requested.
- Is
rel="stylesheet"there? A 200 with no styling is nearly always this. - Is the
<link>in<head>of the page you are actually looking at? - Case. Compare the file name on disk to the
href, character by character. - Cache. Tick Disable cache and reload.
- Only then is it your CSS — and at that point, combinators and the syntax lesson cover the two things that go wrong inside the file.
Steps one to five take under a minute between them, and they catch the large majority of these.
That closes the first section. Next: colour, where the values you write start mattering as much as the selectors.
Prefer learning with a teacher?
WasiLearn Academy runs small-group classes covering this material, online and in Karachi.
See the classes