As I said in the previous article, nowadays, the mobile implementation consideration is one of the most important criteria for intranet projects. That’s a fact: end users must be able to access their intranet from a mobile device. Don’t be confused here: in this situation we are usually talking about the « publishing » part of an intranet, the one where users view the company news, announcements, etc. not the « operational » part where users work together on documents, use business workflows and applications, etc. Generally speaking, this should not be your responsibility to adapt the core SharePoint experience to be mobile (if you really want to do this, take a look at the PnP Responsive UI). That’s why Microsoft recently released the SharePoint Intranet mobile application for iOS. With this application, users are able to do basic « operational » tasks like browse documents, search for documents or people from a mobile device but the responsive/mobile publishing part is still missing. All we have is the legacy publishing infrastructure feature of SharePoint. We know that a new experience is coming soon but we don’t know exactly when and how, so for now you have only two options:
- Wait for the new mobile canvas and use the actual OOTB features (basically no cost).
- Implement your own mobile/responsive solution ($ to $$$ depending of the service provider…).
Both options are viable but, good news, you have now a free starter solution for the second one ;).
Designing an intranet generally involves to work on specific SharePoint artefacts. In this example, design customizations were made on the following artefacts:
- Master page
- Page Layouts
- Display templates (content rollup via search Web Parts for news or search results)
- Custom components (navigation, searchbox, etc.)
All files (JavaScript , CSS, images) are uploaded into the site collection root site « Style Library ». To improve performances, we could use a CDN instead. However, using the default style library is more convenient in a SharePoint allowing you to replace some files manually if needed. If you choose to upload your file in a CDN, be careful, the files are publicly accessible.
The master page

Minimal Master Page

