Command Palette

Search for a command to run...

Hectal
PHASE 6Intermediate ~22 min· topic 1 of 7

Topic 6.1

The Head in Depth

In one line

The head holds information about the page: how to decode it (charset), how to size it on phones (viewport), where relative links point (base), and which styles and scripts to load, and where you place each of them changes how the page behaves.

Think of it like this

The label on a parcel. Before anyone opens the box, the label says what language the note inside is written in, which way is up, where to send replies and what else is coming in other boxes. The <head> is that label: the browser reads it first, and it shapes how everything inside the box is handled.

Words you'll meet

New words in this topic, in plain English. Come back here whenever one feels fuzzy.

head
The part of the page with information about it. Nothing in it is shown in the page itself.
meta
An element that states one fact about the page, like its character set or description.
Character encoding
The rule for turning stored bytes into letters. UTF-8 is the one to use.
Viewport
The area of the screen where the page is drawn. On a phone it is the whole screen width.
CSS pixel
The unit CSS sizes use. A phone may pack 2 or 3 real screen dots into one CSS pixel.
Relative URL
A link written without a domain, like guide.html, worked out from the current page's address.
base
An element that changes the starting address used for all relative URLs on the page.
Parser
The part of the browser that reads HTML text from top to bottom and builds the page's element tree.
Render-blocking
Something the browser must finish before it can show the page, like a stylesheet in the head.

Step by step

01Charset first

The browser starts decoding bytes immediately. Put the charset at the very top of the head so it is known before any text, including the title.

If the browser finds the charset late, it may have to throw away what it decoded and start again.

index.htmlwhole filehtml
<head>
  <meta charset="utf-8">

02Then the viewport

The viewport line is the same on almost every site. Learn it by heart. Add viewport-fit=cover only if you design for the area under a notch and pad the edges with env(safe-area-inset-*) in CSS.

index.htmlwhole filehtml
<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width, initial-scale=1">

03Title, description and the rest of the facts

Next come the <title> and the <meta name="description"> (Topic 6.2), then social tags (6.3), icons (6.4) and SEO links like canonical (6.7).

<meta name="color-scheme" content="light dark"> tells the browser your page supports both themes, so default form controls and scrollbars follow the user's dark mode.

index.htmlwhole filehtml
  <title>Pancake recipes — Asha's Kitchen</title>
  <meta name="description" content="Fluffy pancakes in 20 minutes, with egg-free and gluten-free versions.">
  <meta name="color-scheme" content="light dark">

04Styles and scripts

Stylesheets go in the head so the first paint is styled. Scripts go in the head with defer, so they download while the page is parsed and run once it is complete.

A plain script without defer pauses the parser right where it stands, as the diagram shows.

index.htmlwhole filehtml
  <link rel="stylesheet" href="/styles.css">
  <script src="/app.js" defer></script>
</head>
Styles and scriptsdiagram
Rendering diagram…

05The order that works

A safe, complete order: charset, viewport, title, description, social and SEO tags, icons, preconnects and preloads, stylesheets, deferred scripts. Only the charset position is a hard rule; the rest is a convention that makes heads easy to read and keeps the important bytes early.

index.htmlwhole filehtml
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <title>Pancake recipes — Asha's Kitchen</title>
  <meta name="description" content="Fluffy pancakes in 20 minutes.">
  <link rel="canonical" href="https://example.com/pancakes">
  <link rel="icon" href="/favicon.ico" sizes="32x32">
  <link rel="stylesheet" href="/styles.css">
  <script src="/app.js" defer></script>
</head>
<body>
  <h1>Pancakes</h1>
</body>
</html>

Try it yourself

  1. 1

    Predict the head list

    In the first example, add <meta name="author" content="Asha"> after the description. Before you press Run, predict which line the console adds and where. Then run it and check.

  2. 2

    Move the script

    In the second example, move the whole <script> block from the head to just before </body>. Predict the first line of the console, then run it. (Adding defer to it would do nothing: defer only works on scripts with a src.)

  3. 3

    Change the base

    In the third example, change the base to https://example.com/docs/v2/. Predict all three printed URLs first. Then delete the <base> line: what do the URLs start with now? (The preview's own address is about:srcdoc.)

Code & diagrams

A tour of the head New tab

The script walks through every element in the head and prints its tag and its key attribute.

Preview

Press Run to see the page.

Console

Nothing logged yet.

Expected console output

meta charset=utf-8
meta name=viewport
title Head tour
meta name=description
meta name=color-scheme
link rel=icon
Where does a script in the head run? New tab

The head script runs while the parser is still in the head, so the heading doesn't exist yet. The JavaScript tab runs at the end of the body.

Preview

Press Run to see the page.

Console

Nothing logged yet.

Expected console output

In the head, the heading is: null
At the end of the body, the heading is: <h1#title>
How <base> rewrites relative links New tab

`getAttribute` gives what you wrote; the `href` property gives the full URL the browser will really use. Notice the `#top` link now points at another page.

Preview

Press Run to see the page.

Console

Nothing logged yet.

Expected console output

guide.html -> https://example.com/docs/guide.html
../about -> https://example.com/about
#top -> https://example.com/docs/#top

Break it on purpose

Errors are the best teachers. Make each change, read the error, guess what went wrong, then reveal the answer.

Break #1

Read the page from a head script

In the second example, change the head script to console.log(document.querySelector("h1").textContent);.

terminal
$ Console
── what you'll see ──
Uncaught TypeError: Cannot read properties of null (reading 'textContent')

Break #2

Block zooming

Set the viewport to width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no and open the page on a phone.

terminal
$ Lighthouse → Accessibility
── what you'll see ──
[user-scalable="no"] is used in the <meta name="viewport"> element or the [maximum-scale] attribute is less than 5.

