frontendphil / substyle

Build reusable React components that can be styled with css, css modules, inline styles & many css-in-js libs

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

substyle

There are a lot of competing styling approaches for React applications, from good old css and css modules to inline styles and css-in-js solutions. How can authors of component libraries make sure that component styles can be seamlessly integrated and customized in any application?

substyle is a simple utility for building universally stylable React components. By using substyle your components will support styling through:

Installation

npm install --save substyle

Example

Let's create a simple Popover component using substyle:

import substyle from 'substyle'

const Popover = ({ style, children }) => (
  <div {...style}>
    <button {...style('close')}>x</button>
    { children }
  </div>
)

export default substyle(Popover)

That's all there is for making the styles of the container div and the button element customizable by users of the Popover component.

For using css, assign className
// JSX                                        // Rendered HTML

<Popover className="popover">                 // <div class="popover">
  <span>Hello world!</span>                   //   <button class="popover__close">x</button>
</Popover>                                    //   <span>Hello world!</span>
                                              // </div>
For using inline styles, assign style
// JSX                                        // Rendered HTML

<Popover style={{                             // <div style="background: white;">
  background: 'white',                        //   <button style="right: 0;">x</button>
  close: { right: 0 },                        //   <span>Hello world!</span>
}}>                                           // </div>
  <span>Hello world!</span>
</Popover>

How to use it

Select style for an element

For getting the styling props to pass to an element returned by your component's render functions, call the style function with a key that identifies this element.

style('footer')

In some cases, it is also useful to select multiple styles for the same element. This allows to separate some more specific style definitions from base styles shared with other elments, so that the user would have to provide custom definitions for these base styles only once.

style(['item', 'item-last'])

Pass selected style to simple elements

The return value of any style() call carries different properties depending on the styling approach the user of your component chooses to use. For example, the result could have any form of { className: 'myComponent__footer' }, { style: { borderTop: '1px solid silver' } }, { 'data-css-rdsogp': true }, etc. Use JSX spread attributes to pass these props to the element:

<div {...style('footer')} />

Pass selected style to elements of other substyle-enhanced components

If your component A renders another component B, itself consisting of multiple element that need to be stylable from the outside, you have to enhance B with the substyle higher-order component, too. Select styles for the <B /> element by calling the style prop with the key you choose for this element. Instead of spreading the result of this call to the JSX attributes, you pass it down to <B /> as the style prop. This ensures that also all nested inline style definitions for elements inside of <B /> are passed down.

const A = (props) => (
  <div {...props.style}>
    <B style={props.style('b')} />
    <div {...style('footer')} />
  </div>
)

Define default styling

import { defaultStyle } from 'substyle'

const Popover = ({ style, children }) => (
  <div {...style}>
    <button {...style('close')}>x</button>
    { children }
  </div>
)

const styled = defaultStyle({
  position: 'absolute',

  close: {
    position: 'absolute',
    top: 0,
    right: 0
  }
})
export default style(Popover)

Define style modifiers

In many cases you can distinguish between different variants of how a component looks like based on props. Each of these variants might have slightly different default styles and users must be able to customize styling specifically for each variant. This means each variant needs to be represented by a specific class name and inline styles For each of these variants the default styles might be

Qu the values of some props influence the appearance of the component as they

const styled = defaultStyle({
  position: 'absolute',

  '&align-top': {
    top: 0,
  },

  '&align-bottom': {
    bottom: 0,
  }
}, ({ small, align = 'top'}) => ({
  '&small': small,
  [`&align-${align}`]: true,
}))

If variants of the component are defined for different values of internal component states instead of props, you can select the modifiers also inside the component's render function. To do this, call with style prop with the modifier keys first to derive a new instance of the style prop that you can then call with element keys and spread as JSX attributes.

render() {
  const modifiedStyle = this.props.style({
    '&active': this.state.active
  })
  return (
    <div {...modifiedStyle}>
      <div {...modifiedStyle('header')} />
      ...
    </div>
  )
}

An alternative, potentially simpler, approach is lifting the component state further up, e.g., using recompose's withState, so that the state values are available as props for you to define the props to modifier keys mapping for defaultStyle.

Organize default styles

  • essential styles (required so that the component is functional): co-located with component using defaultStyle
  • some beautiful example styles/different themes: as extra JavaScript file, css file, or both

API

The default export of the substyle module is a higher-order component for enhancing a React component class by injecting the special ´style´ prop.

substyle(Component) (default export)

Returns an enhanced version of Component which supports style, className, and classNames props and maps them to a single special style prop.

Arguments

  • Component (React component)

There is an additional higher-order component creator function that allows to attach default style definitions for the wrapped component:

defaultStyle([defaultStyles], [mapPropsToModifiers])

Returns a version of the substylehigher-order component which is preconfigured to mergedefaultStyles` with user specified style definitions.

Arguments

  • defaultStyle (Object | Function) The default style definitions for the component. Also accepts a function mapping component props to an object of default styles.

  • mapPropsToModifiers(): modifiers (Function | Object) If specified,

Enhancing a component with the substyle higher-order function injects a function as the style prop. This function has properties assigned to it which are supposed to be passed as props to the root element returned by the component's render function.

style([keys]) (injected prop)

Returns a new style instance with the nested style definitions for the passed key. The return value can be passed as style prop to an element of a substyle-enabled component or spread into the props of a DOM element.

It might seem a bit weird at first to use the spread operator on a function, but a function is also just a JavaScript object which supports setting, getting--and spreading--properties.

The style property on any instance of the style function (style.style) contains only the direct styles

style supports chaining, since the return value of style is another instance of the same function.

Arguments

  • key (string | Array | Object) Specifies the key under which to find the nested style definitions. If the component is used with a className prop, a new class name will be derived for the element by appending the key to the base class name.

About

Build reusable React components that can be styled with css, css modules, inline styles & many css-in-js libs

License:MIT License


Languages

Language:JavaScript 100.0%