A CSS Preprocessor Feature for Browser Specific Fixes

Carson Ford
5 min readSep 27, 2021

When I was a new front-end developer first learning about preprocessors I wrote “Learn SASS” on my whiteboard in my home office. Later that day my wife came in and, upon seeing the note, laughed and said, “I can teach you how to be sassy!” To my wife’s disappointment, I meant the CSS extension language. Later I grew to favor the SCSS syntax, so future confusion was avoided.

I have yet to master verbal sass.

There’s a lot to appreciate about preprocessors: partials, nesting, mixins, and other features that can help keep stylesheets both succinct and readable. On the other hand, the capabilities of plain CSS have been growing (variables, filters, and scroll snapping to name a few). Many techniques that once required a preprocessor or even a JavaScript library can now be achieved in CSS alone.

Increasingly, my personal preference is to use plain CSS and vanilla JavaScript whenever possible. For small projects not having a preprocessor can simplify the build process and make things easier to maintain later. However, there are some things that plain CSS can’t quite replicate, like one of my favorite features from Sass, mixins.

Mixins

The Sass website describes mixins as a way to define styles that are meant to be reused. Using a mixin requires two steps: 1) encapsulating styles using the @mixin at-rule and then 2) including those styles elsewhere using the @include at-rule.

For example, you might want several variations of a button. In plain CSS you could create a class for each type of button, but there would be a lot of repetition; if you needed to adjust something like the font or the padding of the buttons, you’d have to edit every button class to keep things consistent.

.button {
border: 1px solid black;
color: black;
font: bold 14px sans-serif;
padding: 8px 16px;
}
.button--primary {
background-color: blue;
border: 1px solid blue;
color: white;
font: bold 14px sans-serif;
padding: 8px 16px;
}
.button--disabled {
border: 1px solid gray;
color: gray;
font: bold 14px sans-serif;
padding: 8px 16px;
}

An alternative would be to create a base button class and then apply a second class with rules for the variations, but with mixins we can simplify matters.

@mixin button {
border: 1px solid black;
color: black;
font: bold 14px sans-serif;
padding: 8px 16px;
}
.button {
@include button;
}
.button--primary {
@include button;
background-color: blue;
border-color: blue;
}
.button--disabled {
@include button;
border-color: gray;
color: gray;
}

We can include the base button styles into each of the button classes by defining a reusable mixin. Then, only the mixin block has to be edited for all the buttons to inherit those changes. When the code compiles to plain CSS it will function just like the first example, but it is much easier to maintain.

We can leverage mixins further by passing arguments to them. Similar to JavaScript functions, arguments are used as local variables within the block of code. To continue with the button example, we can pass a color to each instance like so:

@mixin button($background-color, $border-color, $color) {
background-color: $background-color;
border: 1px solid $border-color;
color: $color;
font: bold 14px sans-serif;
padding: 8px 16px;
}
.button {
@include button(transparent, black, black);
}
.button--primary {
@include button(blue, blue, white);
}
.button--disabled {
@include button(transparent, gray, gray);
}

A single mixin can accept multiple, comma-separated arguments and you can even define default values for them.

Media Query Mixins

Similar to arguments, a mixin can also accept many lines of styles called a content block. Using the @content at-rule, when a mixin is included any code within the curly braces will be injected in its place. I first encountered this method as a way to manage breakpoints; there are many variations of this technique, but here is a simple version:

@mixin bp-desktop {
@media (min-width: 1440px) {
@content;
}
}
@include bp-desktop {
// styles that will be wrapped in a media query
}

By including the bp-desktop mixin, any styles added to that block will be wrapped in the predefined media query. All instances of the include will use the same breakpoint value and if it ever needs to be changed it only has to be edited in the mixin declaration.

Some versions of this method use an argument for the min-width value, making the mixin much more flexible. I tend to use similar breakpoints from project to project though, so oftentimes this simple version is enough to get the job done.

Browser Specific Media Query Mixins

Anyone who has done browser-checks to make sure a site looks good across all browsers (yes, IE was included in the proposal) knows it can be maddening to tweak the CSS so that it behaves consistently. A fix in one browser can create new problems in another. One step forward, two steps back.

Browser support for the latest and greatest CSS features continues to improve, but sometimes, as a last resort, you need a little bit of CSS that is specific to a single browser. Once again, media queries come to the rescue.

A quick Google search for browser-specific media queries will turn up plenty of results for complex, difficult-to-remember media query formulas. However, using the power of mixins and the @content at-rule, we can define reusable, browser-specific media queries.

// Internet Explorer 10+
@mixin ie {
@media screen and (-ms-high-contrast: active), (-ms-high-contrast: none) { @content; }
}
// Microsoft Edge
@mixin edge {
@supports (-ms-ime-align:auto) { @content; }
}
// Safari 10.1+
@mixin safari {
@media not all and (min-resolution:.001dpcm) {
@supports (-webkit-appearance:none) { @content; }
}
}
// Firefox 4+
@mixin firefox {
@media screen and (min--moz-device-pixel-ratio:0) { @content; }
}
// Chrome 29+
@mixin chrome {
@media screen and (-webkit-min-device-pixel-ratio:0) and (min-resolution:.001dpcm) { @content; }
}

With this set of mixins you just have to use the corresponding include for the browser that needs specific styling — much better than trying to remember each unique hack or having to look it up every time you need one!

For more ways to target specific browsers check out BrowserHacks. It can help you create your own list of mixins for cases you encounter frequently.

There’s a lot more to learn about mixins, but I hope this guide is helpful in getting started and that the breakpoint and browser-specific examples prove useful. For further research, be sure to reference the full Sass documentation.

--

--