Design Archive
中EN

A hero image, from full-screen to a spec

On a short laptop screen, the reference site's hero image filled 97% of the viewport. I set out to learn how its height changed, and ended up moving the complexity out of the code and into the image spec.

L.08 / 14
CATEGORY
Study
YEAR
2026
STATUS
Note
TOOLS
css / object-fit / srcset

entryWrite-up

I was building the portal site for a logistics platform. At the top of the home page sits a full-width hero image, a carousel of aerial photos of ports, ship locks and cargo ships, and the design took its cue from another logistics platform’s website. On shorter screens the reference site’s hero almost filled the whole browser window, and nothing below it was visible at a glance; I wanted to know how its height changed, and what the right approach was. Three terms first. The area of the browser actually available to the page is called the viewport, and it is not the same as the screen resolution. When an image scales proportionally until it fills the container and the overflow is cropped, with no distortion and no empty space, that is a cover crop, CSS object-fit: cover. The canvas size the designer draws the hero on is called the artboard.

I had assumed the reference site’s hero height changed continuously with the window. Measuring at several sizes in a real browser and reading the live CSS showed only two rules: 700px by default, and 608px when the screen is 1536px wide or less. Height came in two fixed pixel steps chosen by width, with no regard for how tall the screen was. On a 1366×768 laptop, the visible height after the browser toolbars is about 625px, so the 608px hero took 97% of the viewport; in the same step, a 768×1024 portrait screen gave it only 59%. Two more problems came with it: the image was laid out with background-size: 100% 100%, so whenever the container’s proportions changed, the picture stretched unevenly; and the carousel and the bottom bar in the hero had fixed pixel heights too, so capping the container’s height alone would clip content. The root cause was using width breakpoints to solve a height problem; height problems should be solved with units tied to the viewport height (vh, and svh, which does not jump when the mobile address bar collapses and expands).

Share of the viewport height taken by the reference site's hero on different screens.

My own requirements were three: the hero takes 80% of the viewport height, leaving 20% for the next screen so people know there is more below; the asset was an ultra-wide 3840×800 image, and the cargo ship in it had to stay fully visible on every screen; and the image scales proportionally without distortion. Cover satisfies the third by nature. The second is the hard one: cover crops the sides, and how much depends on the screen’s aspect ratio. Measured pixel by pixel, the ship, including bow and wake, spans 53.6% to 68.2% of the image width, or 14.6% of it. The visible fraction of the image width is f = viewport width ÷ (0.8 × viewport height × 4.8), where 4.8 is the asset’s aspect ratio; as long as the visible window covers the 53.6% to 68.2% stretch, the ship stays whole. The first version aimed the crop focus at the ship (object-position: 63%), which kept it on every desktop screen. The same calculation showed that on a portrait phone f is only 12%, smaller than the ship’s 14.6%, so the three requirements cannot all hold mathematically; phones were set aside for now.

Then came a piece of context: the image had been designed on a 1920×1080 artboard, and the ship was meant to sit in the lower right; that was the composition. Moving the focus onto the ship dragged it toward the center of the frame, which was safer mathematically but broke the intended composition. The second version switched to a strict center crop and protected the ship through a different variable: with a centered crop the visible window runs from 50% − f/2 to 50% + f/2; to reach the ship’s right end at 68.2% needs f ≥ 36.5%, which works out to a viewport aspect ratio ≥ 1.40. Desktop screens at 16:9, 16:10 and 3:2 all meet it; only 4:3 falls a little short, so height: min(80svh, 56vw) acts as a backstop, lowering the height to about 75% only on narrow screens like 4:3, and both the composition and the ship survive. A second slide was added later, a ship-lock panorama with its subject in the center, and the center crop cost it nothing: however the window narrows, the background is cropped evenly from both sides and the subject stays in the middle of the screen. New slides are checked with one formula: f ≥ 2 × max(subject’s right edge − 50%, 50% − subject’s left edge). Before choosing a crop, work out what the image is meant to say.

The solution worked, but the code was full of things that existed only for that off-center ship: a height cap, a focus coordinate, a check formula. I asked a question: if the ship’s position were no longer a constraint, could the solution be simpler? It could, down to a few lines: set height to 80vh first as a fallback for older browsers, then 80svh so the mobile address bar collapsing and expanding does not make it jump; hide the overflow; make the image fill the width and height, and add object-fit: cover. The constraint does not vanish; it moves from the code into the asset spec: once the code stops covering for the asset, the asset has to keep its subject in the area that is visible on every screen. I took that trade, because the code then holds only general rules and does not change for any single image.

Next was whether the 3840 width was still needed; it had been chosen to suit 4K screens and avoid empty space at the sides. Cover prevents empty sides regardless of the asset’s width; when a 4K screen shows the hero full screen, the visible part is itself 3840 physical pixels wide, so 3840 is the physical floor for sharpness and cannot be reduced. The real waste was the aspect ratio: a 4.8:1 image shows only its middle 46% on a 16:9 screen, and on 4K the hero is 1728px tall while the asset is only 800px, upscaled 2.16× and soft. So the artboard was set to 1920×864, the hero’s real shape in a 1920×1080 viewport (2.222:1), exported at 2× as 3840×1728. At the same 3840 width, the new ratio shows the whole image on a 16:9 screen, so the design file is what appears; and on 4K the 1728px height is exactly 1:1, no longer soft. The asset’s ratio is set from the most common viewport and its resolution worked out from the physical pixels of the highest target screen, so the asset’s shape matches the container’s real shape.

