Images were the bandwidth

CSS made product photographs fit a phone while the network still delivered the desktop originals, forcing image cost into the design itself.

C62Y’s first narrow layout looked lighter than the desktop page and weighed exactly the same.

The product photograph filled a smaller box. Navigation stacked. The content became one column. On a phone, the page gave every visual sign that it had adapted.

The browser had still downloaded the original photograph prepared for the desktop composition. CSS changed its rendered dimensions after the transfer. The image was the page’s largest resource, and the responsive work had improved everything except the condition that mattered over a slow mobile connection.

I had designed the size of the box and not the cost of the thing inside it.

That failure turned images from a finishing task into part of C62Y’s delivery architecture. The responsive-image proposals were active, but I could not depend on a mature srcset, sizes, and picture workflow across the browsers in scope. The available choices were awkward. The underlying question was already clear: what image does this context need, and how much evidence does the browser have before it requests one?

The stylesheet could not refund the transfer

The original template emitted one image URL. The browser discovered it while parsing HTML and began the request. By the time CSS established that the image would render at a few hundred pixels wide, the large file was already on its way.

max-width: 100% was still the correct layout rule. It prevented the image from escaping its container and preserved its aspect ratio. It was not a delivery rule.

I needed separate language for the two concerns:

  • Responsive layout: the image box adapts to available space.
  • Responsive delivery: the selected resource is appropriate to the rendered need and device capability.

Calling both “responsive images” had let the first success hide the second failure.

I opened the network panel on a throttled connection and sorted by transferred bytes. One product photograph dominated the initial view. Compressing the stylesheet or combining two small icons could not compensate for serving a source several times larger than its display.

The report changed the design conversation. Image treatment was no longer a vague optimization to perform after the visual work. It was the visual work arriving over a network.

A deliberately conservative first image

The image-selection standards and implementations were still moving. I did not want C62Y’s baseline to depend on a speculative client script that might replace a request after the browser had already fetched it.

The build generated a few derivatives from the source photograph. Each derivative had a named role rather than a device label:

  • Catalogue thumbnail for lists and comparison identity.
  • Product summary image for the initial detail view.
  • Inspection image available through an explicit link.

The HTML used the summary derivative as a conservative default. The detail page linked to the larger inspection asset instead of delivering it to everyone preemptively.

This was less adaptive than a modern responsive source set. It was predictable. The first view transferred an image suited to its task. A person who needed surface detail could request more.

The approach treated resolution as progressive disclosure. The high-resolution photograph was content, not a hidden tax attached to a smaller representation.

I documented the limitation: a high-density display might receive a summary image that was not perfectly sharp. At that point in the project, I preferred a dependable moderate default over a complex selector whose failure could duplicate downloads or strand an image behind script.

Derivatives needed an identity

My first derivative script placed -small and -large beside the original. It did not record how they had been produced. Changing compression settings created a mixture of old and new files whose names looked current.

I moved image processing into the build. A derivative identity included the source digest, transformation recipe, dimensions, format, and quality setting. The output filename contained a content fingerprint.

The manifest recorded:

source
sourceDigest
role
width
height
format
quality
outputDigest
outputPath

Templates referenced the manifest, not a guessed filename. If processing failed, the build could identify the product and image role. A release never activated with HTML pointing at a derivative that had not been produced.

This was my first useful encounter with artifact identity. Once a generated asset affected what a person reviewed, its inputs and result needed enough identity to be reproduced or at least recognized.

Smaller transfers improved loading and exposed layout movement. The browser did not know the image’s dimensions until enough of the file arrived, so surrounding content shifted when the photograph took its space.

The build already knew width and height. I emitted them into the markup. The layout could reserve the correct aspect ratio before the image decoded.

In 2012, this meant using ordinary image dimensions and flexible CSS rather than today’s aspect-ratio conveniences. The width and height attributes described intrinsic proportions; CSS allowed the rendered box to shrink while preserving them.

The change improved more than visual calm. A user beginning to read a specification no longer had the paragraph move after an image appeared. A link did not slide under the pointer. The page’s useful structure became stable before every asset completed.

Performance was no longer only elapsed time or bytes. It included whether the interface remained trustworthy while resources arrived.

Cropping was a content decision

