I already gave this talk at MelbJS
Sorry if you're sitting through it again!
Warning:This talk is about writing CSS...
IN
JAVASCRIPT
Explore some new ideas and
challenge best practices
CSS is not lacking Best Practices™
Sass • Less • Stylus • Myth • Autoprefixer
OOCSS • BEM • SMACSS • SUIT • AMCSS
No IDs • No inline styles • Use meaningful class names • Mobile first • Repetition is OK • Repetition is the devil • Avoid complex selectors • Use @extends • Don't use @extends • Don't write CSS in JavaScript
COMPONENTS!
The Profile component:
views/ profile.htmlstylesheets/ profile.cssjavascripts/ profile.js
structure
<div class="profile"> <img class="profile__avatar" src="{{user.avatar}}"> <strong>{{user.username}}</strong></div>
style
.profile { border: 1px solid #ddd; overflow: hidden;}
.profile__avatar { float: left; margin-right: 10px;}
behaviour
$(".profile").on("click", function(){ alert("hi");});
Best practice:Separate HTML, CSS & JS
Why?Why is separating HTML, CSS & JS a best
practice?
When we were building pages
CSS classes loosely coupled to HTML.important { color: red; font-weight: bold;}
JS behaviour loosely coupled to HTML$(".foo").fancyParallaxSlider()
Now...
BEM Blocks, SMACSS Modules, SUIT Components
Tightly couple styles to a very particular HTML structure
Backbone Views, Angular Directives, Ember & React Components
Tightly couple JS behaviour to a very particular HTML structure
views/ profile.htmlstylesheets/ profile.cssjavascripts/ profile.js
components/ profile/ profile.html profile.css profile.js
Component:A tightly coupled little bundle of HTML,
CSS & JS
React is a library for building components
A React component
• Describes the HTML structure given a particular application state
• Reacts to user events (click, dragenter etc) that originate within the component
<div class="profile"> <img class="profile__avatar" src="{{user.avatar}}"> <strong>{{user.username}}</strong></div>
$(".profile").on("click", function(){ alert("hi");});
var Profile = React.createClass({ render: function () { return( <div className="profile" onClick={this.handleClick}> <img className="profile__avatar" src={this.props.avatarUrl} />
<strong>{this.props.username}</strong> </div> ); }, handleClick: function () { alert("hi"); }});
components/ profile/ profile.html profile.css profile.js
components/ profile/ profile.jsx profile.css
The only real connection: a class name
// JSrender: function () { return ( <div className="profile"> // ... </div> )}
/* CSS */.profile border: 1px solid #ddd
It's a crappy, vague connection.
Also, our CSS builds are a bit backwards
/* app.scss */@import vendor/Normalize.css;@import base;
@import components/header;@import components/profile;@import components/footer;
Can't we do better?
What if our build process automatically built a stylesheet based only on the JS
components we actually use?
Webpack
// ComponentA.jsrequire("./componentA.css");
// ComponentB.jsrequire("./componentB.css");
// Javascript build generates app.css:// .component-a { ... }// .component-b { ... }
components/ profile/ profile.jsx profile.css
React can do inline styles
var profileStyles = { border: "1px solid #ddd", overflow: "hidden"};
var Profile = React.createClass({ render: function () { return <div style={profileStyles} />; }});
<!-- DOM generated by React --><div style="border: 1px solid #ddd; overflow: hidden"><div>
NOBODY LIKES INLINE STYLES
What we really want:• Declare styles in the component file, using
JavaScript, like we do with inline styles
• Build process that:
• converts those styles to a CSS class
• bundles up all generated CSS classes into a shiny CSS file
• JS component magically knows which class name to use for a given element
react-style + webpack
• github.com/SanderSpies/react-style
• andreypopp.com/posts/2014-08-06-react-style.html
• webpack.github.io
var Profile = React.createClass({ styles: ReactStyle({ backgroundColor: "green", border: "1px solid #ddd" }), render: function () { return <div styles={this.styles()} /> }})
/* app.css generated by react-style & webpack */.a { background-color: green; border: 1px solid #ddd;}
<!-- DOM generated by React --><div class="a"> ...</div>
Best practice:
Use meaningful class names & a naming
convention like BEM or SUIT
Why?
We need a sensible class name that we can follow across seperate HTML, CSS & JS files
Nope. All that stuff is in one file now.
var Profile = React.createClass({ styles: ReactStyle({ backgroundColor: "green", border: "1px solid #ddd" }), render: function () { return <div styles={this.styles()} /> }})
Why?
We want to be able to inspect an element in the browser dev tools & see which component it belongs to.
Yeah, this one is valid.
But we can still automate it!
Automatically generate a BEM class name
className = path.basename(fileName).split('.')[0].toLowerCase() + '__' + styleName;
or a SUIT class name
className = path.basename(fileName).split('.')[0] + '-' + styleName.charAt(0).toUpperCase() + styleName.slice(1);
CSS class naming conventions are a project setting, not an inherent property of the component.
• Dev: BEM class names for easy debugging
• Prod: Tiny minified class names
I <3 Sass
What is a preprocessor?
A language that helps us generate blocks of key: value pairs.selector { property: value; other-property: other-value;}
What is a preprocessor?
A language that helps us generate blocks of key: value pairs.selector = { property: "value", otherProperty: "other-value"};
OMG JavaScript can do that!
JS already has lots of Real Programming Things™
• Variables
• Functions
• Arrays & Objects
• Loops
• Maths
• String manipulation
• Dependency management
Things we can do in JS instead of a preprocessorWarning: everything beyond here is totally pseudocode that may or
may not actually work
Example: Color variables
var colors = require("color_palette");
var styles = { color: colors["primary"]}
var Profile = React.createClass({ render: function () { return <div style={styles} />; }});
Example: Generate a grid
var gridColumns = 12;var gridDefinitions = {};
for (var i = 1; i <= gridColumns; i++) { gridDefinitions["span-" + i] = { float: left, width: ((i / gridColumns) * 100) + "%" }}
var GridColumn = React.createClass({ render: function () { var columns = "span-" + this.props.columnCount; return <div style={gridDefinitions[columns]} /> }})
2015 HIPSTER PREPROCESSOR
JAVASCRIPT?!
Give it 5 minutes
the end :)@bensmithett