RizTech Academy logo
RizTech Academy
Capstone ProjectLesson 3 of 430 min

Real content and real images, sized properly

Your site works with the text you invented. This lesson is replacing that with content a real business would give you, and images that do not cost your visitor a second of their data plan.

It is the stage most student projects skip, and it is the stage where a project stops looking like a student project.

Writing the actual words

The brief said not to use lorem ipsum. Now write the real thing.

Write what the visitor needs to decide, not what the business wants to say. Those are different. A studio owner will give you three paragraphs about their philosophy. A visitor wants to know: what happens in the class, do I need to be flexible, when is it, what does it cost, and where is it.

Bad:   Aarambh Yoga is committed to holistic wellness through the ancient
       practice of yoga, nurturing mind, body and spirit in a serene environment.

Good:  A small studio with small classes. Morning and evening hatha, vinyasa and
       pranayama, for complete beginners upwards.

The second says what you get, when, and who it is for, in twenty words.

Answer the unasked question. For a yoga studio it is will I be the worst person there. So:

You do not need to be flexible, and you do not need to be able to touch your toes. Nobody will ask you to do a headstand. Come to a gentle hatha class at 9:00, stand at the back, and stop whenever you like — that is entirely normal and the teacher expects it.

Every small business has one of these. A dentist's is will it hurt. A tailor's is what if it does not fit. Find it and answer it plainly.

Be specific. "Reasonable prices" is worth nothing; "₹300 drop in, ₹2,400 for ten" is worth a booking. Specifics are also what make a page feel real rather than generated.

The things people actually look for

For a local business, in the order they are wanted:

  1. What it is, in one line.
  2. Where it is, and whether that is near them.
  3. When it is open.
  4. What it costs.
  5. How to get in touch, tappably.

That last one matters on a phone:

<a href="tel:+912025431987">020 2543 1987</a>
<a href="https://wa.me/919876543210">WhatsApp us</a>

One tap to call, one tap to message. On an Indian small business site these are worth more than anything else on the page.

Testing with content that does not fit

Module 7's six-card fixture, applied to the real site. Before you go near images, push real text through:

  • The business name at four words. Does the header wrap sensibly?
  • A class description at six lines rather than two. Do the cards still align?
  • A price with no value yet. Does the layout hold?
  • An email address with no spaces in a narrow column. Does it overflow?
  • The whole thing at 400% zoom.

Do this before the images, because image work is slow and you do not want to redo it after a layout change.

Images: how many, and how big

The reference site's answer, which is a reasonable default:

hero            400, 800, 1200, 2000     16/9
class cards     400, 800                 3/2
studio photo    400, 800, 1200           3/2
teacher photos  240, 480                 1/1
map             800                      8/5

Four widths for the hero because it spans the viewport. Two for a card that is never wider than about 22rem. Match the widths to how big the image actually gets, which is what sizes is telling the browser anyway.

Resize the original first

The single most common cause of a slow page, from module 7. A 4000-pixel phone photograph dropped in as-is is several megabytes for something displayed at 400.

Roughly twice the largest displayed size is enough. Beyond that the file grows and nobody can see the difference.

# macOS, built in
sips -Z 2000 hero-original.jpg --out hero-2000.jpg

# ImageMagick
magick hero-original.jpg -resize 2000x hero-2000.jpg

Then a modern format beside each one

for f in *.jpg; do cwebp -q 76 "$f" -o "${f%.jpg}.webp"; done

And AVIF too if you have an encoder — avifenc — as a third <source>.

Be honest about the saving, and measure your own. The number usually quoted is "about 30%", and the reference site gives two very different answers depending on what is in the image:

Images JPEG WebP Saved
The photographs 790 KB 436 KB 45%
The hero alone — — 50%
The generated map (flat colour) 7.8 KB 1.9 KB 75%

Smooth and flat images flatter the format. The map is a plan view of blocks and lines, and WebP eats it. Real photographs — grain, texture, gradients of skin and cloth — land nearer 45%. Both are worth having, and quoting either as "the" figure would mislead you.

Wiring it up

<picture>
  <source
    type="image/webp"
    srcset="images/hero-400.webp 400w, images/hero-800.webp 800w,
            images/hero-1200.webp 1200w, images/hero-2000.webp 2000w"
    sizes="100vw">
  <img
    src="images/hero-1200.jpg"
    srcset="images/hero-400.jpg 400w, images/hero-800.jpg 800w,
            images/hero-1200.jpg 1200w, images/hero-2000.jpg 2000w"
    sizes="100vw"
    alt=""
    width="1200" height="675"
    fetchpriority="high">
</picture>

<picture> chooses the format; srcset chooses the size. The <img> is required — it is what renders and what carries alt, width and height.

A detail worth knowing: write sizes with (min-width: 48rem), not the range syntax (width >= 48rem). Both are valid, but the range form puts a > inside an attribute value, which is legal HTML and breaks a lot of naive tooling — including a check written for this very site.

sizes, which is the part that goes wrong

From module 4: the browser needs to know the displayed width before it has read your CSS, so you are promising it.

sizes="100vw"                              <!-- a full-width hero -->
sizes="(min-width: 48rem) 22rem, 100vw"    <!-- a card: 22rem on wide screens -->
sizes="(min-width: 48rem) 15rem, 100vw"    <!-- a small avatar -->

Leave it at the 100vw default for a card displayed at 22rem and every visitor on a 1400px screen downloads the 2000px file. That is the commonest way a page with srcset is still slow.

Priorities

<img … fetchpriority="high">              <!-- the hero only -->
<img … loading="lazy">                    <!-- everything below the fold -->

Never loading="lazy" on the hero — you would be delaying the one image the visitor came for. The reference site has one high-priority image and four lazy ones on the home page.

Alt text, for this site specifically

