|
| 1 | +# Recommendations for working with CSS in SharePoint Framework solutions |
| 2 | + |
| 3 | +When building SharePoint Framework solutions, you can use CSS to define how your customization should look like and behave. This article describes how you can make the best use of the capabilities provided with the SharePoint Framework and build your CSS styles in a robust way. |
| 4 | + |
| 5 | +## SharePoint Framework customizations are part of the page |
| 6 | + |
| 7 | +When building SharePoint customizations using the add-in model, the solution is isolated from other elements on the page. Your code is executed either in an iframe, as an add-in part, or as an immersive application taking control of the whole page. In both cases, your code is not affected by other elements and styles defined on the page. |
| 8 | + |
| 9 | +SharePoint Framework solutions are a part of the page and integrate fully with the page's DOM. While this removes a number of restrictions that come with the add-in model, it exposes your solution to a risk. Because it's a part of the page, unless explicitly isolated, all CSS styles present on the page will apply to it, potentially resulting in an experience different from intended. To avoid such risks, you should define your CSS styles in such a way so that they won't affect anything else on the page other than your customization. |
| 10 | + |
| 11 | +## Organize CSS files in your solution |
| 12 | + |
| 13 | +The UI of your solutions often consists of multiple building blocks. In many JavaScript libraries these building blocks are called components. A component can be simple and define only the presentation or it can be more complex and include state and other components. Splitting your solution into multiple components allows you to simplify the development process and makes them easier to test and reuse in your solution. |
| 14 | + |
| 15 | +Because components have presentation, they often require CSS styles. Ideally, components should be isolated and be able to be used on their own. With that in mind, it makes perfect sense for you to store CSS styles for the particular component along with all other asset files next to the component. Following, is a sample structure of a React application with a number of components each with its own CSS file. |
| 16 | + |
| 17 | +```text |
| 18 | +todoWebPart\components |
| 19 | + \todoList |
| 20 | + \todoList.tsx |
| 21 | + \todoList.module.scss |
| 22 | + \todoItem |
| 23 | + \todoItem.tsx |
| 24 | + \todoItem.module.scss |
| 25 | +``` |
| 26 | + |
| 27 | +## Use Sass |
| 28 | + |
| 29 | +In the SharePoint Framework you can use both CSS and Sass. Sass is a superset of CSS and offers you a number of features such as using variables, nesting selectors or using mixins - all of which simplify working with and managing CSS styles over long term. For a complete set of features see the [Sass website](http://sass-lang.com). All valid CSS is also valid Sass which is very helpful if you haven't worked with Sass before and want to gradually learn its capabilities. |
| 30 | + |
| 31 | +## Avoid using ID's in markup |
| 32 | + |
| 33 | +Using the SharePoint Framework you build customizations that end-users add to SharePoint. It's impossible to tell upfront if the particular customization will be used only once on a page or if there will be multiple instances of it. To avoid issues, you should always assume that there are multiple instances of your customization on the same page. With that in mind, you should avoid using any ID's in your markup. ID's are meant to be unique on a page and if a user added your web part to the page twice, it would violate this premise possibly leading to errors. |
| 34 | + |
| 35 | +**Bad practice:** |
| 36 | + |
| 37 | +```ts |
| 38 | +// ... |
| 39 | + |
| 40 | +export default class HelloWorldWebPart extends BaseClientSideWebPart<IHelloWorldWebPartProps> { |
| 41 | + |
| 42 | + public render(): void { |
| 43 | + this.domElement.innerHTML = ` |
| 44 | + <div id="helloWorld"> |
| 45 | + Hello world |
| 46 | + </div>`; |
| 47 | + } |
| 48 | + |
| 49 | + // ... |
| 50 | +} |
| 51 | +``` |
| 52 | + |
| 53 | +**Good practice:** |
| 54 | + |
| 55 | +```ts |
| 56 | +// ... |
| 57 | + |
| 58 | +export default class HelloWorldWebPart extends BaseClientSideWebPart<IHelloWorldWebPartProps> { |
| 59 | + |
| 60 | + public render(): void { |
| 61 | + this.domElement.innerHTML = ` |
| 62 | + <div class="${styles.helloWorld}"> |
| 63 | + Hello world |
| 64 | + </div>`; |
| 65 | + } |
| 66 | + |
| 67 | + // ... |
| 68 | +} |
| 69 | +``` |
| 70 | + |
| 71 | +## Use CSS modules to avoid styling conflicts |
| 72 | + |
| 73 | +SharePoint Framework solutions are a part of the page. To ensure that CSS styles for one component don't affect other elements on the page, you should define your CSS selectors in such a way that they apply only to the DOM of your solution. It's tedious and error-prone to do this manually, but SharePoint Framework can do this automatically for you. |
| 74 | + |
| 75 | +To help you avoid stylings conflicts, SharePoint Framework uses [CSS modules](https://github.com/css-modules/css-modules). When building the project, the SharePoint Framework toolchain processes all files with the **.module.scss** extension. For each file, it reads all class selectors and appends a unique hash value to them. Once finished, it writes the updated selectors to intermediate CSS files that are included in the generated web part bundle. |
| 76 | + |
| 77 | +Following the example above, assume you had the following two Sass files: |
| 78 | + |
| 79 | +**todoList.module.scss:** |
| 80 | + |
| 81 | +```scss |
| 82 | +.todoList { |
| 83 | + margin: 1em 0; |
| 84 | + |
| 85 | + .text { |
| 86 | + font-weight: bold; |
| 87 | + font-size: 1.5em; |
| 88 | + } |
| 89 | +} |
| 90 | +``` |
| 91 | + |
| 92 | +**todoItem.module.scss:** |
| 93 | + |
| 94 | +```scss |
| 95 | +.todoItem { |
| 96 | + padding: 0.5em 1em; |
| 97 | + |
| 98 | + .text { |
| 99 | + font-size: 0.9em; |
| 100 | + } |
| 101 | +} |
| 102 | +``` |
| 103 | + |
| 104 | +After building the project, in the **lib** folder you would see the following two CSS files generated (line breaks and indenting added for readability): |
| 105 | + |
| 106 | +**todoList.module.css:** |
| 107 | + |
| 108 | +```css |
| 109 | +.todoList_3e9d35f0 { |
| 110 | + margin:1em 0 |
| 111 | +} |
| 112 | + |
| 113 | +.todoList_3e9d35f0 .text_3e9d35f0 { |
| 114 | + font-weight:700; |
| 115 | + font-size:1.5em |
| 116 | +} |
| 117 | +``` |
| 118 | + |
| 119 | +**todoItem.module.css:** |
| 120 | + |
| 121 | +```css |
| 122 | +.todoItem_f7081cc4 { |
| 123 | + padding:.5em 1em |
| 124 | +} |
| 125 | + |
| 126 | +.todoItem_f7081cc4 .text_f7081cc4 { |
| 127 | + font-size:.9em |
| 128 | +} |
| 129 | +``` |
| 130 | + |
| 131 | +Even though there was a **.text** class defined in both Sass files, notice how in the generated CSS files it has two different hashes appended to it, becoming a unique class name specific to each component. |
| 132 | + |
| 133 | +The CSS class names in CSS modules are generated dynamically, which makes it impossible for you to refer to them in your code directly. Instead, when processing CSS modules, the SharePoint Framework toolchain generates a JavaScript file with references to the generated class names. |
| 134 | + |
| 135 | +**todoList.module.scss.js:** |
| 136 | + |
| 137 | +```js |
| 138 | +"use strict"; |
| 139 | +Object.defineProperty(exports, "__esModule", { value: true }); |
| 140 | +/* tslint:disable */ |
| 141 | +require('./todoList.module.css'); |
| 142 | +var styles = { |
| 143 | + todoList: 'todoList_3e9d35f0', |
| 144 | + text: 'text_3e9d35f0', |
| 145 | +}; |
| 146 | +exports.default = styles; |
| 147 | +/* tslint:enable */ |
| 148 | + |
| 149 | +//# sourceMappingURL=todoList.module.scss.js.map |
| 150 | +``` |
| 151 | + |
| 152 | +To use the generated class names in your code, you first import the styles of your component and then use the property pointing to the particular class: |
| 153 | + |
| 154 | +```ts |
| 155 | +import styles from './todoList.module.scss'; |
| 156 | +// ... |
| 157 | + |
| 158 | +export default class TodoList extends React.Component<ITodoListProps, void> { |
| 159 | + public render(): React.ReactElement<ITodoListProps> { |
| 160 | + return ( |
| 161 | + <div className={styles.todoList}> |
| 162 | + <div className={styles.text}>Hello world</div> |
| 163 | + </div> |
| 164 | + ); |
| 165 | + } |
| 166 | +} |
| 167 | +``` |
| 168 | + |
| 169 | +For the CSS modules to work correctly you have to meet the following conditions: |
| 170 | + |
| 171 | +* your Sass files must have the **.module.scss** extension. If you use the **.scss** extension without **.module**, you will see a warning in the build process. The Sass file will be transpiled to an intermediate CSS file but the class names **will not be made unique**. In cases, when you need to override 3rd party CSS styles, this might be intended |
| 172 | +* your CSS class names must be valid JavaScript variable names, so for example they cannot contain hyphens: `todoList` is correct but `todo-list` isn't |
| 173 | +* camelCase naming for classes is recommended, but it's not enforced |
| 174 | + |
| 175 | +## Wrap your CSS styles in a class named after the component |
| 176 | + |
| 177 | +By combining CSS modules with Sass' support for nesting rule sets you can simplify your CSS styles and ensure that they won't affect other elements on the page. |
| 178 | + |
| 179 | +When building CSS styles for a component, wrap them in a class named after the component. Then, in the component, assign that class to the component's root element. |
| 180 | + |
| 181 | +**todoList.module.scss:** |
| 182 | + |
| 183 | +```scss |
| 184 | +.todoList { |
| 185 | + a { |
| 186 | + display: block; |
| 187 | + } |
| 188 | +} |
| 189 | +``` |
| 190 | + |
| 191 | +**TodoList.tsx:** |
| 192 | + |
| 193 | +```tsx |
| 194 | +// ... |
| 195 | + |
| 196 | +export default class TodoList extends React.Component<ITodoListProps, void> { |
| 197 | + public render(): React.ReactElement<ITodoListProps> { |
| 198 | + return ( |
| 199 | + <div className={styles.todoList}> |
| 200 | + ... |
| 201 | + </div> |
| 202 | + ); |
| 203 | + } |
| 204 | +} |
| 205 | +``` |
| 206 | + |
| 207 | +After transpilation, the generated CSS file will look similar to: |
| 208 | + |
| 209 | +```css |
| 210 | +.todoList_3e9d35f0 a { |
| 211 | + display: block; |
| 212 | +} |
| 213 | +``` |
| 214 | + |
| 215 | +Because the selector begins with the unique class name, specific to your component, the alternative presentation will apply only to hyperlinks inside your component. |
| 216 | + |
| 217 | +## Integrate Office UI Fabric |
| 218 | + |
| 219 | +By making your customizations look and behave like the standard functionality of SharePoint and Office 365 you will make it easier for the end-users to work with them. Office UI Fabric offers you a set of controls and styles for use in your customizations to seamlessly integrate with the existing user experience. For more information on using Office UI Fabric in the SharePoint Framework, read the [Office UI Fabric integration guide](web-parts/guidance/office-ui-fabric-integration). |
| 220 | + |
| 221 | +## Use theme colors |
| 222 | + |
| 223 | +SharePoint allows users to choose the theme color for their sites. In your SharePoint Framework customizations you should follow the theme selected by the users to make your customization look like an integral part of the site rather than unnecessarily stand out. Because the theme is set by users in their site, you cannot tell upfront which colors your customization should use, but SharePoint Framework can dynamically load the currently active color scheme automatically for you. For more information about this capability read the [guide on using theme colors](./use-theme-colors-in-your-customizations). |
0 commit comments