Slide 1

Slide 1 text

STYLING REACT COMPONENTS We're going to talk about Styling React Components.

Slide 2

Slide 2 text

STYLING REACT COMPONENTS If you came for "Styling React Component", don't worry, you're in the right place...

Slide 3

Slide 3 text

I figured people wouldn't know it was me talking unless the title of my talk had a spelling error. Things are better if you pretend English is my second language.

Slide 4

Slide 4 text

I SUCK AT PREDICTING THE FUTURE Let's talk about other things I suck at. I suck at predicting the future.

Slide 5

Slide 5 text

WE SUCK AT PREDICTING THE FUTURE But I don't feel too bad about it because we all suck a little bit at predicting the future.

Slide 6

Slide 6 text

Obviously, this photo was not taken two weeks ago.

Slide 7

Slide 7 text

OUR PREDICTIONS ARE ASPIRATIONAL Our predictions are aspirational. We look into the future hopefully.

Slide 8

Slide 8 text

The overboard is an aspirational prediction. Thinking about it, a overboard has very few similarities with a skateboard. The only real similarity is that you stand on it the same way. A naive view of the future given that this is a pretty uncomfortable way to stand on something that moves itself.

Slide 9

Slide 9 text

While we didn't get hoverboards, we got these smart boards (or whatever they're called). And while these don't look anything like a overboard, they fit the the aspirational need for a hoverboard, in a practical way.

Slide 10

Slide 10 text

THE FUTURE IS CRAFTED BY PAIN And if you're watching carefully, you see that the future is crafted by pain...

Slide 11

Slide 11 text

PAIN IS UNPREDICTABLE and pain is unpredictable.

Slide 12

Slide 12 text

2011 Let's talk about pain a little. In 2011, data-binding was the name of the of the game. All of these JavaScript frameworks were competing on data-binding. This in response to the pain of marshaling real-time updates to the DOM consistently.

Slide 13

Slide 13 text

O.o 2012 Object observe was created as a standards proposal to bring this idea to JS. It was a first-class way to watch for mutations on objects and arrays.

Slide 14

Slide 14 text

2013 By 2013 React entered the seen and said that "shared mutable state is the root of all evil." Make a new DOM every time something changes. Look, we did it!

Slide 15

Slide 15 text

2015 By 2015, all the competing frameworks had been convinced and adopted this radical way of thinking was indeed the future.

Slide 16

Slide 16 text

O.o 2015 And in just four years, we moved in and out of an entire class of pain.

Slide 17

Slide 17 text

2015 And the O.o proposal was withdrawn.

Slide 18

Slide 18 text

THE WEB PLATFORM IS MOVING FAST We have HTML5, ES2015 and attention is turning once again to CSS...

Slide 19

Slide 19 text

CSS IS THE NEW JS Smart people are asking "Now that JS is good, how do we move stylesheets forward?"

Slide 20

Slide 20 text

HOW TO WAIT HAPPILY This talk is about how to wait happily.

Slide 21

Slide 21 text

⚽ GOAL: ONE THING YOU CAN USE TODAY But my goal is that you can walk away with 1 practical way to improve your styling game today.

Slide 22

Slide 22 text

MY PAIN All solutions are contextual. Here's what's guiding our solutions.

Slide 23

Slide 23 text

PLANNING CENTER I work at a company called Planning Center. We make software for churches. It's weird, I know, but don't worry about it.

Slide 24

Slide 24 text

8 APPS I work on frontend tooling for these 8 apps... 7 available now. 1 next year

Slide 25

Slide 25 text

10 YEARS OLD The oldest app 10 years old. So supporting a legacy system is crucially important.

Slide 26

Slide 26 text

2 APPS IN 2013 When I started, we had only two apps. My job became creating a way to share assets, styles, and components between these apps and the 6 we would come to build.

Slide 27

Slide 27 text

RAILS + REACT Pairing down has been instrumental. All the apps are very standard Rails apps, with React powering interactive UI.

Slide 28

Slide 28 text

> 7 FLUX IMPLEMENTATIONS Now we haven't quite paired everything down. As I speak we use no less than 7 flux implementations.. some apps with 2 or more implementations.

Slide 29

Slide 29 text

HURRAY So hurray for that.

Slide 30

Slide 30 text

BEM So, it's 2013 and we started doing all new CSS in BEM.

Slide 31

Slide 31 text

