0.0.2 • Published 9 years ago

flourish v0.0.2

Weekly downloads
3
License
ISC
Repository
github
Last release
9 years ago

flourish

Inline JS styles mixin for React components which work like magic. Component state and props are accessible to your styles, and media queries work too. You can even easily pass styles down from parents as props and choose where to apply them.

This is a mixin, not a decorator or a higher order component, because mixins are simple, inherently useful, play nice together, and don't break other functionality like refs.

Usage is simple.

import flourish from 'flourish'

export default React.createClass({
  mixins: [flourish], // attach the mixin
  ...
  render() {
    let { wrapper, title } = this.style // all styles are on this.style
    return (
      <div style={ wrapper }>
        <h1 style={ title }>This is the title</h1>   
      </div>
    )
  },

  statics: {
    style: require('./style') // import your component styles in statics
  }
})

... and in your style.js:

let wrapper: {
  backgroundColor: '#666',
  padding: 30
}

let title: {
  color: 'white',
  fontSize: 30
}

export default { wrapper, title }

That's it. No bullshit. Just write your styles, attach the mixin, then style any number of elements in your component.

If you want your styles to adapt to component state/props/media queries, all that is handled when you write up your styles. Nothing changes in your component.

Here's an example of a complex input taken from a real live application:

// style.js

import v from '../../styleHelpers/all' // custom SASS-like helpers and app-wide variables

let _greyedOut = {
  borderBottomColor: v.grey,
  color: v.grey
}

let Wrapper = { // base styles
  inherit: true, // append this.props.style here (inherit from parent)
  float: 'left',
  position: 'relative',
  zIndex: 1,
  textAlign: 'left',
  fontFamily: v.font,

  'props:width': { // example of string props
    full: { width: '100%' },
    wide: { width: '66%' },
    half: { width: '50%' },
    narrow: { width: '34%' },
    quarter: { width: '25%' }
  },

  'props:spacing': { // example of string props
    compact: { padding: 0 },
    tight: { padding: v.sh(0, v.xs, v.s) },
    loose: { padding: v.sh(0, v.m, v.xl) }
  }
}

let Label = { // base styles
  position: 'relative',
  transition: v.transitionFast,
  display: 'block',
  width: '100%',
  borderRadius: 2,
  backgroundColor: v.white,
  border: v.sh(1, 'solid', v.greyLight),
  borderBottom: v.sh(1, 'solid', v.greyDark),
  padding: v.sh(v.xs, v.s, 0),
  textTransform: 'uppercase',
  letterSpacing: v.spacedWidest,
  fontSize: 9,
  lineHeight: '9px',
  color: v.greyDark,

  'state:error': { // example of boolean state
    borderBottomColor: v.error,
    color: v.error
  },

  'state:focus': { // example of boolean state
    borderBottomColor: v.greenLight,
    color: v.greenLight
  },

  'state:hasValue': _greyedOut, // example of boolean state
  'props:disabled': _greyedOut, // example of boolean props
  'props:readOnly': _greyedOut // example of boolean props
}

let Input = { // base styles
  fontFamily: v.font,
  display: 'block',
  width: '100%',
  border: 'none',
  outline: 'none',
  background: 'transparent',
  fontSize: 15,
  color: v.greyDark,
  textTransform: 'none',
  letterSpacing: v.spaced,
  lineHeight: '27px',
  height: '27px',

  'props:disabled': { color: v.grey } // example of boolean props
}

let Icon = { // base styles
  position: 'absolute',
  right: v.s,
  top: '50%',
  marginTop: -12,
  lineHeight: '24px',
  fontSize: 14,

  'state:error': { color: v.error }, // example of boolean state
  'state:valid': { color: v.greenLight } // example of boolean state
}

let Message = { // base styles
  transition: v.transitionSlowDelay,
  position: 'relative',
  zIndex: -1,
  top: -56,
  borderRadius: '0 0 2px 2px',
  color: v.greyDark,
  fontSize: 12,
  lineHeight: 1,
  letterSpacing: v.spaced,
  padding: v.sh(v.xs, v.s),
  opacity: 0,

  'state:error': { // example of boolean state
    opacity: 1,
    top: 0,
    margin: v.sh(0, v.s),
    backgroundColor: v.error,
    color: v.white
  },

  'state:focus': { // example of boolean state
    opacity: 1,
    top: 0
  }
}

export default { Wrapper, Label, Input, Icon, Message }

As you can see, styling a component based on props/state is as simple as 'props:KEY': { VALUE: { ...styles }} or 'state:KEY': { VALUE: { ...styles }}.

Media queries follow the same pattern: 'media:phone': { ...styles }.

Out of the box, these are the default breakpoints used for media queries:

{
  tiny: '(min-width: 200px)',
  small: '(min-width: 350px)',
  phone: '(min-width: 450px)',
  medium: '(min-width: 500px)',
  large: '(min-width: 650px)',
  tablet: '(min-width: 700px)',
  huge: '(min-width: 800px)',
  laptop: '(min-width: 950px)',
  desktop: '(min-width: 1200px)',
  tv: '(min-width: 1450px)',
  tinyMax: '(max-width: 199px)',
  smallMax: '(max-width: 349px)',
  phoneMax: '(max-width: 449px)',
  mediumMax: '(max-width: 499px)',
  largeMax: '(max-width: 649px)',
  tabletMax: '(max-width: 699px)',
  hugeMax: '(max-width: 799px)',
  laptopMax: '(max-width: 949px)',
  desktopMax: '(max-width: 1199px)',
  tvMax: '(max-width: 1449px)'
}

But you can use your own instead:

import flourish from 'flourish'

let newBreakpoints = { ... }
flourish.setBreakpoints(newBreakpoints)