Cloud Four Blog

Technical notes, War stories and anecdotes

Behavioral Breakpoints: Beyond Media Queries

Recently, Cloud Four dev Matt Gifford built a slick, responsive off-canvas navigation enhancement for a project (we’ll be releasing the core code shortly, so keep an eye out for Matt’s post about that!).

The project followed the off-canvas menu metaphor for handling navigation on narrow screens. The baseline, mobile-first layout keeps the navigation out of the way—initially as footer nav with a button-ish jump link, then progressively enhanced to convert the link into a trigger element for the off-canvas menu—while a media query for wider screens formats the navigation on-screen.

A sketch of this layout adaptation:

offcanvasmenu-states

In the past we’ve approached implementation of this as:

  1. Implement a baseline CSS layout that has a menu button.
  2. Use a CSS media query to adapt the layout for wider screens, hide the button, reflow the nav.
  3. Use JavaScript, often bound on window.resize, to check media query applicability and adapt behavior accordingly (sometimes alternately handled by looking at the width of the viewport and comparing it to media-query-defined widths).

Bolting on Behavior

Often by the time we get to the behavior implementation of our sites and apps, the process involves bolting on JavaScript as a follower of the CSS-based breakpoints that we’ve generated during the design process. We define the visual breakpoints of our stuff and the JavaScript is expected to use those to indicate how it should behave.

So we bump into things. We want the JavaScript to “know” about the breakpoints defined in CSS, and which media queries are presently active. This leads to heartache, and also some pretty clever hacks. We use matchMedia (or a polyfill to support same) to determine whether a particular, specific media query is active.

Thus: duplicating media queries in two places (CSS and JavaScript), a situation that makes a whole lot of us twitchy. I have definitely fantasized about future CSS module spec revisions that allow for naming and scoping of media queries, to make stuff like this better.

But hold on a minute. What are we trying to accomplish here? Does CSS really hold a monopoly on breakpoints? Should our behavioral components be entirely beholden to the specific formulae of our visual layouts?

(Re-)Defining Breakpoints

But why is the breakpoint for menu/navigation behavioral adaptation entirely linked to a CSS concern? Could there be better indications in the browser that the behavior change is appropriate, beyond a CSS media query or screen width? And, in that case, what does define the breakpoint?

This sort of thing was already on my mind when I started reading Stephen Hay’s excellent new book, Responsive Design Workflow. And Stephen is thinking about these things, too—except he’s already a lot further along!

Stephen’s way of defining a breakpoint is: “the points where certain aspects of a website or web application change depending on specified conditions.”

No mention of CSS there—because the picture is bigger than that. As Stephen continues on to say, “[a]nother reason to consider a more full definition of breakpoints is that CSS is not the only method used to implement changes when a breakpoint has been reached.”

Exactly!

Breakpoint Graphs

To express and plan breakpoints, Stephen advocates the use of breakpoint graphs, an adaptation on bullet graphs he’s invented to communicate both visual and behavioral aspects of breakpoints.

Along the “qualitative” axis (horizontal in these examples), one charts a scale. Often, this is a range of screen width resolutions—the way we tend to think about responsive design and breakpoints.

Using the process from above, we might have a breakpoint graph that looks like this:

first-graph

Thus, we’ve expressed that we have a breakpoint at 40em that alters the page layout.

But Stephen’s graphs go further than this visual design element. Using qualitative “bands,” Stephen’s graphs can communicate behavior or other aspects, like so:

graph-2

This is starting to move toward thinking about behavioral changes as well as visual ones, expressing explicitly that we want to adapt navigation behavior, but the breakpoint is still owned and defined in terms of the CSS breakpoint: 40em. So it’s natural that we’ve been creating media queries:


@screen only and (min-width: 40em) {}

and the JavaScript corollary

if (window.matchMedia( "screen and (min-width:40em)" )) { }

Behavioral Breakpoints

Looking at breakpoints in such a clear way inspired me. I’d seen the screen-width-resolution-style graphs before, but the qualitative dimension was new and exciting. In fact, it frees us from tying our breakpoints to a visual or CSS source at all.

What is the breakpoint, in the case of the navigation menu example here?

When implementing the navigation behavior, Matt chose to use the state of the triggering button as the indicator for which kind of menu behavior to apply. Button extant and visible? Convert the navigation behavior to the corresponding off-canvas menu. Button gone? Deactivate the off-canvas menu and use on-page navigation. Matt does this by observing the state of that button and reacting accordingly, not by duplicating or checking on the status of the CSS media query that put it there in the first place.

Building Behavior into the Process

A behavior-centric breakpoint graph for this could look something like this:

graph-3