.todo-item { color: #111 } .todo-item--small { font-size: 80% } .todo-item__mark::before { content: '*' } For the uninitiated, this is what a BEM object might look for a todo list item

Slide 32

Slide 32 text

.todo-item { color: #111 } .todo-item--small { font-size: 80% } .todo-item__mark::before { content: '*' } < BLOCK Our block is the root element of this CSS object.

Slide 33

Slide 33 text

.todo-item { color: #111 } .todo-item--small { font-size: 80% } .todo-item__mark::before { content: '*' } < MODIFER You can use these additional classes, called modifiers. modifiers allow you to make contextual changes to an entire block. This is the only appropriate way to cascade changes in BEM.

Slide 34

Slide 34 text

.todo-item { color: #111 } .todo-item--small { font-size: 80% } .todo-item__mark::before { content: '*' } < ELEMENT

Slide 35

Slide 35 text

.todo-item { color: #111 } .todo-item--small { font-size: 80% } .todo-item__mark::before { content: '*' } < DESCENDENT In some BEM derivatives, this is better known as a descendent. But I suppose "BDM" doesn't sound as cool. In any case, everything is namespaced, making this some type of fake CSS module. Let's add some state to this...

Slide 36

Slide 36 text

.todo-item { color: #111 } .todo-item--small { font-size: 80% } .todo-item__mark::before { content: '*' } .todo-item.is-complete { color: #aaa } BEM is not opinionated about state because it was designed for layout. I use a convention Necolas Gallagher's sets out in SUIT CSS: .is- prefixed state classes. This has a couple benefits: * doesn't conflate layout and state modifiers

Slide 37

Slide 37 text

.todo-item { color: #111 } .todo-item--small { font-size: 80% } .todo-item__mark::before { content: '*' } .todo-item.is-complete { color: #aaa } .todo-item.is-complete .todo-item__mark::before { content: '✔' } Now that we have our state selector, we need to cascade changes down to certain descendants. `mark` is going to change from asterisk to a check.

Slide 38

Slide 38 text

.todo-item { color: #111 } .todo-item--small { font-size: 80% } .todo-item__mark::before { content: '*' } .todo-item.is-complete { color: #aaa } .todo-item.is-complete .todo-item__mark::before { content: '✔' } .todo-item.is-complete .todo-item__name { text-decoration: line-through } We'll add a new descendent, for `name.` In the complete state, we'll change the mark to have a line-through.

Slide 39

Slide 39 text

.todo-item { color: #111 } .todo-item--small { font-size: 80% } .todo-item__mark::before { content: '*' } .todo-item.is-complete { color: #aaa } .todo-item.is-complete .todo-item__mark::before { content: '✔' } .todo-item.is-complete .todo-item__name { text-decoration: line-through } .todo-item.is-due .todo-item__name { color: red } Finally, we'll add a state for an item that is due. Due items will be red.

Slide 40

Slide 40 text

.todo-item { color: #111 } .todo-item--small { font-size: 80% } .todo-item__mark::before { content: '*' } .todo-item.is-complete { color: #aaa } .todo-item.is-complete .todo-item__mark::before { content: '✔' } .todo-item.is-complete .todo-item__name { text-decoration: line-through } .todo-item.is-due:not(.is-complete) .todo-item__name { color: red } And we'll add a `:not` pseudo-element so items that are done, but due, will just look done. It doesn't matter if an item is `due` if it's done.

Slide 41

Slide 41 text

.todo-item { color: #111 } .todo-item--small { font-size: 80% } .todo-item__mark::before { content: '*' } .todo-item.is-complete { color: #aaa } .todo-item.is-complete .todo-item__mark::before { content: '✔' } .todo-item.is-complete .todo-item__name { text-decoration: line-through } .todo-item.is-due:not(.is-complete) .todo-item__name { color: red } This is our complete CSS BEM object. And for those yelling at me for not doing it in sass...

Slide 42

Slide 42 text

$module: ".todo-item"; #{$module} { color: #111; &--small { font-size: 80%; } &.is-complete { color: #aaa; } &__mark::before { content: '*'; #{$module}.is-complete & { Here it is in all of it's two-slide un-grepable glory

Slide 43

Slide 43 text

And here's what it looks like in the browser. Now This is an OBJECTIVELY trivial example. Even for a todo example. But there's already a lot happening in CSS land.

Slide 44

Slide 44 text

const TodoItem = ({name, due, complete}) => (
); We'll start our component by rendering out the block and block classname.

Slide 45

Slide 45 text

const TodoItem = ({name, due, complete}) => (
{name}
); We'll add the two descendants, with classnames.

Slide 46

Slide 46 text

const TodoItem = ({name, due, complete}) => (
{name}
); We'll throw Jed Watson's `classnames` library in there.

Slide 47

Slide 47 text

const TodoItem = ({name, due, complete}) => (
{name}
); And add our state classes.

Slide 48

Slide 48 text

const TodoItem = ({name, due, complete}) => (
{name}
); Now, we can see how classes get applied but not what they do. We have to consult the stylesheet for that.

Slide 49

Slide 49 text

const TodoItem = ({name, due, complete}) => (
{name}
); Also, this bit of markup is completely opaque. The implementation lives completely within the stylesheet.

Slide 50

Slide 50 text

WHAT WOULD HAPPEN IF WE LET REACT HANDLE STATE-STYLES?

Slide 51

Slide 51 text

.todo-item { color: #111 } .todo-item__mark::before { content: '*' } .todo-item.is-complete { color: #aaa } .todo-item.is-complete .todo-item__mark::before { content: '✔' } .todo-item.is-complete .todo-item__name { text-decoration: line-through } .todo-item.is-due:not(.is-complete) .todo-item__name { color: red } Let's start by removing this most complicated selector—the selector for styling a due item.

Slide 52

Slide 52 text

.todo-item { color: #111 } .todo-item__mark::before { content: '*' } .todo-item.is-complete { color: #aaa } .todo-item.is-complete .todo-item__mark::before { content: '✔' } .todo-item.is-complete .todo-item__name { text-decoration: line-through } [deleted]

Slide 53

Slide 53 text

const TodoItem = ({name, due, complete}) => (
{name}
); Back in our component...

Slide 54

Slide 54 text

const TodoItem = ({name, due, complete}) => (
{name}
); Here's the line we're going to change.

Slide 55

Slide 55 text

const TodoItem = ({name, due, complete}) => (
{name}
); We add a state prop with a simple ternary. If the item is `due` and not `complete`, use the object with `color: red`, otherwise apply no styles. That was easy.

Slide 56

Slide 56 text

.todo-item { color: #111 } .todo-item__mark::before { content: '*' } .todo-item.is-complete { color: #aaa } .todo-item.is-complete .todo-item__mark::before { content: '✔' } .todo-item.is-complete .todo-item__name { text-decoration: line-through } Back to our stylesheet

Slide 57

Slide 57 text

.todo-item { color: #111 } .todo-item__mark::before { content: '*' } .todo-item.is-complete { color: #aaa } .todo-item.is-complete .todo-item__mark::before { content: '✔' } Let's get rid of the `name` descendent all together...

Slide 58

Slide 58 text

const TodoItem = ({name, due, complete}) => (
{name}
); Back in our component...

Slide 59

Slide 59 text

const nameStyles = (due, complete) => { return (due && !complete) ? {color: "red"} : {}; }; const TodoItem = ({name, due, complete}) => (
{name}
); This function is about to get more complex. Let's upgrade it to a proper function.

Slide 60

Slide 60 text

const nameStyles = (due, complete) => { if (due && !complete) return {color: "red"}; return {}; }; const TodoItem = ({name, due, complete}) => (
{name}
); Replace the turnery with a proper if statement...

Slide 61

Slide 61 text

const nameStyles = (due, complete) => { if (due && !complete) return {color: "red"}; if (complete) return {textDecoration: "line-through"}; return {}; }; const TodoItem = ({name, due, complete}) => (
{name}
); and add our second condition for `complete`

Slide 62

Slide 62 text

const nameStyles = (due, complete) => { if (due && !complete) return {color: "red"}; if (complete) return {textDecoration: "line-through"}; return {}; }; const TodoItem = ({name, due, complete}) => (
{name}
); now we can remove the className entirely. This feels pretty good.

Slide 63

Slide 63 text

const nameStyles = (due, complete) => { if (due && !complete) return {color: "red"}; if (complete) return {textDecoration: "line-through"}; return {}; }; const TodoItem = ({name, due, complete}) => (
{name}
); And `is-due` can be removed from the block.

Slide 64

Slide 64 text

const nameStyles = (due, complete) => { if (due && !complete) return {color: "red"}; if (complete) return {textDecoration: "line-through"}; return {}; }; const TodoItem = ({name, due, complete}) => (
{name}
); We've completely localized the way this element gets styled. It's now on the element and this function is very easy to reason about.

Slide 65

Slide 65 text

.todo-item { color: #111 } .todo-item__mark::before { content: '*' } .todo-item.is-complete { color: #aaa } .todo-item.is-complete .todo-item__mark::before { content: '✔' } Back to our stylesheet. Let's get rid of the `mark` descendent. With React components, pseudo-elements are just elements.

Slide 66

Slide 66 text

.todo-item { color: #111 } .todo-item.is-complete { color: #aaa }

Slide 67

Slide 67 text

const nameStyles = (due, complete) => { if (due && !complete) return {color: "red"}; if (complete) return {textDecoration: "line-through"}; return {}; }; const TodoItem = ({name, due, complete}) => (
{name}
); Back in our component.

Slide 68

Slide 68 text

const nameStyles = (due, complete) => { if (due && !complete) return {color: "red"}; if (complete) return {textDecoration: "line-through"}; return {}; }; const TodoItem = ({name, due, complete}) => (
{name}
); Let's focus on the markup for our `mark` descendent.

Slide 69

Slide 69 text

const nameStyles = (due, complete) => { if (due && !complete) return {color: "red"}; if (complete) return {textDecoration: "line-through"}; return {}; }; const TodoItem = ({name, due, complete}) => (
{(complete) ? '✔': '*'} {name}
); And use a simple ternary to change the character based on state.

Slide 70

Slide 70 text

const nameStyles = (due, complete) => { if (due && !complete) return {color: "red"}; if (complete) return {textDecoration: "line-through"}; return {}; }; const TodoItem = ({name, due, complete}) => (
{(complete) ? '✔': '*'} {name}
);

Slide 71

Slide 71 text

.todo-item { color: #111 } .todo-item.is-complete { color: #aaa } Let's take that last state class out.

Slide 72

Slide 72 text

.todo-item { color: #111 } [deleted]

Slide 73

Slide 73 text

const nameStyles = (due, complete) => { if (due && !complete) return {color: "red"}; if (complete) return {textDecoration: "line-through"}; return {}; }; const TodoItem = ({name, due, complete}) => (
{(complete) ? '✔': '*'} {name}
);

Slide 74

Slide 74 text

const nameStyles = (due, complete) => { if (due && !complete) return {color: "red"}; if (complete) return {textDecoration: "line-through"}; return {}; }; const TodoItem = ({name, due, complete}) => (
{(complete) ? '✔': '*'} {name}
); Again, we'll use a ternary to apply this style...

Slide 75

Slide 75 text

const nameStyles = (due, complete) => { if (due && !complete) return {color: "red"}; if (complete) return {textDecoration: "line-through"}; return {}; }; const TodoItem = ({name, due, complete}) => (
{(complete) ? '✔': '*'} {name}
); And lose our need for `classnames`

Slide 76

Slide 76 text

const nameStyles = (due, complete) => { if (due && !complete) return {color: "red"}; if (complete) return {textDecoration: "line-through"}; return {}; }; const TodoItem = ({name, due, complete}) => (
{(complete) ? '✔': '*'} {name}
); To me, this is much easier to reason about than our CSS. We can use JavaScript to resolve styles. As the examples get more complicated, JS does a better job of scaling to the challenge and keeping isolated decisions isolated.

Slide 77

Slide 77 text

TESTABILITY It's really easy to break a UI, when the functionality lives in CSS State changes are UI changes. The should happen in JavaScript and they should be testable.

Slide 78

Slide 78 text

it("looks crossed-out, when complete", () => { }); Let's write a quick test for this...

Slide 79

Slide 79 text

it("looks crossed-out, when complete", () => { shallowRenderer.render(); }); We can use the shallow renderer, we don't need a DOM.

Slide 80

Slide 80 text

it("looks crossed-out, when complete", () => { shallowRenderer.render(); const result = shallowRenderer.getRenderOutput(); }); grab the result

Slide 81

Slide 81 text

it("looks crossed-out, when complete", () => { shallowRenderer.render(); const result = shallowRenderer.getRenderOutput(); assert.deepEqual( result.props.style, { textDecoration: "line-through" } ); }); assert that the resulting object has style text-decoration "line-through"

Slide 82

Slide 82 text

it("accepts a completeStyle prop", () => { }); You have some contextual styles you want to add? We can write a test for that...

Slide 83

Slide 83 text

it("accepts a completeStyle prop", () => { const mergeStyles = { color: "silver" }; }); draw up an object with some arbitrary styles...

Slide 84

Slide 84 text

it("accepts a completeStyle prop", () => { const mergeStyles = { color: "silver" }; shallowRenderer.render( ); }); shallow render with the styles...

Slide 85

Slide 85 text

it("accepts a completeStyle prop", () => { const mergeStyles = { color: "silver" }; shallowRenderer.render( ); const result = shallowRenderer.getRenderOutput(); }); get the result...

Slide 86

Slide 86 text

it("accepts a completeStyle prop", () => { const mergeStyles = { color: "silver" }; shallowRenderer.render( ); const result = shallowRenderer.getRenderOutput(); assert.deepEqual( result.props.style, { color: "silver" } ); }); and assert that they made it through to the component. Easy

Slide 87

Slide 87 text

So, what's good about this? A lot of things.

Slide 88

Slide 88 text

SQUEAKY CLEAN CSS This makes CSS easy again. You're only concerned with APPEARANCE, not functionality.

Slide 89

Slide 89 text

PRECISION It's easier to resolve complex style resolution in a function. It becomes much clearer what's happening and you have an entire programming language at your disposal. If you've worked in a large CSS codebase, you know that state-classes just SPRAAAAAWL.

Slide 90

Slide 90 text

TESTABILITY If you can break functionality by changing CSS, you're component is broken. You don't have the right separation of concerns. If an item is done, it should look done, regardless of the style.

Slide 91

Slide 91 text

LESS POLICING STYLESHEET PATTERNS Less policing around CSS * you can stop enforcing diligence around stylesheet structure * don't have to decide what a component's initial state is. * you don't have to decide how to represent state.

Slide 92

Slide 92 text

WHAT WOULD HAPPEN IF WE DID ALL OUR STYLES THIS WAY? This worked so well that we started to ask: "What would happen if we did all of our styles this way?"

Slide 93

Slide 93 text

INLINE-STYLES

Slide 94

Slide 94 text

CSS in JS Last year, Christopher Chedeux gave a talk called CSS in JS

Slide 95

Slide 95 text

CSS in JS It was insanely popular. His slide deck from the talk has had over half a million views.

Slide 96

Slide 96 text

1.Global Namespace 2.Dependencies 3.Dead Code Elimination 4.Manification 5.Sharing Constants 6.Non-deterministic Resolution 7.Isolation And this list started to show up everywhere.

Slide 97

Slide 97 text

1.Global Namespace 2.Dependencies 3.Dead Code Elimination 4.Manification 5.Sharing Constants 6.Non-deterministic Resolution 7.Isolation SOLVED BY INLINE-STYLES The premise was that all of these problems go away when elements styled inline and that React afforded you a way to do this in a performant way.

Slide 98

Slide 98 text

This talk was an eye opener. Because it showed the problems off CSS at the scale of Facebook. I don't think you should be burdened by the problems Facebook faces until you're running at that scale. But when you've run into a problem as many times as they have, you get names for them, like "non-deterministic resolution," which i'm still not smart enough to understand.

Slide 99

Slide 99 text

Responses were mixed. Joy, fear, anger. You name it. It was a watershed moment for CSS. Tons of libraries and posts came out of this talk. I gave of those talks.

Slide 100

Slide 100 text

As it shook out, there were a couple hard parts...

Slide 101

Slide 101 text

BROWSER EVENTS ARE HARD

Slide 102

Slide 102 text

MEDIA-QUERIES ARE HARD

Slide 103

Slide 103 text

.list-item:hover { background-color: rgba(0,0,0,0.1); } Let's compare a few examples: This is what's required to do :hover in CSS...

Slide 104

Slide 104 text

class ListItem extends React.Component { constructor(props) { super(props); this.state = { hovered: false }; this.handleMouseEnter = () => { this.setState({ hovered: true }); } this.handleMouseLeave = () => { this.setState({ hovered: false }); } } render() { return (
  • ); } } This is the boilerplate for handling the hover event in a React component. This isn't better.
  • Slide 105

    Slide 105 text

    .list-item:hover { background-color: rgba(0,0,0,0.1); } If we want to add a media-query to our CSS, that's pretty simple...

    Slide 106

    Slide 106 text

    .list-item:hover { background-color: rgba(0,0,0,0.1); } @media (max-width: 480px) { .list-item { width: 100%; } }

    Slide 107

    Slide 107 text

    class ListItem extends React.Component { constructor(props) { super(props); this.state = { hovered: false, smallScreen: false }; this.handleMouseEnter = () => { this.setState({ hovered: true }); } this.handleMouseLeave = () => { this.setState({ hovered: false }); } this.handleMatchMediaChange = (e) => { return this.setState({tabletContext: e.matches}); } } componentWillMount() { this.handleMatchMediaChange(window.matchMedia("(max-width: 480px)")); window.matchMedia( "(max-width: 480px)") .addListener(this.handleMatchMediaChange); } componentWillUnmount() { window.matchMedia("(max-width: 480px)") .removeListener(this.handleMatchMediaChange); } render() { return (
  • ); } } ... but it's cripplingly painful in a React component.
  • Slide 108

    Slide 108 text

    So libraries came along to make this easier, my favorite is Radium.

    Slide 109

    Slide 109 text

    const styles = { color: '#0074d9', ':hover': { color: '#0088FF', }, '@media (max-width: 480px)': { width: '100%', } } .my-class { color: #0074d9; &:hover { color: #0088FF; } @media (max-width: 480px) { width: 100%; } } Radium SCSS It's modeled after Sass and, if you squint, the shape of these objects is identical.

    Slide 110

    Slide 110 text

    If you like the approach but don't like Radium, they have a tremendously detailed library comparison on their repository. https://github.com/FormidableLabs/radium/blob/master/docs/ comparison/README.md

    Slide 111

    Slide 111 text

    ??? Is Radium a part of our stack?

    Slide 112

    Slide 112 text

    NO At the end of the day, it didn't work for us.

    Slide 113

    Slide 113 text

    ALL SOLUTIONS REQUIRED NEW TOOLING I wasn't willing to add libraries with this much churn into all of our apps.

    Slide 114

    Slide 114 text

    8 COULDN'T SHARE ABSTRACTIONS Couldn't share abstractions with Rails.

    Slide 115

    Slide 115 text

    But these solutions are really great for certain teams.

    Slide 116

    Slide 116 text

    JS-HEAVY TEAM This is great fit where you have a new app and a js-heavy team. You have the ability to move so quickly with an approach like this, using just JavaScript. I see a lot of promise for this technique.

    Slide 117

    Slide 117 text

    WEB + REACT NATIVE It's great for teams that are building a web and react-native app side-by- side. React Native doesn't have CSS. Inline-styles become a transferrable skill.

    Slide 118

    Slide 118 text

    WHERE AM I AT? So, where does that leave me? Where am I at now?

    Slide 119

    Slide 119 text

    I DON'T LIKE THE ALTERNATIVE The alternative was to keep writing BEM. But there was another problem that really started to get to me.

    Slide 120

    Slide 120 text

    The :rolling_eyes_face: pattern.

    Slide 121

    Slide 121 text

    Thing.jsx You write your component...

    Slide 122

    Slide 122 text

    Thing.jsx Thing.scss you write your stylesheets in a file with the same name...

    Slide 123

    Slide 123 text

    Thing.jsx Thing.scss Thing Component And the these two things become the component abstraction your application is concerned with.

    Slide 124

    Slide 124 text

    SUFFICIENTLY LOCAL CSS IS INDISTINGUISHABLE FROM INLINE-STYLES. Jed Schmidt, BrooklynJS HTTPS://WWW.YOUTUBE.COM/WATCH?V=XJK03IF9O7S Jed Schmidt addressed this in a way that resonates with me. If you've modularized well enough, you're not actually getting the benefits of re-use from your CSS.

    Slide 125

    Slide 125 text

    Thing.jsx Thing.scss Thing Component We're never going to use this again. Nor should we. If it's part of the component implementation, it should be considered private API.

    Slide 126

    Slide 126 text

    RE-USE IS A MYTH

    Slide 127

    Slide 127 text

    Thing.jsx Thing.scss Thing Component I don't want this abstraction and I don't need it. Think about all the coupling: * React component filename * CSS object filename * Use of the string classname in the component * Implementation of the classname in the CSS object file

    Slide 128

    Slide 128 text

    Thing.jsx I want this abstraction...

    Slide 129

    Slide 129 text

    HOW DO WE GET THIS WITHOUT INLINE-STYLES?

    Slide 130

    Slide 130 text

    ATOMIC CSS* This term means a lot. So I'm going to show you what I mean by it.

    Slide 131

    Slide 131 text

    Markup
    This is our markup. I know: "really exciting examples today."

    Slide 132

    Slide 132 text

    (default) This is how it would layout by default

    Slide 133

    Slide 133 text

    .d-f we're going to use some shorthand classes that map to a single property- value. .d-f => { display: flex }

    Slide 134

    Slide 134 text

    .d-f .f_1 .f_1 => { flex: one } I'm using a convention here to affect direct-children. An underscore will apply the style to children.

    Slide 135

    Slide 135 text

    .d-f .f_1 .fd-c .fd-r@md I can use an @ symbol for media queries. So, I want flex column when we're below my medium breakpoint and a row at the medium breakpoint.

    Slide 136

    Slide 136 text

    .d-f .f_1 .fd-c .fd-r@md p_1@md and we can add some padding at the medium breakpoint as well.

    Slide 137

    Slide 137 text

    There are some really nice benefits here, if you've already bought into the inline-styles idea.

    Slide 138

    Slide 138 text

    Defer design The worst part of CSS is that you have to name things first and there's no true modularization.

    Slide 139

    Slide 139 text

    Style without CSS Defer design This frees up developers to style without writing CSS

    Slide 140

    Slide 140 text

    Server-rendered friendly Style without CSS Defer design server-rendered friendly. HIGHLY cacheable

    Slide 141

    Slide 141 text

    Inline workflow + breakpoints Server-rendered friendly Style without CSS Defer design solves the problem of inline-styles having trouble with breakpoints.

    Slide 142

    Slide 142 text

    HISTORICAL CRITICISM...

    Slide 143

    Slide 143 text

    WHERE DOES THE ‘SCOPE’ OF THIS SECTION OF DOM START AND STOP? Harry Roberts, @csswizardry HTTP://CSSWIZARDRY.COM/2015/03/CAN-CSS-BE-TOO-MODULAR/ http://csswizardry.com/2015/03/can-css-be-too-modular/ 8 March, 2015

    Slide 144

    Slide 144 text

    ARE ANY OF THE CLASSES THEMATICALLY RELATED? Harry Roberts, @csswizardry HTTP://CSSWIZARDRY.COM/2015/03/CAN-CSS-BE-TOO-MODULAR/

    Slide 145

    Slide 145 text

    DO THESE CLASSES APPEAR... PURELY COINCIDENTALLY? Harry Roberts, @csswizardry HTTP://CSSWIZARDRY.COM/2015/03/CAN-CSS-BE-TOO-MODULAR/

    Slide 146

    Slide 146 text

    ARE ANY OF THE CLASSES OPTIONAL AT THIS POINT? Harry Roberts, @csswizardry HTTP://CSSWIZARDRY.COM/2015/03/CAN-CSS-BE-TOO-MODULAR/

    Slide 147

    Slide 147 text

    IF ONE OF THOSE CLASSES CHANGES... WILL THEY NEED TO BE CHANGED IN EVERY SIMILAR PIECE OF DOM? Harry Roberts, @csswizardry HTTP://CSSWIZARDRY.COM/2015/03/CAN-CSS-BE-TOO-MODULAR/

    Slide 148

    Slide 148 text

    WE HAVE TINY CSS, BUT OUR HTML HAS... LOST ITS ABILITY TO BE READ, OR TO SELF-DOCUMENT. THIS IS TOO EXTREME. Harry Roberts, @csswizardry HTTP://CSSWIZARDRY.COM/2015/03/CAN-CSS-BE-TOO-MODULAR/

    Slide 149

    Slide 149 text

    "MY HTML IS SO NON-SEMANTIC!" You might hang your head in shame for even having this thought.

    Slide 150

    Slide 150 text

    "WHY CAN'T I CONVEY MEANING??"

    Slide 151

    Slide 151 text

    "WHY DO I MAKE BAD LIFE CHOICES?"

    Slide 152

    Slide 152 text

    "WAIT... WHAT HTML?" I'm writing components. I use JSX. My HTML is an artifact. This article was written about layout.

    Slide 153

    Slide 153 text

    "ISN'T HAVING A COMPONENT CLASS AND A CSS CLASS REDUNDANT?" We're implementing a class in two different languages. Isn't this redundant?

    Slide 154

    Slide 154 text

    "AREN'T COMPONENTS THE ABSTRACTION I WAS AFTER ALL ALONG?"

    Slide 155

    Slide 155 text

    YES Now, to be fair, in Harry's says that creating UI components in Rails would offer a decent abstraction, if it weren't so cumbersome.

    Slide 156

    Slide 156 text

    http://tachyons.io Tachyons Modular, like Lodash. Get just what you need.

    Slide 157

    Slide 157 text

    http://jxnblk.com/gravitons/ Much smaller focus but also really great, if you have small needs. Also, follow Brent Jaxon. He does great work.

    Slide 158

    Slide 158 text

    MINIONS.CSS To call this a library would be a gross overstatement.. It's a thought experiment that we use in production.

    Slide 159

    Slide 159 text

    https://github.com/chantastic/minions.css/ Here's how it differs.

    Slide 160

    Slide 160 text

    "GUESSABLE" NAMING I want the classes to be guessable. You shouldn't have to memorize new class-names to use a library like this. Most classes should dehydrate to classes in a very predictable way.

    Slide 161

    Slide 161 text

    padding-right: 1rem A rule like this...

    Slide 162

    Slide 162 text

    .pr-1r dehydrates to this class selector.

    Slide 163

    Slide 163 text

    padding-right: 1rem clearly representing the underlying implementation.

    Slide 164

    Slide 164 text

    WHATEVER-FIRST We have a 10 year-old-app. We can't just "mobile first" it. So we use a range of selector types that allow us to retrofit "desktop first" apps.

    Slide 165

    Slide 165 text

    .pr-1r@sm ADDITIVE MATCH

    Slide 166

    Slide 166 text

    .pr-1r@!sm REVERSE-ADDITIVE MATCH

    Slide 167

    Slide 167 text

    .pr-1r@=sm EXACT MATCH

    Slide 168

    Slide 168 text

    .pr-1r@!=sm EXCLUDE MATCH

    Slide 169

    Slide 169 text

    THE PURPOSE OF DESIGN IS TO ALLOW YOU TO DO DESIGN LATER, AND IT'S PRIMARY GOAL IS TO REDUCE THE COST OF CHANGE. Sandi Metz, Practical Object Oriented Design in Ruby I can't make it through a talk without a Sandy Metz quote... I want to change this component in one place. I want to rename this component in one place. I want to remove this component in one place.

    Slide 170

    Slide 170 text

    ⛄ WE STILL NEED ABSTRACTIONS Harry Roberts was Right. We still need abstractions for our Rails App. Everything that Harry said is wrong about this approach is still a problem in our Rails apps.

    Slide 171

    Slide 171 text

    CSS MODULES At this point in the talk, I'm talking about things we're not using but I'd like to. I stole these examples from Glen Maddern's talk on CSS Modules. So, they should be pretty close to accurate. CSS Modules is an experimental perspective of the future. There's still a lot to be figured out. But it's headed in an interesting direction.

    Slide 172

    Slide 172 text

    /* button.css */ .default { color: #111 } Here's a very generically named button style.

    Slide 173

    Slide 173 text

    /* button.css */ .default { color: #111 } /* ICSS */ :export { default: default_f34f7fa0; } .default_f34f7fa0 { color: #111 } This is what it gets transformed into through CSS Modules. It creates a globally available class name with your chosen classname, appended with a unique id. It also creates an export statement mapping the name you know as a key to the unique global.

    Slide 174

    Slide 174 text

    /* ICSS */ :export { default: default_f34f7fa0; } .default_f34f7fa0 { color: #111 } /* MyButton.jsx */ import styles from './button.css' You import this via loader into your component...

    Slide 175

    Slide 175 text

    default: default_f34f7fa0; } .default_f34f7fa0 { color: #111 } /* MyButton.jsx */ import styles from './button.css' const MyButton (props) => ( Default ); And use the key as your classname.

    Slide 176

    Slide 176 text

    import styles from './button.css' const MyButton (props) => ( Default ); /* rendered output */ Default When the component is rendered, it's rendered with the unique class.

    Slide 177

    Slide 177 text

    /* button.css */ .base { border-radius: 3px; } .normal { composes: base; background-color: blue; } .error { composes: base; background-color: red; } They've also introduced this `composes` API.

    Slide 178

    Slide 178 text

    /* button.css */ .base { border-radius: 3px; } .normal { composes: base; background-color: blue; } .error { composes: base; background-color: red; } If you've spent much time in Sass your immediate inclination might lead you to think of this like @extend, clobbering the the `.base` definition with an onslaught of additional selectors. It doesn't. Let's step through what it actually does...

    Slide 179

    Slide 179 text

    background-color: red; } /* ICSS */ :export { base: base_81f12d56; normal: base_81f12d56 normal_f3457fa0; error: base_81712d56 error_b7d2ad6f; } .base_81f12d56 { border-radius: 3px; } .normal_f34f7fa0 { background-color: blue; } .error_b7d2ad6f { background-color: red; } Again, CSS Modules transforms the file to ICSS which includes a map of the known keys to unique classnames.

    Slide 180

    Slide 180 text

    background-color: red; } /* ICSS */ :export { base: base_81f12d56; normal: base_81f12d56 normal_f3457fa0; error: base_81712d56 error_b7d2ad6f; } .base_81f12d56 { border-radius: 3px; } .normal_f34f7fa0 { background-color: blue; } .error_b7d2ad6f { background-color: red; } Now unlike @extend, it exports all of the rules in isolation. So, the magic must exist in the export...

    Slide 181

    Slide 181 text

    background-color: red; } /* ICSS */ :export { base: base_81f12d56; normal: base_81f12d56 normal_f3457fa0; error: base_81712d56 error_b7d2ad6f; } .base_81f12d56 { border-radius: 3px; } .normal_f34f7fa0 { background-color: blue; } .error_b7d2ad6f { background-color: red; } Here, we see that the `normal` and `error` keys are mapping to not 1 class but two. It's extending by applying both classes.

    Slide 182

    Slide 182 text

    .base_81f12d56 { border-radius: 3px; } .normal_f34f7fa0 { background-color: blue; } .error_b7d2ad6f { background-color: red; } /* rendered output */ Error! This results in rendered output like this, where bath `error` and the class it `composes` are applied. This is brilliant.

    Slide 183

    Slide 183 text

    .flex-stack { composes: .d-f .f_1 .df-c .fd-r\@md from './minions_.css'; } It means that we can abstract without side-effects. I get giddy when I think about using technology like this in our applications. It's available for those of you using JSPM, Browserify, and Webpack. I recommend using it.

    Slide 184

    Slide 184 text

    WE SUCK AT PREDICTING THE FUTURE I can't predict the future. I can only hope that it's something along the lines of CSS Modules. Until then... I want to build abstractions that build into the future.

    Slide 185

    Slide 185 text

    G INLINE STATE STYLES Your components are fragile. Fix them. Test them.

    Slide 186

    Slide 186 text

    EMBRACE IMMUTABLE SELECTORS The best arguments no longer apply in our component driven apps.

    Slide 187

    Slide 187 text

    ☠ STOP USING JS AND CSS TO BUILD THE SAME ABSTRACTION If you've modularized well enough, You're likely not actually getting re-use from your CSS

    Slide 188

    Slide 188 text

    HOPE FOR ABSTRACTIONS WITHOUT SIDE EFFECTS HOPE Abstract without global available side effects

    Slide 189

    Slide 189 text

    BE PREPARED WHEN THE FUTURE ARRIVES

    Slide 190

    Slide 190 text

    @CHANTASTIC Thanks you listening. I'm @chantastic, if you want to tell me how batshit crazy I am. Thank you.