- CSS Modules
- Inline Styles
- CSS in JS
Let’s start at the beginning…
Documents
— vs. —
Web Apps
Global scope
— vs —
Maintenance
- OOCSS
- SMACSS
- BEM
- SUIT
.Block__Element--Modifier { ... }
<div class="page my-page">
<header class="header">
<h1 class="heading header__heading">...</h1>
</header>
<article class="post">
<header class="post__header">
<h1 class="heading post__heading">Hello World</h1>
</header>
<section class="post__content">...</section>
</article>
<footer class="footer">...</footer>
</div>
The Age
— of —
Components
- Backbone
- Angular’s directives
- Ember.Component
- Web components
- Polymer
React
Components are nothing new
<select>
<option>...</option>
<option>...</option>
<option>...</option>
</select>
<input type="date" />
CSS and images
— are —
private to a component
Unless you use third-party components…
jQuery UI Date Picker
Our tools reflected this
gulp.task('js', function() {
return gulp.src('src/js/index.js')
.pipe(browserify());
});
gulp.task('css', function() {
return gulp.src('src/styles/index.scss')
.pipe(sass());
});
gulp.task('images', function() { ... });
@import "vendor/normalize";
@import "config/colors";
@import "config/media_queries";
@import "modules/btn";
@import "modules/dropdown";
@import "modules/header";
@import "utilities/align";
@import "utilities/clearfix";
Tooling
— for the —
component age?
Webpack
require('./MyComponent.css');
export default () => (
<div className="MyComponent">
<div className="MyComponent__Icon">Icon</div>
...
</div>
);
Simple interface
import MyComponent from 'MyComponent';
export default () => <MyComponent />;
Progressive
Single Page Apps
- Server rendered CSS
- :visited, :before, :hover, :focus, etc.
- Media queries
- CSS animations
- Psuedo classes for non-JS interactions…?
.menu {
transform: translateX(-100%);
transition: all .3s ease;
}
.toggle:checked ~ .menu {
transform: none
}
Late 2014…
“CSS has fundamental flaws at scale that can be solved by writing styles in JS.”
@vjeux — November 2014
MicheleBertoli/css-in-js
react-style
— by @andreypopp —
import StyleSheet from 'react-style';
const styles = StyleSheet.create({
foo: {
color: 'red',
backgroundColor: 'white'
}
});
export default () => (
<div style={styles.foo}>
Hello, world!
</div>
);
but…
-
Media queries -
CSS animations -
Psuedo classes
And yet…
😍
— How do we embrace the —
inline style mindset in regular CSS?
BEM
A block
— is —
a component
“Never use a block outside
a component of the same name”
components/
MyComponent/
MyComponent.js
MyComponent.css
icon.svg
.MyComponent__foo { ... }
.MyComponent__bar { ... }
.MyComponent__bar--expanded { ... }
“Block, Element, Modifying Your JavaScript Components”
Meanwhile…
Local Scope
require('./MyComponent.css');
// becomes...
import styles from './MyComponent.css';
What does ‘styles’
evaluate to?
First, let’s look at the CSS…
:local(.header) { ... }
:local(.footer) { ... }
import styles from './MyComponent.css';
export default () => (
<div>
<div className={styles.header}>...</div>
<div className={styles.footer}>...</div>
</div>
);
:local(.header) { ... }
:local(.footer) { ... }
Becomes:
/* Globally unique classes */
._1rJwx92-gmbvaLiDdzgXiJ { ... }
._ah6Hch-gjsAdSE53CxzaEs { ... }
No more
naming collisions
// Styles object:
{
'header': '_1rJwx92-gmbvaLiDdzgXiJ',
'footer': '_ah6Hch-gjsAdSE53CxzaEs'
}
loader: 'css?localIdentName=[name]__[local]'
.MyComponent__foo { ... }
.MyComponent__bar { ... }
:local(.field) { ... }
:local(.field):focus { ... }
:local(.backdrop) { ... }
:local(.root_isCollapsed) :local(.backdrop) { ... }
:local(.panel) .transition-active-enter { ... }
“What if locally scoped
CSS was the default?”
.backdrop { ... }
.root_isCollapsed .backdrop { ... }
.field { ... }
.field:focus { ... }
.panel :global(.transition-active-enter) { ... }
PostCSS
postcss-local-scope
— Turned .class into :local(.class) —
The End
— of —
Global CSS
“I hope it’s ok if I integrate your postcss-local-scope module into the css-loader”
@sokra — May 24, 2015
‘css-loader?module’
“Have you thought about writing a standard for CSS modules that others could implement?”
@markdalgleish — May 23, 2015
‘css-loader?modules’
- Class composition
- Values, i.e. shared constants
github.com/css-modules
- Webpack
- Browserify
- JSPM
- PostCSS
- Babel
Let’s rewind a little…
Why not CSS in JS?
-
Media queries -
CSS animations -
Psuedo classes
Inline styles
CSS in JS
— isn’t restricted to —
Inline Styles
Khan Academy
Aphrodite
Jamie Wong / Emily Eisenberg
import { StyleSheet } from 'aphrodite';
const styles = StyleSheet.create({
field: {
margin: '0 10px',
':focus': { outline: '2px solid blue' },
'@media (min-width: 600px)': { margin: '0 20px' }
}
});
import { css } from 'aphrodite';
export default () => (
<div>
<input className={css(styles.field)} />
</div>
);
import { StyleSheetServer } from 'aphrodite';
var {html, css} = StyleSheetServer.renderStatic(() => {
return ReactDOMServer.renderToString( );
});
return `
<html>
<head>
<style data-aphrodite>${css.content}</style>
</head>
<body>
<div id='app'>${html}</div>
...
</body>
</html>
`;
“We must deliver the absolute minimum amount of CSS necessary by only sending down CSS in use by components in the server-side rendered body”
Jamie Wong — March 2016
Personal
preference
CSS Modules
— or —
CSS-in-JS…
What do these
approaches have
in common?
First Class Styles
Locally define styles
— then —
Pass directly to elements
.foo { color: red }
import styles from './Component.css';
<div className={styles.foo} />
const styles = StyleSheet.create({
foo: { color: 'red' }
});
<div style={styles.foo} />
We've been putting this into practice
for the past year...
- 70 CSS files
- 782 rules
- 948 selectors
- 2,886 declarations
- 211 media queries
Our global CSS footprint?
/* http://meyerweb.com/eric/tools/css/reset/
v2.0 | 20110126
License: none (public domain)
*/
html, body, div, span, applet, object, iframe,
h1, h2, h3, h4, h5, h6, p, blockquote, pre,
a, abbr, acronym, address, big, cite, code,
del, dfn, em, img, ins, kbd, q, s, samp,
small, strike, strong, sub, sup, tt, var,
b, u, i, center,
dl, dt, dd, ol, ul, li,
fieldset, form, label, legend,
table, caption, tbody, tfoot, thead, tr, th, td,
article, aside, canvas, details, embed,
figure, figcaption, footer, header, hgroup,
menu, nav, output, ruby, section, summary,
time, mark, audio, video {
margin: 0;
padding: 0;
border: 0;
font-size: 100%;
font: inherit;
vertical-align: baseline;
}
/* HTML5 display-role reset for older browsers */
article, aside, details, figcaption, figure,
footer, header, hgroup, menu, nav, section {
display: block;
}
body {
line-height: 1;
}
ol, ul {
list-style: none;
}
blockquote, q {
quotes: none;
}
blockquote:before, blockquote:after,
q:before, q:after {
content: '';
content: none;
}
table {
border-collapse: collapse;
border-spacing: 0;
}
html {
font-family: @base-font-stack;
font-size: unit(@base-font-size, px);
background-color: @sk-background;
box-sizing: border-box;
min-height: 100%;
overflow-y: auto;
}
*,
*:before,
*:after {
font-family: inherit;
box-sizing: inherit;
-webkit-tap-highlight-color: rgba(0,0,0,0);
-webkit-tap-highlight-color: transparent; /* For some Androids */
}
body {
overflow-y: hidden;
}
input {
margin: 0;
}
input[type=search]::-webkit-search-decoration {
-webkit-appearance:none;
}
- Increased authoring speed
- Less fear of change
- Maintainable by default
- Faster debugging
- Simpler onboarding
Easier style guide extraction
But that’s a topic for another day…
👍
😍😍😍