Most modern websites use responsive web design techniques to ensure they look good, are readable, and remain usable on devices with any screen size, i.e. mobile phones, tablets, laptops, desktop PC monitors, televisions, projectors, and more.

Sites using these techniques have a single template, which modifies the layout in response to the screen dimensions:

One big part of responsive web design is the implementation of a CSS or JavaScript media query to detect device size and automatically serve up the appropriate design for that size. We’re going to discuss why these queries are important and how to work with them, but first, let’s discuss responsive design in general.

Why Is Responsive Design Important?

It’s impossible to provide a single page layout and expect it to work everywhere.

When mobile phones first gained rudimentary web access in the early 2000s, site owners would often create two or three separate page templates loosely based around mobile and desktop views. That became increasingly impractical as the variety of devices grew exponentially.

Today, there are numerous screen sizes ranging from tiny wristwatch displays to huge 8 K monitors and beyond. Even if you only consider mobile phones, recent devices can have a higher resolution than many low-end laptops.

Mobile usage has also grown beyond that of desktop computers. Unless your site has a specific set of users, you can expect the majority of people to access it from a smartphone. Small-screen devices are no longer an afterthought and should be considered from the start, despite most web designers, developers, and clients continuing to use a standard PC.

Google has recognized the importance of mobile devices. Sites rank better in Google search when they’re usable and perform well on a smartphone. Good content remains vital, but a slow-loading site that fails to adapt to the screen dimensions of your userbase could harm your business.

Finally, consider accessibility. A site that works for everyone, no matter what device they’re using, will reach a larger audience. Accessibility is a legal requirement in many countries, but even if it’s not where you are, consider that more viewers will lead to more conversions and higher profitability.

In order to tackle responsive web design, you first need to understand JavaScript media queries 🤓 Let's dive in! 🚀Click to Tweet

How Does Responsive Design Work?

The basis of responsive design is media queries: a CSS technology that can apply styles according to metrics such as the output type (screen, printer, or even speech), screen dimensions, display aspect ratio, device orientation, color depth, and pointer accuracy. Media queries can also take user preferences into account, including reduced animations, light/dark mode, and higher contrast.

The examples we’ve shown demonstrate media queries using screen width only, but sites can be considerably more flexible. Refer to the full set of options on MDN for details.

Media query support is excellent and has been in browsers for more than a decade. Only IE8 and below have no support. They ignore styles applied by media queries, but this can sometimes be a benefit (read more in the Best Practices section below).

There are three standard ways to apply styles using media queries. The first loads specific stylesheets in HTML code. For example, the following tag loads the wide.css stylesheet when a device has a screen that is at least 800 pixels wide:

<link rel="stylesheet" media="screen and (min-width: 800px)" href="wide.css" />

Secondly, stylesheets can be conditionally loaded in CSS files using an @import at-rule:

/* main.css */
@import url('wide.css') screen and (min-width: 800px);
Info

Note that @import should be avoided because each imported CSS file is render-blocking. HTML <link> tags are downloaded in parallel, whereas @import downloads files in series.

More typically, you’ll apply media queries in stylesheets using a @media CSS at-rule block that modifies specific styles. For example:

/* default styles */
main {
  width: 400px;
}

/* styles applied when screen has a width of at least 800px */
@media screen and (min-width: 800px) {
  main {
    width: 760px;
  }
}

Developers can apply whichever media query rules are necessary to adapt the layout of a site.

Media Query Best Practises

When media queries were first devised, many sites opted for a set of rigidly fixed layouts. This is conceptually easier to design and code because it effectively replicates a limited set of page templates. For example:

  1. Screen widths less than 600px use a 400px-wide mobile-like layout.
  2. Screen widths between 600px and 999px use a 600px-wide tablet-like layout.
  3. Screen widths greater than 1,000px use a 1000px-wide desktop-like layout.

The technique is flawed. The results on very small and very large screens can look poor, and CSS maintenance can be required as devices and screen sizes change over time.

A better option is to use a mobile-first fluid design with breakpoints that adapt the layout at certain sizes. In essence, the default layout uses the simplest small-screen styles that position elements in linear vertical blocks.

For example, <article> and <aside> inside a <main> container:

/* default small-screen device */
main {
  width: 100%;
}

article, aside {
  width: 100%;
  padding: 2em;
}

Here’s the result in all browsers — even very old ones that don’t support media queries:

Example screenshot without media query support.

Example screenshot without media query support.

