Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Styling React Components

Michael Chan
November 18, 2015

Styling React Components

The styling of React components is a contentious topic in 2015.

It challenges everything we know about styling apps. In this talk, we’ll take a practical look at styling components in JavaScript: good practices, libraries, testing, and how to expose great APIs for distributed components.

We’ll explore when styling in JavaScript is a great fit and when it might be best to "just use CSS."

Michael Chan

November 18, 2015
Tweet

More Decks by Michael Chan

Other Decks in Technology

Transcript

  1. 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.
  2. I SUCK AT PREDICTING THE FUTURE Let's talk about other

    things I suck at. I suck at predicting the future.
  3. 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.
  4. 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.
  5. 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.
  6. THE FUTURE IS CRAFTED BY PAIN And if you're watching

    carefully, you see that the future is crafted by pain...
  7. 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.
  8. 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.
  9. 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!
  10. 2015 By 2015, all the competing frameworks had been convinced

    and adopted this radical way of thinking was indeed the future.
  11. O.o 2015 And in just four years, we moved in

    and out of an entire class of pain.
  12. THE WEB PLATFORM IS MOVING FAST We have HTML5, ES2015

    and attention is turning once again to CSS...
  13. CSS IS THE NEW JS Smart people are asking "Now

    that JS is good, how do we move stylesheets forward?"
  14. ⚽ 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.
  15. 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.
  16. 8 APPS I work on frontend tooling for these 8

    apps... 7 available now. 1 next year
  17. 10 YEARS OLD The oldest app 10 years old. So

    supporting a legacy system is crucially important.
  18. 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.
  19. RAILS + REACT Pairing down has been instrumental. All the

    apps are very standard Rails apps, with React powering interactive UI.
  20. > 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.
  21. .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
  22. .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.
  23. .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.
  24. .todo-item { color: #111 } .todo-item--small { font-size: 80% }

    .todo-item__mark::before { content: '*' } < ELEMENT
  25. .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...
  26. .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
  27. .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.
  28. .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.
  29. .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.
  30. .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.
  31. .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...
  32. $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
  33. 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.
  34. const TodoItem = ({name, due, complete}) => ( <div className="todo-item">

    </div> ); We'll start our component by rendering out the block and block classname.
  35. const TodoItem = ({name, due, complete}) => ( <div className="todo-item">

    <span className="todo-item__mark" /> <span className="todo-item__name">{name}</span> </div> ); We'll add the two descendants, with classnames.
  36. const TodoItem = ({name, due, complete}) => ( <div className={classnames({

    "todo-item": true, })}> <span className="todo-item__mark" /> <span className="todo-item__name">{name}</span> </div> ); We'll throw Jed Watson's `classnames` library in there.
  37. const TodoItem = ({name, due, complete}) => ( <div className={classnames({

    "todo-item": true, "is-due": due, "is-complete": complete, })}> <span className="todo-item__mark" /> <span className="todo-item__name">{name}</span> </div> ); And add our state classes.
  38. const TodoItem = ({name, due, complete}) => ( <div className={classnames({

    "todo-item": true, "is-due": due, "is-complete": complete, })}> <span className="todo-item__mark" /> <span className="todo-item__name">{name}</span> </div> ); Now, we can see how classes get applied but not what they do. We have to consult the stylesheet for that.
  39. const TodoItem = ({name, due, complete}) => ( <div className={classnames({

    "todo-item": true, "is-due": due, "is-complete": complete, })}> <span className="todo-item__mark" /> <span className="todo-item__name">{name}</span> </div> ); Also, this bit of markup is completely opaque. The implementation lives completely within the stylesheet.
  40. .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.
  41. .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]
  42. const TodoItem = ({name, due, complete}) => ( <div className={classnames({

    "todo-item": true, "is-due": due, "is-complete": complete, })}> <span className="todo-item__mark" /> <span className="todo-item__name">{name}</span> </div> ); Back in our component...
  43. const TodoItem = ({name, due, complete}) => ( <div className={classnames({

    "todo-item": true, "is-due": due, "is-complete": complete, })}> <span className="todo-item__mark" /> <span className="todo-item__name">{name}</span> </div> ); Here's the line we're going to change.
  44. const TodoItem = ({name, due, complete}) => ( <div className={classnames({

    "todo-item": true, "is-due": due, "is-complete": complete, })}> <span className="todo-item__mark" /> <span style={(due && !complete) ? {color: "red"} : {}} className="todo-item__name">{name}</span> </div> ); 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.
  45. .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
  46. .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...
  47. const TodoItem = ({name, due, complete}) => ( <div className={classnames({

    "todo-item": true, "is-due": due, "is-complete": complete, })}> <span className="todo-item__mark" /> <span style={(due && !complete) ? {color: "red"} : {}} className="todo-item__name">{name}</span> </div> ); Back in our component...
  48. const nameStyles = (due, complete) => { return (due &&

    !complete) ? {color: "red"} : {}; }; const TodoItem = ({name, due, complete}) => ( <div className={classnames({ "todo-item": true, "is-due": due, "is-complete": complete, })}> <span className="todo-item__mark" /> <span style={nameStyles(due, complete)} className="todo-item__name">{name}</span> </div> ); This function is about to get more complex. Let's upgrade it to a proper function.
  49. const nameStyles = (due, complete) => { if (due &&

    !complete) return {color: "red"}; return {}; }; const TodoItem = ({name, due, complete}) => ( <div className={classnames({ "todo-item": true, "is-due": due, "is-complete": complete, })}> <span className="todo-item__mark" /> <span style={nameStyles(due, complete)} className="todo-item__name">{name}</span> </div> ); Replace the turnery with a proper if statement...
  50. const nameStyles = (due, complete) => { if (due &&

    !complete) return {color: "red"}; if (complete) return {textDecoration: "line-through"}; return {}; }; const TodoItem = ({name, due, complete}) => ( <div className={classnames({ "todo-item": true, "is-due": due, "is-complete": complete, })}> <span className="todo-item__mark" /> <span style={nameStyles(due, complete)} className="todo-item__name">{name}</span> </div> ); and add our second condition for `complete`
  51. const nameStyles = (due, complete) => { if (due &&

    !complete) return {color: "red"}; if (complete) return {textDecoration: "line-through"}; return {}; }; const TodoItem = ({name, due, complete}) => ( <div className={classnames({ "todo-item": true, "is-due": due, "is-complete": complete, })}> <span className="todo-item__mark" /> <span style={nameStyles(due, complete)}>{name}</span> </div> ); now we can remove the className entirely. This feels pretty good.
  52. const nameStyles = (due, complete) => { if (due &&

    !complete) return {color: "red"}; if (complete) return {textDecoration: "line-through"}; return {}; }; const TodoItem = ({name, due, complete}) => ( <div className={classnames({ "todo-item": true, "is-due": due, "is-complete": complete, })}> <span className="todo-item__mark" /> <span style={nameStyles(due, complete)}>{name}</span> </div> ); And `is-due` can be removed from the block.
  53. const nameStyles = (due, complete) => { if (due &&

    !complete) return {color: "red"}; if (complete) return {textDecoration: "line-through"}; return {}; }; const TodoItem = ({name, due, complete}) => ( <div className={classnames({ "todo-item": true, "is-complete": complete, })}> <span className="todo-item__mark" /> <span style={nameStyles(due, complete)}>{name}</span> </div> ); 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.
  54. .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.
  55. const nameStyles = (due, complete) => { if (due &&

    !complete) return {color: "red"}; if (complete) return {textDecoration: "line-through"}; return {}; }; const TodoItem = ({name, due, complete}) => ( <div className={classnames({ "todo-item": true, "is-complete": complete, })}> <span className="todo-item__mark" /> <span style={nameStyles(due, complete)}>{name}</span> </div> ); Back in our component.
  56. const nameStyles = (due, complete) => { if (due &&

    !complete) return {color: "red"}; if (complete) return {textDecoration: "line-through"}; return {}; }; const TodoItem = ({name, due, complete}) => ( <div className={classnames({ "todo-item": true, "is-complete": complete, })}> <span className="todo-item__mark" /> <span style={nameStyles(due, complete)}>{name}</span> </div> ); Let's focus on the markup for our `mark` descendent.
  57. const nameStyles = (due, complete) => { if (due &&

    !complete) return {color: "red"}; if (complete) return {textDecoration: "line-through"}; return {}; }; const TodoItem = ({name, due, complete}) => ( <div className={classnames({ "todo-item": true, "is-complete": complete, })}> <span>{(complete) ? '✔': '*'}</span> <span style={nameStyles(due, complete)}>{name}</span> </div> ); And use a simple ternary to change the character based on state.
  58. const nameStyles = (due, complete) => { if (due &&

    !complete) return {color: "red"}; if (complete) return {textDecoration: "line-through"}; return {}; }; const TodoItem = ({name, due, complete}) => ( <div className={classnames({ "todo-item": true, "is-complete": complete, })}> <span>{(complete) ? '✔': '*'}</span> <span style={nameStyles(due, complete)}>{name}</span> </div> );
  59. const nameStyles = (due, complete) => { if (due &&

    !complete) return {color: "red"}; if (complete) return {textDecoration: "line-through"}; return {}; }; const TodoItem = ({name, due, complete}) => ( <div className={classnames({ "todo-item": true, "is-complete": complete, })}> <span>{(complete) ? '✔': '*'}</span> <span style={nameStyles(due, complete)}>{name}</span> </div> );
  60. const nameStyles = (due, complete) => { if (due &&

    !complete) return {color: "red"}; if (complete) return {textDecoration: "line-through"}; return {}; }; const TodoItem = ({name, due, complete}) => ( <div style={(complete) ? {color: "#aaa"} : {} className={classnames({ "todo-item": true, "is-complete": complete, })}> <span>{(complete) ? '✔': '*'}</span> <span style={nameStyles(due, complete)}>{name}</span> </div> ); Again, we'll use a ternary to apply this style...
  61. const nameStyles = (due, complete) => { if (due &&

    !complete) return {color: "red"}; if (complete) return {textDecoration: "line-through"}; return {}; }; const TodoItem = ({name, due, complete}) => ( <div style={(complete) ? {color: "#aaa"} : {} className="todo-item" > <span>{(complete) ? '✔': '*'}</span> <span style={nameStyles(due, complete)}>{name}</span> </div> ); And lose our need for `classnames`
  62. const nameStyles = (due, complete) => { if (due &&

    !complete) return {color: "red"}; if (complete) return {textDecoration: "line-through"}; return {}; }; const TodoItem = ({name, due, complete}) => ( <div style={(complete) ? {color: "#aaa"} : {} className="todo-item" })}> <span>{(complete) ? '✔': '*'}</span> <span style={nameStyles(due, complete)}>{name}</span> </div> ); 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.
  63. 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.
  64. it("looks crossed-out, when complete", () => { shallowRenderer.render(<Item name="" complete={true}>);

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

    const result = shallowRenderer.getRenderOutput(); assert.deepEqual( result.props.style, { textDecoration: "line-through" } ); }); assert that the resulting object has style text-decoration "line-through"
  66. it("accepts a completeStyle prop", () => { }); You have

    some contextual styles you want to add? We can write a test for that...
  67. it("accepts a completeStyle prop", () => { const mergeStyles =

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

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

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

    { color: "silver" }; shallowRenderer.render( <Item name="" complete={true} completeStyle={mergeStyles}> ); const result = shallowRenderer.getRenderOutput(); assert.deepEqual( result.props.style, { color: "silver" } ); }); and assert that they made it through to the component. Easy
  71. SQUEAKY CLEAN CSS This makes CSS easy again. You're only

    concerned with APPEARANCE, not functionality.
  72. 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.
  73. 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.
  74. 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.
  75. 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?"
  76. CSS in JS It was insanely popular. His slide deck

    from the talk has had over half a million views.
  77. 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.
  78. 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.
  79. 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.
  80. 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 ( <li style={Object.assign({}, styles, this.state.hovered && {backgroundColor: "rgba(0,0,0,0.1)"} )} onMouseEnter={this.handleMouseEnter} onMouseLeave={this.handleMouseLeave} {...this.props} /> ); } } This is the boilerplate for handling the hover event in a React component. This isn't better.
  81. .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...
  82. 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 ( <li style={Object.assign({}, styles, this.state.hovered && {backgroundColor: "rgba(0,0,0,0.1)"} )} onMouseEnter={this.handleMouseEnter} onMouseLeave={this.handleMouseLeave} {...this.props} /> ); } } ... but it's cripplingly painful in a React component.
  83. 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.
  84. 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
  85. ALL SOLUTIONS REQUIRED NEW TOOLING I wasn't willing to add

    libraries with this much churn into all of our apps.
  86. 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.
  87. 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.
  88. 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.
  89. Thing.jsx Thing.scss Thing Component And the these two things become

    the component abstraction your application is concerned with.
  90. 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.
  91. 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.
  92. 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
  93. ATOMIC CSS* This term means a lot. So I'm going

    to show you what I mean by it.
  94. .d-f we're going to use some shorthand classes that map

    to a single property- value. .d-f => { display: flex }
  95. .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.
  96. .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.
  97. .d-f .f_1 .fd-c .fd-r@md p_1@md and we can add some

    padding at the medium breakpoint as well.
  98. Defer design The worst part of CSS is that you

    have to name things first and there's no true modularization.
  99. Inline workflow + breakpoints Server-rendered friendly Style without CSS Defer

    design solves the problem of inline-styles having trouble with breakpoints.
  100. 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
  101. ARE ANY OF THE CLASSES THEMATICALLY RELATED? Harry Roberts, @csswizardry

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

    Roberts, @csswizardry HTTP://CSSWIZARDRY.COM/2015/03/CAN-CSS-BE-TOO-MODULAR/
  103. 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/
  104. 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/
  105. "MY HTML IS SO NON-SEMANTIC!" You might hang your head

    in shame for even having this thought.
  106. "WAIT... WHAT HTML?" I'm writing components. I use JSX. My

    HTML is an artifact. This article was written about layout.
  107. "ISN'T HAVING A COMPONENT CLASS AND A CSS CLASS REDUNDANT?"

    We're implementing a class in two different languages. Isn't this redundant?
  108. 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.
  109. http://jxnblk.com/gravitons/ Much smaller focus but also really great, if you

    have small needs. Also, follow Brent Jaxon. He does great work.
  110. MINIONS.CSS To call this a library would be a gross

    overstatement.. It's a thought experiment that we use in production.
  111. "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.
  112. 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.
  113. 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.
  114. ⛄ 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.
  115. 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.
  116. /* button.css */ .default { color: #111 } Here's a

    very generically named button style.
  117. /* 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.
  118. /* ICSS */ :export { default: default_f34f7fa0; } .default_f34f7fa0 {

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

    */ import styles from './button.css' const MyButton (props) => ( <button className={styles.default}> Default </button> ); And use the key as your classname.
  120. import styles from './button.css' const MyButton (props) => ( <button

    className={styles.default}> Default </button> ); /* rendered output */ <button class="default_f34f7fa0"> Default </button> When the component is rendered, it's rendered with the unique class.
  121. /* 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.
  122. /* 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...
  123. 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.
  124. 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...
  125. 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.
  126. .base_81f12d56 { border-radius: 3px; } .normal_f34f7fa0 { background-color: blue; }

    .error_b7d2ad6f { background-color: red; } /* rendered output */ <button class="base_81f12d56 error_b7d2ad6f"> Error! </button> This results in rendered output like this, where bath `error` and the class it `composes` are applied. This is brilliant.
  127. .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.
  128. 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.
  129. ☠ 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
  130. @CHANTASTIC Thanks you listening. I'm @chantastic, if you want to

    tell me how batshit crazy I am. Thank you.