Skip to content
/ prism Public

Minimal, idiomatic style management for React Native

Notifications You must be signed in to change notification settings

tmpfs/prism

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Prism

Minimal, idiomatic style management for React Native.



Installation

Use your preferred package manager for installation.

npm i --save react-native-prism
yarn add react-native-prism

Synopsis

Prism is a library that returns a Higher Order Component (HOC) exposing access to a style registry containing user-defined colors, fonts and styles.

It provides a simple yet flexible mechanism for mapping properties to styles and finding style declarations in the registry.

For any non-trival RN application the question arises on how to manage styles for your components. The Prism library provides a solution using idiomatic techniques that will leave your JSX clean and serene allowing you to focus on your application's state and logic.

If you want to migrate an existing application you should start with Prism Primitives which provides a drop-in replacement for the RN visual components. See Prism Components for some slightly more advanced components; if you want to see a running application clone and run the RN app in the Prism Components repository.

Getting Started

Defining Styles

To configure your application stylesheets first create a theme with some styles, colors and fonts.

File: theme.js

export default {
  colors: {
    bg: 'steelblue',
    highlight: '#fdfbdf',
    normal: '#9a9a9a'
  },
  fonts: {
    regular: 'WorkSans-Regular',
    medium: 'WorkSans-Medium'
  },
  styles: ({colors, fonts}) => {
    return {
      Label: {
        fontSize: 20,
        fontFamily: fonts.regular,
        color: colors.normal
      },
      bold: {
        fontFamily: fonts.medium
      }
    }
  }
}

Styles

Styles are declared as a function that is passed the style registry, typically you only need access to the colors and fonts.

Colors

Colors are a map from color name to string value. Use of custom color names is optional but it can help make your styles more semantic.

Fonts

Fonts are a map from font identifier to string font family name.

{regular: 'WorkSans-Regular'}

Because Android uses the file name and iOS uses the PostScript name the easiest thing to do is name your fonts using the PostScript name otherwise use platform styles.

Application Configuration

To configure your application create a style registry with your theme and instruct your components to use it:

File: App.js

import React, {Component} from 'react';
import {Prism, StyleRegistry} from 'react-native-prism'
import theme from './theme'
import Label from './Label'

const registry = new StyleRegistry({theme})
Prism.configure(
  registry,
  {
    extendedProperties: true,
    fontProperties: true,
    experimentalPlugins: true,
    textTransform: true,
    colorNames: true
  }
)
export default class Application extends Component {
  render () {
    return (
      <Label
        background='bg'
        color='highlight'
        bold
        align='center'
        textTransform='capitalize'
        padding={15}>
        Prism example application
      </Label>
    )
  }
}

With the extendedProperties option all the built in and extended style properties are available.

Note that you should import all your Prism enabled components before calling configure().

Components

Defining Styled Components

To create a styled component you just need to pass the component class to the Prism function which will return the HOC component.

import {View} from 'react-native'
import {Prism} from 'react-native-prism'
export default Prism(View, 'View')

Here is a working example for the application shown above.

File: Label.js

import React, {Component} from 'react'
import PropTypes from 'prop-types'
import {Text} from 'react-native'
import {Prism} from 'react-native-prism'

class Label extends Component {
  static styleName = 'Label'
  static styleOptions = {
    supportsText: true
  }

  render () {
    // Get the computed style sheet
    const {style} = this.props
    return (
      <Text style={style}>{this.props.children}</Text>
    )
  }
}

export default Prism(Label)

Because the component configured styleName with Label the style rule we created earlier already provides styles for our new component!

Quick Components

Prism.style(Type, style, props)

Sometimes you want to wrap a component using fixed styles without too much fuss, use Prism.style() to wrap a component with basic styles.

This is particularly useful when you just want to draw a shape, a convoluted example to illustrate inheritance:

const Rectangle = Prism.style(
  View,
  {
    flex: 0,
    width: 20,
    height: 20,
    // Set absolute minimum color (defaultProps.style)
    backgroundColor: 'red'
  },
  {
    // Override backgroundColor with extended property
    background: 'green'
  }
)
// Use the green background
<Rectangle />
// Resize and use a blue background
<Rectangle width={50} height={50} background='blue' />

