---
title: Images
source: 'https://academia.sh/en/courses/web-fundamentals-and-html/images'
course: 'Web Fundamentals and HTML'
language: en
updated: '2026-08-17T18:09:32+00:00'
license: 'CC BY-SA 4.0'
---

# Images

What alternative text should say, how a size declaration affects layout, and how the same image gets served for different screens with a candidate set.

The document is sectioned, but it still consists of nothing but text.
The measurement station page's location has to get told with an
image, and that image has to carry a counterpart for the reader who
cannot see it too.

`img` is an empty element; it has no content, its source gets declared
with the `src` attribute. Two of its attributes are required: `src`
and `alt`.

## Alternative Text

The `alt` attribute carries the text that **stands in for** the
image. It is not a description of the image; it is the text that does
the image's job once the image cannot get shown. The test is this: if
I had to translate this image into text, what would I write?

The answer depends on the image's function in the document, and there
are three cases.

**An informative image.** The alternative text gives that information.

```html
<img src="location.png" alt="The station sits in the middle section of
     the north-facing slope, 400 meters above the valley floor.">
```

What gets written here is not how the image looks — colors, drawing
style — it is what it says.

**A functional image.** For an image inside a link or a button, the
alternative text stands for the **action**, not the image.

```html
<a href="/data/2024-03.csv"><img src="download.svg" alt="Download March records"></a>
```

**A decorative image.** For an image that adds no information to the
document, the alternative text gets left **empty**.

```html
<img src="divider.svg" alt="">
```

An empty `alt` and not writing `alt` at all are not the same thing.
The empty value is the declaration "this image has no text
counterpart, skip it," and a screen reader does not announce the
image. If the attribute never gets written, no declaration gets made
at all; many screen readers then read out the file name, which gives
the user nothing.

The words "picture," "image," "photo" do not get written into the
alternative text; the element's type already gets announced from its
role. The text's length depends on the content; for descriptions past
a few sentences, a separate long-description link gets preferred.

## Size Declaration

The `width` and `height` attributes declare the image's **intrinsic
dimensions** in pixels. This declaration matters even if the
presentation layer shows the image at a different size: the browser
calculates the ratio from it and allots space before the image
downloads.

```js
// ratio.mjs — aspect ratio and allotted height
const w = 1440, h = 960;
console.log("width/height =", w + "/" + h, "-> ratio", (w / h).toFixed(4));
for (const width of [300, 480, 720]) {
  console.log(String(width).padStart(4), "pixels wide, height:", (width * h / w).toFixed(1));
}
```

```
width/height = 1440/960 -> ratio 1.5000
 300 pixels wide, height: 200.0
 480 pixels wide, height: 320.0
 720 pixels wide, height: 480.0
```

If the declaration does not get written, the browser allots it zero
height until the image downloads; the moment the image downloads, the
content below it shifts down. If the user is reading at that moment,
or is about to press a button, this shift is a concrete problem. A
size declaration prevents this shift and lets layout get calculated
without waiting for the download.

The attributes get written without a unit; the writing
`width="960px"` is invalid.

The `loading="lazy"` attribute declares that the image does not get
requested until it nears the viewport. It does not get written on
images visible on the first screen — those images are needed right
away, and delaying them slows down the opening.

## Candidate Set

A single image file is not suitable for every screen. Sending a wide
file to a narrow screen wastes bandwidth; sending a narrow file to a
wide, dense screen gives a blurry result.

The `srcset` attribute declares candidates of the same image at
different widths; `sizes` declares the image's width **in the
layout**. The browser makes the choice.

```html
<img src="slope-960.jpg"
     srcset="slope-480.jpg 480w, slope-960.jpg 960w,
             slope-1440.jpg 1440w, slope-1920.jpg 1920w"
     sizes="(max-width: 600px) 100vw, (max-width: 1000px) 50vw, 640px"
     width="1440" height="960"
     alt="The station sits in the middle section of the north-facing slope.">
```

The selection rule is computable: the first matching condition in the
`sizes` list gives the source size, each candidate's density is the
ratio of its `w` value to the source size, and the smallest-density
candidate meeting the device's pixel ratio gets chosen.

