Styling

Applications typically require styling so that the client can apply their branding to the user interface. To assist with this, the StyleService allows an application to define global style variables to be used by components of the application which are replaced at runtime. This means that the colours, font faces, padding and margin globals can all be changed to style an application for a particular client as well as promoting consistency.

Components are styled locally without using global CSS (to avoid such issues as class name clashes or files being loaded out of order).

Inline Styling (React Radium)

Radium has been included largely because it extends Reacts styling handling with additional features such as:

  • handling for modifiers e.g. :hover or :focus
  • merging of multiple styles
  • value fall backs
  • media queries

Please be aware that there is an XSS concern with Radium when using Style and StyleSheet. As these components use dangerouslySetInnerHtml (a method React provides to give freedom to its developers), it enables them to intentionally inject un-escaped code. However, these components are input by the developers, which means that as the developer you are in control of the safety.

Media Queries

Radium can handle inline media queries. Simply define your style object and put the media query as a key:

var style = {
  width: '25%',

  '@media (min-width: 320px)': {
    width: '100%'
  }
};

Then all you need to do is to assign the style object to your component.

Global Properties

Branding

Global properties are particularly useful when we need to re-brand a user interface. The main requirement is to define rules which will streamline all components of an application. Although LESS and SASS can do this, we have introduced the Branding configuration object inside the Style.ix library to embrace inline styling.

The following functions are being exposed in this configuration:

  • load(config): This loads a JSON configuration of global variables. The format is based on the config.json used to build LESS files into CSS:

    {
      "vars" : {
          "@color-bg-main" : "blue",
          "@size-header-logo" : "10px"
      }
    }
    
  • get(varName): This retrieves global variables that match the name passed in the varName and it accepts wild cards (represented by *). For example, where you need to retrieve all ‘h1’ font properties we can GET (@font-*-h1) and this would return font-size-h1, font-family-h1 etc. It is also possible to define variables which refer to other variables. This is a useful feature when for example, you first want to define a brand colour and then use the same colour definition for the border or font colours.

To access these values in inline styles, simply invoke the Branding library with the variable names as shown below:

var Menu = React.createClass({
 ...
 render : function() {
    var styles = {
        myStyle : {
            backgroundColor : [Branding.get('@color-bg-main'), 'blue']
        }
    }
 }
});

Global Variable Naming Convention

Now that the global variables are dictating the standard styling configuration for an application, it is instrumental that it is supported with a naming convention. We recommend the variables follow this convention:

@type-classification-modifier

To clarify, here are some examples:

What you want to describe Use this Not this
Information button colour @colour-button-info @button-colour-info
Error button colour @colour-button-error @error-button-colour
Font Size for H1 @font-size-h1 @h1-font-size
Font Family for H1 @font-family-h1 @h1-font-family

Locale

The UX framework has been developed so it can cater for languages that read:

  • Left to right (LTR)
  • Right to left (RTL)

To enable this, the Locale store was introduced and you can access it from the styling library. Locale follows the Flux architecture and accepts subscribers to listen to its events. The store provides the following functions:

  • getDirection(): Returns the current direction of the document and framework, either ltr or rtl
  • changeDirection(_dir): Accepts either ltr or rtl and changes the direction of the document.body. It also calls emit to notify its subscribers that the direction has changed.
  • localizeStyle(_style): Accepts a style object and converts the keys according to the current document.body direction. This method performs the following conversions:
Generic Key LTR variant RTL variant
paddingStart paddingLeft paddingRight
paddingEnd paddingRight paddingLeft
marginStart marginLeft marginRight
marginEnd marginRight marginLeft
borderStart borderLeft borderRight
borderEnd borderRight borderLeft
float : ‘start’ float : ‘left’ float : ‘right’
float : ‘end’ float : ‘ right’ float : ‘left’
  • addLocaleStylingListener(callback): Subscribe to the Locale store and invoke the supplied callback when the event is triggered. This should be called on component mounting methods such as componentDidMount.
  • removeLocaleStylingListener(callback): Removes the callback function listening to the Locale store. This should be called on component unmounting methods such as componentWillUnmount.

Using Styling Library with react templates

All the features offered in this library need to integrate with existing working procedures of designers and UI developers. This section looks at how to transition from using your preferred UI development environment to the Ixaris UX framework.

React-templates

Markup languages (such as basic HTML) are favoured by designers building user interfaces and it is unlikely they would move to use anything else. Therefore, the UX framework has adopted react-templates so HTML mock ups can be ported to applications in less time. Additionally, React templates provide smart syntax to loop, import and reason in your markup in a very similar way to Angular 1.x (rt-if instead of ng-if, rt-repeat etc.).

It is suggested you save your markup adjacent to your component logic:

--- MyComponent/
    |-- MyComponent.react.js
    |-- MyComponent.rt.html
    |-- MyComponent.rt.css
    |-- MyComponent.i18n.js

