Breakpoints in Drupal: choosing your own and using them for art direction

In the guide to responsive images in Drupal, you set up image styles, a responsive image style with sizes and a media view mode, and you never had to define a single breakpoint. That setup covers most images on most sites.

This article is for the next step: knowing where your layout changes, writing those widths down in a way Drupal understands, and using them for the one job where breakpoints really matter in the Responsive Image module, showing a different crop of an image on different screens. That's called art direction.

What a breakpoint is (and what it isn't)

A breakpoint is the screen width where your layout stops working as it is and has to change: one column becomes two, the menu turns into a button, a text line grows too long to read comfortably.

A breakpoint is not a device. Lists like "iPhone 375, iPad 768, desktop 1024" go out of date with every new model, and the devices in between always end up looking broken. The widths that matter are the ones where your content breaks, and they're different for a recipe grid and for a long article.

Most modern themes, Olivero included, are written mobile first: the CSS starts with the layout for small screens and adds changes with min-width media queries as the screen grows. Each of those min-width values is a breakpoint. If your theme's CSS says @media (min-width: 700px), then 700 pixels is one of your breakpoints, whether or not it's written anywhere else.

Breakpoints in Drupal: the file

Your CSS already uses breakpoints, but Drupal doesn't read CSS. When a module needs to know them (in practice, Responsive Image), the theme or module declares them in a YAML file named after its machine name: mytheme.breakpoints.yml, in the theme's root folder.

This is a real excerpt from Olivero's file in Drupal 11:

olivero.sm:
  label: Small
  mediaQuery: 'all and (min-width: 500px)'
  weight: 0
  multipliers:
    - 1x
olivero.md:
  label: Medium
  mediaQuery: 'all and (min-width: 700px)'
  weight: 1
  multipliers:
    - 1x
olivero.lg:
  label: Large
  mediaQuery: 'all and (min-width: 1000px)'
  weight: 2
  multipliers:
    - 1x

Each entry has a machine name (olivero.sm) and five keys:

  • label: the name shown in the admin forms.
  • mediaQuery: the CSS condition, exactly as you'd write it after @media.
  • weight: the order. It must grow with the width: the smallest screen gets the lowest weight. The Responsive Image module uses it to sort the breakpoints, so a wrong weight means the browser is offered the wrong image.
  • multipliers: the pixel densities you want to serve at that width. 1x for normal screens, 2x for high-density ones; 1.5xalso exists. Each multiplier gets its own image style later.
  • group (optional): which group the breakpoint belongs to. Without it, the breakpoint goes into a group named after the theme.

Breakpoint groups

A group is a set of breakpoints meant for one purpose. When you create a responsive image style, the first thing Drupal asks is which group to use, and the form then shows one row per breakpoint in that group.

You'll normally see three kinds of group:

  • Responsive image. It comes with the module and holds a single breakpoint, Viewport Sizing, with an empty media query. It's the group for the sizes setup, where you don't map anything to breakpoints. Core's Narrow and Wide styles use it.
  • Your base theme's group. If your theme is a sub-theme of Olivero, you'll see Olivero's. Its full file has eight entries: Small (500), Medium (700), Large (1000), X-Large (1300), and others for the navigation (500 and 1200) and the grid (700 and 1440). It mixes layout and navigation breakpoints in one group, which is why not every row is useful for images.
  • Your own theme's group, once you write the file. This is where you choose exactly the widths your images need.

One note for the near future: Drupal 12 moves Olivero out of core into a contributed project (drupal/olivero, version 2.0), with the same breakpoints and settings. If your site uses Olivero or a sub-theme of it, add that project before upgrading.

Choosing your own breakpoints

Start from the content, not from a list of devices:

  1. Open a typical page in the browser and make the window as narrow as it goes.
  2. Widen it slowly. Each time something starts to look wrong (lines too long, cards too wide, too much empty space), note the width. That's a candidate breakpoint.
  3. Compare your notes with the min-width values already in your theme's CSS. Where they're close, use the CSS value: the image and the layout should change at the same point.
  4. Keep three or four. Every breakpoint you use for art direction means more image styles to create and maintain.

One detail about units. Many themes write their media queries in rem (Olivero's 43.75rem is 700 pixels at the default 16-pixel font size). The YAML file accepts either; use the same unit as your CSS so the two can't drift apart.

Writing them for your theme

For a sub-theme with a hero image that should change its crop on phones, tablets and desktops, the file can be this short:

mytheme.mobile:
  label: Mobile
  mediaQuery: 'all'
  weight: 0
  multipliers:
    - 1x
    - 2x
mytheme.tablet:
  label: Tablet
  mediaQuery: 'all and (min-width: 700px)'
  weight: 1
  multipliers:
    - 1x
    - 2x
mytheme.desktop:
  label: Desktop
  mediaQuery: 'all and (min-width: 1000px)'
  weight: 2
  multipliers:
    - 1x
    - 2x

Three things to notice:

  • The first breakpoint has no width ('all'): it covers everything below the next one. That's the mobile-first starting point.
  • The weights rise with the width: 0, 1, 2.
  • Every breakpoint offers 1x and 2x, because phones and many laptops have high-density screens.

Save the file as mytheme.breakpoints.yml (with your theme's machine name), clear the caches (drush cr), and open the form to add a responsive image style. Your theme's group should now be in the Breakpoint group list. If it isn't, check the file name: it must match the theme's machine name exactly.

Art direction: one crop per screen

With sizes, the browser chooses between copies of the same crop. Art direction is for when the crop itself has to change. The classic case is a wide hero: on a desktop a 16:9 photo of a table full of dishes looks generous, but on a phone the same picture becomes a thin strip where nobody can see the food. A taller crop for phones solves it.

It's worth the extra work for a handful of images: the home page hero, section headers, perhaps a featured article. It isn't worth it for cards and teasers.

Step 1: one image style per crop and density

Following the breakpoints above, a hero might use:

BreakpointCrop1x2x
Mobile4:5640 Γ— 8001280 Γ— 1600
Tablet3:21000 Γ— 6672000 Γ— 1333
Desktop16:91600 Γ— 9003200 Γ— 1800

Create them in Configuration β†’ Media β†’ Image styles with Scale and crop and Convert to WebP, as in the first guide. Name them by use: Hero mobile, Hero mobile 2x, and so on.

Scale and crop cuts from the centre. If the subject of your photos is often off-centre, the contributed Focal Point module lets editors mark the important point of each image, and the crops keep it in view.

Step 2: the responsive image style

In Configuration β†’ Media β†’ Responsive image styles β†’ Add responsive image style:

  • Label: Hero.
  • Breakpoint group: your theme's group.
  • The form now shows one block per breakpoint and multiplier: Mobile 1x, Mobile 2x, Tablet 1x, and so on. In each, choose Select a single image style and pick the matching style. For a breakpoint you don't want to use, choose Do not use this breakpoint.
  • Fallback image style: Hero mobile, the image old browsers get.

Then, as with cards, create a media view mode (Hero) that uses this responsive style, and use it wherever the hero appears.

What Drupal outputs

Instead of a single <img>, Drupal now writes a <picture> element:

<picture>
  <source srcset="hero-desktop.webp 1x, hero-desktop-2x.webp 2x"
          media="all and (min-width: 1000px)" type="image/webp">
  <source srcset="hero-tablet.webp 1x, hero-tablet-2x.webp 2x"
          media="all and (min-width: 700px)" type="image/webp">
  <source srcset="hero-mobile.webp 1x, hero-mobile-2x.webp 2x"
          media="all" type="image/webp">
  <img src="hero-mobile.webp" alt="A table full of dishes" width="640" height="800">
</picture>

(File names shortened.) The browser reads the <source> elements from top to bottom and uses the first whose mediamatches. That's why the order matters, and why Drupal sorts by weight with the widest breakpoint first. The <img> at the end carries the alt text and the fallback.

Mixing both

The two methods aren't exclusive. For each breakpoint you can choose Select multiple image styles and use the sizes attribute instead of a single style. For example, the desktop crop could offer 1280, 1600 and 2400 pixel versions with sizes="100vw", while the mobile crop keeps a single style. Drupal then writes a srcset with widths and a sizes attribute inside that breakpoint's <source>.

Use it when one breakpoint covers a wide range of screens, typically the desktop one.

Testing

  • DevTools device toolbar: switch between a phone, a tablet and a desktop width and watch the crop change. Set the device pixel ratio to 2 to check the 2x files.
  • currentSrc: select the <img> inside the <picture> and read currentSrc in the Properties panel; it shows the file actually in use.
  • Network panel, filtered by images: reload at each width and check that only one hero file is downloaded, and the right one.
  • Resize slowly across each breakpoint: the crop should change exactly where the layout does.

Pitfalls

  • Breakpoints copied from a device list. The layout breaks between the "supported" widths. Choose them where your content breaks.
  • The group doesn't appear in the form. Clear the caches, and check that the file name matches the theme's machine name.
  • Weights in the wrong order. The browser gets the <source> elements in an odd order and picks the wrong crop. Weights must rise with the width.
  • Ranges with min and max. Stark's breakpoints use ranges such as min-width: 480px and max-width: 959px. They're harder to maintain and easy to leave a gap between. Prefer min-width steps.
  • Art direction everywhere. Dozens of image styles for no visible gain. Keep it for images whose crop must really change.
  • Forgetting 2x. Sharp on the desktop, blurry on the phone. Add the 2x multiplier and its styles.

Checklist

  • Breakpoints chosen where the content breaks, matching the theme's CSS.
  • mytheme.breakpoints.yml with three or four entries, weights rising with the width, 1x and 2x.
  • Caches cleared; the theme's group appears in the responsive image style form.
  • One image style per crop and density, in WebP.
  • Responsive image style with the theme's group, one style per breakpoint and multiplier, and a fallback.
  • A media view mode using it, placed where the hero appears.
  • Checked in DevTools at each breakpoint and density.

Where to learn more

Changed