Linking CSS and JavaScript
Introduction
HTML describes structure; CSS styles it and JavaScript adds behavior. This chapter shows how to link external stylesheets and scripts, when to use inline or embedded code, and why separating concerns keeps projects maintainable. You will also see why heavy use of HTML event attributes is discouraged.
Prerequisites
- HTML document structure
- A practice folder with
index.html - Optional: basic awareness of the JavaScript track
Three Ways to Add CSS
| Method | Where | Best for |
|---|---|---|
| External file | <link rel="stylesheet"> | Real projects |
| Internal | <style> in <head> | Small demos, email-like constraints |
| Inline | style attribute on one element | Rare exceptions, quick prototypes |
External CSS (Recommended)
<head>
<meta charset="UTF-8">
<title>Styled Page</title>
<link rel="stylesheet" href="css/styles.css">
</head>Project layout:
project/
├── index.html
└── css/
└── styles.cssstyles.css:
body {
font-family: system-ui, sans-serif;
line-height: 1.6;
margin: 2rem;
}
h1 {
color: #1e293b;
}Benefits:
- one file styles many pages
- browser caches CSS across pages
- HTML stays readable
- designers and developers can work on CSS independently
Internal CSS
<head>
<style>
main {
max-width: 40rem;
}
</style>
</head>Fine for tiny examples. Avoid large blocks of CSS in HTML for production sites.
Inline Styles
<p style="color: #dc2626;">This paragraph is red.</p>Hard to maintain and cannot reuse rules. Use sparingly—for example, email HTML where external stylesheets are limited.
Tip
Structure in HTML, Presentation in CSS
Choose elements for meaning (article, nav, button). Use CSS for layout, color, and typography.
class and id for Styling Hooks
HTML provides hooks CSS (and JavaScript) can target:
<article class="card featured">
<h2 id="pricing">Pricing</h2>
<p class="muted">Plans for every team.</p>
</article>| Attribute | Purpose |
|---|---|
class | Reusable group (many elements) |
id | Unique identifier (one per page) |
CSS examples (preview only):
.card {
border: 1px solid #e2e8f0;
padding: 1rem;
}
.featured {
border-color: #2563eb;
}
#pricing {
scroll-margin-top: 2rem;
}Do not choose heading levels for size—use classes and CSS instead.
Linking JavaScript
External Script (Recommended)
<script src="js/app.js" defer></script>Place in <head> with defer, or before </body> without defer.
js/app.js:
const button = document.querySelector("#greet");
button.addEventListener("click", () => {
alert("Hello from JavaScript");
});HTML:
<button type="button" id="greet">Say hello</button>
<script src="js/app.js" defer></script>Internal Script
<script>
console.log("Page loaded");
</script>Use for very small demos. Prefer external files for real apps.
defer vs async
<script src="js/analytics.js" async></script>
<script src="js/app.js" defer></script>| Attribute | Download | Execute | Order |
|---|---|---|---|
| (none) | Blocks parsing | Immediately | Blocking |
defer | Parallel | After HTML parsed | Preserved |
async | Parallel | As soon as ready | Not guaranteed |
Rules of thumb:
- App logic that needs the DOM:
defer - Independent scripts (ads, analytics):
asyncif order does not matter - Avoid blocking scripts in
<head>withoutdefer/async
<head>
<link rel="stylesheet" href="css/styles.css">
<script src="js/app.js" defer></script>
</head>Script Placement: Head vs End of Body
With defer in head — modern default:
<head>
<script src="js/app.js" defer></script>
</head>
<body>...</body>Without defer — classic pattern at end of body:
<body>
...
<script src="js/app.js"></script>
</body>Both work when the DOM exists before the script runs. Pick one convention per project.
Avoid Heavy HTML Event Attributes
This works:
<button type="button" onclick="alert('Hi')">Click</button>Problems at scale:
- mixes behavior into markup
- hard to test and maintain
- Content Security Policy may block inline handlers
- one function name per attribute
Prefer:
<button type="button" id="save">Save</button>
<script src="js/app.js" defer></script>document.querySelector("#save").addEventListener("click", () => {
// save logic
});Keep HTML declarative; keep behavior in JS files.
DOM Concept (Bridge to JavaScript)
The browser parses HTML into a DOM tree. JavaScript can query and change it:
<p id="status">Waiting...</p>
<button type="button" id="load">Load data</button>const status = document.querySelector("#status");
const loadBtn = document.querySelector("#load");
loadBtn.addEventListener("click", () => {
status.textContent = "Loaded.";
});HTML provides the elements; JavaScript responds to events. Full DOM coverage lives in DOM manipulation in JavaScript.
Module Scripts (Awareness)
Modern projects may use ES modules:
<script type="module" src="js/app.js"></script>Modules are deferred by default. They use import/export. Static HTML learning often starts with classic scripts; frameworks add bundlers later.
Putting It Together
Mini Exercise
- Create
css/styles.cssand link it fromindex.html. - Style
.cardand oneh1with external CSS only. - Add
js/app.jswithdeferthat logs a message on load. - Wire one button with
addEventListenerinstead ofonclick. - Confirm in DevTools Network that CSS and JS load with 200 status.
FAQ
Can I put CSS in the body?
<link rel="stylesheet"> belongs in <head>. <style> in body is invalid in strict parsing though browsers may recover.
Multiple CSS files?
Yes. Order matters—later rules override earlier ones (same specificity).
<link rel="stylesheet" href="css/base.css">
<link rel="stylesheet" href="css/theme.css">Should I use jQuery in new HTML pages?
Modern sites use vanilla JS or frameworks. This course uses plain JS for clarity.
Does defer work on inline scripts?
No. defer applies to external src scripts.
What comes next?
Data attributes — store custom metadata on elements for CSS and JavaScript.