prevents you from breaking isolation
---
### Follow these constraint!
- [x] global namespace ← *local by default*
- [x] dependencies ← *import into the JS*
- [x] dead code elimination ← *possible with a browserify transform*
- [x] minification ← *generated class names*
- [x] non-deterministic resolution ← *CSS files can only affect one component*
- [x] breaking isolation ← *Avoid the cascade, and you can't break isolation*
---
class: center, middle, main-title
# Composition
---
### Composition: Why?
This is the way to make all of these rules practical. Without composition, you're stuck with a lot of copy/paste.
---
### Composition: the way to avoid copy/paste
```css
.common {
/* all the common styles you want */
}
.normal {
composes: common;
/* anything that only applies to Normal */
}
.disabled {
composes: common;
/* anything that only applies to Disabled */
}
.error {
composes: common;
/* anything that only applies to Error */
}
.inProgress {
composes: common;
/* anything that only applies to In Progress */
}
```
This is how you can do:
`class=${styles.inProgress}`
---
### Composition !== `@extends`
In Sass,
```css
.Button--common { /* font-sizes, padding, border-radius */ }
.Button--normal {
@extends .Button--common;
/* blue color, light blue background */
}
.Button--error {
@extends .Button--common;
/* red color, light red background */
}
```
…becomes
```css
.Button--common, .Button--normal, .Button--error { /* font-sizes, padding, border-radius */ }
.Button--normal { /* blue color, light blue background */ }
.Button--error { /* red color, light red background */ }
```
[Why this sucks](http://www.sitepoint.com/avoid-sass-extend/) (besides the bloated CSS).
Namely: Non-deterministic resolution is back! Or: `@extends` is DRY, but not safe.
---
### Composition == bug free
```css
.common { /* font-sizes, padding, border-radius */ }
.normal { composes: common; /* blue color, light blue background */ }
.error { composes: common; /* red color, light red background */ }
```
…becomes
```css
.components_submit_button__common__abc5436 { /* font-sizes, padding, border-radius */ }
.components_submit_button__normal__def6547 { /* blue color, light blue background */ }
.components_submit_button__error__1638bcd { /* red color, light red background */ }
```
```js
styles: {
common: "components_submit_button__common__abc5436",
normal: "components_submit_button__common__abc5436 components_submit_button__normal__def6547",
error: "components_submit_button__common__abc5436 components_submit_button__error__1638bcd"
}
```
```html
```
---
### Composition: share across files
```css
/* colors.css */
.primary {
color: #720;
}
.secondary {
color: #777;
}
```
```css
/* submit-button.css */
.common { /* font-sizes, padding, border-radius */ }
.normal {
composes: common;
composes: primary from "../shared/colors.css";
}
```
**Note**: when composing multiple classes from different files the order of appliance is undefined. Make sure to not define different values for the same property in multiple class names from different files when they are composed in a single class.
---
### Composition: extreme granularity
```css
.article {
composes: flex vertical centered from "./layout.css";
}
.masthead {
composes: serif bold 48pt centered from "./typography.css";
composes: paragraph-margin-below from "./layout.css";
}
.body {
composes: max720 paragraph-margin-below from "layout.css";
composes: sans light paragraph-line-height from "./typography.css";
}
```
---
class: center, middle, main-title
# `Variables`
```css
/* colors.css */
@value primary: #BF4040;
@value secondary: #1F4F7F;
.text-primary { color: primary; }
.text-secondary { color: secondary; }
```
```css
/* breakpoints.css */
@value small: (max-width: 599px);
@value medium: (min-width: 600px) and (max-width: 959px);
@value large: (min-width: 960px);
```
```css
/* my-component.css */
@value colors: "./colors.css";
@value primary, secondary from colors;
@value small as bp-small, large as bp-large from "./breakpoints.css";
.header { composes: text-primary from colors; box-shadow: 0 0 10px secondary; }
@media bp-small { .header { box-shadow: 0 0 4px secondary; } }
@media bp-large { .header { box-shadow: 0 0 20px secondary; } }
```
#### Gets around the current limitations of CSS 4 variables
Using the current polyfill is limited by forcing all variables to be global, and not allowing them for use in media queries. `@values` solves it.
---
class: center, middle, main-title
# The Constraints
---
## You can't override with composes
see: https://github.com/css-modules/css-modules/issues/14
The following is an error because you can't be sure which will override.
```css
.common {
background: red;
}
.primary {
composes: common;
background: blue;
}
```
---
## No applying multiple classes
This is not good because you can't determine which class will override the other. It also makes it impossible to guarantee that changing the styles of the component won't accidentally break other components.
```html
```
You have to do this:
```html
```
Fortunately, `composes` makes this easy.
[Some other ideas](https://medium.com/brigade-engineering/don-t-pass-css-classes-between-components-e9f7ab192785):
- Keep the number of properties of your components to a minimum.
- Represent actual different ways a component can look or behave using properties.
- Split out your component into several smaller components that you can compose.
---
## Know when to break the rules
- Sometimes you break the rules. But know *why* you're breaking them and please, please, please: leave a comment in the CSS saying why you're a special snowflake this one time.
- The one constraint you should (probably) never break: never style across components.