Minimal Master Page
For this example, I just created a minimal master page from the SharePoint design manager:
In the <head>section, I’ve added all my dependendcies:
<!-- PnP Starter Intranet links --> <link href='https://fonts.googleapis.com/css?family=Open+Sans:400,600,700,800,300' rel='stylesheet' type='text/css'> <SharePoint:CssRegistration runat="server" ID="PortalCss" Name="<% $SPUrl:~sitecollection/Style Library/PnP/portal.css %>" After="corev15.css"/> <SharePoint:CssRegistration runat="server" ID="BootstrapCss" Name="<% $SPUrl:~sitecollection/Style Library/PnP/bootstrap-iso.css %>" After="corev15.css"/> <SharePoint:ScriptLink ID="VendorJs" Name="~SiteCollection/Style Library/PnP/js/vendor.js" runat="server"/> <SharePoint:ScriptLink ID="AppJs" Name="~SiteCollection/Style Library/PnP/js/app.js" runat="server"/> ...
- The intranet global styles (portal.css) available globally for all components and page layouts.
- The Bootstrap isolated styles (bootstrap-iso.css). See below for more information.
- The third party code (vendor.js). It hasto be loaded before the application code.
- The application code (app.js).
I’ve also used a custom font available from Google: https://fonts.google.com/
The master page is here just to provide the HTML container markup. Without it, you will have some troubles to implement a beautiful responsive interface for all your pages ;). The structure looks like this (notice the Knockout JS custom elements binding syntax for components):
<!-- All Bootstrap styles are applied from here --> <div id="s4-workspace" class="bootstrap-iso"> <div class="wrapper"> <div id="s4-bodyContainer"> <!-- Header --> <header class="header"> <div class="container"> <div class="row"> <div class="col-md-12"> <div id="intranet-logo"> <SharePoint:AjaxDelta id="DeltaSiteLogo" BlockElement="true" runat="server"> <SharePoint:SPSimpleSiteLink runat="server" id="onetidProjectPropertyTitleGraphic"> <SharePoint:SiteLogoImage name="onetidHeadbnnr0" id="onetidHeadbnnr2" LogoImageUrl="//www.sharepointeurope.com/_layouts/15/images/siteIcon.png?rev=43" runat="server"> </SharePoint:SiteLogoImage> </SharePoint:SPSimpleSiteLink> </SharePoint:AjaxDelta> </div> <component-header></component-header> </div> </div> </div> </header> <!-- Top navigation component --> <component-topnav></component-topnav> <div id="main"> <div class="container"> <div id="notificationArea" class="ms-notif-box"></div> <SharePoint:AjaxDelta id="DeltaPageStatusBar" BlockElement="true" runat="server"> <div id="pageStatusBar"></div> </SharePoint:AjaxDelta> <!-- Main content of pages --> <SharePoint:AjaxDelta id="DeltaPlaceHolderMain" IsMainContent="true" runat="server"> <asp:ContentPlaceHolder ID="PlaceHolderMain" runat="server"> <div class="DefaultContentBlock" style="border:medium black solid; background:yellow; color:black;"> This area will be filled in by content you create in your page layouts. </div> </asp:ContentPlaceHolder> </SharePoint:AjaxDelta> </div> </div> </div> <div class="push ms-dialogHidden"></div> </div> <!-- Footer component --> <component-footer></component-footer> </div>
Notice we use directly the .master file instead of the .html file because we don’t want to deal with the conversion process (and also because there is no PnP cmdlet for this).
Bootstrap
For those who have already worked with Bootstrap and SharePoint may know that Bootstrap overrides a lot of default SharePoint styles when imported within the global context. Usually, developers create a dedicated CSS « patch » file to fix style issues generated by this override. In my case, I didn’t want to create such a file and I found this very interesting blog article. The principle is relatively simple: isolate Bootstrap styles inside a custom named CSS class to be able to use it in a more granular way and to not conflict with SharePoint global styles (at least all styles outside the main workspace like, ribbon, app launcher, etc.). Because I don’t use a « patch » file, it may result of a slightly different design especially for Web parts inside the page. If you plan to you OOTB SharePoint styles (list Web Part ,etc.) inside the main workspace area (i.e. in your pages), you may have to create one for overrides.
Because we use Webpack in the solution, it is now very easy to use a LESS loader and do the transformation automatically during the bundling sequence:
.bootstrap-iso { /* We use a precompiled version of the Bootstrap CSS with only required elements (responsive grid and navbar) */ @import (less) 'bootstrap.css'; ... }
... { // Isolate the Bootstrap CSS to avoid conflicts with the SharePoint default CSS // More info here: https://formden.com/blog/isolate-bootstrap // We used a customized CSS version of Bootstrap (http://getbootstrap.com/customize/) because we don't need the full package test: /\.less$/, loader: bootstrapCssExtractTextPlugin.extract( 'style', 'css?sourceMap!string-replace?{multiple:[{search:"\.bootstrap-iso body",replace:".bootstrap-iso"},{ search:"\.bootstrap-iso html",replace:".bootstrap-iso" }]}!less' ), }, ...
Compile your own Bootstrap version!
When working with Bootstrap, this is not mandatory to use ALL the framework and components capabilities. It is possible to pick and choose the part you want to keep the very necessary. For this example, I’ve just needed of the grid layout system, some navigation components and… that’s it! Got to this address to compile your own version: http://getbootstrap.com/customize/.

Bootstrap Customisation
Styles management with SASS
Because we use a component-based approach, styles have to be modular too. SASS is a good CSS extension to do this. As an example, is is possible to split styles for each component and use a shared file for common colors and general parameters. Then, with Webpack (again), we just use a SASS loader to merge all styles either into a single CSS file (portal.css) or separeted files (layouts.css and layouts-edit.css) during bundling. As a best practice, I’ve implemented styles for each component under a single CSS identifier to avoid conflicts with other components:
@import 'variables'; #navbar { ... }
Don’t like the intranet default colors? You can just change them by updating the variable.scss file and re-bundling the application using the webpack cmd:
$font-primary: 'Open Sans', sans-serif; $primary-color: #EA4300; // Main color for the intranet $text-color: #333333; // Used for links and text $error-color: #a80000; /* Nav bar colors */ $navbar-l1-link-color: #ffffff; // Navbar links - Level 1 $navbar-l1-link-color-hover: #ffffff; $navbar-l2-link-color: $text-color; // / Navbar links - Level 2 (Only applied in desktop mode) $navbar-l2-link-color-hover: #EA4300; $navbar-font-size: 13px; $navbar-hover-color: #ffffff; $page-icon-color: #EA4300; // Used in page info component for metadata display $contextual-selected-color: $primary-color;
Page layouts
Usually, the page layouts encountered in intranet projects are generally the same:
- Home page
- Static page
- News page
- Search page
Apply styles depending the display mode
In SharePoint page layouts, you can control what element are displayed (or loaded) in edit or display mode. We use this capability to load different style sheets depending the page mode. In fact, in edit mode, we want apply dedicated styles for fields (for example, background fields) and also, we don’t need (and want) page layouts to be responsive so we stack all fields by default.
<asp:Content ContentPlaceHolderID="PlaceHolderAdditionalPageHead" runat="server"> <!-- Custom styles are loaded only on display mode --> <PublishingWebControls:EditModePanel runat="server" id="DisplayModeControl" SuppressTag="true" PageDisplayMode="display"> <SharePoint:CssRegistration runat="server" ID="PortalLayoutCss" Name="<% $SPUrl:~sitecollection/Style Library/PnP/layouts.css %>" After="corev15.css"/> </PublishingWebControls:EditModePanel> <!-- Custom styles are loaded only on edit mode --> <PublishingWebControls:EditModePanel runat="server" id="EditModeControl" SuppressTag="true"> <SharePoint:CssRegistration runat="server" ID="PortalLayoutEditCss" Name="<% $SPUrl:~sitecollection/Style Library/PnP/layouts-edit.css %>" After="corev15.css"/> </PublishingWebControls:EditModePanel> </asp:Content>

Display Mode

Page Layout
Display templates
In the previous article, I’ve mentionned that I’ve used some utility libraries like « trunk8 » or « Moment.js » to do something like this:

Display Template
Before to be able to do this, you have something to remember: when you use Webpack, your code is by default isolated in the outputted bundle(s). It means your third party libraries and your own functions are not accessible from the global « window » namespace (like « $ » or « ko« ). But what happen if you want to use them inside a SharePoint display template which is not managed by your application (for instance the trunk8 plugin)?
For obvious reasons, and even if it works, we don’t want to expose third party libraries into the global « window » namespace by including them in the master page so we have to find another way to be able to make « communications » between the SharePoint world and the application one. Webpack has a special configuration allowing you to ouput your application as a library like any other third party library. By writing the following code, you basically tell Webpack to « turn visible » your application trough the « Intranet » variable.
module.exports = validate({ ... output: { ... // Expose the entry point as the 'Intranet' global var. // We need this to be able to apply Knockout JS bindings manually for SharePoint display templates (the 'ko' variable is not exposed in the global context) library: ['Intranet'] ... }, ...

Window Namespace
We use this mechanism to apply custom Knockout JS view model inside a SharePoint display template (thanks to this blog post for the idea!). Combined with custom generic binding handlers, we are now able to use our plugin whitout problem like this:
// ======================================== // Base Display Template Item View Model // ======================================== ... export class DefaultDisplayTemplateItemViewModel { ... constructor(currentItem?: any) { this.item = ko.observable(currentItem); ko.bindingHandlers.summarize = { init: (element, valueAccessor) => { // Handler logic }, }; ko.bindingHandlers.formatDateField = { init: (element, valueAccessor) => { // Handler logic }, }; ... } }
... <div class="item-date"> <!-- ko if: $data.IntranetContentPublishingDateOWSDATE --> <span data-bind="formatDateField: IntranetContentPublishingDateOWSDATE"></span> <!-- /ko --> </div> <div class="item-excerpt"> <!-- ko if: $data.PublishingPageContentOWSHTML --> <div data-bind="summarize: { text: PublishingPageContentOWSHTML }"></div> <!-- /ko --> </div> ...
... <div class="item-date"> <!-- ko if: $data.IntranetContentPublishingDateOWSDATE --> <span data-bind="formatDateField: IntranetContentPublishingDateOWSDATE"></span> <!-- /ko --> </div> <div class="item-excerpt"> <!-- ko if: $data.PublishingPageContentOWSHTML --> <div data-bind="summarize: { text: PublishingPageContentOWSHTML }"></div> <!-- /ko --> </div> ...
<!--#_ ... var encodedId = $htmlEncode(ctx.ClientControl.get_nextUniqueId()); var containerId = "NewsItem_" + encodedId; var currentItem = ctx.CurrentItem; AddPostRenderCallback(ctx, function() { Intranet.Main.initNewsDisplayTemplateItemViewModel(currentItem, document.getElementById(containerId)); }); _#-->
Notice the « stopBinding: true » custom Knockout binding handler. It is useful to avoid conflicts with the main ko.applyBindings() call in the main file. More info here.
Be careful, there is a downside using this approach: the script loading order. By default, Web Part search results are always loaded synchronously. Your bindings may not apply because of the loading order (your application is loaded after the search results). To avoid this behavior, we set the loading behavior to asynchronous in the search query settings. By this way we can guarantee your application will be loaded before the search results (because links in master page have priority over dynamic scripts loading).

Async Web Part Results
In the next article we will talk about the navigation implementation. Stay tuned!
Contact the Author: Franck Cornu
– LinkedIn: HTTPS://CA.LINKEDIN.COM/IN/FRANCKCORNU
– Twitter: @FRANCKCORNU
– Blog: HTTP://THECOLLABORATIONCORNER.COM/
Reference:
Cornu, F. (2016). Part 3: Office 365 Starter Intranet Solution (Part 3: Design and mobile implementation) [online] Available at: http://thecollaborationcorner.com/2016/08/25/part-2-frameworks-and-libraries-used-how-it-is-implemented/ [Accessed 16 Feb. 2017].