The Power of Responsive Suffixes in Class Names

07-08-20 Philip Zastrow

Class naming conventions are essential for coherent CSS architecture. Learn to use responsive suffixes, automating classes with sass mixins and sass maps.

As we have discussed in many articles on The Foundry, class naming patterns are one of the most pivotal pieces to coherent CSS architecture. Some years ago we tried developing our own naming conventions, but trial and error led us to see the value and strengths of the BEM naming convention. Today, we’ve wholly adopted Harry Roberts’ BEMIT approach, which combines the BEM naming convention with the Inverted Triangle CSS organization method. In that article, Roberts’ briefly mentions responsive suffixes as a way to create classes scoped to a media query value. Creating media query scoped classes isn’t new, but Roberts’ approach provides a highly visual indicator that we found to coincide with our ideals of good class naming.

A responsive suffix is an identifier appended to an established class name with properties scoped to a media query, e.g. .my-class@large. This allows us to have a class that adds a margin-top: 1rem scoped to occur at a specific screen width. We can then create a system of classes that allow us to apply changes to elements at various sizes, shifting the margin-top of that element from 0 to 2rem then to 4rem as the size of the screen changes.

Getting Started with Responsive Suffixes

The idea of responsive classes is not new or unique to the suffix approach. Bootstrap has had this built into the framework for years. However, the class naming approach in Bootstrap buries the identifier for a responsive class in the middle of the class name, which is not easily discernible (e.g. .d-sm-block is the class used to define display: block at the small breakpoint). With the responsive suffix approach, the breakpoint portion of the class is always at the end. Additionally, the at symbol (@) indicates that it is a media query breakpoint (e.g. .display-block@sm is a utility class that applies a display: block style when the screen width hits the small breakpoint).

While class names are often created by CSS authors, they’re used most by HTML writers. HTML writers range in skillsets from content authors to programmers. Ideal class names are

  1. Clear in their function
  2. Identifiable in the class attribute’s space-separated list
  3. Consistent in their structure
  4. Comprehensible to someone who doesn’t know CSS

The responsive suffix approach hits all four of these ideals, and so much of that work is done by the @. The @ sign makes the function clear since in CSS the @ is most commonly used for media queries–the heart of responsive design. These classes are clearly identifiable in a space-separated list, thanks to the visual difference of the @ symbol. The structure is consistent as it typically starts with an established class name with an @ and abbreviated size name. Due to the abbreviated size names being fairly common, a pattern can be identified and comprehended by those who don’t know CSS.

Automated Classes with Sass Mixins and Maps

The pattern for a responsive suffix class name has a predictable and consistent structure–which is a terrific opportunity for automation. Sass provides tools to quickly create a myriad of classes based on the parameters of our primary breakpoints found in the responsive suffixes.

Breakpoint Map

Start automating with a Sass map. If you are unfamiliar with Sass maps, they are a key value pair set. For our uses, the map is called $breakpoints, and we have four primary breakpoints that will be used to determine our breakpoint suffixes. These keys will be used as the suffixes, and the value will define the media query breakpoint.

$breakpoints: (
  'sm': 30rem,
  'md': 52rem,
  'lg': 68rem,
  'xl': 84rem
);

Loop Mixin

Our mixin does not accept any parameters and utilizes the mixin as a selector block, which means we pass in properties via the @content block. During the first instance the mixin is used, the properties are immediately printed out. Then our map is put into play with the @each rule, which loops over every key value pair in the $breakpoint map. This creates each media query using the value, which is our screen width size with a unit of measure. Then the original class name is passed into the media query block with the ampersand symbol (&). The ampersand is followed by a backslash. Note that the backslash is a CSS requirement to escape the @ sign so it can be used in a class name. Next, the $key is selected from the map to finish generating the class name with a responsive suffix. Finally, the same styles are dropped into the @content block again. This loop repeats until each key value pair in the map is read.

