Skip to content

Commit b7835a0

Browse files
committed
Wrote the rest of the TODO sections.
1 parent 15fd01f commit b7835a0

File tree

2 files changed

+82
-14
lines changed

2 files changed

+82
-14
lines changed

README.md

Lines changed: 82 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -79,7 +79,7 @@ If you're working in the browser, and you spot a component that's misbehaving, y
7979

8080
Noting the name of the component you switch to your editor, hit the key combo for "Quick open file", start typing "head", and there you go:
8181

82-
![quick-open-file](quick-open-file.png)
82+
![Quick open file](quick-open-file.png)
8383

8484
This strict mapping from UI components to the corresponding source files is doubly useful if you're new on the team and don't know the architecture by heart yet: you don't need to, to be able to find the guts of the thing you're supposed to work on.
8585

@@ -179,7 +179,7 @@ Which becomes:
179179
}
180180
```
181181

182-
The above pattern makes it very convenient to use long, unique class names without having to keep typing them over and over again. Convenience is mandatory, because without convenience, there will be cutting of corners.
182+
The above pattern makes it very convenient to use long, unique class names without having to keep typing them over and over again. Convenience is mandatory, because without convenience, we will cut corners.
183183

184184
#### Quick aside on the JS side of things
185185

@@ -293,7 +293,74 @@ This is a good example of understanding the rules, so you know when to break the
293293

294294
### An aside for the curious: Prevent leaking styles *into* the component
295295

296-
TODO: NOT POSSIBLE w/o Shadow DOM or iframes
296+
So have we achieved perfect sandboxing of our styles, so that each component can live in total isolation from the rest of the page? As a quick recap:
297+
298+
* We've prevented leaks **out of our components** by prefixing each class name with the component namespace:
299+
300+
+-------+
301+
| |
302+
| -----X--->
303+
| |
304+
+-------+
305+
306+
* By extension, this means we've prevented leaks **between our components**:
307+
308+
+-------+ +-------+
309+
| | | |
310+
| ------X------> |
311+
| | | |
312+
+-------+ +-------+
313+
314+
* And we've prevented leaks **into child components** by minding our child selectors:
315+
316+
+---------------------+
317+
| +-------+ |
318+
| | | |
319+
| ----X------> | |
320+
| | | |
321+
| +-------+ |
322+
+---------------------+
323+
324+
* But crucially, **styles can still leak into components**:
325+
326+
+-------+
327+
| |
328+
----------> |
329+
| |
330+
+-------+
331+
332+
For example, say we have styled our component with:
333+
334+
```scss
335+
.myapp-Header {
336+
> a {
337+
color: blue;
338+
}
339+
}
340+
```
341+
342+
But then we include an ill-behaving 3rd party library which introduces the following CSS:
343+
344+
```css
345+
a {
346+
font-family: "Comic Sans";
347+
}
348+
```
349+
350+
**There is no simple way to protect your components from such external abuse**, and this is where we often need to just:
351+
352+
![Give up](give-up.gif)
353+
354+
Luckily, you often have some control over the dependencies you use, and can simply look for a more well-behaved alternative.
355+
356+
Also, I said there's no *simple* way to protect your components from this. That doesn't mean there aren't ways. [There are ways, dude](https://www.youtube.com/watch?v=20wUS_bbOHY), they just come with various trade-offs:
357+
358+
* Just brute-forcing it: if you include a [CSS reset](http://cssreset.com/what-is-a-css-reset/) *for every element of every component*, and attach it to a selector that always wins over the 3rd party ones, you're golden. But unless your application is tiny (say, a "Like" button 3rd parties can embed onto their sites), this approach quickly spirals out of control. This is rarely a good idea, it's just listed here for completeness.
359+
* [`all: initial`](https://developer.mozilla.org/en/docs/Web/CSS/all) is a less known new CSS property designed for exactly this. It can [stop inherited properties from flowing in](https://jsfiddle.net/0d9htatc/), and also work as a local reset, [as long as it wins the specificity war](https://jsfiddle.net/e7rw4L8L/) (and as long as you repeat it for each element you want to protect). Its implementation includes [some intricacies](https://speakerdeck.com/csswizardry/refactoring-css-without-losing-your-mind?slide=39) and [isn't yet supported](http://caniuse.com/#feat=css-all) everywhere, but `all: initial` might eventually become a useful tool for style isolation.
360+
* Shadow DOM has already been mentioned, and it's exactly the tool you would want for this job, as it allows declaring clear component boundaries for both JS and CSS. Despite [some recent glimmers of hope](https://developer.apple.com/library/content/releasenotes/General/WhatsNewInSafari/Articles/Safari_10_0.html), the Web Components spec hasn't made great progress in recent years, and unless you're working with a known set of browsers you can't really count on the Shadow DOM.
361+
* Finally, there's the `<iframe>`. It offers the strongest form of isolation the web runtime can offer (both for JS and CSS), but also carries steep penalties in startup cost (CPU cycles) and maintenance cost (retained memory). Still, oftentimes the trade-off is worth it, and most prominent web embeds (Facebook, Twitter, Disqus, etc) are in fact implemented with iframes. For the purposes of this document however -- isolating hundreds or thousands of small components from each other -- this approach would blow our performance budget a 100 times over.
362+
363+
Anyway, this aside is running long, back to our list of CSS rules.
297364

298365
### 6. Respect component boundaries
299366

@@ -333,24 +400,25 @@ The pragmatic solution to this is to (partially) relax our previous rule of only
333400
}
334401
```
335402