Bundling Styles

Component libraries should supply a style registry which is merged with the user-supplied registry to bundle their default styles. Pass a theme and the bundle flag to a style registry assigned to the component, here is how we wire it up:

import {Prism, StyleRegistry} from 'react-native-prism'
const namespace = 'prism'
const theme = {
  styles: () => {
    return {
      'prism|Label': {
        fontSize: 22,
        color: 'white',
        backgroundColor: 'black'
      }
    }
  }
}
const registry = new StyleRegistry({theme, bundle: true})
class Label extends Component {
  static styleName = 'Label'
  static styleOptions = {
    registry: registry
  }
}
export default Prism(Label, {namespace})

Then a user of the component can just overwrite the declarations they need to change:

'prism|Label': {
  color: 'black',
  backgroundColor: 'white'
}

The default fontSize is respected but now the colors are inverted!

An example of bundling default styles for a component library is in the Layout and corresponding theme for Prism Components.

Default Styles

It is recommended that you bundle styles using a theme and style registry however it is possible to set the bare minimum styles for a component with defaultProps, to do so you use an object named using the corresponding property:

static defaultProps = {
  style: {
    fontSize: 16,
    color: 'black'
  }
}

We can declare default styles for child components too.

static mapPropsToStyle = {
  labelStyle: {}
}
static defaultProps = {
  style: {
    flex: 1
  },
  labelStyle: {
    fontSize: 16,
    color: 'black'
  }
}

Style Names

You must give your component a styleName which becomes the default style to use for the component. If a component is namespaced this does not affect the namespace it affects the class name. An earlier version of prism inferred the style name from the class name but this will not work in release builds as the name of a function is not guaranteed after name mangling.

The styleName static declaration is preferred:

class Label extends Component {
  // Look up a style sheet using selector `Label` by default
  static styleName = 'Label'
}

Alternatively you can pass a string or styleName option to Prism:

// Same as above, use `Label` as selector
export default Prism(Label, 'Label')
// Use `prism|Label` as the selector
export default Prism(Label, {namespace: 'prism', styleName: 'Label'})

Style Name Property

You may pass a styleName property to any component and it will overwrite the static styleName defined by the component just for that instance.

This allows you to create completely different groups of styles for an existing component without defining a wrapper component class. For example, it is common to want two or more completely different button styles.

If you pass styleName to a button component:

<TouchButton styleName='TextButton'>
  Label Text
</TouchButton>

You can now style it using the assigned style name:

export default {
  'prism|TextButton': {
    /* TextButton does not have a background style */
  },
  'prism|TextButton label': {
    color: 'white'
  },
  'prism|TextButton label:disabled': {
    color: '#999999'
  }
}

Notice that the namespace for the component definition is respected.

Mapping Properties To Styles

Components have varied needs for mapping properties to style declarations so the library provides several ways to map properties depending upon the requirement.

Each of the mapping options may be either a function or object, when it is a function it is passed the style registry and should return an object.

Prism is flexible with these declarations, the static style is the most terse and preferred when other styleOptions are not needed:

The following are all equivalent:

static mapPropsToStyle = {
  bold: ({styleSheet}) => styleSheet.bold
}
static styleOptions = {
  mapPropsToStyle: ({styleSheet}) => {
    return {
      bold: () => styleSheet.bold
    }
  }
}
static styleOptions = ({styleSheet}) => {
  return {
    mapPropsToStyle: {
      bold: () => styleSheet.bold
    }
  }
}

mapPropsToStyle

Use mapPropsToStyle when you want the presence of a property to trigger inclusion of styles into the computed style. Each object key maps to a property name and the corresponding function is called when the property is defined on the component.

You have access to all the properties so you can apply styles conditionally based on other properties:

static mapPropsToStyle = {
  space: ({prop, props}) => {
    const {horizontal} = props
    const styleProp = horizontal ? 'marginRight' : 'marginBottom'
    const style = {}
    style[styleProp] = prop
    return style
  }
}

Functions declared in this way have access to the style registry and it's properties (colors etc) the props, current prop and propName. Functions should return a style object or array of objects to be included in the computed styles, to take no action return undefined.