where the breakpoint is the state of the trigger button. Yes, the state changes at 40em as a result of a CSS media query, but it’s the state we care about, not the media query (or window width) that did it.

Stephen’s book does an excellent job of pushing the notion that behavior needs to be a part of our responsive design processes, integrated and partnered with visual design, not just adjunct to or beholden to it. Breakpoints span various aspects of the overall experience, and I’m glad Stephen helped me really understand this.

Thanks!: my gratitude to Stephen Hay for his personalized help in making breakpoint graphs, Matt Gifford for the off-canvas menu idea that got me thinking and Tyler Sticka for a bit of sketching help and proofreading.

12 Comments on “Behavioral Breakpoints: Beyond Media Queries”

  1. peeela says:

    User focused UX front-end development. Nice.

  2. Greg Robleto says:

    Seems so simple when you (and Stephen) spell it out that way. Setting the expectations for the functionality breaks at the same time as the visual breaks puts everything in lockstep instead of one element chasing the other. Thanks for the post.

  3. Nicola says:

    I’ve started using this pattern to solve the breakpoints issue:

    @media all and (min-width: 45em) {
    body:after {
    content: 'widescreen';
    display: none;
    }
    }

    The pseudo element is not visible but readable in js:

    var size = window.getComputedStyle(document.body,':after').getPropertyValue('content');

    if (size.indexOf("widescreen") !=-1) {
    // do stuff
    }
    You can find more information here

  4. Nick says:

    I support the idea of building in a layer of abstraction to disconnect design breakpoints from the technology used to make them happen. In fact I’d argue that your example doesn’t go far enough. Using the state of the button (instead of CSS) to trigger different page behaviour seems like trading six of one for a half dozen of the other. That kind of cascading logic doesn’t scale. What happens when you want to replace the button with a gesture?

    Page breakpoints should be based on a layer which detects the device capabilities that matter and determines both which CSS and which Javascript to send down.

    The industry is still figuring this stuff out though :). Thanks for an interesting post.

  5. Mat says:

    I’ve been doing something similar to this for a while. I test for css conditions in jquery, so my JS and css are married.

    Looks like this:


    $mobCheck = $('.tinyNav');
    $isMobile = true;
    if($mobCheck.css('display') === 'none') {$isMobile = false}

    If needed, I do a couple, so I would have $isMobile, $isMedium (if you have that much you don’t then need a $isDesktop). Works really well. This is the first thing I put after jQuery in my projects, which does mean you’re in trouble if your user resizes your browser as it only detects the device onLoad, however I have a fix for that, email if you want it!

    • Andy says:

      Hi Matt. So are you checking for the existence of a style (which gets loaded by a specific break point) and then setting a jQuery variable?

      I’m looking for a way to load different jQuery functionality based on breakpoints.

      Thanks,
      -Andy

      • Mat says:

        Hi Andy, thats exactly it. So if you have the code already in the JS, you could use it like this:


        function doSomething() {
        if( $isMobile ) { // do something for mobile }
        else { // do something for everything else }
        }

        The problem is that $isMobile gets set at the onLoad of the page, and won’t update if you resize your browser, so what I do is…


        function searchMobile() {
        mobileCheck = $('#environment'); // a div with different styles placed at breakpoints
        isMobile = true;
        if ( mobileCheck.css('display') === 'none' ) {
        isMobile = false;
        }
        }

        // If browser resizes, check what JS we should apply

        var rtime = new Date(1, 1, 2000, 12,00,00);
        var timeout = false;
        var delta = 200;

        $(window).resize(function() {
        rtime = new Date();
        if (timeout === false) {
        timeout = true;
        setTimeout(resizeend, delta);
        }
        });

        function resizeend() {
        if (new Date() - rtime < delta) {
        setTimeout(resizeend, delta);
        } else {
        timeout = false;
        searchMobile();
        }
        }

        It waits for the browser resize event to NOT happen for a 200 milliseconds and then it resets the variable(s).

  6. Darren Grant says:

    Such a simple concept! It’s hard to believe we haven’t been thinking this way all along. Thanks :)

  7. Thanks for the writeup. I’ve started doing something similar, testing to see if a certain object was floating, but it was a little loose. This is a little clearer and probably easier to follow. I agree w/ Nick—the more we can abstract, the better.

  8. Andy Westmoreland says:

    Great article and feedback. Got me thinking.

    I’m using a combination of Nicola’s and Mat’s ideas to set/read the content of a pseudo element (so far so good).

    I run into problems when I try to convert the content value to an integer to perform calculations on it.

    Example on Codepen.

  9. Nicola Zanon says:

    I guess variable.indexOf("whatever"); would remove the quotes.

    indexOf is not supported by IE8 though.