The old ratio compared with the new one.

Once the artboard was fixed, what each screen crops became plain geometry. I drew that geometry as a template the designer can lay semi-transparently over the top of the artboard as guides. In the center is the core safe zone: subjects and anything that must stay whole go only here, and it is fully visible on every target screen. The red bands at left and right are cropped first when the viewport is narrower than 16:9; the yellow bands inside them are reached only by narrower viewports such as 4:3; the green bands at top and bottom are cropped when the viewport is wider than 16:9. Three more rules: all four edges fade into the brand blue so any crop looks natural; the composition is anchored on the center of the artboard; no text is drawn into the image, and headings are overlaid as page elements.

The hero safe-zone template (1920×864 artboard).

With the spec and the demo page done, a test on a 1440×900 screen showed the hero whole, without the expected crop into the red bands at the sides. The reason is that screen resolution is not the viewport: the browser’s tab bar and address bar take about 90 to 140px of height, so in everyday browsing a 1440×900 screen has a viewport of only about 1440×770. The hero container becomes 1440×616, a ratio of 2.34, flatter than the artboard’s 2.222, and the cover crop flips direction: instead of cropping the sides it keeps the full width and trims only top and bottom, about 21px each, still inside the green bands. The earlier checks had used an exact 1440×900 viewport, the same as pressing F11 for full screen, which is why they showed the side crop. One rule decides the crop direction: if the viewport height is greater than viewport width ÷ 1.778, the sides are cropped; otherwise top and bottom are. At 1440 wide the threshold is 810px. The spec changed in one place: the 108px green bands at top and bottom, previously treated as an optional constraint that only mattered on ultra-wide monitors wider than 16:9, became a regular constraint, because everyday browsing with toolbars reaches them; a 1920×1080 screen with toolbars crops about 50px from top and bottom. Checks should be done in a browser window with its toolbars showing.

The same template cropped in four viewports.

2K (2560×1440) and 4K (3840×2160) are both 16:9, the same ratio as the artboard, so the composition is identical to 1080p. For sharpness, the hero needs a physical height of 0.8 times the screen’s physical height: 2K scales down and stays sharp; 4K is exactly 1:1; 5K and 6K Retina screens and 5120-wide ultra-wide screens upscale about 1.33× and look slightly soft, and a 3× export could be added. With the new ratio, the image on a 16:9 screen is displayed exactly as wide as the viewport, so the front end can use srcset (the attribute that lets the browser pick an image source to suit the screen) with sizes="100vw", and the browser picks the 1× image for standard screens and the 2× image for high-density ones without getting it wrong.

Two real photos were re-exported to the spec as 3840×1728 WebP files and put into the carousel demo page with the safe-zone template; the template, as the third slide, works as a calibration ruler: switch to it at any time and the current crop can be checked against the bands. Spot-checking the ship image: the stacked shipping containers span 56.8% to 86.5% of the image width, and with bow and wake the ship reaches about x≈1700 on the artboard, finishing roughly at the core safe zone’s right edge (1680) without entering the red or yellow bands. The subject sits right of center for movement, which is the composition the spec intends. The one small issue is file size: the ship image is 523KB, a little over the suggested 400KB budget, and one step down in export quality will fix it.

The ship-lock panorama exported to the spec, in a 1440×900 viewport.

It started as a very small problem, a hero that was too tall on some screens, and it ended as a few lines of front-end code, plus an image spec for designers and a safe-zone template. Portrait phones were not handled.

processBefore & after

Front end (all the rules)

css
.hero {
  position: relative;
  width: 100%;
  height: 80vh;
  height: 80svh;
  overflow: hidden;
}
.slide {
  position: absolute;
  inset: 0;
  opacity: 0;
  transition: opacity 0.8s ease;
}
.slide.active {
  opacity: 1;
}
.slide img {
  width: 100%;
  height: 100%;
  object-fit: cover;
}

Safe zones (1920×864 artboard)

Core safe zone
Central 1440×648 · subjects and anything that must stay whole go only here
Red bands
96px on each side · cropped first when the viewport is narrower than 16:9
Yellow bands
96–240px from each side · reached only by narrower viewports such as 4:3
Green bands
108px top and bottom · cropped when the viewport is wider than 16:9, common in everyday browsing

High-density screens

2K
Needs 1152px · asset 1728px · scaled down, sharp
4K
Needs 1728px · asset 1728px · exactly 1:1
5K / 6K Retina, 5120-wide ultra-wide
About 1.33× upscale · slightly soft; a 3× export could be added

resultTakeaways

Finding
The front end is down to a few lines: 80svh plus object-fit: cover. Composition constraints moved into a safe-zone template on a 1920×864 artboard.
Parameters
Artboard 1920×864 · exported at 1× and 2× (3840×1728) · core safe zone 1440×648 · red band 96px · yellow band 96–240px · green band 108px
Open
Portrait phones are not handled; 5K and 6K screens upscale about 1.33× and look slightly soft; the ship image is 523KB, a little over the suggested 400KB budget.

logChangelog

2026-08-28
First entry

Related experiments

note
L.12(STUDY)2026

One solver, ten resolutions, no distortion

responsivecssclamp+1
note
L.13(STUDY)2026

Translating design constraints into CSS front-end can use as is

responsivecssflexbox+1
note
L.01(STUDY)2026

(1) Choosing a base model

chest-xraypytorchrad-dino+1
← LabL.08 / 14
1 / 1

100%