When media queries are supported and the screen exceeds a specific width, say 500px, the <article> and <aside> elements can be positioned horizontally. This example uses a CSS grid, where the primary content uses approximately two-thirds of the width, and secondary content uses the remaining one-third:

/* larger device */
@media (min-width: 500px) {
  main {
    display: grid;
    grid-template-columns: 2fr 1fr;
    gap: 2em;
  }

  article, aside {
    width: auto;
    padding: 0;
  }
}

Here’s the result on larger screens:

Example screenshot with javascript media query support

Example screenshot with media query support.

Media Query Alternatives

Responsive designs can also be implemented in modern CSS using newer properties that intrinsically adapt the layout without examining the viewport dimensions. Options include:

These options are beyond the scope of this article, but they’re often more practical than cruder media queries, which can only respond to screen dimensions. If you can achieve a layout without media queries, it will probably use less code, be more efficient, and require less maintenance over time.

That said, there are situations where media queries remain the only viable layout option. They remain essential when you need to consider other screen factors such as aspect ratios, device orientation, color depth, pointer accuracy, or user preferences such as reduced animations and light/dark mode.

Do You Need Media Queries in JavaScript?

We’ve mostly talked about CSS up until now. That’s because most layout issues can — and should — be solved in CSS alone.

However, there are situations when it’s practical to use a JavaScript media query instead of CSS, such as when:

The following sections demonstrate three methods that use media queries — or media-query-like options — in JavaScript. All the examples return a state string where:

Option 1: Monitor the Viewport Dimensions

This was the only option in the dark days before media queries were implemented. JavaScript would listen for browser “resize” events, analyse the viewport dimensions using window.innerWidth and window.innerHeight (or document.body.clientWidth and document.body.clientHeight in old IEs), and react accordingly.

This code outputs the calculated small, medium, or large string to the console:

const
  screen = {
    small: 0,
    medium: 400,
    large: 800
  };

// observe window resize
window.addEventListener('resize', resizeHandler);

// initial call
resizeHandler();

// calculate size
function resizeHandler() {

  // get window width
  const iw = window.innerWidth;
 
  // determine named size
  let size = null;
  for (let s in screen) {
    if (iw >= screen[s]) size = s;
  }

  console.log(size);
}

You can view a working demonstration here. (If using a desktop browser, open this link in a new window to make resizing easier. Mobile users can rotate the device.)

The above example examines the viewport size as the browser is resized; determines whether it’s small, medium, or large; and sets that as a class on the body element, which changes the background color.

The advantages of this method include:

The disadvantages:

In summary, do not monitor viewport dimensions unless you have very specific and complex sizing requirements.

Option 2: Define and Monitor a CSS Custom Property (Variable)

This is a slightly unusual technique that changes the value of a custom property string in CSS when a media query is triggered. Custom properties are supported in all modern browsers (but not IE).

In the example below, the --screen custom property is set to “small”, “medium”, or “large” within an @media code block:

body {
  --screen: "small";
  background-color: #cff;
  text-align: center;
}

@media (min-width: 400px) {
 
  body {
    --screen: "medium";
    background-color: #fcf;
  }
 
}

@media (min-width: 800px) {
 
  body {
    --screen: "large";
    background-color: #ffc;
  }
 
}

The value can be output in CSS alone using a pseudo-element (but note that it must be contained within single or double quotes):

p::before {
  content: var(--screen);
}

You can fetch the custom property value using JavaScript:

const screen = getComputedStyle(window.body)
                 .getPropertyValue('--screen');

This is not quite the whole story, though, because the returned value contains all the whitespace and quote characters defined after the colon in the CSS. The string will be ‘ “large”‘, so a little tidying is necessary:

// returns small, medium, or large in a string
const screen = getComputedStyle(window.body)
                 .getPropertyValue('--screen')
                 .replace(/\W/g, '');

You can view a working demonstration here. (If using a desktop browser, open this link in a new window to make resizing easier. Mobile users can rotate the device.)

The example examines the CSS value every two seconds. It requires a little JavaScript code, but it’s necessary to poll for changes — you cannot automatically detect that the custom property value has changed using CSS.

Neither is it possible to write the value to a pseudo-element and detect the change using a DOM Mutation Observer. Pseudo-elements are not a “real” part of the DOM!

Need a hosting solution that gives you a competitive edge? Kinsta’s got you covered with incredible speed, state-of-the-art security, and auto-scaling. Check out our plans

The advantages:

