bufferapp / css-style-guide

How we CSS at Buffer
6 stars 3 forks source link

Naming Conventions #1

Closed stphndxn closed 8 years ago

stphndxn commented 8 years ago

@twanlass and I had a discussion in #circle-design about a few naming conventions that feel great to explore! :smile:

We initially started out with perhaps having the base class name capitalised and sub-categories remain lowercase:

.Button--primary-regular {}

It feels great to perhaps follow BEM for this and maybe explore being a little bit more verbose:

// Base Class
.button {}

// Elements of Base Class
.button__icon {}

// Modifiers of Base Class
.button--primary {}
.button--regular {}

// Elements of Base Class with Modifiers
.button__icon--blue {}
.button__icon--dark {}
.button__icon--light {}

This could give us super-descriptive markup such as:

<div class="button--primary button--regular">
  <i class="button__icon button__icon--light icon-arrow-down></i>
</div>

I've been playing and working with React a lot lately and I'd be super-keen to see how prepending hooks might look, too! I.e. js-button, etc.!

Another semi-thought I've been reading up on and wanted to throw out there is whether or not it feels great to maybe avoid nesting wherever possible and perhaps focus more on abstraction? :)

These are some initial, light thoughts as we start exploring how this feels and are keen to reflect and gather insight on! :grin:

cc: @docal56 @djfarrelly @federicoweber @lorenzvs @moreofmorris @msanroman @nieldlr @twanlass

federicoweber commented 8 years ago

Oh wow @stevemdixon thanks for kicking this out :+1:.

I think BEM is a fantastic way to go, would love to know what are everyone feelings around making a small variation to it, around modifiers. A thing I successfully experimented with in the past is to decouple the modifiers from the rest of the class name and to prefix those with a dash .-a_modifier—can't use the double dash as a prefix cause browser compatibility issues. The benefits are that It make it a little bit less verbose, encourage the sharing of common modifiers ( color, sizes, … ), and usually compile in a lighter CSS .

stphndxn commented 8 years ago

Awesome! Love the insight on that one, Federico! That's such a crucial one, too. I wonder how a -_ might feel for this?

// Modifiers of Base Class
.button-_primary {}
.button-_regular {}

// Elements of Base Class with Modifiers
.button__icon-_blue {}
.button__icon-_dark {}
.button__icon-_light {}

I think initially, it feels great to have modifiers connected to those elements that are being modified so that when writing our markup, we have that clarity. For example:

<a class="button primary" href="#">Schedule Post</a>

For anyone experienced with the code base, we might already have the context that primary modified button and that primary isn't used anywhere else. :smile:

<a class="button button-_primary" href="#">Schedule Post</a>

In this example, anyone not familiar with our code base might draw from this that button-_primary only exists with button and never by itself or used with any other element. :smile:

Love, love love these explorations! I feel I might be totally prescriptive on this one! Definitely going to reflect here!

Another side-thought here; we might use things like @mixin and @extend to have that re-usability of our code as elements within our classes to try and maintain single responsibility for our classes? Super curious for thoughts on this one! :grinning:

federicoweber commented 8 years ago

Oh sorry @stevemdixon I believe I miss explained it :smile:. There isn't any issue with the double dash inside the class name, but only at the beginning of it.

What I was proposing is to detach the modifier from the rest of the class name. Ex. <div class='button--blue button--wide'></div> will be <div class='button -blue -wide'></div> and the css will change from:

.button {…}
.button--blue {…}
.button--wide {…}

.dropdown_toggle {…}
.dropdown_toggle--blue{…}

into

.button {…}
.dropdown_toggle {…}

.-wide {…}
.-blue {…} 

When adding a limited effect modifier it's always possible to specify it in the name .-button_blue. So when we have a super specific modifier in the worst case we will have just one extra character, cause of the modifier prefix; but in most cases we will save some characters, and repeating declarations (ex. .button--blue, .dropdown--blue {} ) in the compiled CSS.

:+1: on @mixin and @extend to reuse the code on the source :smile:.

stphndxn commented 8 years ago

Haha, so sorry for my misunderstanding there @federicoweber! :smile: This feels great! I'm curious as to how we might tackle modifiers like .-blue as an example if say a button was blue and an icon was blue? Both being blue and perhaps having completely different properties! :) It feels like nesting could help solve this -- curious on your thoughts (and any ceiling :cat:s here!) on perhaps navigating away from nesting? :)

federicoweber commented 8 years ago

good call on that @stevemdixon :smile:. In case those twos have entirely different properties nesting can work cause something like this in LESS

.button {
    &.-blue {…}
}

