You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
This repository was archived by the owner on Aug 8, 2023. It is now read-only.
Copy file name to clipboardExpand all lines: css/css-best-practices/readme.md
+3-1Lines changed: 3 additions & 1 deletion
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -27,7 +27,9 @@ That includes things like Flexbox, CSS3 properties, and many more! This also mea
27
27
28
28
## Selector Specificity
29
29
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/)!
*[Commenting Best Practice](#commenting-best-practice)
@@ -23,6 +28,23 @@ Limit nesting as much as possible. Assess every single level of nesting that you
23
28
At most, go no more than 4 levels deep.
24
29
25
30
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...
*[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
+
26
48
## Global vs. Local Variables/Mixins
27
49
28
50
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
32
54
33
55
## `@extends`
34
56
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
+
36
59
37
-
If unavoidable, __never directly extend a standard class__. This can easily lead to enormous bloat in the generated CSS.
60
+
### Pitfalls
38
61
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:
40
63
41
64
```scss
42
-
// BAD
65
+
// SCSS code
66
+
.c-button {}
43
67
44
-
.c-some-class {
45
-
//...
68
+
.c-callout {
69
+
@extend.c-button; //At first glance, this might not look dangerous
46
70
}
47
71
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
50
85
}
51
86
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.
52
98
53
-
// Better
54
99
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;
58
110
}
59
111
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;
62
142
}
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...
63
173
```
64
174
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
+
65
200
66
201
## Filename Naming Convention
67
202
@@ -114,7 +249,7 @@ The second aspect of comments are the comments themselves! There are three types
114
249
115
250
1. General Comments
116
251
117
-
```
252
+
```scss
118
253
// My Component
119
254
// ============
120
255
//
@@ -131,7 +266,7 @@ Direct comments are those that apply to a single line of code as denoted by the
131
266
132
267
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.
133
268
134
-
```
269
+
```scss
135
270
// My Component
136
271
// ============
137
272
//
@@ -171,7 +306,7 @@ So Note A will always refer to the same note.
171
306
172
307
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.
173
308
174
-
```
309
+
```scss
175
310
// My Component
176
311
// ============
177
312
//
@@ -213,7 +348,7 @@ Do note that variables without modifiers are implicitly the base version of that
213
348
214
349
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.
0 commit comments