When the passed prop is returned a style rule is created using the property name and value which is useful when the property name matches the style property name:

{color: ({prop}) => prop}

Is shorthand for:

{
  color: ({prop}) => {
    return {color: prop}
  }
}
Pseudo State

If you call css.pseudo() with a string a style sheet is resolved using the familiar a:hover syntax.

For a component called Notice:

static mapPropsToStyle = {
  error: ({css, prop, propName}) => {
    if (prop === true) {
      // Include the style for Notice:error
      return css.pseudo(propName)
    }
  }
}

Would result in including the rule for Notice:error when the error property is true:

{
  'Notice:error': {
    backgroundColor: 'red',
    color: 'white'
  }
}
<Notice error>Error message</Notice>

This can be an easy way to trigger style variations that are resolved from the style sheet based on a property value. For example, if you have a size property that accepts small|medium|large you can do:

static mapPropsToStyle = {
  size: ({css, prop}) => css.pseudo(prop)
}

To resolve a style sheet for the value of size, eg: Notice:small, Notice:medium or Notice:large.

Child Components

For composite components you can route properties to styles that you apply to child components.

At it's simplest level the empty object just declares that your component wants a style object to pass to a child, but you can also route properties to child style objects:

static mapPropsToStyle = {
  headerStyle: {
    color: ({prop}) => prop
  },
  bodyStyle: {}
}

Which will define and create the headerStyle and bodyStyle properties for your component and route the color property in to the headerStyle object. The propTypes for the child style objects are automatically declared as we know ahead of time they should have the same property type as style.

The immediate benefit is that you can now define style rules for the child components which will automatically be resolved as default styles.

'Panel header': {
  color: 'blue',
  padding: 10
},
'Panel body': {
  padding: 20
}

For style declaration lookup the child component name is determined by the property name with any Style suffix removed. If the component is namespaced use the fully qualified name, eg: prism|Panel header.

Then your render should route the properties to child components, for example:

render () {
  const {style, headerStyle, bodyStyle, label} = this.props
  return (
    <View style={style}>
      <Text style={headerStyle}>
        {label}
      </Text>
      <View style={bodyStyle}>
        {this.props.children}
      </View>
    </View>
  )
}

Now use of the color property on the parent is directed to the headerStyle object (and therefore the child component):

<Panel color='red' />

You can combine css.pseudo() with multiple child components to create some interesting behaviour:

static mapPropsToStyle = {
  titleStyle: {
    size: ({css, prop}) => css.pseudo(prop)
  },
  numberStyle: {
    size: ({css, prop}) => css.pseudo(prop)
  }
}

For a component NumberStack:

<NumberStack size='medium' />

Will resolve NumberStack title:small to include in titleStyle and NumberStack number:small for the numberStyle.

mapStyleToProps

This is the inverse mapping that extracts a style property and assigns it as a property on the component.

It is recommended to only use mapStyleToProps when you absolutely must as it requires flattening the computed styles.

static mapStyleToProps = {
  tintColor: ({prop}) => prop
}

Typically this is used to deal with invariants as in the example above which allows your component to respect tintColor in a style rule:

Activity: {
  tintColor: 'purple'
}

And have it extracted to a property on the component:

render () {
  const {style, tintColor} = this.props
  return (
    <View style={style}>
      <ActivityIndicator tintColor={tintColor} />
    </View>
  )
}

See Activity.js for a complete implementation.

Component State

For most use cases when you are triggering state changes from a property mapPropsToStyle and css.pseudo() will do the job just fine (see pseudo state). However there are times when you need finer control over style invalidation as the component state changes.

To enable state invalidation you need to specify the withState configuration option and enable supportsState in your component:

static styleOptions = {
  supportsState: true
}

A typical scenario is when you are managing state based on events from a child component and want the state change to be reflected in the styles.

render () {
  const toggle = () => this.setState({active: !this.state.active})
  return (
    <ChildComponent onPress={toggle} />
  )
}

Once state invalidation is enabled you can do:

static mapPropsToStyle = {
  state: ({state, css}) => {
    if (state.active) {
      return css.pseudo('active')
    }
  }
}