@mixin loop-mq {
  @content;
  @each $key, $value in $breakpoints {
    @media (min-width: #{$value}) {
      &\@#{$key} {
        @content;
      }
    }
  }
}

Using the Loop Mixin

The mixin is used as a content block and can either wrap a group of selectors or be nested within a selector. When creating utility classes that modify the same property with different values, you must put the related classes inside the mixin. Otherwise, the output will run into a cascade issue, preventing properties from being changed depending on the order of the selectors. In our example below, two utility classes are focused on the display property, both wrapped inside our loop-mq mixin.

@include loop-mq {
  .display-inline {
    display: inline;
  }
  .display-block {
    display: block;
  }
}

Those 8 lines of Sass output several classes that we can now use in our HTML to modify elements and adjust with the screen size.

.display-inline {
  display: inline;
}
.display-block {
  display: block;
}

@media (min-width: 30rem) {
  .display-inline\@sm {
    display: inline;
  }
  .display-block\@sm {
    display: block;
  }
}

@media (min-width: 52rem) {
  .display-inline\@md {
    display: inline;
  }
  .display-block\@md {
    display: block;
  }
}

@media (min-width: 68rem) {
  .display-inline\@lg {
    display: inline;
  }
  .display-block\@lg {
    display: block;
  }
}

@media (min-width: 84rem) {
  .display-inline\@xl {
    display: inline;
  }
  .display-block\@xl {
    display: block;
  }
}

Using the Classes

Now that we have our classes generated, we can hop over to the HTML to start using these new utilities. Here’s our scenario: we need to have an element be inline on a small screen and then be a block at a larger breakpoint. The classes without a responsive suffix will work regardless of screen size. These classes are our base styles. We’ll start with display-inline to define the element as display: inline. Next, we’ll add the display-block@lg class, which will apply a display: block style to the element once the screen reaches 68rem (1088px). We can add further utility classes with and without suffixes to quickly piece together a layout or design that morphs with the device the website is viewed from.

<div class="display-inline display-block@lg">

Bonus: Print Media Queries!

With some modifications to the loop-mq mixin, we can optimize a page for printing. Adding in a print query can come in very handy if you need to adjust spacing or remove an element. For the map, all we need to do is add a new key of print. We don’t need to set the value, as it won’t be needed.

$breakpoints: (
  'sm': 30rem,
  'md': 52rem,
  'lg': 68rem,
  'xl': 84rem,
  'print'
);

The structure of a print media query is much different than a screen width query. Therefore, we have to make a special exception for the print key in our map, using a conditional to choose when to use the correct format. We start by checking that the $key does not equal print and does not print out the screen-width queries first. This allows the loop to work as before until it hits print in the map. Once the loop comes to the print key, the code will jump down to the @else block. Here, the print query is defined, the class is appended with a @print suffix, and the properties are then added.

@mixin loop-mq {
  @content;
  @each $key, $val in $grid-breakpoints {
    @if $key != 'print' { 
      @media (min-width: #{$val}) {
        &\@#{$key} {
          @content;
        }
      }
    }
    @else {
      @media print {
        &\@print {
          @content
        }
      }
    }
  }
}

This can be especially handy for hiding page elements that don’t make sense in the printed format. Examples of this are navigation, the site footer, and maybe sidebar content as well. Taking this further and applying this concept to a grid system would allow changing the layout for an even more optimized print layout.

<div class="display-inline display-block@lg display-none@print">

Sufficient Suffixes

The big drawback that I’m sure some developers will see is that this potentially increases the classes on an element. This is certainly true, though I find the benefits to be a fair trade-off. Additionally, this does not negate the need for creating custom component classes. Always be on the lookout for often repeating groupings of utility classes: they might be better served as a component class. Responsive suffixes expand on the rapid development that utility classes already afford. They allow us to adjust our website designs quickly and can be automated to limit the amount of code written. The loop-mq mixin and responsive suffixes have been helping Sparkbox make responsive utility systems for several years. We hope they help you on your next project as well.