flourish
Inline JS styles mixin for React components which work like magic. Component state and props are accessible to your styles, and media queries work too. You can even easily pass styles down from parents as props and choose where to apply them.
This is a mixin, not a decorator or a higher order component, because mixins are simple, inherently useful, play nice together, and don't break other functionality like refs.
Usage is simple.
import flourish from 'flourish'
export default React.createClass({
mixins: [flourish], // attach the mixin
...
render() {
let { wrapper, title } = this.style // all styles are on this.style
return (
<div style={ wrapper }>
<h1 style={ title }>This is the title</h1>
</div>
)
},
statics: {
style: require('./style') // import your component styles in statics
}
})
... and in your style.js:
let wrapper: {
backgroundColor: '#666',
padding: 30
}
let title: {
color: 'white',
fontSize: 30
}
export default { wrapper, title }
That's it. No bullshit. Just write your styles, attach the mixin, then style any number of elements in your component.
If you want your styles to adapt to component state/props/media queries, all that is handled when you write up your styles. Nothing changes in your component.
Here's an example of a complex input taken from a real live application:
// style.js
import v from '../../styleHelpers/all' // custom SASS-like helpers and app-wide variables
let _greyedOut = {
borderBottomColor: v.grey,
color: v.grey
}
let Wrapper = { // base styles
inherit: true, // append this.props.style here (inherit from parent)
float: 'left',
position: 'relative',
zIndex: 1,
textAlign: 'left',
fontFamily: v.font,
'props:width': { // example of string props
full: { width: '100%' },
wide: { width: '66%' },
half: { width: '50%' },
narrow: { width: '34%' },
quarter: { width: '25%' }
},
'props:spacing': { // example of string props
compact: { padding: 0 },
tight: { padding: v.sh(0, v.xs, v.s) },
loose: { padding: v.sh(0, v.m, v.xl) }
}
}
let Label = { // base styles
position: 'relative',
transition: v.transitionFast,
display: 'block',
width: '100%',
borderRadius: 2,
backgroundColor: v.white,
border: v.sh(1, 'solid', v.greyLight),
borderBottom: v.sh(1, 'solid', v.greyDark),
padding: v.sh(v.xs, v.s, 0),
textTransform: 'uppercase',
letterSpacing: v.spacedWidest,
fontSize: 9,
lineHeight: '9px',
color: v.greyDark,
'state:error': { // example of boolean state
borderBottomColor: v.error,
color: v.error
},
'state:focus': { // example of boolean state
borderBottomColor: v.greenLight,
color: v.greenLight
},
'state:hasValue': _greyedOut, // example of boolean state
'props:disabled': _greyedOut, // example of boolean props
'props:readOnly': _greyedOut // example of boolean props
}
let Input = { // base styles
fontFamily: v.font,
display: 'block',
width: '100%',
border: 'none',
outline: 'none',
background: 'transparent',
fontSize: 15,
color: v.greyDark,
textTransform: 'none',
letterSpacing: v.spaced,
lineHeight: '27px',
height: '27px',
'props:disabled': { color: v.grey } // example of boolean props
}
let Icon = { // base styles
position: 'absolute',
right: v.s,
top: '50%',
marginTop: -12,
lineHeight: '24px',
fontSize: 14,
'state:error': { color: v.error }, // example of boolean state
'state:valid': { color: v.greenLight } // example of boolean state
}
let Message = { // base styles
transition: v.transitionSlowDelay,
position: 'relative',
zIndex: -1,
top: -56,
borderRadius: '0 0 2px 2px',
color: v.greyDark,
fontSize: 12,
lineHeight: 1,
letterSpacing: v.spaced,
padding: v.sh(v.xs, v.s),
opacity: 0,
'state:error': { // example of boolean state
opacity: 1,
top: 0,
margin: v.sh(0, v.s),
backgroundColor: v.error,
color: v.white
},
'state:focus': { // example of boolean state
opacity: 1,
top: 0
}
}
export default { Wrapper, Label, Input, Icon, Message }
As you can see, styling a component based on props/state is as simple as 'props:KEY': { VALUE: { ...styles }}
or 'state:KEY': { VALUE: { ...styles }}
.
Media queries follow the same pattern: 'media:phone': { ...styles }
.
Out of the box, these are the default breakpoints used for media queries:
{
tiny: '(min-width: 200px)',
small: '(min-width: 350px)',
phone: '(min-width: 450px)',
medium: '(min-width: 500px)',
large: '(min-width: 650px)',
tablet: '(min-width: 700px)',
huge: '(min-width: 800px)',
laptop: '(min-width: 950px)',
desktop: '(min-width: 1200px)',
tv: '(min-width: 1450px)',
tinyMax: '(max-width: 199px)',
smallMax: '(max-width: 349px)',
phoneMax: '(max-width: 449px)',
mediumMax: '(max-width: 499px)',
largeMax: '(max-width: 649px)',
tabletMax: '(max-width: 699px)',
hugeMax: '(max-width: 799px)',
laptopMax: '(max-width: 949px)',
desktopMax: '(max-width: 1199px)',
tvMax: '(max-width: 1449px)'
}
But you can use your own instead:
import flourish from 'flourish'
let newBreakpoints = { ... }
flourish.setBreakpoints(newBreakpoints)