An Introduction to Mobile-First Media Queries

I actually prefer to use a combination of min and max. Crazy? Consider this: when designing your site, you have a set of items in mind that visitors will be able to interact with. For very small screens, some of these may need to be hidden so that just the article or whatever can be seen, or perhaps so that certain sections get moved to the bottom. A big concern is when trying to present tabular data, since the solution on mobile is to turn rows into cards with each data column prefixed with a ::before selector. Are you going to do all that for a mobile screen first before getting your table looking right?

The problem, of course, is complexity and keeping things straight; mixins are great for that. I still use min-width for most things like breaking up linear content and creating more columns, so that’s the default for my mixin; that way, I can work on the desktop first just by putting all the layout code within an include. If I find something needs to break earlier or works fine on mobile, I can just move it out of the include without losing context. Then when I look at things on mobile, I can concentrate on finding the issues that are specific to that media and use a ‘max-width’ version of the mixin for those. Of course, this would probably work the other way too, but I find this approach is the best way to avoid unnecessarily resetting values.

When it comes to responsive sites, I think it’s more a question of using a good preprocessor and looking at each objects display requirements as they fit into the site rather than worrying about top-down vs. bottom-up design. Here is my SCSS/SASS mixin with conversions and error-checking. @include mquery { } will creat a medium min-width media query by default. In addition to treating unitless numbers as ems, I also included a standard conversion of px to em so it can be used to cover for programmers who haven’t switched over yet. Feel free to remove that line if you want to enforce good programmer habits.

// _global.scss
$breakpoints: (
  small: 24em,
  medium: 56em,
  large: 96em,
  xlarge: 144em
);
 
// _mixins.scss
@mixin mquery($width: 40, $minmax: min) { 
  @if map-has-key($breakpoints, $width) {
    $width: map-get($breakpoints, $width)
  }
  
  @if type-of($width) == "number" {
  @if unitless($width) { $width: $width * 1em }
  @if unit($width) == px { $width: $width / 16px/em }
    @if unit($width) != em {
      @warn "Please use em values for breakpoints.";
	}
    @else {
      @media (#{$minmax}-width: #{$width}) {
        @content;
      }
    }
  }
  @else {
    @warn "The width could not be understood."
        + "Please provide a valid breakpoint.";
  }
}

// _components.scss example
div {
  padding: 0.5em;
  @include mquery(large) {
    float: left;
    width: 35%;
  }
  @include mquery(30, max) {
    display: none;
  }
}

Change the default width to whatever you want. Using this mixin, the code from ChrisPoteet and RyanReese can be cleaned up like so:

.container {
  width: 100%;
  display: table;
}
.container > div {
  padding: 0.5em;
  @include mquery {
    display: table-cell;
  }
}
.first, .third {
  background: PeachPuff;
  @include mquery {
    width: 25%;
  }
}
.second {
  background: MediumAquamarine;
}

or the media query abstracted out (I prefer to compile things semantically, and it’s possible to have media queries combined using a preprocessor)

.container {
  width: 100%;
}
.container > div {
  padding: 0.5em;
}
.first, .third {
  background: PeachPuff;
}
.second {
  background: MediumAquamarine;
}
@include mquery {
  .container > div {
    display: table-cell;
  }
  .first, .third {
    width: 25%;
  }
}

In fact, by selecting nth-child instead of using classes, this can be made even more compact! With SASS:

.container {
  width: 100%;
  display: table;
  > div {
    padding: 0.5em;
    background: MediumAquamarine;
    @include mquery {
      display: table-cell;
    }
  }
  :first-child, :last-child {
    background: PeachPuff;
    @include mquery {
      width: 25%;
    }
  }
}

Here is the codepen link:

If you want to go desktop-down, you would need to add two lines of extra reset code (after changing the mixin minmax default to max), here is that with the media query abstracted out so you can see that too:

.container {
  width: 100%;
  display: table;
  > div {
    padding: 0.5em;
    display: table-cell;
    background: MediumAquamarine;
  }
  :first-child, :last-child {
    width: 25%;
    background: PeachPuff;
  }
  @include mquery {
    > div {
      display: block;
    }
    :first-child, :last-child {
      width: 100%;
    }
  }
}

—Easier if you’ve got your desktop layout already, but it’s unnecessary bloat when designing from scratch with this approach, even when you are working from a desktop mockup.

1 Like