will result in .button.-blue {…} in CSS. That is enough to avoid conflicts, but it can create some confusion in the codebase.

My hunch is that we should limit the shared modifiers to a small set of rules that can be easily reused all around. It also pays to be a bit more specific in the naming regarding the purpose of the modifier ( ex. .-blu_bg, .-blue_text, … ).

In cases we need specific modifiers, we can always leverage extension and do something like this:

// if we want a new modifier
.-blue_button {
   &:extend(.-blue);
   …
}

// or if we want to tune just a bit a modifier
.amazing_button {
   &.-blue  {
        &:extend(.-blue);
        …
   }
}
stphndxn commented 8 years ago

Love, love, LOVE this! Yes, that feels so great. :)

twanlass commented 8 years ago

@stevemdixon @federicoweber awesome stuff so far all! Keen to jump in later today :-D

djfarrelly commented 8 years ago

Lots of cool ideas here, so many cool things and variations I haven't thought of before! Not to add too many things to the mix here, but just some thoughts on re-usability:

Atomic as we can be Sometimes I try to relate less selectors to encourage re-usability in the UI, for example, a .button and a .button-icon could be good, but I more think of .button and .icon classes as more atomic elements and perhaps if an icon is used within a button it's get the additional .button-icon class which adds minimal changes to the .icon class.

Semantic class names Kind of in line with some of the above, I like the practice of avoiding classes like .blue and instead using a class like .primary or .cta for call to action. Top-level .blue or .wide classes have less re-usability in my opinion.

Cases + modifiers I do like the .Base-class and .Base-class--modifier method for simplicity's sake and not having to jump between underscores and hyphens. I'm not sure I think too much about elements within a base class as it might discourage re-use sometimes.

From a standpoint of matching these up with components, our React components use Uppercase names so matching the case is awesome :wink:

twanlass commented 8 years ago

So awesome to see everyone getting this topic rolling!

@djfarrelly I really appreciate your insight around semantic class names. This is something I've seen get out of hand real quick on large projects (even more so on ones that go 100% OOCSS) where folks try and be clever and build entire components out of modifiers and end up with classes like blue wide extra-padding with-shadow no-border all-the-things. This of course can make refactoring or even small changes very difficult.

Modifiers I'd love to see us explore this some more. I think the beauty of modifiers in the context of BEM is that they are self contained and very easy to reason about. For example, maybe we want to modify an element to have blue text: .twitter-card__body--blue, while verbose, it does what it says on the tin and we now know exactly where to look to learn more – the .twitter-card and specifically it's body element.

If in 6 months we decide the blue version of this component should have a blue background with white text instead, refactoring this is dead simple and we can do so without worrying about regressions in other components.

Contrast this with a mixin or extend like .blue. Where does this live? Can we safely refactor this without first doing a find-all? What if someone decides that our .blue mixin should also have extra padding? You get the point :-)

For creating cohesion across components variables are a great answer as they're specific and and clear. I.e instead of a general .blue modifier or mixin, relying on a variable instead might be more clear. Our .twitter-card__body--blue modifier can simply contain a color: $primary property instead.

I think above all though it's great to make the right thing the easy thing to do :-)

philippemiguet commented 8 years ago

:cat: :house: (ceiling cat)

I'd love to see us explore this some more. I think the beauty of modifiers in the context of BEM is that they are self contained and very easy to reason about. For example, maybe we want to modify an element to have blue text: .twitter-card__body--blue, while verbose, it does what it says on the tin and we now know exactly where to look to learn more – the .twitter-card and specifically it's body element.

If in 6 months we decide the blue version of this component should have a blue background with white text instead, refactoring this is dead simple and we can do so without worrying about regressions in other components.

Contrast this with a mixin or extend like .blue. Where does this live? Can we safely refactor this without first doing a find-all? What if someone decides that our .blue mixin should also have extra padding? You get the point :-)

For creating cohesion across components variables are a great answer as they're specific and and clear. I.e instead of a general .blue modifier or mixin, relying on a variable instead might be more clear. Our .twitter-card__body--blue modifier can simply contain a color: $primary property instead.

I agree with that and I love how it removes some brain work here :) I have a hunch it will be easier from a refactoring standing point to rely on modifiers/variables rather than classes. The issue you talk about @twanlass I think it could be solved with communication between us all, though with a remote team eager to move fast, I have a feeling it will be safer to rely on variables and modifiers :)

Also, one last thought I wanted to share, when I do this (and I do haha) blue wide extra-padding with-shadow no-border all-the-things it feels similar to modifiers to me (in a dirty way) as I often wrapped up classes with another big one to make sure I'm not breaking anything for someone else. Excited to see where this convo is going :)