I initially generated thumbnails by cropping every source to the same rectangle. The catalogue list aligned beautifully. Tall tools lost identifying features, and one product’s control panel disappeared outside the crop.

I separated two transformations:

  • Fit within a bounded box while preserving the complete object.
  • Intentional crop with a maintained focal region.

The default product thumbnail used the complete-object fit. Decorative category art could use an authored crop. The manifest recorded which method had produced the result.

I added a neutral background and predictable padding where source images had inconsistent framing. The catalogue could align thumbnails without pretending every object naturally shared an aspect ratio.

This was another place where a visual system could manufacture misinformation. Uniform cards were not worth making the product harder to recognize.

The image pipeline could automate dimensions and compression. It could not decide which part of a photograph carried meaning. That remained a content decision.

I tried to choose one quality setting by file-size target. Some photographs compressed cleanly. Others contained text, fine markings, or high-contrast edges that became unreadable at the same setting.

The build produced a contact sheet showing source and derivatives with dimensions and sizes. I reviewed representative categories at their actual display size and at the larger inspection view.

The goal was not to preserve every source pixel. It was to preserve the information appropriate to the role. A thumbnail needed recognizable shape. A summary image needed enough detail to distinguish variants. The inspection image needed readable controls and labels where the source supported them.

I used different recipe classes sparingly. Too many per-image exceptions would make the pipeline unmaintainable. When one image could not meet the role at a reasonable size, the source itself often needed improvement.

The contact sheet made quality changes reviewable before publication. A lower byte total did not count as success if the relevant detail disappeared.

After reducing individual files, I added more photographs to the catalogue landing page. The design looked richer and the aggregate transfer climbed back above the working budget.

Optimization had made each choice cheaper and had not made the total free.

I returned to the page’s task. The first view needed category orientation and a path into the catalogue. It did not need a photograph for every item. Representative category images stayed; the complete product list used text and small identity thumbnails only after the user entered a category.

The budget covered the whole scenario:

  • Initial document and styles.
  • Images visible in the first useful view.
  • Required script.
  • Request count and connection overhead.
  • Work performed before the page could respond.

An image did not get approved because its own size was reasonable. It had to justify its place among the other resources competing for the first view.

This kept performance tied to hierarchy. The budget did not flatten the design into a text-only page. It forced the page to decide which images did real work early.

Lazy loading before a standard attribute

The catalogue contained long lists, and loading every below-the-fold thumbnail immediately was wasteful. The modern native loading="lazy" attribute did not exist for me to use in 2012.

I tested a script that delayed image assignment until a row approached the viewport. Its placeholder and source-handling logic added failure states. With script blocked, those images needed a usable path. On a fast scroll, they could arrive late enough to leave an unhelpful blank list.

For the first release, I chose pagination and smaller list pages over a client-side lazy loader. The server returned a bounded number of items, navigation remained ordinary links, and each page stayed within the image budget.

This was not the most fluid interface. It had fewer invisible states and worked across the browser range I could realistically test.

Later, better browser APIs would change the tradeoff. The durable lesson was not “never lazy load.” It was that deferred loading is a product state with placeholders, discovery timing, failure behavior, and a no-script story. A performance technique can increase uncertainty if those are not designed.

The growing presence of high-density screens made image sharpness a visible concern. Serving a larger source could improve detail and undo the transfer savings for everyone.

Without mature source-selection support, I treated high density as one input and not an automatic command to double every dimension. The actual rendered size, subject detail, compression, and connection still mattered.

For small icons, vector or CSS treatments sometimes provided a cleaner answer. For photographs, the explicit inspection link preserved access to detail. The default remained a balanced summary asset.

I avoided device detection on the server as a general solution. User-agent classification aged quickly, intermediaries cached responses, and screen density did not reveal available bandwidth or the person’s need.

The limitation stayed visible in the planning document: the 2012 delivery model optimized a dependable first view and did not claim perfect selection for every display.

That restraint mattered. A system can be historically honest and still make a useful choice before the platform has its later capabilities.

The original data imported image filenames and generated alternative text from the product name. That was adequate for some identity images and repetitive in contexts where the name already appeared beside them.

