57
Styling Components with JavaScript @bensmithett

Styling Components with JavaScript: MelbCSS Edition

Embed Size (px)

Citation preview

Page 1: Styling Components with JavaScript: MelbCSS Edition

Styling Components

with JavaScript@bensmithett

Page 2: Styling Components with JavaScript: MelbCSS Edition

I already gave this talk at MelbJS

Sorry if you're sitting through it again!

Page 3: Styling Components with JavaScript: MelbCSS Edition

Warning:This talk is about writing CSS...

Page 4: Styling Components with JavaScript: MelbCSS Edition

IN

Page 5: Styling Components with JavaScript: MelbCSS Edition

JAVASCRIPT

Page 6: Styling Components with JavaScript: MelbCSS Edition
Page 7: Styling Components with JavaScript: MelbCSS Edition
Page 8: Styling Components with JavaScript: MelbCSS Edition

Explore some new ideas and

challenge best practices

Page 9: Styling Components with JavaScript: MelbCSS Edition

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

Page 10: Styling Components with JavaScript: MelbCSS Edition

COMPONENTS!

Page 11: Styling Components with JavaScript: MelbCSS Edition

The Profile component:

views/ profile.htmlstylesheets/ profile.cssjavascripts/ profile.js

Page 12: Styling Components with JavaScript: MelbCSS Edition

structure

<div class="profile"> <img class="profile__avatar" src="{{user.avatar}}"> <strong>{{user.username}}</strong></div>

Page 13: Styling Components with JavaScript: MelbCSS Edition

style

.profile { border: 1px solid #ddd; overflow: hidden;}

.profile__avatar { float: left; margin-right: 10px;}

Page 14: Styling Components with JavaScript: MelbCSS Edition

behaviour

$(".profile").on("click", function(){ alert("hi");});

Page 15: Styling Components with JavaScript: MelbCSS Edition

Best practice:Separate HTML, CSS & JS

Page 16: Styling Components with JavaScript: MelbCSS Edition

Why?Why is separating HTML, CSS & JS a best

practice?

Page 17: Styling Components with JavaScript: MelbCSS Edition

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()

Page 18: Styling Components with JavaScript: MelbCSS Edition

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

Page 19: Styling Components with JavaScript: MelbCSS Edition

views/ profile.htmlstylesheets/ profile.cssjavascripts/ profile.js

Page 20: Styling Components with JavaScript: MelbCSS Edition

components/ profile/ profile.html profile.css profile.js

Page 21: Styling Components with JavaScript: MelbCSS Edition

Component:A tightly coupled little bundle of HTML,

CSS & JS

Page 22: Styling Components with JavaScript: MelbCSS Edition
Page 23: Styling Components with JavaScript: MelbCSS Edition

React is a library for building components

Page 24: Styling Components with JavaScript: MelbCSS Edition

A React component

• Describes the HTML structure given a particular application state

• Reacts to user events (click, dragenter etc) that originate within the component

Page 25: Styling Components with JavaScript: MelbCSS Edition

<div class="profile"> <img class="profile__avatar" src="{{user.avatar}}"> <strong>{{user.username}}</strong></div>

Page 26: Styling Components with JavaScript: MelbCSS Edition

$(".profile").on("click", function(){ alert("hi");});

Page 27: Styling Components with JavaScript: MelbCSS Edition

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"); }});

Page 28: Styling Components with JavaScript: MelbCSS Edition

components/ profile/ profile.html profile.css profile.js

Page 29: Styling Components with JavaScript: MelbCSS Edition

components/ profile/ profile.jsx profile.css

Page 30: Styling Components with JavaScript: MelbCSS Edition

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.

Page 31: Styling Components with JavaScript: MelbCSS Edition

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;

Page 32: Styling Components with JavaScript: MelbCSS Edition

Can't we do better?

Page 33: Styling Components with JavaScript: MelbCSS Edition

What if our build process automatically built a stylesheet based only on the JS

components we actually use?

Page 34: Styling Components with JavaScript: MelbCSS Edition

Webpack

// ComponentA.jsrequire("./componentA.css");

// ComponentB.jsrequire("./componentB.css");

// Javascript build generates app.css:// .component-a { ... }// .component-b { ... }

Page 35: Styling Components with JavaScript: MelbCSS Edition

components/ profile/ profile.jsx profile.css

Page 36: Styling Components with JavaScript: MelbCSS Edition

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>

Page 37: Styling Components with JavaScript: MelbCSS Edition

NOBODY LIKES INLINE STYLES

Page 38: Styling Components with JavaScript: MelbCSS Edition

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

Page 39: Styling Components with JavaScript: MelbCSS Edition

react-style + webpack

• github.com/SanderSpies/react-style

• andreypopp.com/posts/2014-08-06-react-style.html

• webpack.github.io

Page 40: Styling Components with JavaScript: MelbCSS Edition

var Profile = React.createClass({ styles: ReactStyle({ backgroundColor: "green", border: "1px solid #ddd" }), render: function () { return <div styles={this.styles()} /> }})

Page 41: Styling Components with JavaScript: MelbCSS Edition

/* app.css generated by react-style & webpack */.a { background-color: green; border: 1px solid #ddd;}

<!-- DOM generated by React --><div class="a"> ...</div>

Page 42: Styling Components with JavaScript: MelbCSS Edition

Best practice:

Use meaningful class names & a naming

convention like BEM or SUIT

Page 43: Styling Components with JavaScript: MelbCSS Edition

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.

Page 44: Styling Components with JavaScript: MelbCSS Edition

var Profile = React.createClass({ styles: ReactStyle({ backgroundColor: "green", border: "1px solid #ddd" }), render: function () { return <div styles={this.styles()} /> }})

Page 45: Styling Components with JavaScript: MelbCSS Edition

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!

Page 46: Styling Components with JavaScript: MelbCSS Edition

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);

Page 47: Styling Components with JavaScript: MelbCSS Edition

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

Page 48: Styling Components with JavaScript: MelbCSS Edition

I <3 Sass

Page 49: Styling Components with JavaScript: MelbCSS Edition

What is a preprocessor?

A language that helps us generate blocks of key: value pairs.selector { property: value; other-property: other-value;}

Page 50: Styling Components with JavaScript: MelbCSS Edition

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!

Page 51: Styling Components with JavaScript: MelbCSS Edition

JS already has lots of Real Programming Things™

• Variables

• Functions

• Arrays & Objects

• Loops

• Maths

• String manipulation

• Dependency management

Page 52: Styling Components with JavaScript: MelbCSS Edition

Things we can do in JS instead of a preprocessorWarning: everything beyond here is totally pseudocode that may or

may not actually work

Page 53: Styling Components with JavaScript: MelbCSS Edition

Example: Color variables

var colors = require("color_palette");

var styles = { color: colors["primary"]}

var Profile = React.createClass({ render: function () { return <div style={styles} />; }});

Page 54: Styling Components with JavaScript: MelbCSS Edition

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]} /> }})

Page 55: Styling Components with JavaScript: MelbCSS Edition

2015 HIPSTER PREPROCESSOR

JAVASCRIPT?!

Page 56: Styling Components with JavaScript: MelbCSS Edition

Give it 5 minutes

Page 57: Styling Components with JavaScript: MelbCSS Edition

the end :)@bensmithett