Links and images
href, src, alt, srcset — and the small details that decide whether your page reads on a phone or a screen reader.
Links and images are the two elements that turn a document into the web. They're also the two newcomers ship with the most accidental defects: links that break under JavaScript, images that look fine on the laptop the developer used and ship 3MB of photo to a phone.
Anchors and href
<a> is the anchor element. Its href attribute holds the URL the link goes to. Three flavors of href cover almost every case:
- Absolute URL —
https://example.com/about. Goes anywhere. - Root-relative —
/about. Resolves against the site's origin. Stable across pages within the same site. - Document-relative —
about.htmlor../images/cat.jpg. Resolves against the current page's URL. Fragile when you move pages around.
A few special schemes are worth knowing: mailto:user@example.com opens the user's email client; tel:+15551234 dials on a mobile device; #section-id jumps within the current page.
<a href="/about">About us</a> <a href="mailto:hello@example.com">Email us</a> <a href="#pricing">Jump to pricing</a> <a href="https://github.com/example/repo">View on GitHub</a>
The link text is what the user reads, what screen readers announce, and what search engines weigh. "Click here" links tell a screen-reader user nothing — the link text is what they hear when navigating link-by-link, out of context. Use the destination as the link text whenever the surrounding sentence allows.
Targets and rels
target="_blank" opens the link in a new tab. Two caveats: it can be disorienting (the back button no longer takes the user where they expected), and historically it allowed the new page to manipulate the original via window.opener. Modern browsers default to safer behavior, but the convention is still to add rel="noopener" (and often rel="noreferrer" when you don't want the destination to know where the user came from).
rel carries other useful values:
rel="external"— links to a site outside this one. Often used to style external links differently.rel="nofollow"— tells search engines not to pass link weight. Standard on user-generated content.rel="me"— identifies a link as a representation of the same person. Used by indieweb tooling and by Mastodon's profile-verification feature.rel="stylesheet"— on<link>(not<a>), this is what loads CSS.
<a href="https://example.com" target="_blank" rel="noopener noreferrer"> Example (opens in new tab) </a>
Images and alt
<img> is a void element. The two attributes that always matter are src (where the image lives) and alt (the text equivalent).
alt is the most-misused attribute in HTML. It's not a tooltip and not a caption. It's what the image would say if it were prose — the description a screen reader reads aloud, the text shown when the image fails to load, the text a search engine indexes.
Three patterns cover most cases:
- Informative image — alt is a short description of what the image conveys:
alt="A line chart showing site traffic doubling between Jan and Mar 2025". Not "chart.png". - Decorative image — purely visual filler that adds nothing to the meaning of the surrounding text. Set
alt=""(empty). Screen readers skip it. Not omitting the attribute — that produces unpredictable behavior. Empty. - Image with a caption — if the surrounding text already describes what the image shows, the alt can be empty or a short complement.
<img src="dashboard-light.png" alt="The dashboard in light mode, showing three charts and a data table"> <!-- decorative divider --> <img src="curl.svg" alt="">
Don't write alt="image of a chart" or alt="picture of...". Screen readers already announce that an image is an image. The alt should be its content, not its medium.
Responsive images
A single src ships the same image to a 4K monitor and a phone on a 3G connection. Two attributes fix that.
srcset lets you list multiple image URLs paired with their widths. The browser picks the right one for the device's resolution and the layout slot's size.
<img
src="hero-800.jpg"
srcset="hero-400.jpg 400w,
hero-800.jpg 800w,
hero-1600.jpg 1600w"
sizes="(min-width: 800px) 800px, 100vw"
alt="...">sizes tells the browser how wide the image will be displayed at different breakpoints. Without it, the browser assumes the image fills the viewport and downloads larger files than it needs.
For art-direction differences (a wide landscape photo on desktop, a tighter crop on phone), reach for <picture> instead. It wraps <img> and lets you swap entire image sources at breakpoints.
<picture> <source media="(max-width: 600px)" srcset="hero-portrait.jpg"> <source media="(min-width: 601px)" srcset="hero-landscape.jpg"> <img src="hero-landscape.jpg" alt="..."> </picture>
Two attributes are also worth knowing on <img>:
loading="lazy"— defers loading until the image is near the viewport. Use it on images below the fold; don't use it on the hero image (you want that one now).widthandheight— should always be set, even if CSS resizes the image. The browser uses them to reserve layout space, preventing the page from jumping around as images load (cumulative layout shift).