Create a gist now

Instantly share code, notes, and snippets.

@kof /StyledButton.js
Last active Mar 22, 2017

What would you like to do?
Styled Primitive Components with JSS
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()
import {styled} from 'react-jss-components'
const RedButton = styled('button', {
color: 'red'
})
<RedButton>Click me</RedButton>
@kof

This comment has been minimized.

Show comment Hide comment
@kof

kof Oct 21, 2016

The idea (pseudo code) is to create styled primitive components, similar to https://github.com/styled-components/styled-components but using jss.

Owner

kof commented Oct 21, 2016

The idea (pseudo code) is to create styled primitive components, similar to https://github.com/styled-components/styled-components but using jss.

@kof

This comment has been minimized.

Show comment Hide comment
@kof

kof Oct 22, 2016

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 with createStyled(). 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.
Owner

kof commented Oct 22, 2016

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 with createStyled(). 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.
@kof

This comment has been minimized.

Show comment Hide comment
@kof

kof Oct 22, 2016

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.

Owner

kof commented Oct 22, 2016

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.

@sergeysova

This comment has been minimized.

Show comment Hide comment
@sergeysova

sergeysova Mar 22, 2017

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

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