Prefixing flexbox styles for React

I really like how React purifies events for you so I was surprised to find that they dont prefix your CSS styles for your as well!

Anyways, I started implementing my own basic prefixer like this:

var prefixes = ["ms", "Moz", "Webkit", "O"];
var properties = [
  'userSelect',
  'transform',
  'transition',
  'transformOrigin',
  'transformStyle',
  'transitionProperty',
  'transitionDuration',
  'transitionTimingFunction',
  'transitionDelay',
  'borderImage',
  'borderImageSlice',
  'boxShadow',
  'backgroundClip',
  'backfaceVisibility',
  'perspective',
  'perspectiveOrigin',
  'animation',
  'animationDuration',
  'animationName',
  'animationDelay',
  'animationDirection',
  'animationIterationCount',
  'animationTimingFunction',
  'animationPlayState',
  'animationFillMode',
  'appearance',
  'flexGrow',
];


function vendorPrefix(property, value) {
  var result = {}
  result[property] = value

  if( properties.indexOf(property) == -1 ){
    return result;
  }

  property = property[0].toUpperCase() + property.slice(1);

  for (var i = 0; i < prefixes.length; i++) {
    result[prefixes[i] + property] = value;
  };

  return result;
}

React.prefix = function(obj) {
  var result = {};

  for(var key in obj){
    var prefixed = vendorPrefix(key, obj[key])
    for(var pre in prefixed){
      result[pre] = prefixed[pre]
    } 
  }
  return result;
};

But then I realized a big problem, React uses an object for styles and to appropriately prefix the flexbox, you need to prefix the values, not the properties. Thus, I cannot include all of the following styles at the same time:

.page-wrap {
  display: -webkit-box;      /* OLD - iOS 6-, Safari 3.1-6 */
  display: -moz-box;         /* OLD - Firefox 19- (buggy but mostly works) */
  display: -ms-flexbox;      /* TWEENER - IE 10 */
  display: -webkit-flex;     /* NEW - Chrome */
  display: flex;             /* NEW, Spec - Opera 12.1, Firefox 20+ */
 }

Any ideas how to get around this?


Well, actually with really small XSS trick you can easily achive what you want. Don't be scared, we won't go to the "dark side" ;)

Just by adding small function you can write your React CSS like this

var divStyle = multipleValues({
  color: 'white',
  padding: '10px 20px',
  // if you want property to have multiple values
  // just write them all in the array
  display: [
    '-webkit-box',
    '-webkit-flex',
    '-ms-flexbox',
    'flex'
  ],
  background: ['red', 'blue'],

  extraStyles: {
    background: [ 'yellow', 'pink' ]
  }
});

All properties will apply in the same order as they represented in array. Cool isn't it? :)

The function it self is quite simple

// function that will do a "magic" XSS-ish trick
function multipleValues(style) {
  var result = {};
  // feel free to replace for..in+hasOwnProperty with for..of
  for (var key in style) { 
    if (style.hasOwnProperty(key)) {
      var value = style[key];
      if (Array.isArray(value)) {
        // by adding semicolon at the begging we applying
        // a trick that ofthen used in XSS attacks
        result[key] = ';' + key + ':' + value.join(';' + key + ':');
      } else if (typeof value === "object" && value !== null) {
        // here we doing recursion
        result[key] = multipleValues(value);
      } else {
        // and here we simply copying everything else
        result[key] = value;
      }
    }
  }
  return result;
}

Working Demo :

https://jsfiddle.net/z5r53tdh/1/

PS: event thou we using XSS technics - we don't introduce any new security issue. Standard XSS protection will also work here.


I prefer to just put my styles in separate files and use Autoprefixer.

It's quite easy to set up with grunt and other task runners, and can target the specific browsers you want to support. It checks against the caniuse database to keep things up to date, and will even remove any unnecessary prefixes from your css.

It can also work with less and sass as a post-compiler.

Hope that helps!


Automatic vendor prefixing has been stated as something the React team does not want to maintain. Which makes sense really, every app has different requirements around which browsers to support. Depending how far back support goes, the problem grows exponentially.

Do think React out of the box should have support for vendor prefixed values, as is, with inline styling alone you can't use flexbox (Hello modern web!?).

Think the object literal syntax has little value that couldn't be had elsewhere and mostly adds friction to styling. Guessing it has something to do with backwards compatibility support and being able to pass CSS objects as props and not wanting to force people onto ES5 / ES6 where template strings (` `) syntax can be used. Would be quite ugly if we were adding strings together with + operators to stick our variables in... but that isn't necessary anymore with template literals! (see on mdn)

Was unhappy with all other packages that attacked this problem so made the simplest one to rule them all. Package called Style It that simply lets you write CSS. Also automagically subtree scopes so you're not in the global name space with your CSS (similar-ish to inline but not as high specificity). Similar to inline styles it's also XSS safe by default (escapes your CSS using the same lib React uses), is isomorphic, and tiny at 1.8kb gzipped. Oh and no build step.

npm install style-it --save

Functional Syntax (JSFIDDLE)

import React from 'react';
import Style from 'style-it';

class Intro extends React.Component {
  render() {
    return Style.it(`
      .flex-container {
        padding: 0;
        margin: 0;
        list-style: none;

        display: -webkit-box;
        display: -moz-box;
        display: -ms-flexbox;
        display: -webkit-flex;
        display: flex;

        -webkit-flex-flow: row wrap;
        justify-content: space-around;
      }

      .flex-item {             
        background: tomato;
        padding: 5px;
        width: 200px;
        height: 150px;
        margin-top: 10px;

        line-height: 150px;
        color: white;
        font-weight: bold;
        font-size: 3em;
        text-align: center;
      }
    `,
      <ul class="flex-container">
        <li class="flex-item">1</li>
        <li class="flex-item">2</li>
        <li class="flex-item">3</li>
        <li class="flex-item">4</li>
        <li class="flex-item">5</li>
        <li class="flex-item">6</li>
      </ul>
    );
  }
}

export default Intro;

JSX Syntax (JSFIDDLE)

import React from 'react';
import Style from 'style-it';

class Intro extends React.Component {
  render() {
    return (
      <Style>
      {`
        .flex-container {
          padding: 0;
          margin: 0;
          list-style: none;

          display: -webkit-box;
          display: -moz-box;
          display: -ms-flexbox;
          display: -webkit-flex;
          display: flex;

          -webkit-flex-flow: row wrap;
          justify-content: space-around;
       }

       .flex-item {
          background: tomato;
          padding: 5px;
          width: 200px;
          height: 150px;
          margin-top: 10px;

          line-height: 150px;
          color: white;
          font-weight: bold;
          font-size: 3em;
          text-align: center;
        }
      `}

      <ul class="flex-container">
        <li class="flex-item">1</li>
        <li class="flex-item">2</li>
        <li class="flex-item">3</li>
        <li class="flex-item">4</li>
        <li class="flex-item">5</li>
        <li class="flex-item">6</li>
      </ul>
    </Style>
  }
}

export default Intro;

HTML / CSS pulled from Chris Coyier's A Complete Guide to Flexbox post.

链接地址: http://www.djcxy.com/p/25336.html

上一篇: Android与Facebook SharePhotoContent共享图像URL

下一篇: 为React添加前缀flexbox样式