Package Exports
- @dr.pogodin/react-themes
This package does not declare an exports field, so the exports above have been automatically detected and optimized by JSPM instead. If any package subpath is missing, it is recommended to post an issue to the original package (@dr.pogodin/react-themes) to support the "exports" field. If that is not possible, create a JSPM override to customize the exports field for this package.
Readme
React Themes
UI style themes for React components with CSS modules and theme composition.
Allows to easily reuse the same core component inside different applications and contexts, modifying its look via default, context, and ad hoc themes. Powered by CSS modules and core HTML/CSS mechanics: no unstable dependencies, no restriction on usage of your other tools of choice.

Content
- Getting Started
- API Reference
- Theme Anatomy
COMPOSE– composition modesPRIORITY– theme priority modesthemed(componentName, [defaultTheme], [options])⇒WrapperFunction(default export)themed(componentName, [themeSchema], [defaultTheme], [options])⇒WrapperFunction(default export)<ThemeProvider themes={...}>{children}</ThemeProvider>COMPATIBILITY_MODES– emulate behavior of older librariessetCompatibilityMode(mode)
- Migration from other libraries
Getting Started
npm install --save @dr.pogodin/react-themesThis library relies on CSS modules and SCSS. To install and configure them, follow instructions in babel-plugin-react-css-modules, and node-sass.
Here is the minimal example from the illustration above:
default.scss/* Default component theme. The outer class selectors: *, .ad.hoc, .context, * as well as prefixing of the next-level selectors with & are necessary for * deep theme merging, as explained below. Beside that, this is a regular SCSS * stylesheet. */ *, .ad.hoc, .context { &.component { background: #4169e1; border-radius: 20px; color: #f5f5f5; height: 40px; padding: 0 20px; } }white.scss/* White button theme. Thanks to deep theme merging, we only need to specify * the styles that are different from the base default theme. */ *, .ad.hoc, .context { &.component { background: #f5f5f5; color: #4169e1; } }dark-blue.scss/* Dark blue theme. */ *, .ad.hoc, .context { &.component { background: #273f87; border-radius: 0; color: #f5f5f5; } }ThemedComponent.jsx/* Themed component, wrapped into the default theme. */ import React from 'react'; import themed from '@dr.pogodin/react-themes'; import defaultTheme from './default.scss'; function Component({ children, theme }) { return <div className={theme.component}>{children}</div>; } export themed('ThemedComponent', defaultTheme)(Component);
Demo.jsx/* Showcases component looks from the bottom row of the illustration. */ import React from 'react'; import { ThemeProvider } from '@dr.pogodin/react-themes'; import ThemedComponent from './ThemedComponent.jsx'; /* We intentionally import all themes here (default.scss is not really needed * at this point), and reverse their order. When SCSS is compiled and bundled * by Wepback or another bundler of your choice, the content of these stylesheets * will be present in this very order in the resulting bundle. This would break * deep theme merging in many existing libraries of the same purpose, but it * is handled correctly in our case. */ import adhocTheme from './dark-blue.scss'; import contextTheme from './white.scss'; import defaultTheme from './default.scss'; export default function Demo() { return ( <div> <ThemedComponent>Blue Component<ThemedComponent> <ThemeProvider themes={{ ThemedComponent: contextTheme }}> <ThemedComponent>White Component</ThemedComponent> <ThemedComponent theme={adhocTheme}> Dark Blue Component </ThemedComponent> </ThemeProvider> </div> ); }
API Reference
-
Each theme is a regular SCSS style sheet with rules to apply to elements of your themed component. The only rule is: the top level selectors in theme stylesheet should be wrapped by a few extra selectors:
// Style theme inside SCSS code *, .ad.hoc, .context { &.yourClass1 { ... } &.yourClass2 { ... } }When such stylesheet is imported into JSX, your React CSS Modules setup transforms it into:
// Style theme imported into JSX { ad: '<Transformed name of `ad` class>', hoc: '<Transformed name of `hoc` class>', context: '<Transformed name of `context` class>', yourClass1: '<Transformed name of `yourClass1`>', yourClass2: '<Transformed name of `yourClass2`>', }
To ensure the correct rule priority, without depending on CSS rules ordering in the compiled CSS bundle, this library will add
ad,hoc, andcontextclasses to elements of themed components if necessary to ensure the priority via CSS specifity rules. The SCSS wrapper suggested above ensures that, for example your rules foryourClass1are applied with three alternative selectors of different specifities (for clarity, the classes below are named by their original names, rather the transformed out):.yourClass1- base specifity;.context.yourClass1- higher specifity;.ad.hoc.yourClass1- the highest specifity.
themed(...)decorator supports options to override the exact key names for auxiliaryad.hocandcontextselectors. -
There are three theme sources for your themed components: default theme, set upon the component registration, context theme, coming from the hierarchy of
<ThemeProvider>components, and the ad hoc theme specified for each instance of the themed component. Any of these themes may be omit for a specific component instance.If several themes are applied to the same component instance, they are merged, according to their priorities, explained further below, and the composition modes explained here.
import { COMPOSE } from '@dr.pogodin/react-themes'; const { DEEP, // Equals `DEEP` - deep composition mode. SOFT, // Equals `SOFT` - soft composition mode. SWAP, // Equals `SWAP` - swap composition mode. } = COMPOSE;
Two themes with lower (
L) and higher (H) priorities can be merged in the following ways:Deep composition (default) – all style rules from
LandHare applied, ensuring the higher specifity (thus priority) for the rules fromH(i.e. any style rule fromHwill override similar rule fromL).Soft composition – classes from
Lare applied if they are absent inH, all classes fromHare applied, replacing matching classes fromL(matching by their original names).Swap composition –
Htheme completely overridesL, i.e. only classes fromHare applied.
PRIORITY– theme priority modesThere are three theme sources for you themed components: default themes, context, and ad hoc. When multiple themes are applied to a component instance, they can be composed in the following ways
import { PRIORITY } from '@dr.pogodin/react-themes'; const { ADHOC_CONTEXT_DEFAULT, // Equals 'ADHOC_CONTEXT_DEFAULT'. ADHOC_DEFAULT_CONTEXT, // Equals 'ADHOC_DEFAULT_CONTEXT'. } = PRIORITY;
ADHOC_CONTEXT_DEFAULT (default) – ad hoc theme has the highest priority, followed by context, then default theme.
ADHOC_DEFAULT_CONTEXT – ad hoc theme has the highest priority, followed by default, then context theme.
-
Registers themed component with the specified name, and optional default theme. It can be used in two ways:
themed(componentName, [defaultTheme], [options])⇒WrapperFunction
**themed(componentName, [themeSchema], [defaultTheme], [options])⇒WrapperFunction**
which are distinguished by the second argument type: an array means it isthemeSchema, and thus the second use pattern, otherwise it is assumed to be thedefaultTheme. The second way, withthemeSchemais recommended, while the first way provides backward compatibility.themed(..)can be used as a decorator, or in the following manner:import themed from '@dr.pogodin/react-themes'; import defaultTheme from './default.scss'; function Component() { ... } export default themed('ThemedComponent', defaultTheme)(Component);
When rendered, your component will receive the composed theme via its
themeprop. You will just need to pass the values fromthemeinto theclassNameattributes of your component elements, as shown in the Getting Started example.themed(..)arguments:componentName(String) – name of your component, it will be used to specify context themes for your component via<ThemeProvider />.[themeSchema](String[]) – array of valid theme keys, beside the keys corresponding to adhoc and context tags (see below). It is used by the theme verifier, attached to the.themeTypefield of the created themed component, and also by thecastThemefeature of themed component instances.[defaultTheme](Object) – optional default theme to apply to the component instances.[options](Object) – optional additional settings:[options.composeAdhocTheme](String) – optional override of the composition mode between ad hoc theme, and the result of composition of lower priority themes. It must be one ofCOMPOSEvalues, and defaults toCOMPOSE.DEEP. In compatibility modes other values are accepted and mapped to the correctCOMPOSEvalues.[options.composeContextTheme] (String) – optional override of the composition mode between context, and default themes. It must be one ofCOMPOSEvalues, and defaults toCOMPOSE.DEEP. In compatibility modes other values are accepted and mapped to the correctCOMPOSEvalues.[options.themePriority] (String) – optional override of theme priorities. It must be one ofPRIORITYvalues, and defaults toPRIORITY.ADHOC_CONTEXT_DEFAULT.[
options.mapThemeProps] (Function) – by default, the themed component wrapper does not pass to the original wrapped component any properties introduced by this library. It only passes down the properties it does not know, plus the composedtheme, plus it forwards DOMref. In case a different behavior is needed, the property mapper can be specified with this option. It should be the function of the following signature, and if present the result from this function will be passed down the wrapped component as its props.propsMapper(props, theme)⇒Props[options.contextTag](String) – optional override ofcontextspecifity selector. It must be a valid class name.[options.adhocTag](String) – optional override ofad.hocspecifity selector. It must be exactly two valid class names, joined by dot.
Deprecated Options (Compatibility Mode):
[composeTheme](String)[mapThemrProps](Function)
Additional ThemedComponent props
The wrapped themed component accepts the following aditional properties. They allow to override settings of registered themed component for its individual instances. Any other props are forwared to the wrapped base component.
[castTheme](Boolean) – Iftruethemed component will rely onthemeSchemaprovided tothemed(..)to pick up from ad hoc theme and pass down only expected fields.[theme](Object) – ad hoc theme to apply to the component instance.[composeAdhocTheme](String) – allows to override composition mode of ad hoc theme.[composeContextTheme](String) – allows to override composition mode of context theme.[themePriority](String) – allows to override theme priorities.[mapThemeProps](Function) – allows to override the props mapper.
The wrapped component also holds
.themeTypefunction, which allows easy type checking of themes with Reactprop-types. To work correctly it requiresthemeSchemaspecified forthemed(..), without the schema it will assume empty theme is expected. Here is the usage example:import themed from '@dr.pogodin/react-themes'; function Component({ theme }) { return <div className={theme.container} />; } const ThemedComponent = themed('Component', [ 'container', ])(Component); Component.propTypes = { theme: ThemedComponent.themeType.isRequired, }; export default ThemedComponent;
⇑ This will warn you if theme is missing, contains unexpected fields, or misses ad hoc, or context tag keys. In the case of ad hoc styling you may want to not have a dedicated stylesheet for the ad hoc theme, and it will be seen as an issue by this check. In such case the
castThemeoption comes handly. <ThemeProvider themes={...}>{children}</ThemeProvider>Defines style contexts. It accepts a single property
themes(themein compatibility modes).themes(Object) – the mapping of between themed component names (the first parameter passed intothemed(..)decorator upon the component registration), and context themes to apply to them within the context.
In case of nested context, the context theme from the closest context takes the effect on a component. If the context theme for a component is not set in the closest context, but it is set in an outer context, the theme from outer context will be applied.
COMPATIBILITY_MODES– emulate behavior of older librariesThe library can be switched to compatibility modes by
setCompatibilityMode(mode)function.import { COMPATIBILITY_MODES, setCompatibilityMode, } from '@dr.pogodin/react-themes'; setCompatibilityMode(COMPATIBILITY_MODES.REACT_CSS_THEMR);
REACT_CSS_THEMR – the library will accept the same properties, and composition mode values as the
react-css-themrlibrary. It will deduce all other settings, and sets defaults to match behavior of that lib.REACT_CSS_SUPER_THEMR – the library will accept the same properties, and composition mode values as the
react-css-super-themrlibrary. It will deduce all other settings, and sets defaults to match behavior of that lib.
-
Switches the library into the specified compatibility mode.
Migration
There are similar older libraries out there, including
react-css-themr –
the original inspiration for this work, and
react-css-super-themr.
The setCompatibilityMode(mode) feature allows our
project to closely emulate behavior of those older libs (you will still have to
upgrade your code to use the latest React 16). For a complete migration to our
library, you will need to perform the following updates in your code:
Differences from
react-css-themrComposition mode are renamed from
deeply,softly, and false, toDEEP,SOFT, andSWAP.Default theme priority is
ADHOC_CONTEXT_DEFAULTinstead ofADHOC_DEFAULT_CONTEXT.composeThemeoption of themed component decorator is replaced by two separate options:composeAdhocTheme, andcomposeContextTheme.mapThemrPropsoption of themed component decorator is replaced bymapThemeProps.Themes require specifity wrappers (however, any theming that used to work fine without them, should keep on working fine, although correct deep theme merging won't work in complex scenarios).
Differences from
react-css-super-themrComposition mode are renamed from
deeply,softly, and false, toDEEP,SOFT, andSWAP. Allows any composition mode for any theme pair.Default theme priority is
ADHOC_CONTEXT_DEFAULTinstead ofADHOC_DEFAULT_CONTEXT. Also the priority mode names are renamed fromadhoc-context-default, andadhoc-default-context.mapThemrPropsoption of themed component decorator is replaced bymapThemeProps.Themes require specifity wrappers (however, any theming that used to work fine without them, should keep on working fine, although correct deep theme merging won't work in complex scenarios).