The primary disadvantage is you cannot automatically react to a change in browser viewport dimension. If the user rotates their phone from portrait to landscape orientation, the JavaScript would never know. You can frequently poll for changes, but that’s inefficient and results in the time lag you see in our demonstration.

Monitoring CSS custom properties is a novel technique, but it’s only practical when:

  1. The layout can be fixed at the point a page is initially rendered. A kiosk or point-of-sale terminal is a possibility, but those are likely to have fixed resolutions and a single layout, so JavaScript media queries become irrelevant.
  2. The site or app already runs frequent time-based functions, such as a game animation. The custom property could be checked at the same time to determine whether layout changes are required.

Option 3: Use the matchMedia API

The matchMedia API is slightly unusual but it allows you to implement a JavaScript media query. It’s supported in most browsers from IE10 upward. The constructor returns a MediaQueryList object that has a matches property which evaluates to true or false for its specific media query.

The following code outputs true when the browser viewport width is 800px or greater:

const mqLarge  = window.matchMedia( '(min-width: 800px)' );
console.log( mqLarge.matches );

A “change” event can be applied to the MediaQueryList object. This is triggered every time the state of the matches property changes: It becomes true (over 800px) after previously being false (under 800px) or vice versa.

The receiving handler function is passed the MediaQueryList object as the first parameter:

const mqLarge  = window.matchMedia( '(min-width: 800px)' );
mqLarge.addEventListener('change', mqHandler);

// media query handler function
function mqHandler(e) {
 
  console.log(
    e.matches ? 'large' : 'not large'
  );
 
}

The handler only runs when the matches property changes. It won’t run when the page is initially loaded, so you can call the function directly to determine the starting state:

// initial state
mqHandler(mqLarge);

The API works well when you’re moving between two distinct states. To analyze three or more states, such as small, medium, and large, it’ll require more code.

Start by defining a screen state object with associated matchMedia objects:

const
  screen = {
    small : null,
    medium: window.matchMedia( '(min-width: 400px)' ),
    large : window.matchMedia( '(min-width: 800px)' )
  };

It’s not necessary to define a matchMedia object on the small state because the medium event handler will trigger when moving between small and medium.

Event listeners can then be set for the medium and large events. These call the same mqHandler() handler function:

// media query change events
for (let [scr, mq] of Object.entries(screen)) {
  if (mq) mq.addEventListener('change', mqHandler);
}

The handler function must check all MediaQueryList objects to determine whether small, medium, or large is currently active. Matches must be run in size order since a width of 999px would match both medium and large — only the largest should “win”:

// media query handler function
function mqHandler() {
 
  let size = null;
  for (let [scr, mq] of Object.entries(screen)) {
    if (!mq || mq.matches) size = scr;
  }
 
  console.log(size);
 
}

You can view a working demonstration here. (If using a desktop browser, open this link in a new window to make resizing easier. Mobile users can rotate the device.)

The example uses are:

  1. Media queries in CSS to set and display a custom property (as shown in option 2 above).
  2. Identical media queries in matchMedia objects to monitor dimension changes in JavaScript. The JavaScript output will change at exactly the same time.

The main advantages of using the matchMedia API are:

The disadvantages:

To avoid media query mismatches, you could consider using design tokens in your build system. Media query strings are defined in a JSON (or similar) file and the values are slotted into the CSS and JavaScript code at build time.

In summary, the matchMedia API is likely to be the most efficient and practical way to implement a JavaScript media query. It has some quirks, but it’s the best option in most situations.

Wondering how responsive design works? 👀 JavaScript media queries play an key role... 👩‍💻Click to Tweet

Summary

Intrinsic CSS sizing options are increasingly viable, but media queries remain the basis of responsive web design for most sites. They will always be necessary to handle more complex layouts and user preferences, such as light/dark mode.

Try to keep media queries to CSS alone where possible. When you have no choice but to venture into the realm of JavaScript, the matchMedia API provides additional control for JavaScript media query components, which require additional dimension-based functionality.

Do you have any other tips for implementing a JavaScript media query? Share them in the comments section!


Save time, costs and maximize site performance with:

  • Instant help from WordPress hosting experts, 24/7.
  • Cloudflare Enterprise integration.
  • Global audience reach with 28 data centers worldwide.
  • Optimization with our built-in Application Performance Monitoring.

All of that and much more, in one plan with no long-term contracts, assisted migrations, and a 30-day-money-back-guarantee. Check out our plans or talk to sales to find the plan that’s right for you.