Responsive components using context queries

Media queries make a website respond to the viewport. But what if we could make site components respond to their container?

I’ve been having a play with the idea of determining the context of a piece of content within a page rather than simply querying the media (device) itself.


Media queries introduced us to a new level of customisation, a single component can take on different forms, depending on the device being used. There are restrictions to this though. Imagine a sidebar, say 1/3 the width of the page (about 300px wide on a screen of 1024px, but only about 200px at 768px). We could use a media query to change this column to 100% the width of the page on screens below 768px, so suddenly the same column is about 700px wide.

Imagine if our component could be styled based on the width of the column in which it was contained, rather than the width of the viewport itself. This would open up many new possibilities, not least of which is the ability to drop components into varying width columns and know they will adapt.

I needed a script which would measure the width of columns both on page load and on resize and then label them accordingly with a class I could use to manipulate my component.

The code

I’m sure there are neater and leaner ways to write this, but here’s what I came up with.

Javascript:
$.fn.contentQuery = function() {

    a = typeof a !== 'undefined' ? a : 25; // columns below 25em will be labelled .cq-small
    b = typeof b !== 'undefined' ? b : 45; // columns below 45em will be labelled .cq-medium
    c = typeof c !== 'undefined' ? c : 60;  // columns below 60em will be labelled .cq-large

    $(this).each(funct ion() {
        $(this).removeClass('cq-small cq-medium cq-large’); // clear classes in case the width has changed since it was last measured
        var fontsize = parseFloat($(this).css("font-size"));
        var PXwidth = $(this).width();
        var EMwidth = (PXwidth/fontsize);
        if (EMwidth < 25) {
            $(this).addClass('cq-small');
        } else if (EMwidth >= 25 && EMwidth < 45) {
            $(this).addClass('cq-medium');
        } else if (EMwidth >= 45 && EMwidth < 60) {
            $(this).addClass('cq-large');
        }
    });
};

$(window).load(function(){
    $('.column').contentQuery();
});

$(window).smartResize(function() {
    $('.column').contentQuery();
});

This script will measure each column (or anything else you ask it to) on the page and label it with a class:

  • 0 -> 25em = .cq-small
  • 25em -> 45em = .cq-medium
  • 45em -> 60em = .cq-large
  • 60em + = no class

(You can change the sizes used to determine each class by just adding three parameters.)
eg.
$('.column').contentQuery(10,20,30);

We can then use these classes to manipulate our element depending on its actual size, rather than that of the window we’re viewing it in.

View the demo

I’ve set the background image of each block to change depending on its parent’s width. Resize the window to see how the blocks respond to the width of their containers.

Wrapping up

I haven’t had a chance to try these on real project yet, but I’m quite excited by the possibilities. Technically there’s nothing here that couldn’t be done with media queries, but it would involve a fair bit more CSS and if you ever decided to move a break point or two you’d have a fair bit of editing to remember. This approach, assuming it’s used sensibly, will also degrade gracefully if javascript is not available.

  • http://www.lanarea.eu Ken Verhaegen

    This is not mobile-first behavior. I’d prefer the classes the other way around.

  • malthoff

    Can you tell us a good example how this will degrade gracefully without JavaScript? One would need to put in media queries instead otherwise the component would not react to width changes. I’m I missing something?

  • Mike Stezycki

    Something similar could be applied via Harry Roberts’ responsive-width classes, so you could use those as the parent selector and style accordingly, no? https://github.com/inuitcss/trumps.widths-responsive

  • Mr.A

    Wait, can’t you do the same thing just using media queries?

    You’re already using two queries to switch .col01 from 100% to 50%, can’t you just add two other breakpoints?

Headscape

Boagworld