Jynx logoJynx

createStyleFunction

Create custom style functions

import { createStyleFunction } from 'jynx'

The createStyleFunction utility provides the ability to create custom style functions; useful if you need regular access to properties that aren't accessible through any of the current style functions.

Usage

The utility itself is a simple function that accepts a single argument in the form of a config object and returns a subsequent function that's then used to generate style objects.

To use it, include it within the style argument of your component and pass it a config object.

export const Component = styled.div<Props>`
  ${createStyleFunction<Props>(config)}
`

Config

The config object provides the parser with specific information regarding the CSS properties being parsed, their correlating theme scales and if/how the values are transformed before being added to the style object.

interface StyleFunctionConfig {
  [key: string]: {
    property: keyof CSSProperties
    scale?: keyof DefaultTheme
    transformer?: TransformFunction<any>
  }
}

It's made up of a series of 'CSS property keys' each defining:

  • property: the name of the CSS property used in the style object
  • scale: an optional string reference to the key within the theme object of the property's corresponding scale
  • transformer: an optional function used to transform the values before they're added to the style object

The example below demonstrates how a style function can be made for outline styles:

/* Create a config object, defining CSS properties plus any theme */
/* scales and transformer functions */
const config: StyleFunctionConfig = {
  outline: {
    property: 'outline',
  },
  outlineColor: {
    property: 'outlineColor',
    scale: 'colors'
  },
  outlineWidth: {
    property: 'outlineWidth',
  },
  outlineStyle: {
    property: 'outlineStyle'
  },
  outlineOffset: {
    property: 'outlineOffset',
    scale: 'space'
  }
}

/* When using typescript, a props type should be defined to add typings*/ 
/* to the return function*/
/* See the types section for more details */
interface OutlineProps {
  outline?: StyleProp<CSS.Property.Outline>
  outlineColor?: StyleProp<CSS.Property.OutlineColor | ThemeValue<'colors'>>
  outlineWidth?: StyleProp<CSS.Property.OutlineWidth>
  outlineStyle?: StyleProp<CSS.Property.Outline>
  outlineOffset?: StyleProp<CSS.Property.OutlineOffset | ThemeValue<'space'>>
}


/* Then create an `outline` variable that calls `createStyleFunction` */
/* and passes it the config object */
const outline = createStyleFunction<OutlineProps>(config)


/* Include `outline` within the style argument of any styled component... */
export const ButtonContainer = styled.button<OutlineProps>`
  ${outline}
`


/* ...and style from within your tsx/jsx, the same as with any */
/* other style function */
const Button: React.FC = ({ children }) => {
  return (
    <ButtonContainer
      outlineColor='primary[2]'
      outlineStyle='solid'
      outlineWidth={[1, 1.25, null, 2]}
    >
      {children}
    </ButtonContainer>
  )
}

Transformers

Transformer functions can be used to transform the values passed to the style function before they're inserted into the style object. They are allocated within the config object and applied individually to each value provided to their corresponding CSS prop.

Consider the following scenario:

We want to build a length style function that styles only width and height CSS properties. It will have access to the themes space scale and will allow decimals between 0 and 1 to act as percentage values.

To create this kind of functionality, we're going to need to first create a transformer function to handle the conversion of the decimals:

import { get } from 'lodash'

const lengthTransformer: TransformFunction<CSS.Property.Width> = (value, scale) => {
  /* Create a boolean variable to determine whether `value` is a */
  /* decimal between 0 and 1 */
  const isPercentageDecimal = typeof value === 'number' && value < 1 && value > 0

  /* Create a `fallback` variable that resolves to either a decimal or */
  /* the original value based on `isPercentageDecimal` */
  const fallback = isPercentageDecimal ? `${value * 100}%` : value

  /* Use `lodash.get` to check if the original value returns anything */
  /* from the provided scale and if not, return our fallback value */
  return get(scale, value, fallback)
}

We can then set up the rest of the style function as normal, making sure when we define the config object to pass the our lengthTransformer to the relevant props.

/* Create a config object */
const config: StyleFunctionConfig = {
  width: {
    property: 'width',
    scale: 'space',
    /* Define the transformers for the relevant props */
    transformer: lengthTransformer
  }
  height: {
    property: 'height',
    scale: 'space',
    transformer: lengthTransformer
  }
}


/* Define types for the props passed to `length` */
interface LengthProps {
  width?: StyleProp<CSS.Property.Width | ThemeValue<'space'>>
  height?: StyleProp<CSS.Property.Height | ThemeValue<'space'>>
}


/* Create a `length` variable that calls `createStyleFunction` */
/* and passes the config object */
const length = createStyleFunction<LengthProps>(config)


/* Include `length` within the style argument of any styled component */
export const Container = styled.div<LengthProps>`
  ${width}
`

/* Then style from within any jsx/tsx */
export const Layout: React.FC = ({children}) => {
  return (
    <Container width={['100vw', 0.75, 0.5 ]}>
      {children}
    </Container>
  )
}

Types

The createStyleFunction utility is defined as a function that takes a single argument of a config object and returns a style function.

A generic parameter T is used to define typings for the props that can be passed to the returned style function.

const createStyleFunction: <T>(config: StyleFunctionConfig): StyleFunction<T>