highstyle
v0.2.6
Published
A toolkit for fast and flexible manipulation of inline styles
Downloads
6
Readme
HighStyle
A toolkit for fast and flexible manipulation of inline styles using composable transformations.
npm install --save highstyle
Overview
HighStyle is a collection of utility functions for working with 'CSS in JS' style objects as simply as any other JS objects.
Specifically, these utilities smooth over the difficulties of working with CSS shorthand properties, as well as numeric properties provided as strings, which together cause numerous problems when working with style objects.
// given the provided style object:
// style = { margin: 20, fontSize: '16px' }
// returns undefined, not 20
style.marginTop;
// returns NaN, not 24
style.fontSize * 1.5
Inspired by higher-order components, all HighStyle utilities are (parameterised) transforms - pure functions which take one style object and return another. This allows them to be easily composed together, as well as memoized for efficiency.
transform(...args) = (style => style)
TOC
- Manipulating specific CSS properties
- Merging together style objects
- Working with nested styles
mapStyle()
- React higher-order component helper
Manipulating specific CSS properties
highstyle.filter(...properties: string[]): (style => style)
Filter and return specific style properties.
highstyle.filter('width', 'marginTop')({ width: 100, height: 50, margin: 20 });
// returns { width: 100, marginTop: 20 }
highstyle.expandFor(...properties: string[]): (style => style)
Expand relevant shorthands to access the given properties.
highstyle.expandFor('marginTop')({ width: 100, margin: 20 });
// returns { width: 100, marginTop: 20, marginRight: 20, marginBottom: 20, marginLeft: 20 }
highstyle.numeric(...properties: string[]): (style => style)
Ensure the given properties are in numeric form (or 0 if undefined).
highstyle.numeric('width', 'marginTop')({ margin: '20px' });
// returns { width: 0, marginTop: 20, marginRight: '20px', marginBottom: '20px', marginLeft: '20px' }
Merging together style objects
highstyle.merge(...styles: Object[]): (style => style)
Merge in the given list of styles from left-to-right.
highstyle.merge({ margin: 20 }, { width: 100, marginRight: 30 })({ marginTop: 10 });
// returns { width: 100, marginTop: 20, marginRight: 30, marginBottom: 20, marginLeft: 20 }
highstyle.defaults(...styles: Object[]): (style => style)
Use the given defaults for undefined styles, merged in left-to-right.
highstyle.defaults({ width: 100, margin: 10 })({ marginTop: 20 });
// returns { width: 100, marginTop: 20, marginRight: 10, marginBottom: 10, marginLeft: 10 }
Working with nested styles
Roughly equivalent to CSS pseudo-classes, HighStyle can work with nested styles.
const style = {
fontSize: 16,
focus: {
fontWeight: 'bold',
},
hover: {
color: 'red',
focus: {
fontWeight: '900',
},
},
}
highstyle.mergeKeys(...keys: string[] | [{ [key]: boolean }]): (style => style)
Merge in the modifiers for the given keys, with deeper nested modifiers taking higher priority.
If the keys are provided as an object, those with truthy values will be used.
highstyle.mergeKeys('focus', 'hover')(style);
// returns { fontSize: 16, fontWeight: '900', color: 'yellow' }
highstyle.mergeKeys({ focus: true, hover: false })(style);
// returns { fontSize: 16, fontWeight: 'bold', hover: { color: 'red', fontWeight: '900' } }
highstyle.filterKeys(...keys: string[]): (style => style)
Filter and return only the nested styles for the given keys.
highstyle.filterKeys('hover')(style);
// returns { fontSize: 16, hover: { color: 'red' } }
mapStyle()
- React higher-order component helper
HighStyle also comes with a higher-order component mapStyle
to simplify using the above utilities to build complex React components which manipulate their provided inline styles.
This HOC allows for using the standard React style prop to hold individual or multiple styles, which are transformed according to the provided transforms.
// single
props.style = {
fontSize: 16,
margin: '10px 20px',
...
}
// multi
props.style = {
text: {
fontSize: 16,
},
panel: {
background: '#ddd',
},
...
}
The HOC accepts a single parameter mapPropsToTransforms
, which has one of three forms depending on the shape of the style prop. For the second and third forms, the new styles are combined with the existing ones, overwriting only when the keys are the same.
// basic (maps single => single)
mapStyle(
mapPropsToTransforms: (props) => (style => style)[]
): HigherOrderComponent
// split (maps single => multi)
mapStyle(
mapPropsToTransforms: (props) => { [newKey]: (style => style)[] }
): HigherOrderComponent
// multi (maps multi => multi)
mapStyle(
mapPropsToTransforms: (props) => { [baseKey]: { [newKey]: (style => style)[] } }
): HigherOrderComponent
Example 1
Here we use the basic mapStyle api to create a component ShortText
which forces its contents to be red whenever they exceed 20 characters, along with a default fontFamily of Verdana.
import highstyle, { mapStyle } from 'highstyle';
const ShortText = mapStyle(
({ children }) => [
highstyle.defaults({ fontFamily: 'Verdana' }),
highstyle.merge({ color: children.length > 20 ? 'red' }),
],
)('p');
// This will be blue
<ShortText style={{ color: 'blue' }} >Hello World!</ShortText>
// This will be red
<ShortText style={{ color: 'blue' }} >Hello World! Hello World!</ShortText>
Example 2
Here we combine HighStyle with Recompose to create a HOC addHover
which applies the nested hover style when the wrapped component is hovered.
import highstyle, { mapStyle } from 'highstyle';
import { compose, withState } from 'recompose';
const addHover = compose(
withState('hovered', 'setHovered', false),
withHandlers({
onMouseMove: ({ setHovered }) => () => setHovered(true),
onMouseLeave: ({ setHovered }) => () => setHovered(false),
}),
mapStyle(({ hovered }) => [
highstyle.mergeKeys('hover'),
]),
);
const Link = addHover('a');
// This will be red when hovered over
<Link href="#" style={{ hover: { color: 'red' } }}>Click me!</Link>