336-
But we also don't want to allow breaching the child's sandbox arbitrarily:
403+
This is in fact perfectly OK, as long as we don't allow breaching the child's sandbox arbitrarily:
337404

338405
```scss
339406
// COUNTER-EXAMPLE; DON'T DO THIS
340407
.myapp-Header {
341408
> .myapp-LoginForm {
342409
color: blue;
410+
padding: 20px;
343411
}
344412
}
345413
```
346414

347-
Because we'd lose our safety net of knowing our local changes can never have global repercussions. So where do we draw the line between what's OK and what's a no-no?
415+
We don't want to allow this, because we'd lose our safety net of knowing our local changes can never have global repercussions. So where do we draw the line between what's OK and what's a no-no?
348416

349417
We want to respect the sandbox *inside* each child component, as we don't want to rely on its implementation details. It's a black box to us. What's *outside* the child component, conversely, is the sandbox of the parent, where it reigns supreme. The distinction between inside and outside emerges quite naturally from one of the most fundamental concepts in CSS: [the box model](https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Box_Model/Introduction_to_the_CSS_box_model).
350418

351-
TODO: image: box-model
419+
![CSS Box Model](box-model.png)
352420

353-
My analogies are usually terrible, but here goes: just like *being inside a country* means being within its physical *borders*, we establish that a parent can effect styles on its (direct) children only outside the border of the component. That means properties related to positioning and dimensions (e.g. `position`, `margin`, `display`, `width`, `float`, `z-index` etc) are OK, while properties that reach inside the border (e.g. `border` itself, `padding`, `color`, `font` etc) are no-no.
421+
My analogies are terrible, but here goes: just like *being inside a country* means being within its physical borders, we establish that a parent can effect styles on its (direct) children only outside the border of the component. That means properties related to positioning and dimensions (e.g. `position`, `margin`, `display`, `width`, `float`, `z-index` etc) are OK, while properties that reach inside the border (e.g. `border` itself, `padding`, `color`, `font` etc) are a no-no.
354422

355423
As a corollary, this is also very obviously forbidden:
356424

@@ -367,8 +435,8 @@ As a corollary, this is also very obviously forbidden:
367435

368436
There are a few interesting/boring edge cases, such as:
369437

370-
* `box-shadow` - The effect is clearly rendered outside the border, then again a subtle shadow might be an integral part of the look-and-feel of the component.
371-
* `color`, `font` and other [inherited properties](https://developer.mozilla.org/en-US/docs/Web/CSS/inheritance) - `.myapp-Header > .myapp-LoginForm { color: red }` reaches into the insides of the child component, but on the other hand is functionally equivalent to `.myapp-Header { color: red; }`, which is OK by our other rules.
438+
* `box-shadow` - A specific type of shadow can be an integral part of the look-and-feel of a component, and thus it should contain those styles itself. Then again, the visual effect is clearly rendered outside the border, so it's going into its parent component's territory.
439+
* `color`, `font` and other [inherited properties](https://developer.mozilla.org/en-US/docs/Web/CSS/inheritance) - `.myapp-Header > .myapp-LoginForm { color: red }` reaches into the insides of the child component, but on the other hand can be functionally equivalent to `.myapp-Header { color: red; }`, which is OK by our other rules.
372440
* `display` - If the child component uses a [Flexbox](https://css-tricks.com/snippets/css/a-guide-to-flexbox/) layout, it's possibly relying on having `display: flex` set on its root element. However, the parent might choose to hide its child by setting `display: none` on it.
373441

374442
The important thing to realize is that in these edge cases, you're not risking thermonuclear war, just introducing a tiny bit of the CSS cascade back into your styles. As with other things that are bad for you, enjoying the cascade *in moderation* is fine. For instance, taking a closer look at the last example, the [specificity contest](https://developer.mozilla.org/en-US/docs/Web/CSS/Specificity) works out exactly like you'd want it to: when the component is visible, `.myapp-LoginForm { display: flex }` is the most specific rule, and takes precedence. When the owner decides to hide it with `.myapp-Header-loginBoxHidden > .myapp-LoginBox { display: none }` that rule is more specific, and wins.
@@ -379,7 +447,7 @@ To avoid repetitive work, you sometimes need to share styles between components.
379447

380448
As a concrete example, let's consider using some styles from [Bootstrap](http://getbootstrap.com/css/), as it's a perfect example of an annoying framework to work with. Considering everything we've talked about above, with regard to sharing a single global namespace for styles, and collisions being bad, Bootstrap will:
381449

382-
* Export a ton of selectors (as of version 3.3.7, 2481 to be precise) to that namespace, whether you actually use them or not. (Fun aside: IE's up to version 9 can only process 4095 selectors before silently ignoring the rest. I've heard of people spending *days* debugging and wondering what the hell's going on.)
450+
* Export a ton of selectors (as of version 3.3.7, 2481 to be precise) to that namespace, whether you actually use them or not. (Fun aside: IE's up to version 9 can only process 4095 selectors before silently ignoring the rest. I've heard of people spending *days* debugging this wondering what the hell's going on.)
383451
* Use hard-coded class names such as `.btn` and `.table`. Can't imagine those ever being accidentally reused by some other developer or project. :sarcasm:
384452

385453
Regardless, let's say we want to use Bootstrap as a basis for our `Button` component.
@@ -402,7 +470,7 @@ Consider [extending](http://sass-lang.com/documentation/file.SASS_REFERENCE.html
402470
}
403471
```
404472

405-
This has the benefit of not giving anyone (including yourself) any ideas about relying on the presence of the ridiculously named `btn` class on the HTML component. The origin on of the styles that `Button` uses is an implementation detail that need not show on the outside at all. As a consequence, should you ever decide to ditch Bootstrap in favor of another framework (or just writing the styles yourself), the change won't be externally visible in any way (except, uhh, the visible changes in how `Button` *looks*).
473+
This has the benefit of not giving anyone (including yourself) any ideas about relying on the presence of the ridiculously named `btn` class on the HTML component. The origin of the styles that `Button` uses is an implementation detail that need not show on the outside at all. As a consequence, should you ever decide to ditch Bootstrap in favor of another framework (or just writing the styles yourself), the change won't be externally visible in any way (except, uhh, the visible changes in how `Button` looks).
406474

407475
The same principle applies to your own helper classes, and there you'll have the option of using more sensible class names:
408476

@@ -420,7 +488,7 @@ Or [forgoing emitting the class](http://sass-lang.com/documentation/file.SASS_RE
420488
}
421489
```
422490

423-
Finally, all CSS preprocessors support the concept of [mixins](http://sass-lang.com/documentation/file.SASS_REFERENCE.html#mixins), which allow you to more or less whatever you want:
491+
Finally, all CSS preprocessors support the concept of [mixins](http://sass-lang.com/documentation/file.SASS_REFERENCE.html#mixins), which are a tremendously powerful tool:
424492

425493
```scss
426494
.myapp-Button {
@@ -438,9 +506,9 @@ Finally, as mentioned before, when you understand the rules you've laid out (or
438506
<button class="myapp-Button myapp-utils-button">
439507
```
440508

441-
That added value might be -- for instance -- that your test framework can then be more clever in automatically figuring out what things act as buttons, and can be clicked.
509+
That added value might be -- for instance -- that your test framework can then be more clever in automatically figuring out what things act as buttons, and can be clicked on.
442510

443-
Or you might decide that it's OK to break component isolation when the breach is tiny, and the additional work from splitting components would be too great. While I'll want to remind you that it's a slippery slope, and that consistency is king, bla bla... as long as your team is in agreement, and you get stuff done, you're doing the right thing.
511+
Or you might decide that it's OK to break component isolation when the breach is tiny, and the additional work from splitting components would be too great. While I'll want to remind you that it's a slippery slope, and that consistency is king, as long as your team is in agreement, and you get stuff done, you're doing the right thing.
444512

445513
## License
446514

give-up.gif

567 KB
Loading

0 commit comments

Comments
 (0)