Styling

Styling

Styling is a complex topic because with Web Resources, styling occurs through CSS and CSS is not easy to use in complex applications for just normal programs.

Dynamics uses dozens of style sheets per page although there are just a few core ones. Since Web Resources are loaded in iframes, the dynamics styles are not available in your Web Resource--that's the whole point of iframes. This is both good and bad. Good because they could cause namespace conflicts if you tried to use them and bad because you want to style your components similar to Dynamics style.

Over time, the Dynamics style should match that of O365 and O365 already publishes a growing library of react components that have MS styling built-in. You can find them at http://dev.office.com/fabric#/components. The office styles are slightly different but they are good starting points for your own Web Resources. They have a robust set of icon resources.

To style your components, you can take a few routes:

  • Use your F12 browser debugger to identify styles and reproduce them in your stylesheets.

  • Grab the CRM stylesheets and include them in your project. You'll still need a layer on top of them for your components.

  • Use the O365 components and styles. It is recommended that you use office-ui-fabric-react's styling approach (css-in-js) even if you do not use fabric components. The css-in-js solution is independent of the fabric components and works well.

The F12 approach allows you to quickly zoom in on the styles used in a particular org, but you have to watch out as different CRM themes can introduce different styles. Themes adjust basic styles. I do not think its practical to try and build a stylesheet that smartly keys of the theme stylesheet that CRM uses. It's probably best to pick the theme for your org, then develop your stylesheets to reflect it.

I found that I prefer css-in-js and the fabric solution (much like glamor) is easy to use and makes sense. Your mileage may vary. If you have a small amount of css that does not vary much, just use a static file. If you use css-in-js, you can also, cleverly, adjust for changes in dynamics themes. In fact, if you are super eager, you can adjust your colors dynamically based on the dynamics theme--which is really, really nice.

classnames vs css for classname combining

You will need to combine classnames together for styling, regardless of what you do. You can use [classnames](https://github.com/JedWatson/classnames or just use css in fabric's merge-styles. They are very similar. If you use some of the mergeStyles or mergeStyleSets from fabric, they can take a list of classnames as well. For Dynamics work, I typically now use css, mergeStyles and mergeStyleSets.

Including 3rd party styles in typescript/javascript files

One trick you need to have is to include 3rd party css files without having them go through the webpack loader processors. You can do this easily. Here's how to include the 3rd party css file for react-big-calendar.

// load styles but do nothing with them...this is needed for a number of react components that
// use statically generated CSS or build-time generated CSS that you don't want to fool with e.g. SCSS, LESS. 
const _styles = require("!!style-loader!css-loader!react-big-calendar/lib/css/react-big-calendar.css")

Including your CSS files

Similar to the above, you can just load your files, but in this case you probably want to use the webpack loaders to process them, e.g. to use css-next.

const styles = require("./MyComponent.css")
// to use styles: styles.root, styles.someclass, ...

Notice that a require is used. You have to use a special loader in typescript to load the CSS through your loaders and this syntax is easy but still useful.

Use F12 to discover dynamics styles

The shot below shows the chrome F12 focused on a link in a grid. The Style panel to the right shows the style browser that shows how the specific style was determined from all the style sheets.

Widget Libraries

You can use your own widget libraries, e.g. bootstrap or react-widgets, with CRM. However, the styles will most likely clash somewhat. Dynamics CRM does not use consistent styles as it focuses on backward compatibility more than updated styles. There are at least 2-3 style "generations" present in Dynamics.

A new Microsoft provided widget libraries is available call http://github.com/officedev/office-ui-fabic-react. This is a good source of different widgets available and they are consistently themed. At the very least, you can claim that you are using a MS standard theme if someone asks you why the themes in the widgets are different than the standard Dynamics theme.

The fabric widget use a themeing library built into to the base office-ui-fabric library which the react library sits on top of. The themeing engine is provided through a react context to the widgets and the widgets are fairly impervious to potentially conflicting CSS. You can change some of the defaults associated with the themes but it can be difficult. To find defaults, you need to look in the office-ui-fabric library and then look the Fabric react component in the react library which sets up the themeing engine among other housekeeping chores. Your react component tree will look like:

<Outer>
  <Fabric>
     <TopLevelComponent .../>
  </Fabric>
</Outer>

To be more impervious to build configurations and bad CSS, the MS Fabric react libraries loads its own styles and uses css-in-js with something similar to glamor called "merge-styles" which is a sub-project of office-ui-fabric-react.

Webpack Style Loaders

In a previous section I mentioned using css-modules for CSS processing. The webpack config looks like:

    { loader: "style-loader" },
    { loader: "css-loader", options: { modules: true } },
 ]