I may have a random question, will BEM help to have a less deep class tree, and if so, could it improve performances somehow?

stphndxn commented 8 years ago

Atomic as we can be Sometimes I try to relate less selectors to encourage re-usability in the UI, for example, a .button and a .button-icon could be good, but I more think of .button and .icon classes as more atomic elements and perhaps if an icon is used within a button it's get the additional .button-icon class which adds minimal changes to the .icon class.

I absolutely love this, @djfarrelly. So, this might look like:

.Button {
  color: $white;
  text-align: center;

  background-color: $oceanBlue;
  border: 1px solid rgba(0, 0, 0, 0.2);
  border-radius: $borderRadius;
}

.Button {
  .Icon--small,
  .Icon--regular {
    margin-right: 16px;
  }
}

// ...

.Icon--small {
  width: 14px;
  height: 14px;
}

.Icon--regular {
  width: 16px;
  height: 16px;
}

Where .Icon--small has it's own properties and then only when it's an element of .Button, does it inherit scoped properties? :)

I'm curious on the nesting in my example. :) It feels like .Icon--small and .Icon--regular might be contained altogether in .Button? My hunch is to lean towards separating them out -- I might be wrong on this, though! :)

On the note of capitlization, it feels great to lean towards that consisitency with the naming of our React components! Right now, it feels like we might lean towards our CSS being a separate entity with its own rules and if it felt great, we might try and have that bridge towards React and have both languages be constructed in a similar way? :)

@philippemiguet, it feels like BEM might be a nice, collective methodology for us when we approach exploring new components and elements! Right now, I've been scoping my CSS changes etc. to the specific pages/areas so that I don't break anything. My hunch here is that over time, our markup may have a lot of classes being called where many of them are being overridden and not being used by the element entirely! This feels that browsers might have to load the stylings of elements a few times before it settles on the latest stylings! Curious for thoughts on this! This might cause increased load times? :)

djfarrelly commented 8 years ago

Re: selectors - @stevemdixon, I was more thinking:

.Button {}
.Button-Icon {}
.Icon {}
.Icon--small {}
<button class="Button">
  <i class="Icon Icon--small Button-Icon"></i>
  My button
</button>

That was just a thought, although either approach works for me!!

Re: performance - Adding more CSS will add load times as well as time for the browser to parse the CSS file. More specificity in naming can make the browser render the page slower. There are some interesting points in this document:

https://developer.mozilla.org/en-US/docs/Web/Guide/CSS/Writing_efficient_CSS

There is much more reading on this throughout the web. Honestly, I try to apply some of the principles, but don't let it stop me from simply writing CSS that works. If we were working on a media website that needs to load and render super fast on Mobile, I would think about some of these things a bit more. I think these perf things are great to know and understand, but at this point, it feels our goal is more maintainable CSS and not so much rendering performance. Not sure if that triggers thoughts for us.

As for size, I'd love to reduce the file size of things like buffer.css and app.css, 60 and 69 kb gzipped currently, 399 and 499 kb without gzip compression respectively. Since these are such large files, the browser has a lot of parsing to do. Should be ok for desktop, but that much will definitely hurt performance on Mobile. I hope these thoughts help!

twanlass commented 8 years ago

@stevemdixon thanks for blocking all that out. It really helps to see it all :-) Great thoughts too around something really common (buttons with icons!).

One thing I really like in @djfarrelly example is that there is no interdependency in the classes. Specifically .Button-Icon handles any specific styles when an icon is present within a button. The resulting class tag <i class="Icon Icon--small Button-Icon"></i> is then super clear and readable even when looking at just the markup!

stphndxn commented 8 years ago

@djfarrelly I absolutely love this approach! It feels so great, structured and objectified, too. I'm also a huge fan of your approach of finding what principles feel right and then applying those! On the performance versus maintainable, it feels great to perhaps explore that maintainability and maybe performance is a wonderful outcome of that? :smile:

federicoweber commented 8 years ago

This is a fantastic discussion.

@djfarrelly I love your thoughts around performances :+1:.

In this days I'm doing a survey on Buffer web performance and on average we load in than 5.2 seconds. GZIP is definitely a fantastic help in speeding things up. Another thing we might consider doing is clean up our CSS a bit — a quick audit on the dashboard show that 89% of the CSS rules are unused, plenty of those will be used in different part of the UI, but it's possible that we have some old unused code as well.

On the long run I would love to split the CSS and leverage CSS modules :smile: .

djfarrelly commented 8 years ago

@federicoweber :+1: @stevemdixon :+1: