Links, images and alt text
Two elements, and between them most of the mistakes on the average small business
site. Links that say "click here" and images with no alt are the two things an
accessibility audit finds first, every time.
Links
<a href="prices.html">Our prices</a>
href is the destination. Everything else is detail.
Relative and absolute paths
This is the part that causes broken links, so it is worth being precise. Say your folder looks like this:
site/
index.html
about.html
images/
shop.jpg
prices/
index.html
From index.html:
<a href="about.html">About</a> <!-- same folder -->
<a href="prices/">Prices</a> <!-- folder; serves prices/index.html -->
<img src="images/shop.jpg" alt="…"> <!-- down into a folder -->
From prices/index.html:
<a href="../about.html">About</a> <!-- ../ means up one folder -->
<img src="../images/shop.jpg" alt="…">
And the two that behave differently:
<a href="/about.html">About</a> <!-- root-relative: from the site root -->
<a href="https://example.com">Example</a> <!-- absolute: another site -->
A leading / means "from the root of the site", not "from this folder". This
is the one that trips people up, because on file:// a leading slash means the
root of your entire hard disk, so root-relative links appear broken locally and
work once deployed. If your links break the moment you open a page from a
subfolder, you have used relative paths where you wanted root-relative ones.
Prefer root-relative (/about.html) for a site you will deploy. Then the same
link works from any depth, and moving a page does not break its links.
Link text that works
Screen reader users can pull up a list of all links on a page, out of context. So:
<!-- useless in a list: "click here, click here, read more, read more" -->
<p>For our prices, <a href="/prices/">click here</a>.</p>
<a href="/atta.html">Read more</a>
<!-- makes sense alone -->
<p>See <a href="/prices/">our current prices</a>.</p>
<a href="/atta.html">More about our atta</a>
The test: does the link text make sense read on its own? "Click here" fails. "Read more" fails. "Download" fails — download what?
Also: never put the URL itself as the text for a long URL. A screen reader reads it character by character, which takes twenty seconds.
The three link attributes worth knowing
<a href="/menu.pdf" download>Download our price list (PDF, 240 KB)</a>
<a href="https://gst.gov.in" target="_blank" rel="noopener noreferrer">
GST portal (opens in a new tab)
</a>
<a href="tel:+919876543210">Call us</a>
<a href="mailto:hello@sharmakirana.com">Email us</a>
<a href="https://wa.me/919876543210">WhatsApp us</a>
Say the file type and size for a download. Somebody on a metered connection deserves to know before they tap.
target="_blank" needs rel="noopener", which stops the new page getting a
handle on yours. Modern browsers imply it, but older ones do not and the attribute
costs nothing. And tell the person it opens a new tab — an unannounced new tab
is disorienting, and the Back button appears broken.
tel: and wa.me links are worth real money on an Indian small business site,
because most visitors are on a phone and one tap is the difference between a call
and a bounce.
In-page links
<h2 id="delivery">Delivery</h2>
…
<a href="#delivery">Jump to delivery</a>
An id must be unique on the page. href="#top" with no matching id goes to the
top of the page; href="#" also does, and is usually a placeholder somebody
forgot.
Images
<img src="images/shop.jpg" alt="The shopfront of Sharma Kirana on Karve Road">
src and alt are both required. alt being required is not a style guide — the
HTML specification says so.
What alt is actually for
Not "a description of the image". A replacement for the image: what you would write if the picture could not be shown.
It is used by a screen reader, shown when the image fails to load, read by search engines, and used by anybody browsing with images off to save data — which on a metered Indian mobile plan is a real choice people make.
<img src="atta.jpg" alt=""> <!-- decorative: correct -->
<img src="atta.jpg" alt="atta"> <!-- too vague -->
<img src="atta.jpg" alt="image of atta"> <!-- "image of" is noise -->
<img src="atta.jpg" alt="atta.jpg"> <!-- worse than nothing -->
<img src="atta.jpg" alt="A 10kg bag of Sharma Kirana atta"> <!-- good -->
Three rules that cover nearly everything:
Never start with "image of" or "picture of". A screen reader already announces "image". You are making it say "image, image of atta".
alt="" — empty, but present — is correct for a decorative image. A
background flourish, a divider, an icon next to text that already says the same
thing. Empty alt tells a screen reader to skip it. Leaving alt off entirely is
different and worse: with no alt at all, many screen readers read out the
filename, so your user hears "i-m-g underscore 2 0 2 6 0 9 dot j-p-g".
If the image is inside a link, the alt describes the destination, not the picture, because that image is the link:
<a href="/"><img src="logo.svg" alt="Sharma Kirana home"></a>
And if an image carries information — a price list as a picture, a chart — the alt
must carry that information, or the information is not on your page. A photo of a
price board with alt="price board" has hidden your prices from a portion of your
customers. Better: put the prices in a table and use the photo as decoration.
Always set width and height
<img src="shop.jpg" alt="…" width="1200" height="800">
Not to size it — CSS does that. These are the intrinsic dimensions, and giving them lets the browser reserve the right space before the image arrives.
Without them, the page renders, then the image loads, then everything below it jumps down. That jump is called layout shift, it is measured by Lighthouse as CLS, and it is why you sometimes tap a link and hit the wrong thing because the page moved under your thumb. Two attributes fix it.
Then in CSS, always:
img {
max-width: 100%;
height: auto;
}
Otherwise a 2000-pixel-wide image overflows a 360-pixel phone screen and gives you a horizontal scrollbar on every page.
Which format
| Format | Use for |
|---|---|
| SVG | Logos, icons, anything drawn. Infinitely sharp, usually tiny |
| WebP | Photographs. Roughly 30% smaller than JPEG at the same quality |
| JPEG | Photographs, when you need maximum compatibility |
| PNG | Screenshots, or anything needing real transparency |
| AVIF | Photographs, smallest of all, now safe in current browsers |
Never a PNG of a photograph — it can be five times the size of the JPEG for no visible benefit. And never a 4000-pixel-wide phone photo dropped straight into a page: resize it to roughly twice its display width and no more.
Lazy loading
<img src="shop.jpg" alt="…" width="1200" height="800" loading="lazy">
loading="lazy" tells the browser not to fetch the image until it is near the
viewport. Use it for everything below the fold; do not use it for the
image at the top of the page, because you are delaying the one image the visitor
is waiting for.
Figures and captions
<figure>
<img src="shop.jpg" alt="The shopfront on Karve Road" width="1200" height="800">
<figcaption>Our shop in Kothrud, open since 1998.</figcaption>
</figure>
<figcaption> is a caption everybody sees. alt is a replacement for people who
cannot see the image. They are different jobs — do not write the same words twice,
and do not skip the alt because there is a caption.
Check your work
What a leading / means. From the site root, not the current folder — and on
file:// it means your hard disk's root.
Why prefer root-relative links. The same link works from any folder depth.
The link text test. Does it make sense read on its own, in a list? "Click here" and "Read more" fail.
Why rel="noopener" with target="_blank". It stops the new page getting a
handle on yours — and announce the new tab.
What alt really is. A replacement for the image, not a description of it.
Why not "image of". The screen reader already says "image".
alt="" versus no alt. Empty means skip this decoration; missing means many
screen readers read the filename aloud.
Alt for an image inside a link. Describe the destination.
What width and height prevent. Layout shift — the page jumping as images
arrive.
What max-width: 100% prevents. A wide image forcing horizontal scroll on a
phone.
When not to use loading="lazy". On the image at the top of the page.
alt versus <figcaption>. A replacement versus a caption for everybody.
Practice
- Build the folder structure from this lesson and make every link work from both
index.htmlandprices/index.html. - Change one link to root-relative and open the page from
file://. Explain what happens. - List the links on any real site using a screen reader's links list, or read the link texts aloud in isolation. Count how many are meaningless.
- Rewrite three "click here" links properly.
- Add a
tel:and awa.melink and tap both on a real phone. - Add a PDF download link stating the type and size.
- Put an image on a page with no
altand listen to it with a screen reader. Thenalt="". Then a real alt. Note all three. - Break an image's
srcdeliberately and look at the page. That broken-image text is your alt doing its job. - Load a page with a large image and no
width/heighton a throttled connection. Watch the jump. Add them and watch it stop. - Photograph something, drop the full-size file into a page, and check its size in the Network tab. Resize it to 1200px wide and compare.
- Save the same photo as PNG, JPEG and WebP. Compare the three file sizes.
- Add
loading="lazy"to images below the fold and confirm in the Network tab that they are not fetched until you scroll.
Official documentation
- MDN — The img element — Every attribute, including
srcsetandsizesfor module 4. - W3C WAI — Images tutorial — A decision tree for what
alttext to write, including the decorative case. - MDN — The rel attribute — What
noopener,noreferrerand the rest actually do.
Next: lists and tables, and the one thing tables must never be used for.
Stuck on this lesson?
Being stuck is part of it — but being stuck alone for three days is not. Our internship programme pairs this curriculum with code review and one-to-one help from working developers, and it is free.
About the internship