Last active
March 22, 2017 18:31
-
-
Save kof/3277ee04d2100bab3e6c6c33656edce7 to your computer and use it in GitHub Desktop.
Styled Primitive Components with JSS
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import injectSheet, {jss} from 'react-jss' | |
export function createStyled(options) { | |
const sheet = jss.createStyleSheet(null, options) | |
return function styled(tag, style) { | |
const {className} = sheet.addRule(Math.random(), style) | |
const StyledComponent = props => createElement(tag, {className, ...props}) | |
return injectSheet(sheet)(StyledComponent) | |
} | |
} | |
export const styled = createStyled() |
Interesting observations about this approach:
- No HOC around the entire user component, which means public API of user component remains untouched.
- When styled components are created at the top level of a module (same like import statements), JSS only creates virtual rules. All rules get rendered when a first primitive is about to render. It is a buffering for free.
- There are 2 options, use one global
styled()
function or create it for every file or user component withcreateStyled()
. If first option is used, all rules for all primitives are virtually created and will render in one style tag once first primitive renders. If using the second option, styles are separated on per-component/file basis and will render also when a first primitive of that component renders.
Good point by @nathanmarks:
For SSR we need to detect when a new request comes in. We can achieve that by wrapping the entire application into a JssProvider and either setting something on a context that tells our component that new request was created or provider creates a new jss instance and we just use it within the styled() fn.
Work example:
import React, { PropTypes } from 'react'
import cn from 'classnames'
const domElements = ['a', 'button', 'div', 'span', 'h1'] // etc
export default function createStyledJss(injectSheet) {
const styledJss = (tag, name, styles) => {
const themedStyles = { [name]: styles() }
const sheetInjector = injectSheet(themedStyles)
const component = ({ sheet, classes, className, ...props }) => (
React.createElement(tag, { ...props, className: cn(classes[name], className) })
)
const styledComponent = sheetInjector(component)
styledComponent.displayName = name
return styledComponent
}
domElements.forEach(tag => {
styledJss[tag] = styledJss.bind(null, tag)
})
return styledJss
}
Usage:
// app/styled.js
import { create as createJss } from 'jss'
import { create as createInjectSheet } from 'react-jss'
import presetDefault from 'jss-preset-default'
import createStyledJss from 'jss-styled'
export const jss = createJss()
jss.setup(presetDefault())
export const injectSheet = createInjectSheet(jss)
export default createStyledJss(injectSheet)
// app/components/button.js
import React from 'react'
import styled from '../styled'
const Button = styled.button('Button', () => ({
backgroundColor: 'black',
padding: 20,
color: 'white',
}))
export default Button
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
The idea (pseudo code) is to create styled primitive components, similar to https://github.com/styled-components/styled-components but using jss.