Character Encoding and the Viewport Meta Tag

Two lines that every page needs. One decides whether your text is readable at all, the other decides whether your page works on a phone.

Character encoding

<meta charset="utf-8">

A file arrives as bytes. Encoding is the mapping from those bytes to characters, and the browser has to know it before it can read a single word.

Guess wrong and you get the familiar damage: a rupee sign becoming a row of symbols, an accented letter turning into two nonsense characters, an em dash appearing as three. The text is intact on the server; it is being decoded with the wrong table.

Why UTF-8

UTF-8 encodes every character in Unicode - every script, every symbol, every emoji - while keeping plain ASCII as single bytes, so English text costs nothing extra. It is the encoding of over ninety eight percent of the web and the only correct choice for new work.

It must come first

The browser reads the first bytes looking for the declaration. If it is not found early it either guesses from the content or falls back to a default, and by the time your declaration appears the damage is already done.

<!-- correct: nothing before it -->
<head>
  <meta charset="utf-8">
  <title>Fee structure - Rs 48,000</title>
</head>

<!-- broken: the title is decoded before the declaration is read -->
<head>
  <title>Fee structure - Rs 48,000</title>
  <meta charset="utf-8">
</head>

Three places encoding is decided

  1. The HTTP header - Content-Type: text/html; charset=utf-8. This wins over everything.
  2. The meta tag - the fallback when the header says nothing.
  3. The file itself - the bytes must actually be UTF-8. Declaring it does not convert anything.

All three have to agree. A file saved as Windows-1252 and declared as UTF-8 is still broken, and this is the usual cause of mojibake that survives every attempt to fix the markup. Set the editor to save as UTF-8 without a byte order mark.

The old syntax

<!-- still valid, unnecessarily long -->
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">

<!-- use this -->
<meta charset="utf-8">

The viewport

<meta name="viewport" content="width=device-width, initial-scale=1">

Without this line, a mobile browser assumes the page was designed for a desktop. It pretends the screen is around nine hundred and eighty pixels wide, renders the page at that width, then shrinks the whole thing to fit - producing a page rendered correctly and displayed at about a third of readable size.

Every media query in your stylesheet is evaluated against that fictional width, so responsive layouts never activate. The page is not broken; it is being rendered for a screen that does not exist.

What the values mean

ValueEffect
width=device-widthUse the real screen width as the viewport width
initial-scale=1Start at one hundred percent zoom, no shrinking
minimum-scaleLower zoom limit
maximum-scaleUpper zoom limit
user-scalableWhether pinch zoom is allowed
viewport-fit=coverExtend into the display cutout area on notched devices

Never disable zoom

<!-- an accessibility failure -->
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no">

Zoom is how readers with low vision use the web. Blocking it fails the Web Content Accessibility Guidelines directly, and modern browsers increasingly ignore the restriction anyway. The line above appears in a great many templates and should be deleted wherever it is found.

The usual reason it was added - preventing an unwanted zoom when a form field is focused on iOS - has a proper fix: set form field font size to at least sixteen pixels.

input, select, textarea { font-size: 16px; }

Devices with a display cutout

<meta name="viewport" content="width=device-width, initial-scale=1, viewport-fit=cover">
body {
  padding-left:  env(safe-area-inset-left);
  padding-right: env(safe-area-inset-right);
  padding-bottom: env(safe-area-inset-bottom);
}

Other meta tags in this family

<!-- browser interface colour on mobile -->
<meta name="theme-color" content="#1e3a8a">

<!-- a different colour per scheme -->
<meta name="theme-color" media="(prefers-color-scheme: light)" content="#ffffff">
<meta name="theme-color" media="(prefers-color-scheme: dark)"  content="#0f172a">

<!-- which schemes the page supports -->
<meta name="color-scheme" content="light dark">

color-scheme is worth adding: it tells the browser to render form controls, scrollbars and default backgrounds in the matching scheme, which removes the white flash a dark themed site otherwise shows on load.

Important rules

  • Declare the encoding in the first kilobyte of the document.
  • The HTTP header overrides the meta tag.
  • The file must actually be saved in the encoding you declare.
  • Without a viewport tag, mobile browsers assume a desktop width.
  • Never disable user scaling.
  • Both tags belong on every page, without exception.

Common mistakes

  • No charset declaration, then confusion when symbols break in production but not locally.
  • Declaring UTF-8 while saving the file in another encoding.
  • Declaring the encoding after content that uses non ASCII characters.
  • Omitting the viewport tag and shipping a desktop layout to phones.
  • Copying a template containing user-scalable=no.
  • Setting form field text below sixteen pixels and getting the iOS zoom, then blocking zoom to fix it.

Best practices

  • Make both tags the first two lines of every head.
  • Configure the editor and the build to write UTF-8 without a byte order mark.
  • Serve the charset in the HTTP header as well.
  • Use exactly width=device-width, initial-scale=1 and nothing more unless there is a reason.
  • Add color-scheme and theme-color when the site has a dark mode.
  • Test on a real phone, not only a resized desktop window.

Practice

  1. Remove the charset declaration from a page containing accented characters and reload. Then move it below the title and reload again.
  2. Save a file in a non UTF-8 encoding while declaring UTF-8, and describe what appears.
  3. Load a responsive page on a phone with the viewport tag removed. Do the media queries fire?
  4. Add color-scheme to a dark themed page and note what happens to the scrollbars and form fields.

Useful resources

Hand picked references for this topic
Written by Lorens Mishra

Software Engineer Notes Management System Administrator

Continue reading

All HTML notes →
HTML

Favicons and App Icons

The small icon in a tab, and the larger ones a device uses when a site is saved to a home screen. A short modern set replaces the twenty file mess of...

Read more

Discussion

0 comments
Sign in to join the discussion.

No comments yet. Be the first to say something.