Module 1's rule was that alt replaces the image. Applied here:

<!-- decorative: the heading beside it already says everything -->
<img src="hero-1200.jpg" alt="">

<!-- content: says what you would see -->
<img src="class-hatha-800.jpg"
     alt="A quiet studio floor laid out with mats before a hatha class">

<!-- inside a link: describes the DESTINATION -->
<a href="https://www.openstreetmap.org/search?query=Karve+Road+Kothrud+Pune">
  <img src="map-kothrud-800.jpg"
       alt="Map showing Aarambh Yoga on Karve Road, Kothrud, Pune. Opens OpenStreetMap.">
</a>

The hero is alt="" because the <h1> immediately beside it carries the meaning; describing the photograph as well would be repetition. Empty, but present — leaving alt off entirely makes many screen readers read the filename.

The map, without a megabyte of JavaScript

Module 1's lazy-embed pattern, and it is the right answer here:

<a class="map-link" href="https://www.openstreetmap.org/search?query=Karve+Road+Kothrud+Pune">
  <img src="images/map-kothrud-800.jpg"
       alt="Map showing Aarambh Yoga on Karve Road, Kothrud, Pune. Opens OpenStreetMap."
       width="800" height="500" loading="lazy">
</a>

A static image and a link. 7.8 KB rather than roughly a megabyte of embedded map JavaScript, no third-party cookies, and tapping it opens the map app on a phone — which is what the visitor wanted. Almost nobody does this and it is better in every respect.

What it all weighed

The reference site's home page, measured in the Network tab:

Requests            6
Transferred        44 KB
Largest file       hero-2000.webp   16.5 KB
Stylesheet                         15.0 KB
Three card images                  12.5 KB

Against the budget from module 7 — under 500 KB and under 20 requests — that is an order of magnitude of headroom. A four-page brochure site has no excuse to be heavy.

Note that the browser chose hero-2000.webp at a 706px viewport, because the screen is high-density and sizes="100vw" means the full width. That is srcset working correctly, and it is why the 2000px version exists.

Sourcing the photographs

Unsplash, Pexels and Pixabay are the usual free sources. Download and commit them — never hotlink to somebody else's server, which can vanish, rate-limit you or start charging. Check the licence allows what you are doing; Pexels and Unsplash both allow commercial use with no attribution required, and crediting anyway costs nothing.

The reference site fetches its photographs with a small fetch-photos.py that downloads by photo id and crops each to the exact ratio and widths the HTML expects. That is worth copying: fix the filenames and aspect ratios first, then fetch, so changing a photograph later is a file replacement rather than a rewrite of every srcset.

Look at every photograph before you write its alt

The lesson that came out of building this site, and it is not obvious.

Stock photographs are labelled by their uploader and by a search engine, and those labels are frequently wrong about what is actually in the frame. Building the reference site, two of seven chosen photographs turned out to be unusable once somebody looked:

  • One labelled "studio portrait", chosen for a male teacher called Anil, is a woman in a moody fashion portrait. Publishing it under that name would have been indefensible.
  • One labelled "yoga pose" is a portrait-orientation arm balance. No 3:2 crop of it can contain the pose, so the card rendered as an ambiguous close-up of a bare torso. Two different crops were tried before it was replaced.

Neither was visible from the filename, the search result or the uploader's caption.

So: open every image at the size it will actually render, then write the alt from what you can see. If a photograph cannot be cropped to your ratio without losing its subject, replace it rather than re-cropping again. And if you cannot write an honest alt for it, that is the signal it is the wrong photograph.

Check your work

What to write instead of philosophy. What the visitor needs to decide.

The unasked question. Every business has one; find it and answer it plainly.

Why specifics matter. "₹300 drop in" is worth a booking; "reasonable prices" is worth nothing.

When to test with awkward content. Before the image work, because image work is slow to redo.

How many widths. Match them to how big the image actually gets.

How much to resize the original to. Roughly twice the largest displayed size.

How honest to be about WebP. 45% measured on the reference site's photographs, 75% on its flat generated map. Smooth images flatter the format — measure your own.

What <picture> chooses versus srcset. Format versus size.

Why (min-width: …) rather than the range syntax in sizes. It keeps a > out of an attribute value.

The commonest way a page with srcset is still slow. Leaving sizes at 100vw for an image displayed much narrower.

Which image is never lazy. The hero.

Why the hero is alt="". The heading beside it carries the meaning — but the attribute must be present.

What a static map link costs against an embed. 7.8 KB against roughly a megabyte.

Practice

  1. Rewrite your home page's main paragraph to say what the visitor gets, in twenty words.
  2. Identify your business's unasked question and answer it in a short paragraph.
  3. Replace every vague claim with a specific number.
  4. Add tel: and wa.me links and tap both on a real phone.
  5. Put a four-word business name in your header and a six-line description in a card.
  6. View everything at 400% zoom before touching any image.
  7. Take one photograph, check its pixel dimensions, and resize it to four widths.
  8. Generate WebP beside each. Record the actual percentage saved, not the one you read.
  9. Open each photograph at the size it will render and check it contains what its label claims. Write the alt from what you see, not from the search term you used.
  10. Wire up <picture> with both formats. In the Network tab, confirm exactly one file is fetched and which.
  11. Set sizes="100vw" on a card image displayed at 22rem. Note the file chosen. Then fix sizes and note it again.
  12. Put loading="lazy" on your hero, throttle to Slow 4G, and watch the delay.
  13. Write alt for three images: one decorative, one content, one inside a link.
  14. Remove alt entirely from one and listen with a screen reader.
  15. Replace a map embed with a static image and a link. Compare requests and bytes.
  16. Open the Network tab, sort by size, and justify your largest file.

Official documentation

Next: the audit, and putting it online.

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