To add the :active pseudo class to the component when state.active is true. If you want the state change to automatically be applied to child components you can use the cascadeState option:

static styleOptions = {
  supportsState: true,
  cascadeState: true
}
static mapPropsToStyle = {
  state: ({state, css}) => {
    if (state.active) {
      return css.pseudo('active')
    }
  }
  // We don't need to declare a state handler
  // for these as `cascadeState` is enabled
  // the `:active` pseudo class will be triggered
  // for these styles automatically
  headerStyle: {},
  footerStyle: {}
}

You should try to match the default style of your component to the default state but you can trigger invalidation of the styles using the initial component state when it mounts by declaring the mountStateStyle option for your component.

static styleOptions = {
  supportsState: true,
  mountStateStyle: true
}

Lifecycle

The component state functionality decorates your component with a simple lifecycle that lets you decide when state changes should trigger style invalidation.

By default as soon as you enable this feature every call to setState() will trigger invalidation of the styles but you can implement shouldStyleUpdate() on your component to control this behaviour.

shouldStyleUpdate
shouldStyleUpdate(state, newState)

Implement this function to control whether to invalidate the styles when the state changes, it should return a boolean.

Options

  • supportsState opt-in to state style invalidation.
  • cascadeState call primary state handler for child component styles.
  • mountStateStyle automatically invalidate using the state of the component when it mounts.

Property Type Validation

It is important to know that the propTypes you declare are assigned to the HOC so properties work as expected and that your static propTypes are augmented with all the style properties.

Built in propTypes are merged first so your propTypes will win if there is a property name collision however the behaviour is undefined so you should take care that your propTypes do not conflict.

If you need it the Prism.propTypes field exposes the system property types.

Namespaces

The Prism function accepts a namespace option which can be used to specify a namespace for your component. This is useful (and recommended) when designing reusable component sets.

const namespace = 'prism'
class Label extends Component {
  static styleName = 'Label'
  /* ... */
}
export default Prism(Label, {namespace})

Now the default component style declaration name uses CSS-style namespaces prism|Label and a consumer needs to declare the style using the fully qualified name:

'prism|Label': {
  color: 'black'
}

Requirements

Sometimes a component or library of components needs certain conditions to be met to be able to work correctly.

You may pass a requirements option to Prism() which is a function passed the registry and config and can be used to validate the component requirements.

Here is an example from the Prism Components:

const requirements = ({config}) => {
  if (config.extendedProperties !== true) {
    return `extendedProperties must be set in config ` +
      `to use the ${namespace} component library`
  }
}

export default Prism(Layout, {requirements})

If the component requirements are not met you can throw an error or return an error or a string. When a string is returned it is wrapped in an error and thrown.

Note that you can use this technique to validate style rules exist, for example:

const requirements = ({registry}) => {
  if (!registry.has('bold')) {
    return `bold style rule is required`
  }
}

Properties

Style Properties

By default plugins are enabled that expose the following properties on all styled components.

The property mapping API and these properties should be sufficient for most applications and indeed it would be considered best practice not to use the extended and experimental properties so that all styling information can be maintained in a single file.

style

Array | Object

Inline styles for the component.

className

String | Array<String>

Assign stylesheets to the component. When a string is given separate stylesheet names should be delimited with whitespace.

The declaration in the style sheet should use a dot prefix as is the CSS convention:

'.highlight': {
  color: 'orange'
}

Extended Style Properties

Extended properties allow for rapidly mocking layouts with a variety of convenient shortcuts for common style properties. Enable the extendedProperties option to use these properties.

Some extended properties require a component opt-in using styleOptions for the style to be applied, for example to receive the color property:

static styleOptions = () => {
  return {
    supportsText: true
  }
}
  • supportsText: Component can receive text style props.
  • supportsDimension: Component can receive width and height.

Note that the supportsText option is also used to test whether a component can receive textTransform on it's children.

background

String

Set the backgroundColor style property.

border

String | Array | Object

Enables a border for the component, this shortcut is great for quickly visualizing component dimensions.

When a string is given borderColor is set and a default borderWidth is used.

When an array is given it takes the form [width, color].

{
  color: 'red',
  top: 0,
  right: 0,
  bottom: 0,
  left: 0
}

