Skip to content
Vesa Juvonen edited this page Mar 9, 2017 · 23 revisions

Using Office UI Fabric Core and Fabric React in SharePoint Framework

Office UI Fabric is the official front-end framework for building experiences in Office 365 and SharePoint. SharePoint Framework provides a seamless integration with Fabric that enables Microsoft to deliver robust and consistent design language across various SharePoint experiences such as modern team sites, modern pages, etc... Additionally, we have a goal for Office UI Fabric to be available for developers in SharePoint Framework to build SharePoint solutions.

Goals

There are two parts of Office UI Fabric that are expected to be used by developers:

  1. Office UI Fabric Core a.k.a. Fabric Core - this is a set of core styles, typography, responsive grid, animation related class, icons and other fundamental building blocks of the overall design language.

  2. Office UI Fabric React a.k.a. Fabric React - this package contains a set of React components built on top of the Fabric design language.

One of the goals of SharePoint Framework is to allow both Microsoft and customers to build rich, beautiful and consistent solutions on top of SharePoint. In that spirit, we wanted to ensure that SharePoint Framework allows:

  • All customers to smoothly and reliably consume Fabric Core and Fabric React in their solutions.
  • Microsoft to roll out updated experiences that consume updated versions of Fabric Core and Fabric React in SharePoint without conflicting with customer's solutions.
  • Customers to choose and bundle their desired version of Fabric Core and Fabric React for their solutions.
  • Customers to customize the styles, designs and components tailored to their solution's needs.

Current State

Microsoft uses a version of Fabric Core and Fabric React in SharePoint. In SharePoint online Microsoft keeps pushing updates to the service on a regular basis i.e. SharePoint experiences are likely to keep updating the version of Fabric Core and Fabric React they use. These updates can potentially conflict with your deployed solution as it could break existing styles for components that are using Fabric Core and Fabric React in your solution. For example, the "submit" button in your web part is now using a dark red background color scheme instead of the intended blue background color scheme due to a Fabric Core update pushed in SharePoint by Microsoft that uses an updated color scheme for buttons.

Problem

The main reason conflicts occur between the customer solution and Microsoft experiences is due to the use of global styles in both Fabric Core and Fabric React. Since the client side components (e.g. web parts) live in the same window namespace as the overall page, changes to global styles, apply to all components in the page, including customer solutions deployed on that page. The conflicts could be as small as a change in the color scheme to as big as the component failing to render resulting in a critical loss of functionality. Further, if you override the styles then the overrides would apply to the Microsoft code also. This could make the Microsoft expreriences not look or behave correctly.

For the above mentioned reasons, at the current time customer solutions cannot safely use Fabric Core and Fabric React and guarantee they will never be broken. That said, we do understand that developers need Fabric Core and Fabric React to build their solutions. Hence, we are wroking hard to resolve as quickly as possible.

This discussion explains the problem well. There is plenty of other documentation on the web abould the solutions to the global namespace menace.