Break #3

Fragment links under a base

Add <base href="https://example.com/"> to a page with a table of contents that uses <a href="#install">.

terminal
$ click the link
── what you'll see ──
The browser leaves the page and opens https://example.com/#install instead of scrolling.

Myth vs fact

Myth

The order of tags in the head doesn't matter.

Fact

Most order is convention, but the charset must be in the first 1024 bytes, <base> must come before any URL it should affect, and a plain script blocks everything after it.

Myth

The viewport tag is only for responsive sites.

Fact

Without it every phone renders a pretend 980px-wide page and shrinks it. Even a simple text page needs it to be readable on a phone.

Myth

Putting scripts at the end of the body is the best practice.

Fact

It works, but defer in the head is better: the browser finds the script early and downloads it while parsing, instead of discovering it only at the end.

Pro corner

Extra depth for experienced readers. New to this? Skip it for now and come back later.

  • ▸

    Browsers run a preload scanner: a light second parser that looks ahead for src and href URLs while the main parser is blocked by a script, so files start downloading early. Resources injected by JavaScript are invisible to it, which is one reason markup beats script-inserted tags for critical files.

  • ▸

    Encoding detection order per the HTML standard: byte order mark, then the HTTP Content-Type charset, then a <meta charset> found by prescanning the first 1024 bytes, then guesses. Since HTML5, <meta charset> is the only valid encoding declaration for HTML pages, and the value should be utf-8.

  • ▸

    A stylesheet in the head is render-blocking but not parser-blocking. However, a classic script that comes after a stylesheet waits for that stylesheet to load, because the script might read styles. So a slow CSS file can delay script execution too.

  • ▸

    <meta http-equiv> mimics HTTP headers (content-security-policy, refresh), but real headers are stronger: some directives, like CSP's frame-ancestors, are ignored when set by meta.

Remember this

  1. 1

    Only a few elements may live in the head: <meta> (facts about the page), <title> (the tab text), <base> (the starting point for relative links), <link> (connections to other files, like stylesheets and icons), <style> (CSS written in the page), <script> (JavaScript), <noscript> (fallback content when scripts are off) and <template> (inert HTML for scripts to copy). Anything else, such as a <p> or <img>, makes the parser close the head early and move the element into the body, as you saw in Topic 0.4.

  2. 2

    <meta charset="utf-8"> tells the browser how to turn the file's bytes into letters. A character encoding is that rule, and UTF-8 covers every language and emoji. It must appear within the first 1024 bytes of the file, so put it first in the head. If the server sends an encoding in its Content-Type header, that header wins over the meta tag (a byte order mark at the start of the file beats both).

  3. 3

    <meta name="viewport" content="width=device-width, initial-scale=1"> fixes how phones show your page. Without it, mobile browsers pretend the screen is about 980 CSS pixels wide and shrink the whole page to fit, so text becomes tiny. width=device-width makes the layout as wide as the phone's screen, and initial-scale=1 starts at 100% zoom. Other options exist: viewport-fit=cover lets the page draw under a phone's notch (then you pad with CSS env(safe-area-inset-top) and friends), and interactive-widget=resizes-content asks the browser to shrink the layout when the on-screen keyboard opens (supported in Chromium-based browsers). Never add maximum-scale=1 or user-scalable=no: they stop people from zooming, which fails accessibility guidelines for people with low vision.

  4. 4

    <base href="..."> changes the starting point for every relative URL (a link like guide.html that doesn't start with https://). It can also set a default target, such as _blank. Only one <base> is allowed, and it must come before any element that uses a URL. It also affects #fragment links, which then point at the base page instead of the current one, a classic bug. Most sites never need <base>.

  5. 5

    <link> connects the page to other resources. Its rel attribute says how: stylesheet (CSS to apply), icon (the favicon), canonical (the main URL of this page), manifest, preload, preconnect, alternate and more. Each gets its own topic later in this phase. <style> holds CSS directly in the page, which is handy for small pages or for the few rules needed to draw the first screen.

  6. 6

    Script placement matters because a plain <script> stops the HTML parser until the script has run. A script in the head therefore runs before the body exists, so document.querySelector("h1") returns null there. The old fix was to put scripts at the end of the body; the modern fix is <script src="app.js" defer> in the head, which downloads in parallel and runs after the whole page is parsed (Topic 6.5 goes deeper). Styles belong in the head, so the first paint is already styled.

Explain it without notes

01

Which elements are allowed in the head, and what happens if you put a <p> there?

02

What does width=device-width, initial-scale=1 do, and why should you never add user-scalable=no?

03

Why does a plain script in the head see null when it looks for an element in the body, and how do you fix it?

04

What does <base> do, and what is the surprising effect on #fragment links?

Practice

01

Write a complete head for a page called Contact us — Green Leaf Café that sets UTF-8, the standard viewport, the title, a one-sentence description and a deferred script /js/contact.js.

02

A page has <base href="https://shop.example/en/">. What full URLs do cart, /help and ../fr/ resolve to?

Trade-offs

  • ↔

    Inlining a small <style> in the head saves a request and speeds the first paint, but that CSS is downloaded again with every page instead of being cached once.

  • ↔

    <base> can shorten many relative links, but it changes how every relative URL and fragment link behaves; on most sites root-relative URLs (/docs/…) are clearer.

Done when you can

  • I can list the elements allowed in the head.

  • I put <meta charset="utf-8"> first and know why.

  • I can write the viewport tag from memory and know why zoom must stay enabled.

  • I know how <base> changes relative and fragment links.

  • I can explain why a plain head script can't see the body, and three ways to fix it.