Note that not all RN components will set borders as expected when different widths are given for each side, if you experience problems with this syntax ensure the style is applied to a View rather than Image etc.

padding

Number | Object | Array

Sets padding properties, a number sets all edges to be equal.

Arrays are a shorthand for setting vertical and horizontal values and take the form: [vertical, horizontal].

{top: 0, right: 0, bottom: 0, top:0}
[5,10]

margin

Number | Object | Array

Sets margin properties, a number sets all edges to be equal.

Arrays are a shorthand for setting vertical and horizontal values and take the form: [vertical, horizontal].

{top: 0, right: 0, bottom: 0, top:0}
[5,10]

flex

Number | Boolean | Object

Shorthand for flex properties. A number is assigned directly to the flex style property, boolean is coerced to a number (yields zero or one).

Object notation supports the grow, row and wrap fields:

{
  grow: 1,
  row: true,
  wrap: true
}

The row boolean sets flexDirection, wrap sets flexWrap and grow sets the flex property.

row

Boolean

Set the flexDirection style property to row.

wrap

Boolean

Set the flexWrap style property.

justify

Enum<String> (center|start|end|between|around)

Set the justifyContent style property, note that the flex- prefixes are omitted.

position

Object

Makes a component absolutely positioned (relative to the parent as is the RN way) and sets the style properties to the given values.

{top: 0, right: 0, bottom: 0, top:0}

radius

Number | Object

Sets border radius style properties.

{
  top: {left: 0, right: 0},
  bottom: {left: 0, right: 0}
}

width

Number | String

Pass the width property into the computed style, requires the supportsDimension flag.

height

Number | String

Pass the height property into the computed style, requires the supportsDimension flag.

Font Properties

When the fontProperties option is given these properties are configured.

Only Text and TextInput components can accept these style properties so components that wish to receive them in their computed stylesheet must specify the supportsText option.

color

String

Set the color style property.

align

Enum<String> (left|center|right)

Sets the textAlign style property.

bold

Boolean

Looks for a style rule named bold and returns it if available otherwise sets fontWeight to bold.

font

The font property provides a convenient shortcut for all the Text Style Props and can be useful if you have a lot of inline text styling.

Note that to propagate down to children this property requires that experimentalPlugins is enabled so that context is used, see experimental properties.

An example using Prism Components:

<Layout font={{size: 'large', color: 'red'}}>
  <Layout>
    <Label>Red text</Label>
    <Label font={{color: 'green'}}>
      Font style properties combined with those inherited from the grandparent
    </Label>
  </Layout>
</Layout>

The shape of the font object is described in propTypes.js.

Experimental Properties

When the experimentalPlugins option is given these properties are configured.

They are considered experimental due to use of context to propagate values to child components and the behaviour is undefined when components wrapped by Prism() make use of the context.

textTransform

lowercase|uppercase|capitalize

The textTransform property provides a means to apply text transformations to components, it requires the supportsText flag on receiving components and requires that the textTransform and experimentalPlugins options are enabled.

This property is distinct from the font property as it's behaviour is very different, instead of injecting values into a style sheet it modifies a component's children.

In a style sheet:

'Panel header': {
  textTransform: 'capitalize'
}

Inline property usage illustrating inheritance:

<List space={5} textTransform='uppercase'>
  <List space={10}>
    <Paragraph>
      This is some uppercase text <Label textTransform='lowercase'>including some lowercase text in a Label</Label> in a paragraph. <Label textTransform='capitalize'>We can capitalize too</Label>.
    </Paragraph>
  </List>
</List>

Caveat that you cannot undo a transformation on a child (none is not supported), you can only override with a new transformation.

Cascade

This section gives an overview of the cascade or inheritance mechanism using the default configuration.

  1. Compute style from defaultProps
  2. Include styles from defaultStyleRule
  3. Process global plugins (property mapping)
  4. Process property plugins (eg: extended and experimental)
  5. Run processors

Note that property plugins are massaged so that style, labelStyle etc always execute after other configured property plugins.

This means the inheritance for say color of Label can be described in this order.

Default Properties

static defaultProps = {
  style: {
    color: 'red'
  }
}
// Color is red
<Label />

