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

Bulletproof CSS in React

Bulletproof CSS in React

Watch: https://vimeo.com/173122870 or https://www.youtube.com/watch?v=U86-MFPsarQ

CSS is notoriously troublesome to work with. It can get complicated quickly, and style clashes + inheritance problems start stacking up. Throw in running your CSS in a third party environment and you're gonna have a bad time.

David Wells focuses on how you can eliminate these common CSS issues FOREVER, and will explore how this works with React-powered static site generator phenomic.io

David Wells

June 30, 2016
Tweet

More Decks by David Wells

Other Decks in Technology

Transcript

  1. UI/UX Engineer at React and Redux Training for companies Next

    Class Saturday, July 16th in SF register.reactclass.com Tweet at me @DavidWells DavidWells.io
  2. Challenge Make components stand alone Make them work in legacy

    applications Avoid external collisions and dont collide with others Have styles & assets consumable by others
  3. Raw CSS (OOCSS, SMACSS, BEM, SUIT) Inline styles in JSX

    CSS-in-JS PostCSS + CSS Modules Current styling landscape
  4. Easy to implement Recompute styles at anytime Dynamic state based

    styles etc import React from 'react' class ShamWOW extends React.Component { render(){ const styles = { color: (this.props.danger) ? 'red' : 'black' fontSize: '15px' } const otherStyles = { color: 'purple' fontSize: '12px' } return ( <div style={styles}> <span style={otherStyles}> WOW! <span> </div> ) } }
  5. No Pseudo Classes/Elements No Media Queries No vender autoprefixing No

    Animations/keyframes !important for overrides No Style Fallbacks Composition? Performance (large HTML, larger JS) Debugging is hard INLINE STYLES GOTCHAS
  6. import React, {Component} from 'react' import {StyleSheet, css} from 'aphrodite'

    const styles = StyleSheet.create({ container: { textAlign: 'center' }, button: { backgroundColor: '#ff0000', width: '320px', borderRadius: '5px', ':hover': { color: '#fff', }, '@media (max‐width: 480px)': { width: '160px' } } }) export default class Button extends Component { render() { return ( <div className={css(styles.container)}> <button className={css(styles.button)}>Click me!</button> </div> ); } }
  7. Flash of Incorrectly Styled Content* Pseudo Classes/Elements* Media Queries* No

    Style Fallbacks Animations/keyframes* Debugging is hard* (no sourcemaps) Performance (larger bundle, DOM calcs) library/flavor lock-in Globals? Composable? CSS in JS Considerations
  8. Local (and global) scope AST is very very powerful Huge

    plugin ecosystem Write normal CSS and more... Why PostCSS?
  9. Component architecture /src/components/ └── Button/ # Component folder ├── Button.js

    # src code ├── Button.css # styles ├── Button.spec.js # tests ├── index.js # Exports your component └── examples/ # Usage Examples └── button_usage.js # Examples of component usage
  10. Localized Styles via PostCSS + CSS modules /* Button.js React

    Component */ import React from 'react' import styles from './Button.css' class Button extends React.Component { render(){ const text = this.props.text return ( <div className={styles.wrapper}> <button className={styles.button}> {text} </button> </div> ) } }
  11. CSS as per usual /* Button.css */ .wrapper { font‐size:

    20px; } .button { border: 20px solid #bada55; }
  12. How CSS modules work /* Button.js */ import React from

    'react' /* styles is imported as an object with classnames as keys */ import styles from './Button.css' /* styles = { button: "Button__button___I_bKh" <‐‐‐ Filename__classname__HASH wrapper: "Button__wrapper___3fslE" } */ class Button extends React.Component { render(){ const text = this.props.text return ( <div className={styles.wrapper}> <button className={styles.button}> {text} </button> </div> ) } }
  13. The end result <!‐‐ Localized Classes! ‐‐> <div class="Button__wrapper___3fslE"> <button

    class="Button__button___I_bKh"> My button Text </button> </div> VS <!‐‐ collision city ‐‐> <div class="wrapper"> <button class="button"> My button Text </button> </div>
  14. Other benefits Use CSS4 Polyfill Flexbox back to IE8 a11y

    rules linting?!?!! intial: all; Composability Critical CSS paths Separate out all mediaQueries (bit.ly/responsiblejs) Perf gains dev linting via styleLint Designers can write* *gasp* ability to use Global
  15. IS GLOBAL CSS ALL BAD? NOPE! modal, drawer, etc. classes

    on body Sharing keyframe animations External selector hooks (double edged sword) Sometimes you want the cascade
  16. .local‐class { color: red; } :global(.prefix‐modal‐open) .local‐class { color: green;

    } /* or */ :global { .this‐is‐global { color: yellow; } .potential‐collision‐city { color: crap; } } You decide Local by default, global when you want
  17. DO I NEED THIS? Are you working on a team?

    Are you including third party CSS? Markup running inside of a third party environment? Do you want raw AST power? Want your styles to look correct no matter what?
  18. CAVEATS No react native* Requires build step (but what doesn't

    these days) No "dynamic" computed styles based on state* Be careful of inventing your own syntax Don't go nuts with plugins Testing quirks if asserting on localized classnames You have to write CSS*
  19. Our choice PostCSS + inlineStyles PostCSS handles majority of styling

    When computed values are needed inlineStyles on the `style`
  20. Javascript powered variables 16. /* require global variables */ 17.

    require('postcss-simple-vars')({ 18. variables: function () { 19. return require('./variables') 20. }, 21. unknown: function (node, name, result) { 22. node.warn(result, 'Unknown variable ' + name) 23. } 10. /* enable mixins like Sass/Less */ 11. require('postcss-mixins')({ 12. mixins: require('./mixins') 13. }), 14. /* enable nested css selectors like Sass/Less */ 15. require('postcss-nested'), 24. }), 25. /* PostCSS plugin for making calculations with math.js */ 26. require('postcss-math'), 27. /* transform W3C CSS color function to more compatible CSS. */ 28. require('postcss-color-function') 29. ]
  21. Mixin & variable usage in CSS .hero { @mixin DinProLight;

    /* defined in mixins.js */ color: $primaryBlue; /* defined in variables.js */ }
  22. Variable usage in JS import React from 'react' import variables

    from 'path/to/global‐variables' class Button extends React.Component { render(){ const styles = { color: variables.primary zIndex: variables.aboveModal } return ( <div style={styles}> WOW! </div> ) } }
  23. Build for production localIdentName: [hash:base64:4] module: { loaders: [ {

    test: /.css/, loaders: ['style‐loader', 'css‐loader?modules&localIdentName=[hash:base64:4]!postcss‐loader'], } ] }, Output: <div class="3fsl"> <button class="I_bKh"> My button Text </button> </div> <!‐‐ wow thats some tiny markup!!!!!! ‐‐>
  24. BEM FO FREE localIdentName: [name]__[local] module: { loaders: [ {

    test: /.css/, loaders: ['style‐loader', 'css‐loader?modules&localIdentName=[name]__[local]!postcss‐loader'], } ] }, Output: <div class='fileName__yourClassName'> <!‐‐ ooooo thats handy ‐‐> </div>
  25. npm i classnames via @jedWatson import styles from './Button.css' import

    classnames from 'classnames' ... const { className, isGhost, disabled, isLoading } = this.props const propBasedClasses = { [styles.ghost]: isGhost, /* if isGhost === true */ [styles.disabled]: disabled || isLoading } const classes = classNames( styles.button, /* localized styles */ className, /* user specified classNames */ propBasedClasses /* prop based classnames */ )
  26. catch undefined via @Chrisui .undefined { display: flex !important; position:

    fixed !important; top: 0 !important; right: 0 !important; bottom: 0 !important; left: 0 !important; background: red !important; color: white !important; justify‐content: center !important; align‐items: center !important; font‐size: 30px !important; font‐weight: bold !important; } .undefined::after { display: block !important; padding: 15px 30px !important; content: 'ERROR! You are missing a class definition in your css module! Inspect me to find out where.' !important }
  27. Questions? React and Redux Training Next Class Saturday, July 16th

    in SF React Basics through Redux and Serverside Rendering register.reactclass.com Tweet at me @DavidWells DavidWells.io
  28. Questions? React and Redux Training Next Class Saturday, July 16th

    in SF React Basics through Redux and Serverside Rendering register.reactclass.com Tweet at me @DavidWells DavidWells.io