In Ye Olden Days of Webbe Development™️, if we wanted to create websites and applications that were responsive, it meant writing a series of media queries based around specific device breakpoints and re-working our content for each size (inb4 someone in the comments says the truly old days of web dev were table-based layouts – I remember them, and they sucked too). CSS frameworks stepped in to ease the device-breakpoint paint point – Bootstrap, Skeleton, etc. – because writing all of those was a lot of work! Slowly, we saw the best practices around writing media queries shift (by necessity) from device-based breakpoints to content-based breakpoints as more and more devices came out in all shapes and sizes. Now, with screens available in more or less any size you can imagine, it's time to shift our approach once again – the era of breakpoints is over and the era of fluid design is here. Rather than hitting a point and snapping to a new layout, our content should always be adjusting based on the amount of space available.
Thankfully, CSS has also come a long way since then as well, but a lot of us got used to just throwing in a media query or five to make things responsive, and never quite broke the habit. Now that we have modern CSS features like grid
, flexbox
, vh
, vw
, calc
, clamp
, min
, max
, aspect-ratio
, and more, it's time to ditch the media queries. In this article, we're going to start at the top and work our way down from the big-picture display formatting all the way to the smallest units. And as for your old stylesheets...we can refactor. We have the technology.
The Big Picture: Grid and Flexbox
Let's start with some of the most popular responsive display options, grid
and flexbox
. They've been around for a minute, with grid
hitting full browser support in 2017 and flexbox
in 2013, but a lot of folks still don't fully understand the difference between them, or when they should use one or the other (or both!). Many people assume a false equivalency between the two that can create a misguided either/or approach to choosing a tool – when in reality they not only have very different use cases, but also can (and should) be used together to compliment each other!
For our purposes, though, let's focus on how they work and how you can use them to handle the layout of elements on your page in a way that allows for natural wrapping and responsive adjustment without the use of media queries.
Grid
grid
is primarily focused on, well, creating a grid with multiple rows and columns that you can populate with elements. You do this by creating a container <grid>
element which you then fill with child elements. There's a great number of customizations you can make to the grid rows, columns, and cells, which allows you to create grids as simple or complex as you can imagine.
But for our goal of creating simple, responsive designs, let's take a look at the most relevant features. Grid comes with the ability to repeat columns or rows automatically, as well as auto-fit
which will automatically size your columns into the available space. For example, the code below:
.grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
}
---
<div class="grid">
<div></div>
<div></div>
<div></div>
</div>
In this situation, the grid will turn your content into columns which naturally fit in the space you have, with each column having a min width of at least 300px
and a max width of no more than 1fr
. The 1fr
is a new CSS unit that stands for fraction, which tells the browser to divide the space evenly and give each column one fraction of that space. In this case, it will create three equal columns that each take up 1/3 of the available space, but never get smaller than 300px. Were we to add another div, or take one away, the grid would automatically handle the calculation and adjust the display. And if the screen is too small to fit all the columns side by side at the same time, it will automatically wrap the remaining columns down to the next row.
If you're looking to create a more complex layout using grid, here are a few of my favorite reference resources:
- A Complete Guide to Grid, which offers a fantastic breakdown of all the ways you can customize your grid – including creating columns of varying widths, setting template areas, setting the gap between grid cells, and so much more.
- Grid By Example is my favorite place to start when I'm looking for a jumping-off point on creating a new grid structure.
- And if you're looking for a real-world example of a more complex grid, check out the
frame.scss
file in the kendo-demo app, where CSS grid was used extensively to create the menu frame that's used on every page.
Flexbox
flexbox
takes a similar (but not quite the same) approach in helping you lay out your elements on the page and adjust them automatically as needed. Rather than creating a full grid with multiple rows and columns, however, flexbox
is about the positioning of child elements relative to each other within the parent element. Basically: if you need to work in two directions at once (rows and columns) look at grid. If you need to focus on one direction (a single row or column), this is where you'll want flexbox
.
Again, there's a lot to look at in terms of when to use each one and what the differences of application are, but let's leave that to the other article. For now, let's focus on how flexbox handles responsiveness for you, so you can delete that unnecessary media query.
The main point of interest for this is the flex-wrap
property. Flex will start by attempting to fit everything you've written into one row or column, but if that isn't going to work, you can tell flex to allow wrapping by setting flex-wrap
to wrap
. If flex-wrap
isn't specified, flex will squish the elements to keep them all on the same line (if no min-width
is specified for the child elements), or allow them to overflow out of view (if a min-width
is specified).
.flex {
display: flex;
flex-wrap: wrap;
}
.flex > div {
width: 300px
}
---
<div class="flex">
<div></div>
<div></div>
<div></div>
</div>
Basically, with both grid and flex, you just have to decide whether you want your content to squish or wrap, and then set the properties accordingly! Both are good tools to have in your toolbox for creating responsive UIs.
If you're looking for some resources to master Flexbox, here are a few of my favorites:
- Flexbox Froggy: a fun game to help you master what all the different properties do using some adorable frog illustrations.
- FLEX: a simple, visual cheatsheet for you to reference as you work!
Middle of the Road: CSS Properties
Once we've got our general layout in place, it's time to set some specific properties on our elements – and good news, no media queries needed here either!
aspect-ratio
Did you know that there's a CSS property specifically designed for aspect ratios? This is especially great if you're dealing with video or big hero images, where keeping the correct aspect ratio is very important. There's a fantastic deep dive on CSS Tricks, but in general, it's pretty easy to apply – all you need is that one line:
.video-wrapper { aspect-ratio: 16/9; }
If you set a specific width
value along with aspect-ratio
, then it will use that as a basis and automatically generate the height
value to match (or vice-versa). However, if you declare both a width
and a height
, it will honor those over the aspect-ratio
, so be careful!
min-height, max-height, min-width, and max-width
Although min and max height and width properties can often be replaced in many use cases with the clamp()
function (which we'll look at in the next section), these are still valid CSS and good to have in your pocket. They're also easy, since they pretty much do exactly what they say on the tin:
.img {
width: 100%;
max-width: 50%;
min-width: 200px;
}
Here, we have an image that we're telling first and foremost to take up as much space as it has available, up to the max-width
so that we default to the largest image size possible. Then, we're giving it some guide rails – we don't want it to take up more than half the container, but we also want to make sure it's still larger than a postage stamp, so we also don't want it smaller than 200px
wide. In this case, we set an absolute value only on the small end, because we know exactly how small the image can get before it becomes unrecognizable. Now, no matter how the browser is resized, we know our image will always be as big as it can be...without being either ridiculously giant, or unreadably small.
Fully Functional: Math Functions
The math functions are a newer addition to CSS, and incredibly powerful. They fill a void that CSS developers had complained over for ages – the ability to compute values within stylesheets and set ranges for properties. The sooner you can master them and put them to use in your code, the more you'll benefit!
clamp()
clamp()
allows us to set a base value for a property, along with upper and lower caps to contain it as it adjusts. This is one of our best tools to ditch the media queries, as it allows us to set those responsive limits without needing to define specific breakpoints to do so. Here's how it works:
.img {
/* the format is `clamp(min value, base value, max value)` */
width: clamp(200px, 100%, 50%);
}
Here's an updated version of our last example – the image with the min and max widths. Only here, we're doing it in one line using clamp()
by telling the image to take up 100%
of the available space, but never larger than half the container size, or smaller than 200px
wide.
calc()
If you've ever looked at CSS and wished it had more math, calc()
is for you. It allows us to do simple arithmetic with CSS units, right in the stylesheet. This is great for when you need to base something off a unit that's not absolute. You can also use CSS variables in your computations, for extra flexibility! Let's say we want to create columns that are evenly divided based on the current width of the browser:
.child { width: calc(100vw / 3); }
Setting 1/3 widths is difficult using percentage units because of the whole .333 repeating situation – but now, we can just say "You know what, CSS – you handle it!" Here, we take the full window width, divide it by three, and set that as the width property of the child element.
min() and max()
If you're a fan of having your stylesheets handle as many decisions for you as possible, enter min()
and max()
. They work similarly to clamp()
, but rather than setting a range in both directions, it focuses on just one at a time, allowing us to set the smallest (or largest) value depending on how the math works out. Again, this is a super useful tool for handling non-fixed values, which is one of those things you have to do a lot when creating something responsive.
.img1 { width: min(30%, 200px) }
.img2 { width: max(50vh, 800px) }
In this case, we're setting the widths for two different images – one we'd like to keep small, and one we'd like to be large. min()
will evaluate whether 200px
is greater or lesser than 30%
of the current container and choose whichever value is the smallest. max()
will make the same comparison between 50vh
and 800px
and automatically go with the larger.
All the Small Things: CSS Units
It's all well and good to think about things from a birds-eye view, the way we need to when laying out the elements on our page, but we also can't forget about the little guys – the units that specify size for our various properties. When we set everything in hard units, like pixels, we have to go in and adjust it manually each time we need to resize it. But when we work in responsive units, we can let them do the work for us!
vw and vh
Two of my favorite and most used responsive CSS units are vh
and vw
– viewport width and viewport height. Although there's no percentage sign, vh
and vw
are really a type of percentage-based measurement – 1vw
is 1% of the viewport width.
Gone are the days when viewport measurements had to be obtained via window.innerWidth
and window.innerLength
(again, dark times); we can now get them automatically in our stylesheets. They'll also automatically update as a user resizes the window, so no hoops to jump through there, either. vh
is especially useful for those times when you want to center something vertically, or stick something to the bottom of the viewport. So to create a container element that's always the exact height and width of the user's current browser, all you need is:
.page-wrapper {
height: 100vh;
width: 100vh;
}
rem and em
If you're still defining font sizes in pixels, let me introduce you to the beauty of rem
and em
units. The name em
actually comes from old typographic standards of measurement, based around the width of the letter M in the font (which is also where the words em-dash and en-dash come from – dashes equal to the width of the M or N characters). em
units are a way of defining your font sizes relative to the font size of the parent element. So, you could do something like:
.h2 { font-size: 2.5em }
And that would set your h2 headers to 2.5 times the size of the body font in the parent element.
rem
, on the other hand, allows you to declare your font size relative to the root font size of the entire page. In fact, that's what the R stands for – root em. Both rem
and em
work in the same way, the difference is what they're using as the base size and scaling based upon. In general, I lean towards rem
over em
because I like the consistency it creates, but if you're looking to create a specific design in one area that differs from the overall style, em
will be what you want to reach for.
If you're looking for a neat trick, you can combine vw
with rem
and clamp()
to create a fluid typography system for your application:
p { font-size: clamp(1rem, 2.5vw, 2rem); }
Here, we set the base font size to 2.5vw
(remembering that 1vw
is equal to 1% of the viewport width), and then set min and max values on either side to ensure that you don't end up in either a situation where the text becomes unreadably small or a situation where the text just keeps getting bigger.
Percentages
And, last but certainly not least, the humble percentage unit. If you're going to be setting widths by hand for any reason (as opposed to using a layout tool like grid
or flex
), you'll almost certainly want to reach for the %
instead of any absolute measurement unit.
.container { width: 25%; }
This probably isn't one you really needed an example on, but just in case – here we have an element that we're setting to be 1/4 the width of its parent. As the parent width changes, this element will automatically adjust as well. It's simple, yes, but foundational for building responsive UIs.
Conclusion
If you're still relying primarily on media queries to ensure your site or app is fully responsive, it might be time to take a look at everything CSS has to offer and see if you can swap some of those old-fashioned breakpoints out for some of these new, fluid approaches to styling. Working to create a fully fluid layout means less drastic changes for your user when they move between mobile and desktop, as well as less maintenance work for you – no more updating every breakpoint whenever there's a change to the design. Leverage these adaptive approaches to styling, and get ready to go with the flow!
Top comments (31)
Great article! Intrinsic sizing is awesome, and definitely a better approach for most use-cases.
Nowadays the main use for media queries is when you need to completely change how an element works. For instance, switch your navigation to a hamburger menu on mobile.
Also, there's much more to media queries than just screen sizes. Things like user preferences (reduced motion, color scheme, etc) are relatively new additions that takes a great role for accessibility. And a whole bunch of awesome new stuff is coming right up ;)
We need media queries more than ever, just not for sizing
This is a super fair point!! I was laser-focused on the responsiveness use case when writing this article, but user preferences and accessibility are really good examples of where media queries still shine. Thank you for adding this comment 😊
Nice article -
clamp
is the single biggest tool to use in the box here. I find working with grids a pain in the butt, compared to flex.However...
This is not against you, so please don't take it as a personal criticism. Because everyone makes this mistake. But people who write about CSS really gotta stop writing this nonsense about EMs and REMs.
EMs are a nightmare to work with specifically because they are a proportion of their parent element and in the simple example, it's too much to really have to comprehend when writing clean styles
So you revert to REM? But REM is literally bound to a root value, (usually 16px as default, commonly reset to 10px by
:root { font-size: 62.5% }
.)This nonsense about 'herp, derp, are you still using pixel sizes' -- well, newsflash, using REM is just the same as using an explicty pixel size as far as I can tell. You just write it in a different format.
The REAL reason for using REM is to tie to your measurements to a single base size in a way that means a change to the root size would cause your UI to scale nicely in a proportional way.
I would like to hint you that making your font-size explicitly with 'px' values and not say REMs or EMs would make your user's browser typography preference overruled. Meaning that there would be no scaling of your font-sizes if the user chose to do so in the browser settings. So, it's always better to use resizible units like REMs unless you want something to have the same font size no matter the user's preference.
What did you not get about:
EMs are just recipes for disaster.
Your original comment makes little sense to me then. Why make an obvious point about the difference between EM and REM when the author quite literraly stated the fact that EMs should generally be avoided and REMs should be used instead. You then go on about saying REMs and PX values are just the same..when in reality they aren't, simply 'couse of the fact that REMs are scalable and PX are not. They are only the same when looked at from coding perspective...not to the browser and users who alter their settings only to find out, to their dissapointment, that the UI hasn't changed. I'd kindly advise you to edit your comment accordingly and not to restate what author had already stated in the blog b'couse it leads to unnecessary confusion for the readers.
Ty and wish u successful coding. Sry for the long comment :/
This is only true if you set the base value to be a pixel value. Ideally you wouldn’t touch it and allow the user to decide the base size. This is 16px by default but fully depends on the size of the text a user has selected.
This is great because larger text requires more whitespace, which is exactly why you would use REM for spacing as well because it scales with the font size of the user!
The mistake developers make is setting the base REM value to be a hardcoded value.
This article makes many valuable points but one place I think media queries retain a role is in the detection of pixel density. Relying on screen dimensions alone is not always sufficient to identify the most appropriate screen presentation.
That's a very fair point! I think there are still specific use cases where you might want media queries - but in general I think they shouldn't be the first thing we reach for anymore.
I'm not a fan of all-or-nothing approaches, which is why this article is called "You Probably Don't Need Media Queries" and not "Stop Using Useless Media Queries Immediately!!!" Only a Sith deal in absolutes 😉 There are exceptions to every rule, so I agree it's unwise to throw out a tool entirely.
Sadly, many iOS devices last longer than Apple support and many older iOS devices are stuck with iOS 12.x. To make matters worse, iOS says that "the device has latest updates" in settings, when the real status would be "your device is no longer supported and should but be used to interface with internet".
In 5 - 10 years we can probably do as you say, that's nice, honestly media query will still have a place also they aren't just for screen size, print, accessibility etc to name just a few purposes. Still exciting ☺️
This is a good summary of how to responsively position website elements ... but remember that designing for multiple devices (or 'sizes') isn't just about the position of text and images, but also how content is edited: how much text is shown, and how images and videos are presented. A mobile website is not just a desktop site squeezed into a smaller form factor, and vice versa. Maybe that's a different article ...
Agreed!! Creating a truly responsive layout is a blend between design and development. In this article, I chose to focus exclusively on the development and technical angle, but maybe you're right and there needs to be a part 2 looking at the UX side of the problem!
Great article!
Thank you very much for putting this together!
Tl;dr
With your line of code (below), how can first and last column widths be set when the number of columns are dynamic?
Example:
Holy Grail layout where:
<aside>
appears/disappears when users click on items in the center<main>
.<nav>
should be a fixed width of250px
<main>
should be1fr
<aside>
should be300px
Is there a way to dynamically add/remove the right
<aside>
and not have to write a second line to cover the difference?codepen.io/dragontheory/pen/dyZbJP...
Thank you again for your time and effort. It is greatly appreciated!
No mention of container queries?
Container queries are cool, but still experimental and not widely supported yet: caniuse.com/css-container-queries ☹️
In this article, I wanted to focus on code folks could put to use right away. But I do think these are something to keep an eye on, for sure!
Oh sure, but they're set to be a game-changer. Thought it was worth a mention is all :).
It's been a long... Long long long long time in the waiting
I read this article the other day and it made for an engrossing read. A terrific article actually.
The migration from using media queries to some or all of the things above makes sense.
As it stands, there will be many developers who will be reluctant to jump ship instantly as many are still quite at home with the classic RWD approach using media queries.
I would love to see a video where a developer hand codes (from scratch) the same website layout, one using media queries and the other using lot of whats shown and mentioned in this article. and timing the whole thing. I'd be intrigued to see which one takes the longest.
Reviving this olde thread because I recently discovered and started using: Utopia. Like a lot of these solutions it dosen't entirely eliminate the need for a media query but it works very well in a lot of situations where I would have previously used a media query.
Some comments may only be visible to logged-in visitors. Sign in to view all comments. Some comments have been hidden by the post's author - find out more