Default Style Rule

'Label': {
  color: 'green'
}
// Color is green now we have a default style rule
<Label />

Component Mapping

static mapPropsToStyle = {
  color: () => {
    return {color: 'blue'}
  }
}
// Our component now says it should be blue
<Label />

Class Name

'.highlight': {
  color: 'steelblue'
}
// Prefer the color in another style rule
<Label className='highlight' />

Inline Property

// Prefer the inline property over the className style
<Label className='highlight' color='orange' />

Inline Style

// Inline style beats everything else
<Label className='highlight' color='orange' style={{color: 'purple'}} />

Configuration

You can pass a configuration object as the second argument to Prism.configure() to modify the library configuration. These are the common configuration options, some more advanced options are shown in plugin configuration.

When no configuration object is given support for the className property is enabled and the global plugins to support mapping properties to styles and resolving default styles.

This is a sensible minimal default configuration which will be sufficient for many applications and creates the least chance of conflict if you want to integrate Prism with an existing application.

To use the extended style properties and enable color name lookup:

Prism.configure(registry, {extendedProperties: true, colorNames: true})

To use textTransform you need to enable experimentalPlugins:

Prism.configure(
  registry,
  {
    experimentalPlugins: true,
    textTransform: true
  }
)

File: configuration.js

export default {
  plugins: [],
  processors: [],
  defaultProps: true,
  defaultStyleRule: true,
  mapPropsToStyle: true,
  mapStyleToProps: true,
  className: true,
  extendedProperties: false,
  fontProperties: false,
  experimentalPlugins: false,
  withState: false,
  inlineStyle: true,
  colorNames: false,
  textTransform: false,
  pure: false,
  debug: __DEV__,
  invariants: [],
  sizes: {
    'xx-small': 12,
    'x-small': 13,
    'small': 14,
    'medium': 16,
    'large': 18,
    'x-large': 22,
    'xx-large': 26
  }
}

Appendix

Platform Styles

You can use platform specific styles for your fonts and style sheets by using the standard notation passed to Platform.select().

If you need a platform-specific font family specify an object in your fonts map:

export default {
  regular: {
    ios: 'WorkSans-Regular',
    android: 'worksans'
  }
}

Platform-specific styles are merged over the top of default rules using a selective merge so you can overwrite declarations and inherit the other styles. To illustrate:

export default {
  Label: {
    fontSize: 20,
    color: 'red'
  },
  android: {
    Label: {
      // Overwrite for android but inherit
      // fontSize from the top-level Label
      color: 'green'
    }
  }
}

Color Names

Styles are much easier to change and we can add semantic meaning to our colors if we can refer to them by name, use the colorNames option to enable this functionality.

With the colorNames option enabled and a theme like:

{
  colors: {
    primary: '#333333',
    muted: '#999999',
  },
  styles: ({colors}) => {
    return {
      Label: {
        fontSize: 16,
        color: colors.primary
      }
    }
  }
}

You can now override the color by name:

<Label color='muted' />

To work in all scenarios (default properties, style sheets and properties) this logic is implemented as a processor and adds overhead therefore it is not enabled by default.

Consider that there may be a better way for your application to manage named colors internally before enabling this option.

Flat Styles

Sometimes you are wrapping a third-party component and want to proxy the style object to the component but it does not accept an array for the style property; it enforces an object only property type.

The computed style property passed to your component is guaranteed to be an array; here however we need it to be an object. To do so you can use the flat option:

static styleOptions = {
  flat: true
}

Now you can just proxy it to the child component knowing it will be an object:

render () {
  const {style} = this.props
  return (
    <NonIdiomaticComponent style={style} />
  )
}

Flat styles are applied to child components too.

Plugins

Plugins allow you to change the default behaviour.

Creating Plugins

To create a plugin you pass a plugin name, handler function and plugin options:

import {Plugin} from 'react-native-prism'
new Plugin(
  'pluginName',
  () => { /* ... */ },
  {/* options */}
)
Property Plugins

If your plugin is for a property you should use the propType option:

import PropTypes from 'prop-types'
const plugins = [
  new Plugin(
    'transform',
    ({prop, propName, styleSheet, colors}) => {
      // Return some transform specific style declarations
    },
    {propType: PropTypes.object}
  )
]