... 
module: {
  rules: [
    {
       test: /\.css$/,
       use finalStyleLoaders
    },
    ...   
]}

You may want to look at postcss-loaders which provide a variety of "babel" like benefits but to style processing. In general, I use different style authorship models depending on the module I am authoring (yes, I am bad in this regard).

Here's a more advanced style processing configuration that allows jss to be used to author styles as well as the latest CSS tricks such as CSS variables and @apply.

const finalStyleLoaders = [
    { loader: "style-loader" },
    { loader: "css-loader", options: { modules: true, importLoaders: 1 } },
    { loader: "postcss-loader", options: {
        plugins: () => [require("postcss-cssnext")] }}
]

...
module: {
  rules: [
             {
                // Load css, convert to js object, also load via stylesheet.
                test: /\.css(\.js)?$/,
                use: finalStyleLoaders
            },
            {
                // Order is important for this loader, run after babel but before other css loaders.
                test: /\.css\.js?$/,
                use: [ { loader: "css-js-loader"}]
            },
            {
                test: /\.jsx?$/, // picks up .js and .jsx
                exclude: /node_modules/,
                use: ["babel-loader"]
            },
            ...  
]}
...

Here, we are processing styles authored in js using babel since the babel loader is listed last in the rules array. css-js-loader allows you to author style rules in js. If our style file uses .css.js as the extension, then the loader rules to use css-js-loader will be activated but only after it is has been passed through babel for processing.

After css-js-loader, our final style loaders are applied. First, postcss-loader is used to process the raw CSS. postcss-loader uses a parser to parse CSS. The postcss compatible plugins process the CSS AST tree. In our case, we use postcss-cssnext to get next generation CSS features available to our CSS regardless of how it was authored. Since we used a regex \.css(\.js)?$ as our test, these style loaders will be applied to both .css files and .css.js files.

We can also use glamor for css-in-js, processing. Glamor helps with "last mile" style application that creates styles to be applied in your code. It overlaps with css-js-loader.

Regardless of what you choose, its essential that you choose something that can adapt to Dynamic's changing styles. You do need something as broad as a theme based style manager, but you do need to adapt to colors, fonts and layout somewhat. For that, your best best may be build-time selection of assets to include (easy to do with webpack) that set different top level values that are then picked up by your application. You could code-generate these top level assets based on the seetings in a specific Dynamics instance e.g. download theme related information, parse it, then generate top-level information. You could also dynamically adjust to it in a toplevel style processing module but code-generation based on a-prior knowledge of the target environment is much easier e.g. the UCI interface uses 14px font size by default, is kind of easy.

With these few modules, we can compose build-time and run-time styles written in js and css.

office-ui-fabric-react styling

office-ui-fabric-react styling is handled through a mergeStyles function that acts like glamor and is a css-in-js solution that creates stylesheets instead of raw styles to add to your react components. Previous version of fabric used scss but there is no need for scss if you use css-in-js and the overhead penalty is not large in general unless you have an extreme situation. So while we had the postcss loaders listed above, those are build-time CSS processors and for simple CSS are probably fine.

I generally find that I only need 20-30 lines (at most) of most to customize the styling in fabric components because they are already heavily styled.

There are some readmes you should access in the fabric documentation on mergeStyles and other styling topics such as how to customize your components to have flexible, themed styles. Essentially, you create a "getStyles" function that dynamically returns your styles via mergeStyles. Note that mergeStyles, like many solutions similar to it, uses a singleton underneath to dynamically build and add/remove styles from the DOM.

You can import mergeStyles and mergeStyleSetsusing

import { mergeStyles } from "@uifabric/merge-styles"
// or import from "office-ui-fabric-react/lib/Styling"
...

If at all possible and if you are using fabric, use merge-styles. It's generally just easier. You'll probably still want to configure your static CSS processors in webpack.

There are a few critical links for styling with office-fabric-ui-react:

merge-styles is a full css-in-js solution that works well and has a well formed design pattern.

Last updated