themeit makes it easy to create and use different themes for your react components while using CSS Modules or JS style objects.
It's built with code splitting in mind and allows you to load only the themes you need at a time by utilizing code splitting.
You can also pass in a JS style object for themes which will be processed automatically with aphrodite.
npm i -S react-themeit
(examples assume usage of webpack)
Component Declaration
import { themeit } from 'react-themeit';
const themeOptions = {
base: cb => require(['./base.less'], cb),
themes: {
blue: cb => require(['./themes/blue.less'], cb),
big: cb => require(['./themes/big.less'], cb),
italic: {
label: {
fontStyle: 'italic'
}
}
}
};
const MyComponent = props => (
<div className={props.styles.container}>
<label className={props.styles.label}>react-themeit</label>
</div>
);
export default themeit(themeOptions)(MyComponent);
Component Usage
import MyComponent from './MyComponent';
export default () => (
<MyComponent
theme="blue big italic"
styles={{ label: { textDecoration: 'underline' } }}
addStyleFiles={cb => require(['./additionalStyles.less', './someMoreStyles.css'], cb) }
/>
);
themeit({
themes
(object): an object in which the keys represent theme names and the values can either be a function which should return a localized class map like { container: 'container_38h2f02h' } or a js style object like { container: { backgroundColor: '#000' } }- [
default
] (string): name of a default theme - [
base
] (func): base styles which should always be applied - [
mergeContext (default: false)
] (boolean): whether styles contexts should be merged})
A component which is wrapped with themeit accepts these additional props:
addStyleFiles
(func): a function to pass additional theme classes to the componentstyles
(object): additional js css styles to be passed to the component (will be processed with aphrodite)
The target component will receive the combined style classes in a property called styles
.
It will also receive a prop named themeit
which contains all options you passed to themeit and a function setTheme(name)
which you can invoke to change the current theme of the component.
If you use storybook you can use the babel plugin generate-stories to automatically generate stories for each of your components themes. Simply set the "withThemeit"
option to true, make sure you're using themeit >= 2.4.0 and you're all set!
This will auto generate stories like this:
- theme: theme1
- theme: theme2
- theme: theme3
If you want to style nested components, react-themeit makes it super-simple to handle even more complex scenarios. You have two options for different situations:
When you wrap a component with themeit(opts)(Comp), themeit will put the styles object which the component receives as a property into the child context too.
This means that all child components may access the same styles object (which contains all the classnames) via context.
To do this, you'd define your child component like so:
import React, { Component, PropTypes } from 'react';
const MyComponent = (props, context) => (
<div className={context.styles.container} />
);
MyComponent.contextTypes = {
styles: PropTypes.object
}
If you use themeit to wrap your child components too, there might be two ways you'd like themeit to handle context and passed style information:
a) Merge the eventual context.styles
information from parent themeit components.
import React, { Component, PropTypes } from 'react';
import { themeit } from 'react-themeit';
const themeOptions = {
mergeContext: true
};
const MyComponent = (props, context) => (
<div className={context.styles.container} />
);
MyComponent.contextTypes = {
styles: PropTypes.object
}
export default themeit(themeOptions)(MyComponent)
When is this useful? Imagine you have a calculator widget where the calculater itself is a themeable component and the individual keys in the calculator component are also themeable components.
You might want to define themes / style information for the keys with themeit. If you were to use the calculator widget however, you'd also want to pass new/additional style information for the keys to the calculator widget.
One way to deal with this would be to accept a separate prop like stylesForKeys
or addStyleFilesForKeys
and pass these props down to each individual key. However, this sucks.
Instead, you may just use the addStyleFiles
prop to pass style information for the whole widget + its themeable child components. And utilize the mergeContext
option to merge all style information for the Key components.
b) Ignore parent styles information and create a new styles context from the component
If you'd like to completely ignore parent styles information, you can just use themeit as usual. By default, it won't merge style information for nested themed components.
This is important, since it could lead to unwanted behavior when you use the same class names (e.g. "container") for totally different components. That's why themeit wants you to explicitly activate the behavior of merging styles information for nested themeable components.
Yes. Configuring async css files for HMR require a bit of more code though. To make it easier and shorter, react-themeit exports a hot(...)
function which can be used.
Check this snippet for example:
import { themeit, hot } from 'react-themeit';
const themeOptions = {
base: cb => require(['./base.less'], s => {
hot(s, cb, c => module.hot.accept('./base.less', c))
}),
themes: {
blue: cb => require(['./themes/blue.less'], cb)
}
};
In this case, only the base
.less will be hot reloadable.
The hot
function automatically checks whether module.hot is
defined and only enables HMR if it is.
Initially built for use in the ovos play game designer to allow designers and developers to easily work together.
Contributions are very welcome!