Right now, I believe CSS should be minimal, meaningful (to people), and not descriptive of what it looks like (visually). Also, I can be a bit of an idealist. Especially at the beginning of a project.

As a warning, this post isn’t meant to teach basic CSS concepts. Get ready for some CSS nerdery.

Project Overview: BEM and Utility %placeholders

I’m using BEM (Block, Element, Modifier) syntax, and it’s amazing. I can build a solid component marked up with one or two BEM classes per element, keep my Sass mostly un-nested to avoid specificity issues, and the HTML looks clean.

But, utility CSS classes are sooooo handy. Want it to be smaller text? We have a class for that called .small. Align it to the right? How about something elegantly named .align-right? Want it a different color? Maybe, .blue? or .green? Just add a class to your HTML and we’re done!

Well, at some point, I’d rather write my CSS by hand in a CSS file. Not to mention, that HTML is no longer so fresh, so clean.

How to get the best of both worlds? Lots and lots of %placeholders in my CSS. All of the %placeholders. So those utility classes become Sass %placeholders that I @extend.

This all still sounds so fresh, so clean. Here’s where it gets uglier.

You can’t use Sass extends inside media queries.

Well, you can, but it gets hairy. I used Hugo Giraudel’s Cross-Media Query @extend Directives in Sass to work around the issue, and his solution is pretty clever and hard to understand. I’m not going to attempt to explain it. Using his method in place of the default %placeholder, I could use his @include _(‘foo’); inside my pre-defined breakpoints like this:



@include placeholder('font--small') {
   font-size: 0.8rem;
}

@include placeholder('font--large') {
   font-size: 2.8rem;
}

// why I don't like visually descriptive
// classes in HTML

.big-thing {
    @include _(font-small);

    @include breakpoint(medium) {
        @include _(font--large);
    }
}


Extending Extends of Extends to Extend their Extensiveness

Quick note. This is supposed to be overly contrived. The point is, I’m abusing placeholders and hoping they’ll operate like a @mixin, which they clearly don’t. Skip to Wait, What? if you’re already convinced.

So, If I’m using @extends as utilities, why shouldn’t I @extend my extends? I mean, besides that I’m blatantly ignoring how they’re supposed to work and hoping things will turn out OK. Let me give you an example.



@include placeholder('icon--blue-bg') {
    background: $blue;
    fill: #fff;
}

@include placeholder('icon--circle') {
    border-radius: 50%;
    @include _(icon--blue-bg);
}

@include placeholder('icon--white-bg') {
    background: #fff;
    fill: $blue;
}

@include placeholder('icon--normal') {
    width: 1.2rem;
    height: 1.2rem;
}

@include placeholder('btn--icon__icon--circle') {
    @include _(icon--circle);
    @include _(icon--white-bg);
    @include _(icon--normal);
    margin-left: 0.5em;
    position: relative;
    top: 0.15rem;
}

.btn--icon__icon--circle {
    @include _(btn--icon__icon--circle);
}


That’s %placeholders three levels deep and BEM to the max, y’all. That last class, .btn--icon__icon--circle means “a button that has an icon and this is styling the icon within that button, and the icon should be in a circle”. Whew.

So, that all kind of makes sense? Maybe? Right?

The issue is, that once you apply that placeholder to something and want to override it, the CSS specificity gets weird. The specificity is now applying to the location of your placeholders because of how Sass compiles @extends. So, if I wanted to make a button icon have a circle, but I actually want that circle to be a blue background?

I’d just use my icon--blue-bg placeholder! Easy. Right?



.btn--icon__icon--circle--blue {
    @include _(btn--icon__icon--circle);
    @include _(icon--blue-bg);
}


Nope.

The issue is that the placeholder('icon--blue-bg') has to come after the placeholder('icon--white-bg'). _('icon--blue-bg') can never override _('icon--white-bg'). I’ve introduced specificity into order of the %placeholders. Not at all what I wanted to do.

Wait, what?

To review, I used these weird placeholder @includes to replace Sass’s default %placeholders so I could use @extends inside of media queries so I could keep my HTML so fresh and so clean.

I wish I had typed that awful sentence before I did all the work to incorporate it. Maybe that would have shown me that I was over-engineering something in the pursuit of idealism.

As my friend Jonathan Vieker says, “What would this look like if it were easy?”. Well, Jonathan, certainly not this.

But, That’s Just Your Weird @include placeholder(‘stuff’)!

That’s what I was hoping, too. The same specificity issue still applies with good ol’ placeholders. Check this:



%font--large {
  font-size: 2.8rem;
}

%font--small {
  font-size: 1rem;
  @extend %font--large;
}

.extend-big {
  @extend %font--small;
  @extend %font--large;
}

Obviously this is really contrived (but not really once you’re using breakpoints). Will .extend-big be small or large? Any guess?

It’s small. SMALL, I SAY!

The specificity isn’t about when you apply it to the .extend-big class, it’s about the location of the @extend itself. So, want to make it large? Don’t worry about changing the order on the .extend-big class, you have to change the order of the %placeholders themselves like this:



%font--small {
  font-size: 1rem;
  @extend %font--large;
}

%font--large {
  font-size: 2.8rem;
}

.extend-big {
  @extend %font--small;
  @extend %font--large;
}

Now .extend-big will be large.

Here’s a codepen for you to play around with to see it in action:

See the Pen Extensively Extending Sass Extends by Jerry Jones (@jeryj) on CodePen.

How I Could Have Made it Easy

You know what you can use inside of a media query and not worry about specificity? Yup. A @mixin. I knew that before I started. But I wanted so fresh, so clean! What I got was a headache of vague specificity.

For my next project, my current plan is to use @mixins as utility classes, and then use CSSO with Gulp to trim down the CSS, effectively doing what my elusive ideal %placeholder would have done.

Here’s an example using the same example above but with @mixins and CSSO.



@mixin font--large {
  font-size: 2.8rem;
}

@mixin font--small {
  font-size: 1rem;
  @include font--large;
}

.mixin-big {
  @include font--small;
  @include font--large;
}

// just to contrive it more for good measure,
// let's add another class

.csso-mixin-big {
  @include font--large;
}


.mixin-big is actually large like you’d think it’d be. Here’s what it compiles to in Sass, before hitting CSSO:



.mixin-big {
  font-size: 1rem;
  font-size: 2.8rem;
  font-size: 2.8rem;
}

.csso-mixin-big {
  font-size: 2.8rem;
}


Not so fresh, so clean. Let’s run it through CSSO:



.csso-mixin-big,
.mixin-big{
    font-size: 2.8rem;
}


So fresh, so clean! So fresh, so clean!

Andre 3000 and Big Boi from Outkast busting sweet moves in their video for So Fresh, So Clean

I haven’t tried this with a complex project or nested in lots of weird breakpoints, so I don’t know how well it’ll work. I haven’t stress tested it yet.

But rest assured, when I do, I’ll write a post called “How I overused @mixins and CSSO”.

  • Jonathan Vieker

    Nice! I don’t know what most of this means, but I can follow the overall message. Also, nice gif!

    • http://jeremyjon.es Jerry Jones

      Thanks! I was concerned it was too lengthy and not focused on making one specific point. Glad you could get some kind of message out of it 🙂 I’ll be curious to hear what you took away from it.