CSS with a hash
...from a convention to a hash
css-modules && styled-components
Who
Why
How
Who
...a little bit of ❤️
CSS Modules
💅🏻 styled-components
Glen Maddern
@geelen
Glen Maddern
@geelen
Mark Dalgleish
@markdalgleish
Max Stoiber
@mxstbr
Tobias Koppers
@sokra
Why
...problems with CSS at scale
-
Global Namespace
-
Implicit dependencies between CSS and JS
-
Dead code elimination
-
Sharing constants & theming
-
Isolation & cascade
🔴
✅
✅
🔴
🔴
css-modules
The local scope and webpack
css-modules
...it starts with a hash
-
Write CSS as in CSS
-
No inline styles or "CSS in JS"
-
-
Processed to add a hash
-
Webpack or PostCSS
-
-
Imported in JS components
-
React, Angular, etc
-
-
Adds custom composition
-
Avoid cascading in CSS
-
:local scope
gets hashed
when imported
._1rJwx92-gmbvaLiDdzgXiJ { ... }
._ah6Hch-gjsAdSE53CxzaEs { ... }
:local(.header) { ... }
:local(.footer) { ... }
import styles from './stylez.css';
{
'header': '_1rJwx92-gmbvaLiDdzgXiJ',
'footer': '_ah6Hch-gjsAdSE53CxzaEs'
}
:global scope
:local became default
.header { ... }
:global(.header) { ... }
CSS
JS & HTML
Output
import styles from './button.css';
buttonElem.outerHTML = `
<button class=${styles.normal}>Submit</button>
`
.normal { /* all styles for Normal */ }
.disabled { /* overrides for Disabled */ }
<button class="button__normal__abc5436">
Hit it!
</button>
Style Composition I
Output
.normal { /* all the common styles you want */ }
.inProgress {
composes: normal;
color: red
}
.button__normal__abc5436 { /* font-sizes, padding, border-radius */ }
.button__inProgress__def6547 { /* blue color, light blue background */ }
styles: {
normal: "button__normal__abc5436",
inProgress: "button__normal__abc5436 button__inProgress__def6547"
}
Internally & Exports
Style Composition II
.primary {
color: #720;
}
.secondary {
color: #777;
}
.common { ... }
.normal {
composes: common;
composes: primary from "../constants/colors.css";
}
.component {
composes: padding-large margin-small from "./layout.css";
}
.component {
composes: large from "./typography.css";
composes: dark-text from "./colors.css";
composes: subtle-shadow from "./effect.css";
}
PostCSS and webpack
{
test: /\.css$/,
loader: '
style!css-loader?modules&importLoaders=
1&localIdentName=[name]__[local]___[hash:base64:5]'
}
:global .page {
padding: 20px;
}
.title {
composes: title from "./mixins.css";
color: green;
}
webpack loader configuration
postcss-modules
._title_116zl_1 {
color: black;
font-size: 40px;
}
{
"title": "_title_xkpkl_5 _title_116zl_1"
}
Interoperable CSS (ICSS)
-
Explicit cross-language dependencies
- Describe each file's dependencies
- Describe each file's dependencies
-
CSS - JS interoperability
- Dynamic classnames (elem.addClass( styles.elemClass ))
- Dynamic classnames (elem.addClass( styles.elemClass ))
-
Specification
- :import and :export as pseudo selectors
styled-components
Enforcing best practices
styled-components
All about Higher Order Components
Remove mapping
between styles and component
import React from 'react';
import styled from 'styled-components';
const Title = styled.h1`
font-size: 1.5em;
text-align: center;
color: palevioletred;
`;
styled.*
const Title = styled.h1`
font-size: 1.5em
`;
in DOM
<h1 class="ae0869e501">...</h1>
while CSS
<link href="stylez.css" />
React components
const Title = styled.default.h1`
font-size: 1.5em;
text-align: center;
color: palevioletred;
`;
const Wrapper = styled.default.section`
padding: 4em;
background: papayawhip;
`;
class Example extends React.Component {
render() {
return (
<Wrapper>
<Title>Hello World!</Title>
</Wrapper>
)
}
}
ReactDOM.render(
<Example />,
document.getElementById('container')
);
Composition of components
import React from 'react';
import styled from 'styled-components';
import Button from './Button';
const TomatoButton = styled(Button)`
color: tomato;
border-color: tomato;
`;
export default TomatoButton;
Animations
import styled, { keyframes } from 'styled-components';
const rotate360 = keyframes`
from {
transform: rotate(0deg);
}
to {
transform: rotate(360deg);
}
`;
const Rotate = styled.div`
display: inline-block;
animation: ${rotate360} 2s linear infinite;
`;
Definition
Usage
Interpolation
const padding = '3em';
const Section = styled.section`
color: white;
padding: ${padding};
background: ${(props) => props.background};
border: ${(props) => props.theme.main}
`;
Change by props
Theming
import { ThemeProvider } from 'styled-components';
const theme = {
main: 'mediumseagreen',
};
const GreenSection = (props) => {
return (
<ThemeProvider theme={theme}>
{props.children}
</ThemeProvider>
);
}
ThemeProvider
props.theme
const Button = styled.button`
background: ${props => props.theme.main};
border: 2px solid ${props => props.theme.main};
`;
Why
...problems with CSS at scale
-
Global Namespace
-
Implicit dependencies between CSS and JS
-
Dead code elimination
-
Sharing constants & theming
-
Isolation & cascade
✅
✅
✅
✅
✅
❗️ Thanks ❗️
❓ Questions ❓
CSS with a hash
By Tobias Deekens
CSS with a hash
From a convention to a hash. Tales of CSS modules and styled-components.
- 1,333