I made image purpose explicit. A product photograph that added recognition received a concise description of the distinguishing view when useful. A redundant thumbnail beside a complete product heading could use empty alternative text. A diagram required text describing the information the diagram conveyed, not its visual style alone.

The catalogue did not use alternative text as a place to repeat every specification. The nearby structured content already served that job.

Image credits and source information entered the asset record separately. A caption appeared when it added context; attribution remained available without forcing it into alternative text.

This work connected accessibility and performance through purpose. Once the project knew what an image contributed, it could decide how to describe it, which role derivative it needed, and whether it belonged in the first view at all.

Cache names had to change with content

Long cache lifetimes were attractive for generated derivatives. My first URLs used stable names, so replacing a source photograph could leave an old cached derivative under the same address.

Content-fingerprinted output solved that conflict. Changed source or recipe produced a new URL. Unchanged derivatives could be cached for a long time.

HTML stayed comparatively short-lived and referenced the current manifest. Old HTML could still reference an older derivative, so release cleanup retained assets alongside the releases that used them.

The cache design therefore followed artifact identity rather than a collection of invalidation rules. The browser did not need to be told that image-small.jpg had changed; the new content had a new name.

This was one of the few image-pipeline decisions that improved both performance and release safety, so I kept it as the default for subsequent releases.

I kept a build report sorted by contribution to the initial scenario. The largest product photograph remained at the top until the summary derivative replaced it. After that, the next image—not a tiny script—became the obvious target.

This prevented optimization from following convenience. It is satisfying to remove a small dependency or compress an icon. The report kept attention on the resources that materially shaped the experience.

I also recorded decoded dimensions. A compressed image can be modest on the network and expensive in memory after decoding. The early phone tests made that cost visible when several large-dimension images caused slow scrolling and tab reloads.

Reducing intrinsic dimensions for each role improved transfer, decode, and memory together. CSS-only resizing had improved none of them.

The investigation broadened my definition of an image budget. Bytes were the first problem, not the only one.

The initial responsive page used one desktop source and declared victory because the box shrank. It confused presentation with delivery.

The first crop recipe optimized alignment and removed identifying details. Complete-object thumbnails became the baseline.

I lowered a universal compression setting and damaged labels on high-detail photographs. Role-based recipes and visual contact sheets replaced the single number.

After making each image smaller, I increased the count and lost the aggregate gain. The budget moved to the whole first-view scenario.

The lazy-loading experiment delayed work and weakened the baseline. Bounded server-rendered pages were a more responsible choice for that release and browser range.

Stable derivative filenames made cache invalidation ambiguous. Content fingerprints tied the URL to the artifact.

Each correction moved the project away from treating images as decoration exported at the end. Images became content, generated artifacts, layout participants, cached resources, and sometimes optional high-detail requests.

The image is part of the interface contract

By the end of the investigation, a product image carried more than a source path. It had a purpose, intrinsic dimensions, derivative roles, transformation recipe, output identity, description policy, source record, and position in a page budget.

That may sound like too much machinery for a personal catalogue. Most of it was a small manifest and build step. The important change was conceptual: the image’s visual role and delivery behavior could be reviewed together.

The design no longer asked only, “How large should this photograph appear?” It asked:

  • What information does it add here?
  • Which version supports that task?
  • When should the browser request it?
  • What space should exist before it arrives?
  • What remains when it does not arrive?
  • How will the release identify and cache it?

Those questions survived later platform improvements. srcset, sizes, modern formats, native lazy loading, and image components would provide better mechanisms. They would not decide the product role automatically.

C62Y’s image problem was the first time network cost altered my visual design before implementation was “finished.” It made performance part of composition rather than a score applied afterward.

I also stopped accepting a local warm-cache reload as representative review. The image scenario began from an empty cache under throttling, then repeated with a populated cache to verify that fingerprints and headers earned the faster return. Both mattered; the first exposed the cost of acquisition, and the second exposed whether the release model allowed the browser to reuse what had not changed.

The paired tests kept optimization grounded. A fast repeat visit could not excuse an unreasonable first view, and a strong first view could not excuse downloading identical derivatives on every navigation.

The next article moves from resource selection back to layout timing: a breakpoint should appear where content relationships need a new rule, not where a device chart happens to place a familiar width.