These plugins will only execute when the property is defined on the component.

See extendedPropertyPlugins.js for several examples.

Global Plugins

Global plugins are those without a propType option:

new Plugin(
  'globalPlugin',
  ({props, styleSheet}) => { /* ... */ },
  {requireOptions: true}
)

These plugins provide the ability to modify the computed style sheets without being triggered by the presence of a property.

They can provide options that filter when they are executed. For example requireOptions means only run this plugin for components that have declared a corresponding options object.

For the example above a component needs to explicitly enable the plugin:

static styleOptions: {
  // Trigger execution of the plugin for this component
  globalPlugin: {}
}

Plugin Configuration

Use these configuration options to control plugins:

  • additionalPlugins array of plugin definitions to append to the system plugins.
  • disabledPlugins array of string plugin names to disable.
  • plugins array of plugin definitions to use, overrides the system plugins.
plugins

Use your own plugins array when you want to specify a list of plugins to use before any plugins enabled using the configuration flags, you can disable className and mapPropsToStyle etc to use only the custom plugins you specify.

additionalPlugins

Use the additionalPlugins option to add custom functionality to all your styled components, see plugins for information on defining custom plugins.

Prism.configure(
  registry,
  {
    extendedProperties: true,
    additionalPlugins: [
      new Plugin(
        'customGlobalPlugin',
        ({props, styleSheet}) => {
          // Do something cool
        }
      )
    ]
  }
)
disabledPlugins

You may want to remove plugins you don't need or if you find a property name collision:

Prism.configure(
  registry,
  {
    extendedProperties: true,
    disabledPlugins: ['position', 'wrap']
  }
)

The disabledPlugins option is processed after plugins and additionalPlugins so you may use this to disable your custom plugins. If you give a plugin name that does not exist it is ignored.

Processor

Processors are plugins that operate on a flat representation of the computed styles, they are used to support named colors and invariants. The default configuration does not enable any processors but it is worth knowing that certain configuration options add some processing overhead:

  • colorNames
  • textTransform

Invariants

Invariants are unknown style declarations that would trigger an error when compiling style sheets with StyleSheet.create(). This strict behaviour of RN is very useful but there are occasions where it makes more sense to put the information in a style sheet and invariants allow us to do that. Internally they are extracted as invariant style rules and later resolved when styles are computed.

An example of this is tintColor where we need to assign to the tintColor property of ActivityIndicator but really it's styling information and is better suited to being in a style sheet.

Also the experimental textTransform property is treated as an invariant so it can be declared in style rules and processed using the plugin system yet never appear in compiled or computed style sheets.

Invariants use a processor to ensure computed styles do not contain these properties so they incur the same performance penalty.

Best Practices

You are free to do as you please however here are some guidelines:

  • Avoid setting styles in defaultProps.
  • Use class name style rule for default styles (eg: Label).
  • Prefer className as first option for style overrides.
  • Use extended properties sparingly, useful for rapid devlopment, later migrate to className.
  • Avoid inline style properties.

Pure Mode

If you like the style sheet paradigm that Prism has but want to get closer to the metal we offer a pure mode of operation. When the pure configuration option is given plugins and style computation are disabled. Your components are given direct access to the style registry instead.

In pure mode your components are only passed two properties by the HOC:

  • styleRegistry the application style registry.
  • styleSheet alias for the registry style sheet.

Note that style is no longer passed!

Now you need to pass the styles manually by finding them in the registry:

render () {
  const {styleSheet} = this.props
  <Text style={styleSheet.text}>
    {this.props.children}
  </Text>
}

Typically you would decide to use this mode of operation from the beginning. If you enable pure mode for an existing application using Prism features your views will revert to zero style.

This would apply to any third-party components you may be using too!

Performance

We have made every effort to keep iterations and function calls to a bare minimum and in it's default configuration performance impact should be minimal.

However performance may be impacted if you use any of the following features as they all require a flat representation of the computed styles:

And of course caveat emptor this is not the sort of thing you want to use to style say many thousands of game sprites.

License

MIT


Created by mkdoc on June 18, 2018