The goal of this library is to be a complete solution to using inline styles instead of CSS.
Everything should be done "the React way" when possible. We're not going to invent new language features like inlining CSS in our JSX, we're not going to introduce new lifecycle methods or static properties for configuration, etc. We're going to use patterns like state and props, higher order components, ...context.
Where possible, it should be compatible with both React and React Native, with the necessary features and optimizations for both.
(After this point, I assume you've read the README already, so take a look at it if you haven't yet.)
Imagine this library as the future version of StyleSheet.create() that will go into React Native, and that will be used in React DOM for superior performance (styles will be converted into stylesheets behind the scenes, similar in spirit to what happens on Native). Then we won't need to use HoCs and decorator syntax, but can pass the styles we create to StyleSheet.create() and use them as the style prop, e.g. <Component style={styles.myStyles}>. We should try to minimize what goes into the core styling APIs, keeping them to the bare minimum needed to replace CSS. Convenience utilities in separate packages can make the API easier/simpler to work with for specific use cases (e.g. responsiveness).
The main idea: We have an API to compute dynamic styles from props
What this idea solves:
It should be easy and intuitive to create styles configured by component props
The API for styling a component based on its props should be a logical extension of the normal styling APIs. Maybe a button has a "type" prop, or an activity indicator has a "size" prop. These will most likely need to affect styling, but right now the API is too flexible: you just do anything you want in JS logic, and then pass a style object to a child component. This is very manual, and most apps/authors use different techniques to create and merge, e.g. {...this.props.style, color: this.props.type === 'warning' ? 'yellow' : 'green'}
Styles are all in one place
There shouldn't be any difference defining dynamic styles and static styles; we shouldn't suddenly have to start creating style objects in the render function just because we decide to make our component more flexible.
Dynamic styles are performant automatically
Normally creating dynamic style objects in the render function and passing them to children as props causes unnecessary re-renders. This happens because a new style object is created each render, and children are probably PureComponents that only test shallow equality of props.
Opt-in on a per-component basis
At least for the basics, there shouldn't be any global setup. You shouldn't have to create a context provider. A component should be able to use this API completely standalone.
Use cases
We need to solve nearly all use cases for this to be a serious replacement for CSS, since CSS gives us a lot of power and flexibility.
Here are a few, and potential solutions:
a. Vendor prefixing
This is pretty easy. We just (on web) vendor prefix the returned styles created by @createStyles.
b. Responsiveness
Use the Provider/connect pattern to provide some kind of description of the window/device, as a prop. This should likely be user-configurable, since "responsive" could mean a lot of different things to different apps. However, we should provide a sensible default for web and native. this.props.responsive could be an object or string containing things like isMobile. Or perhaps we have a helper function for the developer to define breakpoints. Implementation: we'll update context when the screen size changes.
This nice thing about using a prop for responsiveness is that you can render totally different components, not just different styles. This allows for much more flexible designs for different platforms.
Add a function to either the props of the component wrapped with @createStyles or to the styles prop called getStyles, which you can call to compute styles based on state. (This is described more in the readme) I think this API could easily be abused to do lots of crazy stuff, but sometimes you do want to compute styles based on arbitrary state. Most often, "state" in this case means component state, e.g. when a menu is open, but you could call this with anything really. It's a useful escape hatch. But that also makes it dangerous.
d. Hover/active state
hover and active can be state or props, and then styles can be computed based on these. Two solutions come to mind.
Use the getStyles mentioned for computing arbitrary app state, and always store hover and active in component state (a lot of boilerplate/manual effort)
import createStyles, { appendStyleSheet } from 'react-styles-provider'
@createStyles((props) => {
// idk, how does Radium/glamor do this sort of thing?
appendStyleSheet(`input::placeholder { color: 'red' }`)
return {
input: { color: 'black' },
}
})
f. Styling :before and :after
These are low priority, since you can't really create pseudo elements via React anyway. We don't need to be super compatible with other libraries that use classnames. If we have an arbitrary CSS escape hatch, that would solve.
The goal of this library is to be a complete solution to using inline styles instead of CSS.
Everything should be done "the React way" when possible. We're not going to invent new language features like inlining CSS in our JSX, we're not going to introduce new lifecycle methods or static properties for configuration, etc. We're going to use patterns like state and props, higher order components, ...context.
Where possible, it should be compatible with both React and React Native, with the necessary features and optimizations for both.
(After this point, I assume you've read the README already, so take a look at it if you haven't yet.)
Imagine this library as the future version of
StyleSheet.create()
that will go into React Native, and that will be used in React DOM for superior performance (styles will be converted into stylesheets behind the scenes, similar in spirit to what happens on Native). Then we won't need to use HoCs and decorator syntax, but can pass the styles we create toStyleSheet.create()
and use them as thestyle
prop, e.g.<Component style={styles.myStyles}>
. We should try to minimize what goes into the core styling APIs, keeping them to the bare minimum needed to replace CSS. Convenience utilities in separate packages can make the API easier/simpler to work with for specific use cases (e.g. responsiveness).The main idea: We have an API to compute dynamic styles from props
What this idea solves:
It should be easy and intuitive to create styles configured by component props
The API for styling a component based on its props should be a logical extension of the normal styling APIs. Maybe a button has a "type" prop, or an activity indicator has a "size" prop. These will most likely need to affect styling, but right now the API is too flexible: you just do anything you want in JS logic, and then pass a style object to a child component. This is very manual, and most apps/authors use different techniques to create and merge, e.g.
{...this.props.style, color: this.props.type === 'warning' ? 'yellow' : 'green'}
Styles are all in one place
There shouldn't be any difference defining dynamic styles and static styles; we shouldn't suddenly have to start creating style objects in the render function just because we decide to make our component more flexible.
Dynamic styles are performant automatically
Normally creating dynamic style objects in the render function and passing them to children as props causes unnecessary re-renders. This happens because a new style object is created each render, and children are probably PureComponents that only test shallow equality of props.
Opt-in on a per-component basis
At least for the basics, there shouldn't be any global setup. You shouldn't have to create a context provider. A component should be able to use this API completely standalone.
Use cases
We need to solve nearly all use cases for this to be a serious replacement for CSS, since CSS gives us a lot of power and flexibility.
Here are a few, and potential solutions:
a. Vendor prefixing
This is pretty easy. We just (on web) vendor prefix the returned styles created by
@createStyles
.b. Responsiveness
Use the
Provider
/connect
pattern to provide some kind of description of the window/device, as a prop. This should likely be user-configurable, since "responsive" could mean a lot of different things to different apps. However, we should provide a sensible default for web and native.this.props.responsive
could be an object or string containing things likeisMobile
. Or perhaps we have a helper function for the developer to define breakpoints. Implementation: we'll update context when the screen size changes.This nice thing about using a prop for responsiveness is that you can render totally different components, not just different styles. This allows for much more flexible designs for different platforms.
return this.props.responsive.mobile ? this.renderForMobile() : this.renderForDesktop()
c. Compute styles based on arbitrary app state
Add a function to either the props of the component wrapped with
@createStyles
or to thestyles
prop calledgetStyles
, which you can call to compute styles based on state. (This is described more in the readme) I think this API could easily be abused to do lots of crazy stuff, but sometimes you do want to compute styles based on arbitrary state. Most often, "state" in this case means component state, e.g. when a menu is open, but you could call this with anything really. It's a useful escape hatch. But that also makes it dangerous.d. Hover/active state
hover
andactive
can be state or props, and then styles can be computed based on these. Two solutions come to mind.Use the
getStyles
mentioned for computing arbitrary app state, and always storehover
andactive
in component state (a lot of boilerplate/manual effort)Make an HoC that adds hover/active to props.
Then this can be used like
e. Styling
::placeholder
for text input placeholder stylingSince AFAIK it's impossible to set this placeholder color via JS, we need to have a way to add CSS to the page for this.
Handle it automagically (how?) by inventing a new style
An escape hatch to add arbitrary CSS.
f. Styling
:before
and:after
These are low priority, since you can't really create pseudo elements via React anyway. We don't need to be super compatible with other libraries that use classnames. If we have an arbitrary CSS escape hatch, that would solve.