```js
// candidates.mjs — model of the srcset/sizes candidate selection rule
const candidates = [
  { src: "slope-480.jpg", w: 480 },
  { src: "slope-960.jpg", w: 960 },
  { src: "slope-1440.jpg", w: 1440 },
  { src: "slope-1920.jpg", w: 1920 },
];

// "(max-width: 600px) 100vw, (max-width: 1000px) 50vw, 640px"
const rules = [
  { maxWidth: 600, value: (v) => v * 1.0 },
  { maxWidth: 1000, value: (v) => v * 0.5 },
  { maxWidth: Infinity, value: () => 640 },
];

function sourceSize(viewportWidth) {
  return rules.find((r) => viewportWidth <= r.maxWidth).value(viewportWidth);
}

function pick(viewportWidth, pixelRatio) {
  const size = sourceSize(viewportWidth);
  const densities = candidates.map((c) => ({ ...c, density: c.w / size }));
  const sufficient = densities.filter((c) => c.density >= pixelRatio);
  const chosen = sufficient.length > 0
    ? sufficient.reduce((min, c) => (c.density < min.density ? c : min))
    : densities.reduce((max, c) => (c.density > max.density ? c : max));
  return { size, chosen };
}

const cases = [[390, 1], [390, 3], [800, 1], [800, 2], [1440, 1], [1440, 2]];
console.log("viewport  ratio  source size  chosen candidate  density");
for (const [width, ratio] of cases) {
  const { size, chosen } = pick(width, ratio);
  console.log(
    String(width).padStart(7),
    String(ratio).padStart(5),
    String(size).padStart(14),
    "  " + chosen.src.padEnd(15),
    chosen.density.toFixed(2),
  );
}
```

```
viewport  ratio  source size  chosen candidate  density
    390     1            390   slope-480.jpg   1.23
    390     3            390   slope-1440.jpg  3.69
    800     1            400   slope-480.jpg   1.20
    800     2            400   slope-960.jpg   2.40
   1440     1            640   slope-960.jpg   1.50
   1440     2            640   slope-1440.jpg  2.25
```

The output's first two lines compare the same screen width at two
different pixel densities: for an image shown at the same size, a
three-times-denser screen gets a three-times-bigger file chosen. The third and fifth lines show that as the screen widens,
the source size changes according to the `sizes` rule — half the
image at 800 pixels, a fixed 640 pixels at 1440.

Writing `sizes` incorrectly produces a silent defect: the browser
chooses by the declared size, not the actual size. The declaration
has to stay consistent with the layout.

The choice is the browser's decision, and it can also weigh network
conditions, cached files, or user settings. The calculation above is
the rule itself; it is not a guarantee of the result.

## Format Selection

The `picture` element also gets used to choose the candidate set among
different **file formats**. Its `source` elements get tried in order,
and the first supported format gets chosen; the `img` element serves
as both the fallback and the alt-text carrier.

```html
<picture>
  <source srcset="slope.avif" type="image/avif">
  <source srcset="slope.webp" type="image/webp">
  <img src="slope.jpg" width="1440" height="960"
       alt="The station sits in the middle section of the north-facing slope.">
</picture>
```

The same mechanism also shows a different crop on a narrow screen:
when a `media` condition gets written on the `source` elements, choice
gets made by screen width. The distinction
between `srcset` and `picture` is this — `srcset` chooses different
scales of the same image, `picture` chooses different images or
different formats.

## Summary

- `alt` is not a description of the image, it is the text that stands
  in for it; in functional images it stands for the action, in
  decorative images for an empty string.
- An empty `alt` and not writing `alt` differ: the first is the "skip"
  declaration, the second makes no declaration at all.
- A `width` and `height` declaration gives the aspect ratio and
  prevents content from shifting by letting space get allotted before
  the image downloads.
- `srcset` declares candidates, `sizes` declares the width in the
  layout; the choice gets calculated from the source size and the
  device's pixel ratio.
- `picture` chooses between different formats or different crops;
  `srcset` between scales of the same image.

## Next Step

The image entered the document, but context is still missing: which
measurement point it shows, when it got taken, and which text it
belongs to — none of that has been declared. The next lesson takes up
the element that
associates an image with a caption, and shows how this relationship
changes the accessible name.
