Sharing Breakpoints Between Drupal 8 and Sass

Share breakpoints between a Drupal 8 theme's breakpoints.yml and Sass by leveraging eyeglass.

Among the many great new features in Drupal 8 is the Breakpoint module. This module allows you to define sets of media queries that can be used in a Drupal site. The most notable place that Drupal core uses breakpoints is the Responsive Image module.

To add and configure breakpoints, you create a file named *theme-name*.breakpoints.yml in the root directory of your theme, replacing *theme-name* with the theme’s machine name. Modules can also have *module-name*.breakpoints.yml configuration files at their root. In this file you define each breakpoint by these properties:

  • label - the human readable name of the breakpoint
  • mediaQuery - a valid @media query
  • weight - where the breakpoint is ordered in relation to other breakpoints: the breakpoint targeting the smallest viewport size should have a smaller weight, and larger breakpoints should have larger weight values
  • multiplier - the ratio between the physical pixel size of the active device and the device-independent pixel size

Exposing all this information to Drupal from a single file is great. But having this file creates a problem. The breakpoints defined here are not exposed to the Sass code used to style our site.

A solution

One best practice in software development is avoiding repetition whenever possible, often called keeping things DRY (Don’t Repeat Yourself). To apply the DRY method to the breakpoints.yml file and Sass, I wrote drupal-sass-breakpoints. This is an Eyeglass module that allows importing breakpoints from our theme’s breakpoints.yml into our Sass code.

In this article we are going to setup Drupal Sass Breakpoint in a minimal Drupal 8 theme. If you would like an introduction to creating a Drupal 8 theme, I recommend going through John Hannah’s article on Drupal 8 Theming Fundamentals. For this article's example (neelix), we are going to start with the following files:

├── scss
│   └── style.scss
├── css
├── neelix.libraries.yml
├── neelix.info.yml
├── neelix.breakpoints.yml

This gives us a directory for our Sass, a directory for the compiled CSS, a file to declare a library for our CSS, an *.info.yml file, and our breakpoints.yml file. Inside the neelix.breakpoints.yml file add the following:


neelix.small:
  label: small
  mediaQuery: ''
  weight: 0
  multipliers:
    - 1x
neelix.medium:
  label: medium
  mediaQuery: 'screen and (min-width: 560px)'
  weight: 1
  multipliers:
    - 1x
neelix.wide:
  label: wide
  mediaQuery: 'screen and (min-width: 600px)'
  weight: 2
  multipliers:
    - 1x
neelix.xwide:
  label: xwide
  mediaQuery: 'screen and (min-width: 860px)'
  weight: 2
  multipliers:
    - 1x

Now that we have a starting point for the new theme and some breakpoints in place, we need to install drupal-sass-breakpoints. For the example here, we are going to use Grunt to compile Sass. But It is important to mention that drupal-sass-breakpoints can work with Gulp or any other task runner that supports Eyeglass modules. To get started with Grunt, we first need to set up dependencies. This can be done by defining the dependencies in a file named package.json in the root directory of the neelix theme:


{
  "devDependencies": {
    "breakpoint-sass": "^2.6.1",
    "drupal-sass-breakpoints": "^1.0.1",
    "eyeglass": "^0.7.1",
    "grunt": "^0.4.5",
    "grunt-contrib-watch": "^0.6.1",
    "grunt-sass": "^1.0.0"
  }
}

Then run npm install this from the command line while in the theme’s root directory.

This installs all the necessary dependencies we need, which were defined in package.json. Next, we need to define Grunt tasks. Do so by adding the following in a Gruntfile.js file in the root directory of the theme:


'use strict';

var eyeglass = require("eyeglass");

module.exports = function(grunt) {
  grunt.initConfig({
    watch: {
      sass: {
        files: ['scss/*.{scss,sass}', 'Gruntfile.js', 'neelix.breakpoints.yml'],
        tasks: ['sass:dist']
      },
    },
    sass: {
      options: require("eyeglass").decorate({
        outputStyle: 'expanded'
      }),
      dist: {
        files: {
          'css/style.css': 'scss/style.scss'
        }
      }
    }
  });
  grunt.registerTask('default', ['sass:dist', 'watch']);
  grunt.loadNpmTasks('grunt-sass');
  grunt.loadNpmTasks('grunt-contrib-watch');
};

This creates all that we need to compile Sass with Eyeglass. Now we can set up a test case to our scss/style.scss file by adding the following:


@import "drupal-sass-breakpoints";

body {
  @include media('medium') {
    content: "Styles that apply to our 'medium' breakpoint";
  }
}

In the above example we are using the media() mixin. This is a mixin added by drupal-sass-breakpoints. The argument we pass to it is the label for the media query we are targeting ( medium). Now run this from the command line in the root directory of our theme to compile Sass:


grunt

Now check out the compiled results inside css/style.css:


@media (min-width: 560px) {
  body {
    content: "Styles that apply to our 'medium' breakpoint";
  }
}

That is it! We now have Sass importing the media queries from our theme’s breakpoints.yml file.

What about srcset and sizes?

If you find you're overwhelmed by the number of breakpoints in your theme, remember that there are many instances where you only need to use srcset for responsive image styles. The srcset attribute is particularly useful when used in conjunction with the sizes attribute. I recommend this article on CSS-Tricks which outlines the scenarios which srcset and sizes are best for. In these cases, it may not be necessary to define all of our breakpoints in the .breakpoints.yml file.

Feedback

Do have any feature requests or suggestions for improving drupal-sass-breakpoints? Feel free to contribute on GitHub.

You can find a fully working example of this article here. Happy styling!

Get in touch with us

Tell us about your project or drop us a line. We'd love to hear from you!