To use a template in your component, simply require it to the render function. Declaring .rt.html in your package.json for the react-templatify to capture while building the bundle.js will expose all files with that extension to be parsed into React native syntax. By default, react-templatify will look for .rt.

rt.css

React templates separate markup concerns from JavaScript logic and it was a desirable feature to adopt the same separation for styling. The UX framework has a package, radium-stylify which uses react-styling behind the scenes meaning that you can write your stylesheet in LESS or CSS syntax. It will be translated during compilation to JSON, which can then be accessed from your React components or templates. Just like react-templates, simply declare .rt.css as a styleExtension in your package.json. By default radium-stylify will look for .rts extension.

When you are using the Radium square brackets inside your style definition to dictate fall-backs, make sure you use double quotes on the values e.g.

background-color:["@color-background-default","red"];

Outside of square brackets, branding variables can be used without quotes e.g.

background-color: "@color-background-default";
background-color: @color-background-default;

Radium features like :hover can also be used.

It is also important to note the react-styling is very sensitive to the syntax especially with regards to tabs and spaces. Please ensure that any spacing is performed using TABS, otherwise react-styling might not compile.

compose method

The compose method in the UX Framework Styling Library is used to compose a styling object from a given JSON object, translating all locale specific or Branding variables to their actual values.

Example Component

The following example was taken from the ixaris-uxf-seed. The first block shows the creation of the React component. As you can see, our template was required as a normal dependency and it was passed on to the render function:

var MenuTemplate = require('./Menu.rt.html');

var Menu = React.createClass({
    mixins: [PureRenderMixin, I18nMixin],
    propTypes: {
        setContent: React.PropTypes.func.isRequired,
        showModal: React.PropTypes.func.isRequired,
        kind: React.PropTypes.oneOf(['red','blue']).isRequired,
        size: React.PropTypes.oneOf(['large','small'])
    },
    getInitialState: function() {
        return {};
    },
    componentWillMount: function() {
        this.route = Router.add(this, '').when('', function() {
            this.props.setContent(<Screen1 />);
        }, true).when('screen_2', function() {
            this.props.setContent(<Screen2 />);
        }, true);
    },
    componentDidMount: function() {
        this.route.start();
    },
    componentWillUnmount: function() {
        this.route.remove();
    },
    render: MenuTemplate,

    changeLocation: function(e) {
        e.preventDefault();
        this.route.location(e.target.getAttribute("href"));
    },
    changeLocale: function(e) {
        e.preventDefault();
        I18n.setLocale(e.target.getAttribute("href"));
    }
});
module.exports = Radium(Menu);

The next code block is the React template. It is important to highlight the dependency on the Ixaris UXF Styling library, which is then used to call the compile method on our imported styles. This method will transform any variables or locale specific keys to their corresponding values:

<rt-require dependency="../../common/ix/style.ix" as="Style"/>
<rt-require dependency="./Menu.rt.css" as="Styles"/>
<div rt-scope="Style.compose(Styles.liStyles) as liStyles;
               Style.compose(Styles.divStyles) as divStyle;
               Style.compose(Styles.aStyles) as aStyles">   
    <ul>
        <li key="1" style={[liStyles[this.props.kind],liStyles[this.props.size]]}>
            <a style={aStyles[this.props.kind]}
               href="/screen_1"
               onClick={this.changeLocation}>
               Screen 1
             </a>
        </li>
        <li key="2" style={[liStyles[this.props.kind],liStyles[this.props.size]]}>
            <a style={aStyles[this.props.kind]}
               href="/screen_2"
               onClick={this.changeLocation}>
               Screen 2
             </a>
        </li>
        <li key="3" style={[liStyles[this.props.kind],liStyles[this.props.size]]}>
            <a style={aStyles[this.props.kind]}
               href="en"
               onClick={this.changeLocale}>
               English
             </a>
        </li>
        <li key="4" style={[liStyles[this.props.kind],liStyles[this.props.size]]}>
            <a style={aStyles[this.props.kind]}
               href="es"
               onClick={this.changeLocale}>
               Spanish
             </a>
        </li>
    </ul>
    <div style={divStyle.red}>Red Block</div>
    <div style={divStyle.blue}>Blue Block</div>
    <div style={divStyle.green}>Green Block</div>
</div>

Lastly, we have the stylesheet code block. This will be translated to a JSON object to be used in the components:

.liStyles {
    .blue {
        background-color : @color-background-default;
        border : @normal-border;
        padding-start : 25px;
        :hover {
            background-color : red;
        }
    }
    .red {
        background-color : red;
        border : 1px solid black;
        padding-start : 5px;
    }  
    .large {
        font-size : 20px;
    }
    .small {
        font-size : 10px;
    }
}
.divStyles {
    .red {
        width: 50px;
        float: start;
        background-color: red;
    }
    .blue {
        width: 50px;
        float: start;
        background-color: blue;
    }
    .green {
        width: 50px;
        float: start;
        background-color: green;
    }
}
.aStyles {
    .blue {
        color : @color-text-default;
    }
    .red {
        color : pink;
    }
}