Skip to content

Instantly share code, notes, and snippets.

@rszewczyk
Last active October 1, 2016 20:10
Show Gist options
  • Save rszewczyk/28775ef0b8ce989bce22bcde2dd505bd to your computer and use it in GitHub Desktop.
Save rszewczyk/28775ef0b8ce989bce22bcde2dd505bd to your computer and use it in GitHub Desktop.
Experimenting with css in js
import React, { PropTypes } from 'react'
import CSSPropertyOperations from 'react/lib/CSSPropertyOperations'
import cn from 'classnames'
let classId = 0
const domStyleNode = document.createElement('style')
document.head.appendChild(domStyleNode)
const styleCache = {}
function insertStyle(className, markup, psuedo = '') {
domStyleNode.appendChild(document.createTextNode(`.${className}${psuedo} {
${markup}
}
`))
}
function stylesToCss({ styles, hoverStyles }) {
const markup = CSSPropertyOperations.createMarkupForStyles(styles)
const hoverMarkup = CSSPropertyOperations.createMarkupForStyles(hoverStyles)
if (!(markup || hoverMarkup)) {
return
}
let className = styleCache[markup + hoverMarkup]
if (!className) {
className = 'styled___' + classId++
styleCache[markup + hoverMarkup] = className
if (markup) {
insertStyle(className, markup)
}
if (hoverMarkup) {
insertStyle(className, hoverMarkup, ':hover')
}
}
return className
}
const inheritableStyleProps = {
color: 'initial',
fontSize: 'initial',
lineHeight: 'initial',
fontWeight: 'initial',
letterSpacing: 'initial',
textAlign: 'initial',
fontFamily: 'initial',
cursor: 'initial'
}
const styleProps = {
display: true,
flex: true,
flexGrow: true,
flexShrink: true,
flexDirection: true,
alignItems: true,
justifyContent: true,
margin: true,
marginLeft: true,
marginRight: true,
marginTop: true,
marginBottom: true,
padding: true,
paddingLeft: true,
paddingRight: true,
paddingTop: true,
paddingBottom: true,
height: true,
width: true,
overflow: true,
fontSize: true,
color: true,
backgroundColor: true,
position: true,
cursor: true,
fontFamily: true,
textAlign: true,
lineHeight: true,
visibility: true,
letterSpacing: true
}
function resolveProps(props) {
const passOn = {}
const styles = {}
const hoverStyles = {}
for (let p of Object.keys(props)) {
if (p === 'className' || p === 'as') {
continue
}
const val = props[p]
if (typeof p === 'string') {
let baseName = p
const isHover = baseName.startsWith('__hover__')
if (isHover) {
baseName = baseName.slice(9)
}
if (styleProps.hasOwnProperty(baseName)) {
if (isHover) {
hoverStyles[baseName] = val
} else {
styles[baseName] = val
}
continue
}
}
passOn[p] = val
}
for (let p of Object.keys(inheritableStyleProps)) {
if (!styles.hasOwnProperty(p)) {
styles[p] = inheritableStyleProps[p]
}
}
const className = stylesToCss({ styles, hoverStyles })
if (className) {
passOn.className = cn(props.className, className)
}
return passOn
}
export function hover(styles) {
return Object.keys(styles).reduce(function (acc, key) {
acc['__hover__' + key] = styles[key]
return acc
}, {})
}
export default function createStyled(displayName, baseStyle) {
function styled(props) {
return React.createElement(props.as || 'div', resolveProps(Object.assign({}, baseStyle, props)))
}
styled.displayName = displayName
return styled
}
import React from 'react'
import ReactDOM from 'react-dom'
import createStyled, { hover } from '../src/styled'
const Row = createStyled('Row', {
display: 'flex',
})
const Column = createStyled('Column', {
display: 'flex',
flexDirection: 'column'
})
function App() {
return (
<div>
<Row backgroundColor="gray" padding="10px">
<h2>One</h2><h2>Two</h2>
</Row>
<Column backgroundColor="green" {...hover({ color: 'red' })}>
<h2>One</h2><h2>Two</h2>
</Column>
</div>
)
}
ReactDOM.render(
<App/>,
document.getElementById('root')
)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment