Maintainable CSS in React
with glamorous 💄
Kent C. Dodds
Utah
1 wife, 3 kids
PayPal, Inc.
@kentcdodds
![](https://s3.amazonaws.com/media-p.slid.es/uploads/55780/images/2504801/slides-logo-black-1400x550-e5784ee4ba30e28a9b9ddf59ca5651a6.png)
Please Stand...
if you are able
![](https://s3.amazonaws.com/media-p.slid.es/uploads/kentcdodds/images/1237297/smile.png)
What this talk is
- The "why" of glamorous
- Some "whats"
- Some "hows"
![](https://s3.amazonaws.com/media-p.slid.es/uploads/kentcdodds/images/1237299/grinning.png)
What this talk is not
- A library you can use without React... (mostly...)
![](https://s3.amazonaws.com/media-p.slid.es/uploads/kentcdodds/images/1237298/neutral_face.png)
Let's
Get
STARTED!
![](https://s3.amazonaws.com/media-p.slid.es/uploads/55780/images/3174548/car.png)
![](https://s3.amazonaws.com/media-p.slid.es/uploads/55780/images/3174549/dash.png)
PayPal's most popular Open Source Project
Let's talk about CSS in JS for a sec
Our Component:
![](https://s3.amazonaws.com/media-p.slid.es/uploads/55780/images/3766240/toggle.gif)
JavaScript + HTML = JSX
class Toggle extends Component {
state = { toggledOn: false };
handleToggleClick = () => {
this.setState(
({ toggledOn }) => ({ toggledOn: !toggledOn }),
(...args) => {
this.props.onToggle(this.state.toggledOn);
},
);
};
render() {
const { children } = this.props;
const { toggledOn } = this.state;
const active = toggledOn ? 'active' : '';
return (
<button
className={`btn btn-primary ${active}`}
onClick={this.handleToggleClick}
>
{children}
</button>
);
}
}
export default Toggle;
Components!
import { PrimaryButton } from './css-buttons';
class Toggle extends Component {
state = { toggledOn: false };
handleToggleClick = () => {
this.setState(
({ toggledOn }) => ({ toggledOn: !toggledOn }),
(...args) => {
this.props.onToggle(this.state.toggledOn);
},
);
};
render() {
const { children } = this.props;
const { toggledOn } = this.state;
const active = toggledOn ? 'active' : '';
return (
<PrimaryButton
active={active}
onClick={this.handleToggleClick}
>
{children}
</PrimaryButton>
);
}
}
export default Toggle;
Components!
function AppButton({ className = '', active, ...props }) {
return (
<button
className={`btn ${className} ${active ? 'active' : ''}`}
{...props}
/>
);
}
function PrimaryButton({ className = '', ...props }) {
return <AppButton className={`btn-primary ${className}`} {...props} />;
}
export default AppButton;
export { PrimaryButton };
CSS? Where are the styles?
CSS? 🤔
.btn {
display: inline-block;
font-weight: 400;
line-height: 1.25;
text-align: center;
white-space: nowrap;
vertical-align: middle;
user-select: none;
border: 1px solid transparent;
padding: .5rem 1rem;
font-size: 1rem;
border-radius: .25rem;
transition: all .2s ease-in-out;
}
.btn.btn-primary {
color: #fff;
background-color: #0275d8;
border-color: #0275d8;
}
.btn-primary:hover, .btn-primary.active {
background-color: #025aa5;
border-color: #01549b;
}
- Conventions?
- Who's using these styles?
- Can I change these styles?
- Can I delete these styles?
CSS?
Pit of Success
a well-designed system makes it easy to do the right things and annoying (but not impossible) to do the wrong things.
Convention
Using conventions is just us compensating for not having a wide enough pit of success.
![](https://s3.amazonaws.com/media-p.slid.es/uploads/55780/images/3832305/1495479904896-95d121bc-47f1-4464-b7d0-91a4337acb05_.jpg)
![](https://s3.amazonaws.com/media-p.slid.es/uploads/55780/images/3832306/1495479985268-f4bf06d7-24a6-4e74-a11f-f0d71b88866e.jpg)
Components
![](https://s3.amazonaws.com/media-p.slid.es/uploads/55780/images/3766025/Screen_Shot_2017-05-03_at_15.56.57.png)
![](https://s3.amazonaws.com/media-p.slid.es/uploads/55780/images/3766027/Screen_Shot_2017-05-03_at_15.57.06.png)
Default
Styled
Unstyled
![](https://s3.amazonaws.com/media-p.slid.es/uploads/55780/images/3766039/Screen_Shot_2017-05-03_at_4.01.36_PM.png)
Focus
Toggled
![](https://s3.amazonaws.com/media-p.slid.es/uploads/55780/images/3766042/Screen_Shot_2017-05-03_at_3.59.28_PM.png)
![](https://s3.amazonaws.com/media-p.slid.es/uploads/55780/images/3766043/Screen_Shot_2017-05-03_at_3.59.39_PM.png)
![](https://s3.amazonaws.com/media-p.slid.es/uploads/55780/images/3766042/Screen_Shot_2017-05-03_at_3.59.28_PM.png)
Components
What is a UI component made of?
HTML
CSS
JS
Component
Remember our CSS?
<style>
.btn {
display: inline-block;
font-weight: 400;
line-height: 1.25;
text-align: center;
white-space: nowrap;
vertical-align: middle;
user-select: none;
border: 1px solid transparent;
padding: .5rem 1rem;
font-size: 1rem;
border-radius: .25rem;
transition: all .2s ease-in-out;
}
.btn.btn-primary {
color: #fff;
background-color: #0275d8;
border-color: #0275d8;
}
.btn-primary:hover, .btn-primary.active {
background-color: #025aa5;
border-color: #01549b;
}
</style>
<script>
function AppButton({ className = '', active, ...props }) {
return (
<button
className={`btn ${className} ${active ? 'active' : ''}`}
{...props}
/>
);
}
function PrimaryButton({ className = '', ...props }) {
return <AppButton className={`btn-primary ${className}`} {...props} />;
}
export default AppButton;
export { PrimaryButton };
</script>
Glamor!
import { css } from 'glamor';
const appButtonClassName = css({
display: 'inline-block',
fontWeight: '400',
lineHeight: '1.25',
textAlign: 'center',
whiteSpace: 'nowrap',
verticalAlign: 'middle',
userSelect: 'none',
border: '1px solid transparent',
padding: '.5rem 1rem',
fontSize: '1rem',
borderRadius: '.25rem',
transition: 'all .2s ease-in-out',
});
const highlightStyles = {
backgroundColor: '#025aa5',
borderColor: '#01549b',
};
const primaryButtonClassName = css({
color: '#fff',
backgroundColor: '#0275d8',
borderColor: '#0275d8',
':hover': highlightStyles,
});
const activeClassName = css(highlightStyles);
function AppButton({ className = '', active, ...props }) {
return (
<button
className={`${appButtonClassName} ${className} ${active ? activeClassName : ''}`}
{...props}
/>
);
}
function PrimaryButton({ className = '', ...props }) {
return (
<AppButton
className={`${primaryButtonClassName} ${className}`}
{...props}
/>
);
}
export default AppButton;
export { PrimaryButton };
Enter glamorous 💄Â
import glamorous from 'glamorous';
const AppButton = glamorous.button({
display: 'inline-block',
fontWeight: '400',
lineHeight: '1.25',
textAlign: 'center',
whiteSpace: 'nowrap',
verticalAlign: 'middle',
userSelect: 'none',
border: '1px solid transparent',
padding: '.5rem 1rem',
fontSize: '1rem',
borderRadius: '.25rem',
transition: 'all .2s ease-in-out',
});
const highlightStyles = {
backgroundColor: '#025aa5',
borderColor: '#01549b',
};
const PrimaryButton = glamorous(AppButton)(
{
color: '#fff',
backgroundColor: '#0275d8',
borderColor: '#0275d8',
':hover': highlightStyles,
},
({ active }) => (active ? highlightStyles : null),
);
export default AppButton;
export { PrimaryButton };
Existing CSS
A Nicer API
Media Queries
Animations
Theming
Server Side Rendering
Testing
Â
jest-glamor-react
Â
📸
![](https://s3.amazonaws.com/media-p.slid.es/uploads/55780/images/3998029/screenshot.png)
Performance
Resources
- CSS in JS - Vjeux - "The original"
- glamor - Sunil Pai - css in your javascript
- glamorous - PayPal - React component Styling Solved 💄Â
- jest-glamor-react - Kent C. Dodds - Jest utilities for Glamor and React
- A Unified Styling Language - Mark Dalgleish - Why you should care about CSS in JS
![](https://s3.amazonaws.com/media-p.slid.es/uploads/55780/images/3174556/books.png)
Thank you!
![](https://s3.amazonaws.com/media-p.slid.es/uploads/55780/images/3174555/wave.png)
Maintainable CSS with React
By Kent C. Dodds
Maintainable CSS with React
TODO
- 7,096