3. Responsive Images — srcset and <picture>
What are Responsive Images?
Responsive images are images that adapt to the user's device, viewport size, and display conditions.
For example, a website may be opened on:
📱 Mobile → 390px wide
📱 Tablet → 768px wide
💻 Laptop → 1366px wide
🖥️ Desktop → 1920px wide
Instead of always downloading a very large image, we can provide different image versions and allow the browser to choose an appropriate one.
The main technologies used for responsive images are:
srcset
sizes
<picture>
<source>
Why Do We Need Responsive Images?
Suppose we have:
hero.jpg
4000 × 3000
5 MB
But on a mobile phone, the image is displayed at only:
390px wide
Downloading a huge 4000px image for a small display can waste:
- Bandwidth
- Loading time
- Mobile data
- Browser resources
Responsive images allow us to provide multiple versions:
hero-400.jpg
hero-800.jpg
hero-1200.jpg
hero-1600.jpg
Then the browser can choose an appropriate image.
Normal <img>
A normal image looks like:
<img src="hero.jpg" alt="Mountain landscape">
Here:
src
↓
The image file to load
alt
↓
Alternative text describing the image
The problem is that there is only one image source.
srcset
srcset allows us to provide multiple versions of the same image with different resolutions.
Example:
<img
src="hero-800.jpg"
srcset="
hero-400.jpg 400w,
hero-800.jpg 800w,
hero-1200.jpg 1200w
"
alt="Mountain landscape"
>
We are telling the browser:
Here are different versions of the same image.
400w → 400px wide image
800w → 800px wide image
1200w → 1200px wide image
The browser can then choose an appropriate candidate.
What does w mean?
The w descriptor represents the intrinsic width of the image file.
For example:
hero-400.jpg 400w
means:
This image file is 400px wide.
Similarly:
hero-800.jpg 800w
means:
This image file is 800px wide.
So:
400w → 400px image
800w → 800px image
1200w → 1200px image
How Does the Browser Choose?
Suppose we provide:
<img
srcset="
image-400.jpg 400w,
image-800.jpg 800w,
image-1200.jpg 1200w
"
>
The browser considers factors such as:
- Viewport size
- How large the image will be displayed
- Device pixel density
- Available image candidates
and chooses an appropriate image.
The important idea is:
srcset
↓
provides choices
↓
browser evaluates them
↓
browser chooses a suitable candidate
We don't manually have to write JavaScript to detect the screen size.
What is sizes?
sizes tells the browser:
How wide the image is expected to be displayed in the layout.
Example:
<img
src="hero-800.jpg"
srcset="
hero-400.jpg 400w,
hero-800.jpg 800w,
hero-1200.jpg 1200w
"
sizes="100vw"
alt="Mountain landscape"
>
Here:
sizes="100vw"
means:
The image is expected to occupy approximately 100% of the viewport width.
srcset vs sizes
This is one of the most important things to understand.
srcset
↓
WHAT IMAGE FILES ARE AVAILABLE?
sizes
↓
HOW LARGE WILL THE IMAGE APPEAR?
For example:
<img
srcset="
image-400.jpg 400w,
image-800.jpg 800w,
image-1200.jpg 1200w
"
sizes="100vw"
>
Here:
srcset
→ available image candidates
sizes
→ expected displayed width
sizes Does NOT Resize the Image
This is a common beginner mistake.
If we write:
sizes="50vw"
it does not mean:
Make the image 50vw wide.
Instead, it tells the browser:
The image is expected to be displayed at approximately 50vw wide.
The actual visual size can be controlled with CSS.
For example:
img {
width: 50%;
}
Using Media Conditions Inside sizes
We can provide different expected sizes depending on the viewport.
Example:
<img
src="hero-800.jpg"
srcset="
hero-400.jpg 400w,
hero-800.jpg 800w,
hero-1200.jpg 1200w,
hero-1600.jpg 1600w
"
sizes="
(min-width: 1200px) 50vw,
(min-width: 768px) 70vw,
100vw
"
alt="Mountain landscape"
>
This means:
Viewport >= 1200px
→ image is expected to be about 50vw wide
Viewport >= 768px
→ image is expected to be about 70vw wide
Otherwise
→ image is expected to be about 100vw wide
This helps the browser choose an appropriate candidate from srcset.
Responsive Display vs Responsive Source
These are different concepts.
CSS:
img {
width: 100%;
height: auto;
}
controls:
How large the image appears on the page.
srcset controls:
Which image candidates are available to the browser.
sizes tells the browser:
How large the image is expected to appear.
So:
CSS
↓
controls displayed size
srcset
↓
provides image candidates
sizes
↓
describes expected displayed size
x Descriptors
srcset can also use x descriptors.
Example:
<img
src="image.jpg"
srcset="
image.jpg 1x,
image@2x.jpg 2x
"
alt="Logo"
>
Here:
1x
↓
normal pixel density
2x
↓
higher pixel density
This is useful for high-density displays.
For example, an image displayed at 200 CSS pixels wide may need more physical pixels on a high-density screen.
w vs x
w
Represents the intrinsic width of the image.
image-400.jpg 400w
image-800.jpg 800w
x
Represents pixel-density variants.
image.jpg 1x
image@2x.jpg 2x
For general responsive image layouts, w descriptors combined with sizes are very important to understand.
<picture>
<picture> is another HTML element used for responsive images.
It is especially useful when we need:
- Different image crops
- Different image compositions
- Different image formats
- Different image sources based on conditions
Basic structure:
<picture>
<source
media="(max-width: 600px)"
srcset="mobile.jpg"
>
<source
media="(min-width: 601px)"
srcset="desktop.jpg"
>
<img
src="desktop.jpg"
alt="Landscape"
>
</picture>
Why Use <picture>?
Imagine a desktop image:
┌────────────────────────────────────┐
│ │
│ PERSON │
│ │
│ Large background │
│ │
└────────────────────────────────────┘
On mobile, simply shrinking the same image might produce a poor composition.
Instead, we can create a mobile-specific crop:
┌──────────────┐
│ │
│ PERSON │
│ │
└──────────────┘
Then:
Mobile
→ mobile.jpg
Desktop
→ desktop.jpg
This is called art direction.
<picture> for Different Image Formats
<picture> can also be used to provide different image formats.
For example:
hero.avif
hero.webp
hero.jpg
We can write:
<picture>
<source
srcset="hero.avif"
type="image/avif"
>
<source
srcset="hero.webp"
type="image/webp"
>
<img
src="hero.jpg"
alt="Mountain landscape"
>
</picture>
The browser can use a supported format and fall back to the <img> source when necessary.
What is <source>?
<source> provides an alternative image source inside <picture>.
For example:
<source
srcset="hero.webp"
type="image/webp"
>
means:
This is an available WebP image source.
Another example:
<source
media="(max-width: 600px)"
srcset="mobile.jpg"
>
means:
Use this source when the specified media condition matches.
The <img> element inside <picture> acts as the fallback/default image.
srcset vs <picture>
This is the most important comparison in responsive images.
srcset
Usually used when we have:
The same image in different sizes.
Example:
hero-400.jpg
hero-800.jpg
hero-1200.jpg
Think:
SAME IMAGE
+
DIFFERENT SIZES
<picture>
Used when we need:
Different image sources depending on conditions.
For example:
Mobile → mobile-crop.jpg
Desktop → desktop-crop.jpg
or:
AVIF → WebP → JPEG
Think:
DIFFERENT SOURCES
+
DIFFERENT CONDITIONS / FORMATS
Simple Mental Model
Remember:
srcset
↓
"Here are different-sized versions
of the same image."
sizes
↓
"Here is approximately how large
the image will appear."
<picture>
↓
"Here are different image sources
depending on the situation."
Complete Responsive Image Example
<img
src="hero-800.jpg"
srcset="
hero-400.jpg 400w,
hero-800.jpg 800w,
hero-1200.jpg 1200w,
hero-1600.jpg 1600w
"
sizes="
(min-width: 1200px) 50vw,
(min-width: 768px) 70vw,
100vw
"
alt="Mountain landscape"
>
The structure is:
src
↓
fallback/default source
srcset
↓
different image candidates
sizes
↓
expected displayed width
alt
↓
alternative text
When Should You Use srcset?
Use srcset when you have:
Same image
↓
Different resolutions
For example:
product-400.jpg
product-800.jpg
product-1200.jpg
When Should You Use <picture>?
Use <picture> when you need:
Different crop
Different composition
Different image format
Different source based on conditions
For example:
Mobile → portrait image
Desktop → landscape image
or:
AVIF → WebP → JPEG
Important Summary
Responsive Images
│
├── srcset
│ ↓
│ multiple image candidates
│
├── sizes
│ ↓
│ expected displayed width
│
└── picture
↓
different image sources
/ formats / crops
The main goal is:
Deliver an appropriate image resource instead of unnecessarily downloading a huge image for every device.
4. CSS Methodologies — BEM Naming
What is a CSS Methodology?
A CSS methodology is a systematic approach for writing, naming, and organizing CSS so that the code remains understandable and maintainable as a project becomes larger.
In a small project, we might write:
<div class="box">
<h2 class="title">Product</h2>
<button class="button">Buy</button>
</div>
and:
.box {
...
}
.title {
...
}
.button {
...
}
This may work for a small project.
But in a large application, we might have:
100+ components
500+ classes
multiple developers
many pages
Now generic class names such as:
.title
.button
.container
.box
.item
can become confusing and cause style conflicts.
CSS methodologies provide conventions to solve these problems.
Examples of CSS Methodologies
Some commonly known CSS approaches are:
BEM
OOCSS
SMACSS
ITCSS
Utility-first CSS
CSS Modules
Each approach has a different philosophy for organizing CSS.
For this topic, the most important one is:
BEM
What is BEM?
BEM stands for:
B → Block
E → Element
M → Modifier
BEM is a naming methodology that helps us create clear and predictable CSS class names.
The main idea is to describe:
What is the component?
What parts belong to it?
What variations does it have?
BEM Syntax
The basic BEM naming patterns are:
Block
block
Element
block__element
Modifier
block--modifier
Element Modifier
block__element--modifier
The two important symbols are:
__ → Element
-- → Modifier
1. Block
A Block is an independent, reusable component.
Examples:
card
navbar
header
footer
button
form
modal
search
product-card
Example:
<div class="card">
Here:
card
↓
Block
A Block should be meaningful and independent.
For example:
Card
Navbar
Button
Modal
can exist as separate UI components.
Real-Life Analogy for a Block
Think of a LEGO block.
🧱
It is an independent piece that can be reused in different places.
Similarly:
.card
is an independent UI component.
2. Element
An Element is a meaningful part of a Block.
BEM uses two underscores:
__
The syntax is:
block__element
For example:
card__image
card__title
card__description
card__button
Here:
card
↓
Block
title
↓
Element
card__title
↓
Element belonging to card
Example of Block + Elements
<div class="card">
<img class="card__image" src="phone.jpg" alt="Phone">
<h2 class="card__title">
Smartphone
</h2>
<p class="card__description">
A powerful smartphone.
</p>
<button class="card__button">
Buy Now
</button>
</div>
The structure is:
card
│
├── card__image
├── card__title
├── card__description
└── card__button
3. Modifier
A Modifier represents a variation, state, or different version of a Block or Element.
BEM uses:
--
The syntax is:
block--modifier
Example:
card--featured
card--dark
card--large
For example:
<div class="card card--featured">
Here:
card
↓
Block
card--featured
↓
Modifier
The card is still a card, but it is the featured version of the card.
Why Use Both Classes?
A common BEM pattern is:
<button class="button button--primary">
Submit
</button>
Here:
button
↓
Base Block
button--primary
↓
Modifier
The base class provides the common styles:
.button {
padding: 10px 20px;
border-radius: 8px;
}
The modifier provides the variation:
.button--primary {
background: blue;
color: white;
}
So:
button
+
button--primary
↓
Primary button
Modifiers Can Represent Different States
Modifiers are not only for colors.
They can represent:
Different types
button--primary
button--secondary
button--danger
Different sizes
button--small
button--large
Different themes
card--dark
card--light
Different states
button--disabled
menu--open
input--invalid
Element Modifiers
Elements can also have modifiers.
Example:
card__title
Normal title.
A highlighted version:
card__title--highlighted
So the structure becomes:
card
│
└── card__title
│
└── card__title--highlighted
Complete BEM Example
Let's create a product card.
<article class="product-card product-card--featured">
<img
class="product-card__image"
src="laptop.jpg"
alt="Laptop"
>
<h2 class="product-card__title">
Laptop
</h2>
<p class="product-card__price">
₹80,000
</p>
<button class="product-card__button product-card__button--primary">
Buy Now
</button>
</article>
Let's identify everything.
Block
product-card
Block Modifier
product-card--featured
Elements
product-card__image
product-card__title
product-card__price
product-card__button
Element Modifier
product-card__button--primary
The structure is:
product-card
│
├── product-card--featured
│
├── product-card__image
├── product-card__title
├── product-card__price
│
└── product-card__button
│
└── product-card__button--primary
CSS for the BEM Example
.product-card {
padding: 20px;
border: 1px solid #ddd;
border-radius: 10px;
}
.product-card__image {
width: 100%;
}
.product-card__title {
font-size: 1.5rem;
}
.product-card__price {
font-size: 1.2rem;
}
.product-card__button {
padding: 10px 20px;
}
.product-card--featured {
border: 2px solid gold;
}
.product-card__button--primary {
background: blue;
color: white;
}
The names clearly tell us which component each style belongs to.
Real-Life Example: Navbar
A navigation bar can be represented as:
navbar
│
├── navbar__logo
├── navbar__menu
├── navbar__item
└── navbar__link
HTML:
<nav class="navbar">
<div class="navbar__logo">
MySite
</div>
<ul class="navbar__menu">
<li class="navbar__item">
<a class="navbar__link" href="#">
Home
</a>
</li>
<li class="navbar__item">
<a class="navbar__link" href="#">
About
</a>
</li>
</ul>
</nav>
A dark version could be:
<nav class="navbar navbar--dark">
Here:
navbar
↓
Block
navbar--dark
↓
Modifier
Real-Life Example: Login Form
Block:
login-form
Elements:
login-form__label
login-form__input
login-form__button
login-form__error
Example:
<form class="login-form">
<label class="login-form__label">
Email
</label>
<input
class="login-form__input"
type="email"
>
<button class="login-form__button">
Login
</button>
</form>
An invalid input could be:
<input class="login-form__input login-form__input--invalid">
Here:
login-form
↓
Block
login-form__input
↓
Element
login-form__input--invalid
↓
Element Modifier
HTML Nesting vs BEM Naming
One of the most important BEM rules is:
BEM naming does not have to follow the exact HTML nesting structure.
Suppose we have:
<div class="card">
<div class="card__header">
<h2 class="card__title">
Product
</h2>
</div>
</div>
Even though the title is inside the header, we normally don't write:
card__header__title
Instead:
card__header
card__title
Both are Elements of the card Block.
BEM describes logical relationships, not every level of HTML nesting.
Avoid Deep BEM Chains
Avoid names such as:
card__header__title__text
Instead, keep the relationship directly connected to the Block:
card__title
This keeps the naming simpler and easier to maintain.
Can a Block Contain Another Block?
Yes.
For example:
<div class="page">
<header class="header">
...
</header>
<article class="product-card">
...
</article>
<footer class="footer">
...
</footer>
</div>
Here:
page
header
product-card
footer
can all be independent Blocks.
If a component can exist independently and be reused, it can often be treated as a Block.
Block vs Element
A useful question to ask is:
Can this component exist independently?
If yes:
Block
If it is a meaningful part of another Block:
Element
For example:
product-card
↓
Block
product-card__title
↓
Element
But a reusable Button could be its own Block:
button
instead of:
product-card__button
if the Button is an independent reusable component.
BEM Is Not "Every HTML Element Gets a Class"
BEM does not mean:
card__div__h2__span
The goal is not to name every HTML tag.
The goal is to identify meaningful components and relationships.
For example:
card
card__title
card__price
card__button
is much more useful than naming every nested <div>.
Naming Modifiers by Meaning
A modifier can describe the meaning or role of a variation.
For example:
button--danger
is often better than:
button--red
Why?
Because the design may change.
Today:
danger → red
Tomorrow:
danger → orange
The meaning of the component hasn't changed.
Therefore:
button--danger
still makes sense.
BEM in React
BEM can also be used with React.
Example:
function ProductCard() {
return (
<article className="product-card">
<h2 className="product-card__title">
Laptop
</h2>
<p className="product-card__price">
₹80,000
</p>
</article>
);
}
CSS:
.product-card {
padding: 20px;
}
.product-card__title {
font-size: 1.5rem;
}
.product-card__price {
color: gray;
}
BEM works well with component-based development because the class names clearly describe the component structure.
Other CSS Methodologies
BEM is only one CSS methodology.
Some other approaches include:
BEM
OOCSS
SMACSS
ITCSS
Utility-first CSS
CSS Modules
OOCSS
OOCSS stands for:
Object-Oriented CSS
The idea is to create reusable CSS objects and separate structure from appearance.
For example:
<div class="card rounded shadow">
Conceptually:
card
↓
structure
rounded
↓
reusable appearance
shadow
↓
reusable appearance
The goal is to create reusable styling patterns instead of making everything completely unique.
SMACSS
SMACSS stands for:
Scalable and Modular Architecture for CSS
It organizes CSS into categories such as:
Base
Layout
Module
State
Theme
For example:
Base
↓
body, h1, p
Layout
↓
header, sidebar, grid
Module
↓
card, navbar, button
State
↓
is-active, is-hidden
Theme
↓
dark theme, light theme
The main idea is to organize CSS based on the role of each style.
ITCSS
ITCSS stands for:
Inverted Triangle CSS
It organizes CSS from lower specificity to higher specificity.
A simplified structure is:
Settings
Tools
Generic
Elements
Objects
Components
Utilities
The goal is to manage CSS specificity and make large stylesheets easier to control.
Utility-First CSS
Utility-first CSS uses small classes that each perform a specific styling task.
For example:
p-4
text-center
bg-blue-500
rounded-lg
flex
items-center
A popular example of a utility-first framework is Tailwind CSS.
Instead of creating:
.card {
padding: 20px;
background: white;
border-radius: 10px;
}
you can compose styles using utilities.
The detailed comparison between utility-first styling and component-scoped styling is a separate concept.
Why Are CSS Methodologies Useful?
As projects become larger, CSS can become difficult to maintain.
Without a clear methodology:
Generic class names
↓
Style conflicts
↓
Specificity problems
↓
More overrides
↓
!important
↓
Hard-to-maintain CSS
With a methodology:
Clear naming
↓
Predictable structure
↓
Easier maintenance
↓
Easier collaboration
↓
Scalable CSS
BEM Cheat Sheet
Block
.block
Independent component.
Example:
.card
Element
.block__element
Meaningful part of a Block.
Example:
.card__title
Modifier
.block--modifier
Variation or state of a Block.
Example:
.card--featured
Element Modifier
.block__element--modifier
Variation or state of an Element.
Example:
.card__title--highlighted
The Two Symbols to Remember
__ → Element
Read it as:
"part of this Block"
Example:
card__title
Title belonging to the card.
And:
-- → Modifier
Read it as:
"variation or state of this component"
Example:
card--featured
Featured version of the card.
Final Mental Model
When creating a BEM class, ask three questions:
1. Is this an independent component?
↓
BLOCK
2. Is this a meaningful part of the Block?
↓
ELEMENT
3. Is this a variation or state?
↓
MODIFIER
So:
BEM
│
┌──────────┼──────────┐
↓ ↓ ↓
BLOCK ELEMENT MODIFIER
│ │ │
card card__title card--featured
And the complete pattern is:
block
block__element
block--modifier
block__element--modifier
Final Takeaway
BEM is not a new CSS property or CSS feature.
It is a naming and organization methodology.
Its purpose is to make CSS class names predictable and understandable.
Remember:
Block
→ Independent component
Element
→ Part of a Block
Modifier
→ Variation or state
Example:
product-card
product-card__image
product-card__title
product-card__price
product-card__button
product-card--featured
product-card__button--primary
The most important rule is:
__means Element, and--means Modifier.
Once you understand Block → Element → Modifier, BEM becomes much easier to use in real projects.
Top comments (0)