Skip to content
This repository was archived by the owner on Aug 8, 2023. It is now read-only.

Commit 2f7aad7

Browse files
committed
Merge pull request #36 from mobify/add-note-on-specificity
Post-Documentation Tweaks
2 parents 481c47e + 366ce28 commit 2f7aad7

2 files changed

Lines changed: 156 additions & 19 deletions

File tree

css/css-best-practices/readme.md

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,9 @@ That includes things like Flexbox, CSS3 properties, and many more! This also mea
2727

2828
## Selector Specificity
2929

30-
We strive to write performant, portable, selectors whenever possible.
30+
We strive to write performant, portable, selectors whenever possible. In short, [keep your CSS selectors short](http://csswizardry.com/2012/05/keep-your-css-selectors-short/).
31+
32+
To help do that, it might be helpful to know how to measure specificity which [Smashing Magazine has an article just for that](http://www.smashingmagazine.com/2007/07/27/css-specificity-things-you-should-know/)!
3133

3234
### Dos:
3335

css/sass-best-practices/readme.md

Lines changed: 153 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,13 @@ As mentioned earlier, we use [Sass](http://sass-lang.com/) using the `SCSS` synt
66
## Table of Contents
77

88
* [Nest Only When Necessary](#nest-only-when-necessary)
9+
* [Beware Nested Comma Separated Selectors](#beware-nested-comma-separated-selectors)
910
* [Global vs. Local Variables/Mixins](#global-vs-local-variablesmixins)
1011
* [`@extends`](#extends)
12+
* [Pitfalls](#pitfalls)
13+
* [Workaround](#workaround)
14+
* [Caveats](#caveats)
15+
* [Genuine Usecases](#genuine-usecases)
1116
* [Filename Naming Convention](#filename-naming-convention)
1217
* [Note on Partials](#note-on-partials)
1318
* [Commenting Best Practice](#commenting-best-practice)
@@ -23,6 +28,23 @@ Limit nesting as much as possible. Assess every single level of nesting that you
2328
At most, go no more than 4 levels deep.
2429

2530

31+
### Beware Nested Comma Separated Selectors
32+
33+
[This example](http://sassmeister.com/gist/891f2002ef23bf8e4788) demonstrates a real-world scenario that happens when developers recklessly author code to match the markup too closely.
34+
35+
It is a bad habit to nest every or most times an element is appears nested in the markup. CSS is not HTML, so we can't treat it in the same way. We have to be mindful of the selectors that we are compiling.
36+
37+
What strategy do we use to avoid unnecessary nesting? Let's use the above [sassmesiter.com](http://sassmeister.com/gist/891f2002ef23bf8e4788) example and tweak it to show a few ways we could approach it instead. We could...
38+
39+
* [Nest less](http://sassmeister.com/gist/12ca39f4fa72cafc5a75)
40+
* [Don't nest at all](http://sassmeister.com/gist/67f8fd11522e1d4692a9) and use template classes
41+
* or, [don't nest at all](http://sassmeister.com/gist/036b0a161a47f321b776) and use component classes
42+
43+
The approach you use depends on many factors. How much control you have over the markup? How reusable are the styles that you are writing? Are the desktop or inline styles that you must account for?
44+
45+
Chances are you will have to use a combination of all these strategies that works based on the state of your project and its markup.
46+
47+
2648
## Global vs. Local Variables/Mixins
2749

2850
Any `$variable` that is used in more than one file should be placed in the `/vellum/variables.scss` file. Others should be placed at the top of the file in which they're used.
@@ -32,36 +54,149 @@ Any `@mixin` that is used in more than one file should be placed in the `/utilit
3254

3355
## `@extends`
3456

35-
__Avoid extends when possible__. It's always preferable to add a class to the markup than it is to use an extend.
57+
As a rule of thumb, try to avoid `@extend`.
58+
3659

37-
If unavoidable, __never directly extend a standard class__. This can easily lead to enormous bloat in the generated CSS.
60+
### Pitfalls
3861

39-
When using @extends, __only extend a placeholder class__. This avoids the most problematic issues of Sass `@extend`s.
62+
The main problem with `@extend` is that it is easy to bloat your code by using it. When first starting to use it, the code can be very innocent in appearance, such as:
4063

4164
```scss
42-
// BAD
65+
// SCSS code
66+
.c-button {}
4367

44-
.c-some-class {
45-
// ...
68+
.c-callout {
69+
@extend .c-button; // At first glance, this might not look dangerous
4670
}
4771

48-
.t-pdp .client-class {
49-
@extend .c-some-class;
72+
// Compiled output
73+
.c-button, .c-callout {}
74+
.c-callout {}
75+
```
76+
77+
That does appear pretty innocent. However, what happens if we do this:
78+
79+
```scss
80+
// SCSS code
81+
.c-button {}
82+
83+
.c-callout {
84+
@extend .c-button; // At first glance, this might not look dangerous
5085
}
5186

87+
.t-home .cta .c-button {
88+
// this looks pretty innocent too, right?
89+
}
90+
91+
// Compiled output
92+
.c-button, .c-callout {}
93+
.c-callout {}
94+
.t-home .cta .c-button, .t-home .cta .c-callout {}
95+
```
96+
97+
Whoa! See what happened? Notice the extra selector that got compiled that we probably didn't expect to happen: `.t-home .cta .c-callout`. This happens because Sass extends every single instance of that selector, regardless of the selector chain. That means any, and I mean really any instance that `.c-button` is written in a selector, they all get extended by `.c-callout`, which can be a massive amount of unwanted code.
5298

53-
// Better
5499

55-
.c-some-class, // A placeholder should ALWAYS have a standard class to go with it. Add the placeholder
56-
%c-some-class { // AFTER the standard class. This makes the code easier to find based on the compiled selectors.
57-
// ...
100+
### Workaround
101+
102+
So there is a workaround for the problem we described above: __never directly extend a standard class__. Instead, __only extend placeholder classes__. Let's see what that looks like using the same example from above:
103+
104+
```scss
105+
// SCSS code
106+
.c-button, %c-button {} // Notice that this is a placeholder class
107+
108+
.c-callout {
109+
@extend %c-button;
58110
}
59111

60-
.t-pdp .client-class {
61-
@extend %c-some-class;
112+
.t-home .cta .c-button {}
113+
114+
// Compiled output
115+
.c-button, .c-callout {}
116+
.c-callout {}
117+
.t-home .cta .c-button {}
118+
```
119+
120+
Notice how the bloated CSS from the original example is now gone!
121+
122+
Also notice how we still included the original `.c-button` class in the SCSS. After all, we still want to use that in our HTML, we just never extend it directly.
123+
124+
This technique is described in detail in Chris Lamb's article [Mastering Sass Extends and Placeholders](http://8gramgorilla.com/mastering-sass-extends-and-placeholders/).
125+
126+
127+
### Genuine Usecases
128+
129+
__Scenario 1__: There are situations where you want to have default styles on elements like lists or headings, but you may also need classes for those same styles to use when you can't use the exact markup. Good real life example is when you need a heading to be an `<h3>` but it must look like an `<h1>` or vice versa.
130+
131+
This is how we deal with this scenario using `@extend`:
132+
133+
```scss
134+
// In `/vellum`
135+
// ---
136+
137+
h1,
138+
h2,
139+
// etc.
140+
%headings {
141+
font-family: sans-serif;
62142
}
143+
144+
h1,
145+
%h1 {
146+
font-size: 18px;
147+
}
148+
149+
h2,
150+
%h2 {
151+
font-size: 16px;
152+
}
153+
154+
// and so on...
155+
156+
157+
// In `/components`
158+
// ---
159+
160+
.c-heading {
161+
@extend %headings;
162+
}
163+
164+
.c-heading.c--1 {
165+
@extend %h1;
166+
}
167+
168+
.c-heading.c--2 {
169+
@extend %h2;
170+
}
171+
172+
// and so on...
63173
```
64174

175+
Please note that in Mobify's way of writing CSS, we do not declare classes in vellum (a.k.a. globals) and we do not style elements or tags in components, which is why these two are declared separately in their respective directories.
176+
177+
__Scenario 2__: This scenario is very specific to Mobify and here is why... as you probably know by now, using Mobify's Adaptive.js you are basically transforming a site's DOM and applying new CSS to the new DOM. Typically, we would expect to have full control over the markup, and therefore over all the classes added to the DOM. Unfortunately, there are times when parts of the DOM cannot be changed exactly like you might want, such as when the website is using third-party plugins like BazaarVoice.
178+
179+
What are the consequences of not being able to control parts of the DOM? It means we might not be able to add classes to the DOM reliably, which means that we can't apply our styles as easily as we would like. Instead, we are forced to write some gnarly selectors to ensure these situations can work. Let's use BazaarVoice again as our example:
180+
181+
```scss
182+
[id="BVRRContainer"] {
183+
184+
.BVRRSortSelectWidget {
185+
@extend %c-select;
186+
}
187+
188+
.BVRRDisplayContentSubtitle {
189+
@extend %h2;
190+
}
191+
192+
.BVRRContextDataContainer > div {
193+
@extend %c-rows;
194+
}
195+
}
196+
```
197+
198+
So what is happening here? Well because we can't add classes to the DOM on the BazaarVoice widget, instead we have to directly select the IDs and classes that they are using already. Now because we want BazaarVoice to look like our website, we want to reuse some of the CSS we've written. The solution to this is to use `@extend` on BazaarVoice selectors.
199+
65200

66201
## Filename Naming Convention
67202

@@ -114,7 +249,7 @@ The second aspect of comments are the comments themselves! There are three types
114249

115250
1. General Comments
116251

117-
```
252+
```scss
118253
// My Component
119254
// ============
120255
//
@@ -131,7 +266,7 @@ Direct comments are those that apply to a single line of code as denoted by the
131266

132267
Be aware that these notes typically only refer to the code directly beneath it, as far as just before the next section (i.e. the next sub-component). That next section could have it's own Direct Notes, but they will only apply to that section despite using the same numbers.
133268

134-
```
269+
```scss
135270
// My Component
136271
// ============
137272
//
@@ -171,7 +306,7 @@ So Note A will always refer to the same note.
171306

172307
This is a rare use case, but can be useful sometimes when you have the same set of changes that need to be applied across more than one section of code.
173308

174-
```
309+
```scss
175310
// My Component
176311
// ============
177312
//
@@ -213,7 +348,7 @@ Do note that variables without modifiers are implicitly the base version of that
213348

214349
For color gradients, we follow a convention that looks like `{modifier}-{name}-{number}` where the number _roughly_ corresponds to some property level of that color, such as the greyscale level.
215350

216-
```
351+
```scss
217352
$grey-10 // 10% greyscale
218353
$grey-20 // 20% greyscale
219354
$grey-30 // 30% greyscale

0 commit comments

Comments
 (0)