1. Mobile-First Media Queries
What is a Media Query?
A media query is a CSS feature that allows us to apply different CSS styles based on the conditions of the device or viewport.
In simple words:
"If this condition is true, apply these CSS rules."
For example:
@media (min-width: 768px) {
.container {
display: flex;
}
}
This means:
If the viewport width is 768px or greater, apply the styles inside the media query.
Media queries are mainly used to create responsive websites that adapt to different screen sizes such as mobile phones, tablets, laptops, and desktops.
What is Responsive Web Design?
A responsive website automatically adapts its layout and appearance according to the available screen size.
For example:
Mobile:
[ Card 1 ]
[ Card 2 ]
[ Card 3 ]
Desktop:
[ Card 1 ] [ Card 2 ] [ Card 3 ]
The same website can use different layouts depending on the viewport size.
Basic Syntax of a Media Query
@media media-type and (condition) {
/* CSS rules */
}
Example:
@media screen and (max-width: 600px) {
body {
background: lightblue;
}
}
There are three important parts:
@media
↓
media type
↓
condition
↓
CSS rules
However, the media type is optional, so this is also very common:
@media (max-width: 600px) {
body {
background: lightblue;
}
}
Media Types
A media type specifies the type of output device or presentation.
1. screen
Used for screens such as:
- Mobile phones
- Tablets
- Laptops
- Desktop monitors
Example:
@media screen and (max-width: 600px) {
body {
font-size: 14px;
}
}
2. print
Used when the webpage is being printed or viewed in print preview.
For example, we may want to hide navigation when printing:
@media print {
nav {
display: none;
}
}
3. speech
Used for speech-based presentation, such as speech synthesizers and screen-reading technologies.
@media speech {
/* speech-related styles */
}
This is less commonly used in everyday responsive development.
4. all
Represents all media types.
@media all {
/* styles */
}
In normal responsive development, we usually don't need to explicitly write all.
Important Media Features
Media queries can check many different conditions.
The most commonly used ones are:
min-width
max-width
min-height
max-height
orientation
prefers-color-scheme
hover
pointer
resolution
min-width
min-width means:
"The viewport width must be greater than or equal to this value."
Example:
@media (min-width: 768px) {
.container {
display: flex;
}
}
This applies when:
768px or more → YES
767px or less → NO
Think of it as:
width >= 768px
max-width
max-width means:
"The viewport width must be less than or equal to this value."
Example:
@media (max-width: 768px) {
.container {
padding: 10px;
}
}
This applies when:
768px or less → YES
769px or more → NO
Think of it as:
width <= 768px
What is Mobile-First Design?
Mobile-first design means we first write the default CSS for smaller screens and then progressively add styles for larger screens.
The basic pattern is:
/* Mobile / default styles */
.container {
display: block;
}
/* Larger screens */
@media (min-width: 768px) {
.container {
display: flex;
}
}
The idea is:
Mobile
↓
Base CSS
↓
Tablet / larger screen
↓
min-width
↓
Desktop
So mobile-first development generally uses min-width media queries.
Example of Mobile-First Design
Suppose we have three cards.
On mobile, we want them stacked:
.container {
display: flex;
flex-direction: column;
gap: 20px;
}
On larger screens, we want them in a row:
@media (min-width: 768px) {
.container {
flex-direction: row;
}
}
The result is:
Mobile:
[ Card 1 ]
[ Card 2 ]
[ Card 3 ]
Larger screen:
[ Card 1 ] [ Card 2 ] [ Card 3 ]
Why Mobile-First?
Mobile-first design starts with the smallest and simplest layout and progressively enhances it for larger screens.
Advantages include:
- Better mobile experience
- Simpler base CSS
- Easier responsive design
- Less unnecessary CSS
- Works well with modern responsive development
Instead of starting with a complicated desktop layout and trying to remove things for mobile, we start simple and add features as more space becomes available.
min-width vs max-width
Mobile-first
/* Default = mobile */
.card {
width: 100%;
}
@media (min-width: 768px) {
.card {
width: 50%;
}
}
The direction is:
Small → Large
Desktop-first
/* Default = desktop */
.card {
width: 25%;
}
@media (max-width: 768px) {
.card {
width: 100%;
}
}
The direction is:
Large → Small
Both approaches are valid, but mobile-first is a very common and useful approach for responsive websites.
and
The and operator means both conditions must be true.
Example:
@media (min-width: 768px) and (max-width: 1200px) {
.container {
padding: 30px;
}
}
This means:
width >= 768px
AND
width <= 1200px
So:
700px → ❌
768px → ✅
900px → ✅
1200px → ✅
1300px → ❌
Using Multiple Conditions with ,
A comma acts like OR between media queries.
Example:
@media (max-width: 600px), (orientation: portrait) {
.container {
padding: 10px;
}
}
This means the styles apply if:
width <= 600px
OR
orientation is portrait
If either condition is true, the styles can apply.
not
The not keyword negates a media query.
For example:
@media not print {
/* styles */
}
This means the styles apply when the presentation is not print.
not is useful to know, but in everyday responsive development, min-width and max-width are used much more frequently.
orientation
The orientation feature checks whether the viewport is portrait or landscape.
Portrait
@media (orientation: portrait) {
.container {
padding: 10px;
}
}
Portrait generally means the viewport is taller than it is wide.
┌──────────┐
│ │
│ │
│ │
│ │
└──────────┘
Landscape
@media (orientation: landscape) {
.container {
padding: 30px;
}
}
Landscape generally means the viewport is wider than it is tall.
┌──────────────────┐
│ │
│ │
└──────────────────┘
Other Useful Media Features
prefers-color-scheme
Detects whether the user prefers a light or dark color scheme.
@media (prefers-color-scheme: dark) {
body {
background: black;
color: white;
}
}
hover
Checks whether the user's primary input device supports hovering.
@media (hover: hover) {
button:hover {
transform: scale(1.05);
}
}
pointer
Can be used to detect pointer characteristics.
@media (pointer: coarse) {
button {
padding: 20px;
}
}
This can be useful for touch-based interfaces where larger controls are easier to use.
Media Queries Are Not Only for Width
Media queries can change almost any CSS property.
For example:
h1 {
font-size: 2rem;
}
@media (min-width: 768px) {
h1 {
font-size: 3rem;
}
}
You can also change:
display
flex-direction
grid columns
font-size
padding
margin
width
height
gap
visibility
position
and many other CSS properties
Important Rule About Breakpoints
Do not blindly memorize:
768px = tablet
1024px = laptop
1200px = desktop
These are commonly used values, but they are not strict device definitions.
Choose breakpoints based on when your layout actually needs to change.
For example, if your cards become too cramped at 850px, you might use:
@media (min-width: 850px) {
/* layout changes */
}
The design should determine the breakpoint.
Simple Mental Model for Media Queries
Think of a media query like a CSS if statement:
IF condition is true
↓
apply these CSS rules
For example:
@media (min-width: 768px) {
.container {
display: flex;
}
}
Means:
IF viewport width >= 768px
↓
make .container a flex container
2. Fluid Typography
Fluid typography means creating text whose size can smoothly grow or shrink according to the viewport size.
Traditional responsive typography might look like this:
h1 {
font-size: 32px;
}
@media (min-width: 768px) {
h1 {
font-size: 48px;
}
This creates fixed steps:
Mobile → 32px
Desktop → 48px
Fluid typography allows the size to change gradually:
32px
↓
34px
↓
36px
↓
39px
↓
42px
↓
45px
↓
48px
The main CSS concepts used for fluid typography are:
rem
vw
clamp()
rem
rem stands for root em.
It is relative to the font size of the root <html> element.
Browsers commonly use:
html {
font-size: 16px;
}
Therefore:
1rem = 16px
2rem = 32px
3rem = 48px
4rem = 64px
Example:
h1 {
font-size: 3rem;
}
With a 16px root font size:
3rem = 48px
Why Use rem?
rem provides scalable and consistent sizing.
For example:
body {
font-size: 1rem;
}
h1 {
font-size: 3rem;
}
p {
font-size: 1rem;
}
Instead of hardcoding every value in pixels, the sizes are based on the root font size.
rem is especially useful for:
- Typography
- Spacing
- Scalable layouts
- Accessibility-friendly sizing
vw
vw means viewport width.
1vw = 1% of the viewport width
For example, if the viewport width is:
1000px
then:
1vw = 10px
If the viewport width is:
500px
then:
1vw = 5px
Therefore, vw naturally changes when the viewport width changes.
Example of vw
h1 {
font-size: 5vw;
}
The font size changes according to the viewport width.
However, using only vw can be problematic.
On a very small screen, the font may become too small.
On a very large screen, it may become extremely large.
For example:
Small screen
5vw → potentially too small
Huge screen
5vw → potentially too large
This is why clamp() is extremely useful.
clamp()
clamp() allows us to define a minimum value, preferred value, and maximum value.
The syntax is:
clamp(minimum, preferred, maximum)
Example:
h1 {
font-size: clamp(2rem, 5vw, 4rem);
}
This means:
Minimum → 2rem
Preferred → 5vw
Maximum → 4rem
In simple words:
Let the font size scale according to
5vw, but never let it become smaller than2remor larger than4rem.
How clamp() Works
Suppose:
h1 {
font-size: clamp(2rem, 5vw, 4rem);
}
Assuming:
1rem = 16px
we have:
2rem = 32px
4rem = 64px
So the range is:
Minimum = 32px
Maximum = 64px
Now imagine different viewport sizes.
Small viewport
5vw = 16px
But the minimum is 32px.
Therefore:
font-size = 32px
Medium viewport
5vw = 40px
40px is within the allowed range.
Therefore:
font-size = 40px
Large viewport
5vw = 60px
60px is still within the allowed range.
Therefore:
font-size = 60px
Very large viewport
5vw = 100px
But the maximum is 64px.
Therefore:
font-size = 64px
So the result is approximately:
Viewport Font Size
Small 32px ← minimum
Medium 40px
Large 60px
Very Large 64px ← maximum
The Three Parts of clamp()
Remember:
clamp(2rem, 5vw, 4rem)
↑ ↑ ↑
MIN IDEAL MAX
First value → Minimum
2rem
The value should not go below this.
Second value → Preferred
5vw
The value tries to scale fluidly according to the viewport.
Third value → Maximum
4rem
The value should not exceed this.
Why Use clamp() for Typography?
Without clamp(), we might write many media queries:
h1 {
font-size: 32px;
}
@media (min-width: 600px) {
h1 {
font-size: 36px;
}
}
@media (min-width: 900px) {
h1 {
font-size: 42px;
}
}
@media (min-width: 1200px) {
h1 {
font-size: 48px;
}
}
With clamp():
h1 {
font-size: clamp(2rem, 5vw, 3rem);
}
The browser can calculate an appropriate size between the minimum and maximum.
Does clamp() Replace Media Queries?
No.
clamp() can reduce the need for media queries when we only need to change a value smoothly.
For example:
h1 {
font-size: clamp(2rem, 5vw, 4rem);
}
This can replace several typography-specific breakpoints.
However, media queries are still needed when the layout or behavior itself needs to change.
For example:
Mobile:
[ Menu Button ]
Desktop:
[ Home | About | Projects | Contact ]
We still need a media query to change the layout or visibility.
So remember:
clamp()
→ controls a fluid value
media query
→ changes CSS rules based on a condition
rem vs vw vs clamp()
rem
font-size: 2rem;
Means:
Scale relative to the root font size.
vw
font-size: 5vw;
Means:
Scale relative to the viewport width.
clamp()
font-size: clamp(2rem, 5vw, 4rem);
Means:
Scale fluidly using
5vw, but stay between2remand4rem.
Fluid Typography Example
A modern responsive heading could be:
h1 {
font-size: clamp(2rem, 5vw, 4rem);
}
A paragraph could use:
p {
font-size: clamp(1rem, 1.5vw, 1.25rem);
}
A section's spacing can also be fluid:
section {
padding: clamp(1rem, 5vw, 5rem);
}
So clamp() is not limited to fonts. It can be used for many CSS properties that accept suitable numeric values.
Mobile-First + Fluid Typography Together
These two concepts work together very well.
For example:
/* Base/mobile styles */
.hero-title {
font-size: clamp(2rem, 5vw, 4rem);
}
.container {
display: grid;
grid-template-columns: 1fr;
}
/* Larger screens */
@media (min-width: 768px) {
.container {
grid-template-columns: 1fr 1fr;
}
}
Here:
clamp()
→ controls the fluid heading size
media query
→ changes the layout
We don't have to choose between them.
Final Comparison
MEDIA QUERY
↓
"When this condition is true,
change my CSS rules."
Example:
@media (min-width: 768px)
rem
↓
"Scale relative to the root font size."
Example:
2rem
vw
↓
"Scale relative to the viewport width."
Example:
5vw
clamp()
↓
"Keep a value between a minimum
and maximum while allowing it
to scale fluidly."
Example:
clamp(2rem, 5vw, 4rem)
Quick Cheat Sheet
Media Query
@media (min-width: 768px) {
/* styles */
}
Mobile-first
/* Mobile/default */
.element {
...
}
/* Larger screens */
@media (min-width: 768px) {
.element {
...
}
}
rem
font-size: 2rem;
rem = relative to the root font size.
vw
font-size: 5vw;
vw = 1% of viewport width.
clamp()
font-size: clamp(2rem, 5vw, 4rem);
MINIMUM → PREFERRED → MAXIMUM
2rem 5vw 4rem
Most important idea:
Media queries decide WHEN CSS rules change.
remandvwprovide relative sizing, whileclamp()allows a value to scale fluidly within a safe minimum and maximum range.
Top comments (0)