Styling Images
Images sell products, introduce people and make articles worth reading. But an image that stretches, overflows a phone screen, or jumps the page around as it loads makes the whole site feel broken. A handful of CSS properties fix all of these problems and add a polished look on top.
In this lesson you will make images responsive, crop them neatly into any shape of box, create circular avatars, style captions and loading placeholders, and build smooth hover effects. The HTML side, such as alt text, srcset and <picture>, is covered in HTML Images and Responsive Images.
The responsive baseline
Start every project with this rule:
img {
max-width: 100%; /* never wider than the container */
height: auto; /* keep the original proportions */
display: block; /* remove the small gap below inline images */
}Keep the width and height attributes in your HTML, such as width="800" height="500". The browser uses them to reserve space with the correct proportions before the file arrives, so the text below does not jump. height: auto is important here: without it, the height attribute would fix the image at 500 pixels tall even after max-width shrank its width, and the picture would look squashed or letterboxed.
For CSS background images, image-set() lets the browser choose a sharper file on high-resolution screens:
.hero {
background-image: image-set("hero-1x.webp" 1x, "hero-2x.webp" 2x);
}Cropping with object-fit and object-position
You met object-fit in Sizing, Aspect Ratio & Overflow. It decides how an image fills a box that has a different shape from the picture:
| Value | Result |
|---|---|
fill | Default. Stretches to fill the box and distorts the picture |
cover | Fills the whole box and crops the edges |
contain | Shows the whole picture, leaving empty bands |
scale-down | Like contain, but never enlarges a small image |
none | Keeps the original size and crops whatever does not fit |
object-position picks which part stays visible when the image is cropped. It works like background-position: center is the default, and you can use keywords such as top or percentages such as 50% 20% (centred across, near the top).
Combined with aspect-ratio, this turns photos of every shape into a tidy, matching grid:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Uniform thumbnails</title>
<style>
body { font-family: system-ui, sans-serif; padding: 1rem; }
.grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(9rem, 1fr)); gap: 0.75rem; max-width: 40rem; }
.grid figure { margin: 0; }
.grid img {
width: 100%;
height: auto;
aspect-ratio: 4 / 3; /* every thumbnail has the same shape */
object-fit: cover; /* crop instead of stretching */
border-radius: 8px;
background: #e2e8f0; /* placeholder colour while loading */
}
.grid .top { object-position: 50% 15%; } /* keep the top of the tower in view */
figcaption { font-size: 0.85rem; margin-top: 0.3rem; color: #475569; }
</style>
</head>
<body>
<div class="grid">
<figure><img src="https://course.ethicscomputer.in/assets/samples/landscape.svg" alt="Hills and a river at sunrise" width="800" height="500"><figcaption>Landscape, cover</figcaption></figure>
<figure><img src="https://course.ethicscomputer.in/assets/samples/portrait.svg" alt="Tall brick clock tower" width="400" height="600"><figcaption>Portrait, cover (centre)</figcaption></figure>
<figure><img class="top" src="https://course.ethicscomputer.in/assets/samples/portrait.svg" alt="Tall brick clock tower" width="400" height="600"><figcaption>Portrait, 50% 15%</figcaption></figure>
</div>
</body>
</html>The tall clock tower and the wide landscape now share the same 4:3 frame. The middle thumbnail shows the centre of the tower, which cuts off the clock. The last one uses object-position: 50% 15% so the clock face and roof stay in view. Change the ratio to 1 to make square thumbnails.
Rounded and circular images
border-radius rounds an image's corners just like any box. A value of 50% makes a circle, but only if the image is square. On a rectangle, 50% gives an oval. The safe recipe for avatars sets the shape first and then crops:
.avatar {
width: 4rem;
height: auto;
aspect-ratio: 1; /* force a square */
object-fit: cover; /* crop any photo to fill it */
border-radius: 50%; /* then round it into a circle */
}To add a ring around an avatar without changing its size, use outline with outline-offset, or a border in the page background colour when avatars overlap each other.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Avatars</title>
<style>
body { font-family: system-ui, sans-serif; padding: 1.5rem; background: #f8fafc; }
.avatar { width: 5rem; height: auto; aspect-ratio: 1; object-fit: cover; border-radius: 50%; display: block; }
.row { display: flex; gap: 1.5rem; align-items: center; margin-bottom: 2rem; }
.ring { outline: 3px solid #16a34a; outline-offset: 3px; } /* "online" ring */
.oval { width: 8rem; height: 5rem; border-radius: 50%; object-fit: cover; }
.group { display: flex; }
.group img { width: 3rem; border: 3px solid #f8fafc; } /* border matches the page */
.group img + img { margin-left: -0.9rem; } /* overlap */
</style>
</head>
<body>
<div class="row">
<img class="avatar" src="https://course.ethicscomputer.in/assets/samples/avatar.svg" alt="Cartoon avatar of a smiling person" width="200" height="200">
<img class="avatar ring" src="https://course.ethicscomputer.in/assets/samples/avatar.svg" alt="Cartoon avatar of a smiling person, shown as online" width="200" height="200">
<img class="oval" src="https://course.ethicscomputer.in/assets/samples/landscape.svg" alt="Hills and a river at sunrise" width="800" height="500">
</div>
<div class="group" role="group" aria-label="Members of the Pune cycling club">
<img class="avatar" src="https://course.ethicscomputer.in/assets/samples/avatar.svg" alt="Cartoon avatar of a smiling person" width="200" height="200">
<img class="avatar" src="https://course.ethicscomputer.in/assets/samples/avatar.svg" alt="Cartoon avatar of a smiling person" width="200" height="200">
<img class="avatar" src="https://course.ethicscomputer.in/assets/samples/avatar.svg" alt="Cartoon avatar of a smiling person" width="200" height="200">
</div>
</body>
</html>The ring sits 3 pixels away from the circle and never changes the layout. The landscape with border-radius: 50% becomes an oval, because its box is not square. In the group, each avatar slides under the next one, and the border in the page colour draws a clean gap between them.
Captions, placeholders and broken images
A few small touches make images feel finished:
figure { margin: 0; }
figcaption {
font-size: 0.875rem;
color: #475569;
padding-block-start: 0.4rem;
}
img {
background-color: #e2e8f0; /* grey box until the image loads */
font-style: italic; /* styles the alt text if the image fails */
}
.pixel-art {
image-rendering: pixelated; /* sharp squares when small images are scaled up */
}The background colour shows while the file downloads, and behind any transparent parts of PNG or SVG images, so pick a light neutral colour. If an image fails to load, many browsers show its alt text inside the box, and that text uses the image's font styles. image-rendering: pixelated keeps hard edges when you enlarge pixel art or a QR code, instead of blurring them.
Hover effects
A popular effect zooms the photo slightly when you point at a card, while the card itself stays the same size. The trick is a frame with overflow: hidden around the image, so the bigger picture is cropped by the frame.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Image hover</title>
<style>
body { font-family: system-ui, sans-serif; padding: 1.5rem; }
.tour { display: block; position: relative; max-width: 20rem; border-radius: 12px; overflow: hidden; color: white; text-decoration: none; }
.tour img { width: 100%; height: auto; display: block; transition: scale 0.4s ease, filter 0.4s ease; }
.tour .caption {
position: absolute;
inset: auto 0 0 0;
padding: 2rem 1rem 0.8rem;
background: linear-gradient(transparent, rgb(0 0 0 / 70%));
font-weight: 600;
}
.tour:hover img, .tour:focus-visible img { scale: 1.08; filter: saturate(1.3); }
.tour:focus-visible { outline: 3px solid #f59e0b; outline-offset: 3px; }
@media (prefers-reduced-motion: reduce) {
.tour img { transition: none; }
}
</style>
</head>
<body>
<a class="tour" href="#munnar">
<img src="https://course.ethicscomputer.in/assets/samples/landscape.svg" alt="Green hills and a river at sunrise in Munnar" width="800" height="500">
<span class="caption">Munnar sunrise trek, ₹2,499</span>
</a>
</body>
</html>Point at the card, or press Tab to focus it: the photo grows by 8% and gets a little more colourful, but the rounded frame stays fixed, because overflow: hidden crops the extra. The caption sits on a dark gradient so the white text stays readable over any part of the photo. Hover effects also apply on keyboard focus with :focus-visible, and people who ask their system for reduced motion get an instant change instead of an animation.
For more effects, filter can turn photos grey, blur them or adjust brightness (see Filters & Blend Modes), and clip-path can cut images into circles, hexagons and other shapes (see Clip-path, Shapes & Masks).
Common mistakes
max-width: 100%withoutheight: auto. Theheightattribute keeps the old height and the image looks squashed.border-radius: 50%on a rectangle. You get an oval. Addaspect-ratio: 1andobject-fit: coverfirst.- Stretched thumbnails. Use
object-fit: coverinstead of letting the defaultfilldistort pictures. - Zoom effects that spill out of the card. Put
overflow: hiddenon the frame so the bigger picture is cropped. - Hover-only effects. Repeat them for
:focus-visible, and respectprefers-reduced-motion. - Text on photos without a backdrop. Add a gradient or solid layer behind captions.
Key points
img { max-width: 100%; height: auto; display: block; }is the responsive baseline; keepwidthandheightattributes in HTML.object-fit: covercrops an image to fill its box, andobject-positionchooses the visible part.aspect-ratiogives every image the same shape, whatever the original file.- Circular avatars need a square box:
aspect-ratio: 1,object-fit: cover,border-radius: 50%. - A light
background-coloracts as a loading placeholder, andimage-rendering: pixelatedkeeps pixel art sharp. - Hover zoom needs a frame with
overflow: hidden; also support:focus-visibleand reduced motion.
Exercise
Build a "Meet the team" section for a Jaipur travel agency with three people. Each person has the avatar sample image as a circular 6rem photo with a coloured ring, their name and role below. Above the team, add a holiday package card using the landscape image in a 16:9 frame with object-fit: cover, rounded corners, a caption on a dark gradient and a gentle zoom on hover and keyboard focus.
Show solution
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Meet the team</title>
<style>
body { font-family: system-ui, sans-serif; padding: 1.5rem; }
.package { display: block; position: relative; max-width: 24rem; border-radius: 14px; overflow: hidden; color: white; text-decoration: none; margin-bottom: 2rem; }
.package img { width: 100%; height: auto; aspect-ratio: 16 / 9; object-fit: cover; display: block; transition: scale 0.4s; }
.package span { position: absolute; inset: auto 0 0; padding: 2rem 1rem 0.75rem; background: linear-gradient(transparent, rgb(0 0 0 / 70%)); }
.package:hover img, .package:focus-visible img { scale: 1.06; }
@media (prefers-reduced-motion: reduce) { .package img { transition: none; } }
.team { display: flex; flex-wrap: wrap; gap: 2rem; }
.team figure { margin: 0; text-align: center; }
.team img { width: 6rem; height: auto; aspect-ratio: 1; object-fit: cover; border-radius: 50%; outline: 3px solid #e11d48; outline-offset: 3px; }
.team figcaption { margin-top: 0.6rem; }
.team small { display: block; color: #64748b; }
</style>
</head>
<body>
<a class="package" href="#aravalli">
<img src="https://course.ethicscomputer.in/assets/samples/landscape.svg" alt="Hills and a river at sunrise" width="800" height="500">
<span>Aravalli hills and lakes, 5 days, ₹18,999</span>
</a>
<div class="team">
<figure><img src="https://course.ethicscomputer.in/assets/samples/avatar.svg" alt="Cartoon avatar of a smiling person" width="200" height="200"><figcaption>Rohan Mehta<small>Tour manager</small></figcaption></figure>
<figure><img src="https://course.ethicscomputer.in/assets/samples/avatar.svg" alt="Cartoon avatar of a smiling person" width="200" height="200"><figcaption>Ananya Singh<small>Bookings</small></figcaption></figure>
<figure><img src="https://course.ethicscomputer.in/assets/samples/avatar.svg" alt="Cartoon avatar of a smiling person" width="200" height="200"><figcaption>Kabir Rathore<small>Guide</small></figcaption></figure>
</div>
</body>
</html>The package image is cropped to 16:9 and only the picture zooms, because the link is the frame with overflow: hidden. Each avatar is forced square before border-radius: 50% turns it into a circle, and the outline ring does not change its size.
Quick Quiz
1. Why add height: auto next to max-width: 100% on images?
Without height: auto, the height attribute fixes the height while the width shrinks, which squashes the picture.
2. An image is 8rem wide and 5rem tall with border-radius: 50%. What shape is it?
50% of each side gives elliptical corners; only a square box becomes a circle.
3. Which property chooses which part of a cropped image stays visible?
object-fit: cover crops the image, and object-position decides where the crop is centred.
4. On hover, a zoomed photo spills outside its rounded card. What is missing?
The frame keeps its size and shape and crops the enlarged image inside it.
Learn CSS with a mentor
Free lessons get you started. Our Full Stack Web Development training adds real projects and code reviews from working developers.
- Project-based learning
- Mentor code reviews
- Free counselling call