Useful CSS Concepts to understand

  • Global CSS styles and how avoid them at all cost: this is a big problem. Today, both Fabric Core and Fabric React have global styles. Lack of any native solutions to manage the style scoping makes this a very difficult problem.

    • Scope CSS is in early stages of discussion.
    • iframes are not a good option to isolate styles.
    • Web Components is another standard that talks about scoped styles but is still in discussion stages.

    Please read through the end to understand the options we are trying to use to solve the global styles problem.

  • CSS Specifity and how it applies to web UI. Higher specificity styles override the lower specificity styles. But the key thing to understand is how the specificity rules apply. In general the precedence order is as follows:

    • The style (e.g. style="background:red;") attribute has the highest specifity.
    • Id selectors (e.g. #myDiv { }) is next in the list.
    • Class selectors (e.g. .myCssClass{}), attributre selectors (e.g. [type="radio"]) and pseudo-classes (e.g. :hover) come next.
    • Type selectors (e.g. h1) come last.

    Other key facts to understand

    Loading order If two classes are applied on a element and they have the same specificity, the class that is loaded later take precedence. As shown in the code, the button will appear red. If the order of the style tags is changed, the button will appear green. This is an important concept and if not used correctly, the user experience can become load order dependent i.de. inconsistent.

<style>
  .greenButton { color: green; }
</style>
<style>
  .redButton { color: red; }
</style>
<body>
  <button class="greenButton redButton"></button>
</body>

Scenarios that are not supported by Microsoft

Because of the problems described above, the following scenarios are not officially supported by Microsoft because they are likely to break.

  • Explicitly loading a copy of the unscoped Fabric core css (i.e. fabric.css) in your solution.
  • Loading a copy of the fabric.component.css file in your solution. The class names used in this class are same as those in React Fabric. Loading this file will cause UI to not look correct. If you really need to depend on fabric.component.css, the only option is to make a copy of this file and rename the class names to custom class names.
  • Overriding Fabric Core styles is not supported.
  • Overriding Fabric React styles should be avoided as much as possible.

Plan going forward

Here are some high level details about what we are doing to alleviate these concerns and support the scenarios.

Short term

In the short term we are planning to release the following updates:

  1. Office UI Fabric Core:
  • The web part developer will not be required to do anything explicitly to get the scoping to work.
  • We plan to solve this problem through CSS specificity and descendant selector. The Fabric Core team will ship multiple copies of Fabric Core css. e.g. fabric-6.css, fabric-6-scoped.css, fabric-6.0.0.css, fabric-6.0.0-scoped.css.
  • All the styles in the scoped css files are inside a descendant selector e.g. "ms-Fabric-core--v6 ms-Icon--list".
  • At compile time tooling will collect the version of the Office UI Fabric Core the web part was built with. This
    version can be the one that comes with SPFx. Alternatively, web part developers could take an explicit dependency on a specific version if Office UI Fabric Core in their package.json file.
  • The web part div will be assigned this scope i.e.
  • The framework will load the specific major version of the Fabric core scoped css file. If the web part is built
    with version 6.0.0 of Fabric core css, the framework will download fabric-6-scoped.css at web part load time.
  • The rest of the page will contain uncoped Office UI Fabric Core.
  • This way, as per CSS specificity rules, the scoped CSS take precedence inside the web part div.
  • The whole web part and its contents will allign to a specific version of Office UI Fabric Core the developer has chosen.
  • Overriding Fabric Core styles is not supported.
  • // Sample of how the scoping would work.
    import { SPComponentLoader } from '@microsoft/sp-loader';
    
    export default class MyWebPart {
        constructor() {
            super();
    
            SPComponentLoader.loadCss('https://static2.sharepointonline.com/files/fabric/office-ui-fabric-core/6.0.0/css/fabric-6.0.0.scoped.css');
        }
    }
    
    protected render(): void {
      <div className={css('ms-Fabric-core--v6')}>
        { // Rest of the web part UI }
      </div>
    }

    Understanding this approach and its shortcomings This approach has some shortcomings and it is best that the devlopers understand those before shipping web parts using this approach.

    This approach only works if the Microsoft user experience (i.e. page and first party web parts) use uncoped version of Office UI Fabric Core. i.e. ms-Icon--list and the third paty web parts use only the scoped Office UI Fabric Core css as explained above. The reason being that the specificity of the scoped CSS applied on the web part overrides unscoped CSS on the page. Please note, as explained above, if CSS spcificity of two classes is the same then their loading order plays a role in how the css classes will be applied. The class that loads later will take precedence. Hence, the higher specificity of the scoped css is important in getting a consistent experience.

    Further, multiple extensions, one contained in the other, cannot use different Fabric Core versions. i.e. in the following example, only ms-Fabric-core--v6 will get applied.

    <div className={css('ms-Fabric-core--v6')}>
      { // Rest of the web part UI }
        { // inside of this SPExtension trying to use different Fabric core version does not work }
        <div className={css('ms-Fabric-core--v8')}>
        </div>
    </div>
    1. Office UI Fabric React: This section applies if you are building solutions in React. For non-React scenarios please refer to the next section.
    • Office UI Fabric React components use CSS modules.
    • CSS Modules gives unique class names to all Fabric React component classes. i.e. ms-Button becomes ms-Button_a578389. The last part is a hash generated from the version of Fabric React and the contents of the ms-Button class. This will prevent namespace conflicts between different versions of Fabric React. As Microsoft uses newer versions of Fabric React on the surrounding page, the web part styles will not get affected.
    • The solution devloper will need to statically link to the Fabric React components. Please see the static linking example below. This will include the Fabric React component in your solution bundle. This implies that, if the page level implementation of the Button were to change, it will not affect your web part adversly.
    • Overriding Fabric React component styles should be done only when needed. On each Fabric React component, the global style name i.e. "ms-Button" is also applied along with the "ms-Button_a578389". Hence, you can use the "ms-Button" class to override. But please note, your override will apply to the surrounding UI outside the web part also.
    • Theming ??
    // Remember to explicitly add a dependency on a specific version of office-ui-fabric-react in your package.json file.
    
    // correct - static linking.
    import { Button, ButtonType } from 'office-ui-fabric-react/lib/Button';
    
    // incorrect - dynamic linking.
    import { Button, ButtonType } from '@microsoft/office-ui-fabric-react';

    Understanding this approach and its shortcomings

    • Components in your solution are locked to version of Fabric React you used when you built the solution. They will
      not automatically evolve with the Fabric React. You will need to manually update your solution to adapt to newer versions of Fabric React.
    • The page may slightly be less performant as it may load more css but the chances of css conflicts will be much less.
    • The static linking bloats up your web part bundle. But dynamic linking is too fragile. And hence, in the interest of reliability, static linking is the only approach that is officially supported.
    1. Non React scenarios: SPFx is framework agnostic. Developers should be able to build solutions in any framework with the same ease. How should these solutions consume Fabric Core and how should they build components in the Microsoft design language?
    • Fabric Core integration should be same as described earlier.
    • To build components that follow the design language, the following pattern can be used by solution developers.
      • Statically import component styles from React Fabric components.
      • Immitate HTML and javascript behaviours from Fabric React components in the framework of your choice.
    • Overriding styles should be done using custom styles.
    • Theming ??
    // Remember to explicitly add a dependency on a specific version of office-ui-fabric-react in your package.json file.
    
    const buttonStyles = require('office-ui-fabric-react/lib/components/Button.css');

    Understanding this approach and its shortcomings

    • Components in your solution are locked to version of Fabric React styles you used when you built the solution. They will not automatically evolve with the Fabric React. You will need to manually update your solution to adapt to newer versions of Fabric React.

    Mid term

    We are constantly looking to improve our patterns and practices and make them more robust. In that direction, the following options are being evaluated.

    • Although the decendancy based selectors allow you to include load your own copy of Fabric Core in your web part, we are evaluating other approaches to make the dependancy on Fabric Core completely foolproof.
    • For React Fabric also we are looking at more foolproof and dynamic approaches then CSS modules. Some examples are
      • glamor: this looks like a promising solution the global styles problem. css-in-js is a good direction to go in.
    • Should we ship styles for React Fabric components independent of Fabric React? Note, we do that through fabric.component.css but because of global styles. It is still an open question if we should ship a version of fabric.component.css that does not use global styles.
Clone this wiki locally