In a previous post, I showed you how to add Angular's Service Worker (NGSW) module to an Ionic project.

In this post, I'll show you how to optimise your CSS delivery.

Step 1 - Create a Production build

We'll start by creating a production build:

npm run build --prod

Now check your project's www/build directory:

305 KB, that seems like a lot!

Step 2 - Create a Sass configuration file

To get Ionic Apps up and running quickly version 3 of the Ionic Framework includes Ionic App Scripts.

@ionic/app-scripts provides default configuration files for each of it's build tasks, we can override the defaults by providing our own configuration files.

sass.config.js

@ionic/app-scripts default Sass configuration file describes the Sass files and options required to compile a project.

I created a copy of the file in my project's config directory:

├── /brew
    └── /config
       ├── sass.config.js
       ├── webpack.config.js
    └── /e2e
    └── /node_modules            - Packages managed by npm
    └── /src                     - Angular scripts
       
       ...
    
    └── /www                     - Ionic's 'dist' folder
    ├── package.json
    ├── tsconfig.ts
    
    ...

Note: Check out this post about webpack configuration.

Let's take a look at the file's excludeFiles section:

/**
 * excludeFiles: An array of regex patterns for files which
 * should be excluded. If a file matches both include and exclude
 * patterns, then the file will be excluded.
 */
excludeFiles: [
  /*  /\.(ios|md|wp).(scss)$/i  */
],

My PWA needs to support Android:

And iOS:

So we can update the excludeFiles section as follows:

excludeFiles: [
  /\.(wp).(scss)$/i
],

You can also exclude the Sass files of the components that your project doesn't use:

excludeFiles: [
  /\.(wp).(scss)$/i,
  /(backdrop|chip|note|picker| ... |cordova)/i
],

variables.scss

I also updated variables.scss as follows:

...

$colors: (
  primary: $blue,
);

// @import "ionic.ionicons";

...

Note: Check out this post about theming your Ionic App using SVG icons and this post about Ionic's $colors map.

Step 3 - Create a Copy configuration file

@ionic/app-scripts also provides a default configuration file for the copy task, we can override the defaults by providing our own configuration file.

copy.config.js

@ionic/app-scripts default Copy configuration file describes the static assets to copy to the www directory.

I created a copy of the file in my project's config directory:

├── /brew
    └── /config
       ├── copy.config.js
       ├── sass.config.js
       ├── webpack.config.js
    └── /e2e
    └── /node_modules            - Packages managed by npm
    └── /src                     - Angular scripts
       
       ...
    
    └── /www                     - Ionic's 'dist' folder
    ├── package.json
    ├── tsconfig.ts
    
    ...

And updated it as follows:

module.exports = {
  copyAssets: {
    src: ['{{SRC}}/assets/**/*'],
    dest: '{{WWW}}/assets'
  },
  copyIndexContent: {
    src: ['{{SRC}}/index.html',
          '{{SRC}}/favicon.ico',
          '{{SRC}}/apple-touch-icon.png',
          '{{SRC}}/apple-touch-icon-precomposed.png',
          '{{SRC}}/manifest.json',
          '{{SRC}}/browserconfig.xml'],
    dest: '{{WWW}}'
  },
  copyFonts: {
    src: ['{{ROOT}}/node_modules/ionic-angular/fonts/noto-sans**',
          '{{ROOT}}/node_modules/ionic-angular/fonts/roboto**'],
    dest: '{{WWW}}/assets/fonts'
  },
  copyPolyfills: {
    src: [`{{ROOT}}/node_modules/ionic-angular/polyfills/${process.env.IONIC_POLYFILL_FILE_NAME}`],
    dest: '{{BUILD}}'
  },
  copySwToolbox: {
    src: ['{{ROOT}}/node_modules/sw-toolbox/sw-toolbox.js'],
    dest: '{{BUILD}}'
  }
};

package.json

Ionic projects use the package.json file for configuration. I updated it as follows:

"config": {
  "ionic_copy": "./config/copy.config.js",
  "ionic_sass": "./config/sass.config.js",
  "ionic_webpack": "./config/webpack.config.js"
}

Now we can update our production build:

npm run build --prod

And check the project's www/build directory:

176 KB, better but it still seems like a lot.

Step 4 - Optimise CSS Delivery

I followed the advice of the PageSpeed Tools Insights and used the Critical Path CSS Generator to inline a small CSS file:

<head>

   ...
   
  <style>
    .first-meaningful-paint{background-image:url( ...
  </style>
  
</head>

I also load CSS files that are not critical to the initial rendering of the App, asynchronously:

...

<body>

  <ion-app></ion-app>

  <script src="build/polyfills.js"></script>
  <script src="build/vendor.js"></script>
  <script src="build/main.js"></script>

  <link rel="preload" href="build/main.css" as="style"
        onload="this.onload=null;this.rel='stylesheet'">
  <noscript><link rel="stylesheet" href="build/main.css"></noscript>
  
</body>

Now we're ready to perform an audit in Chrome's DevTools:

Good, but not great! There's still room for improvement :)

What's Next

In the next post, I'll walk you through the Progressive Web App-related tips and tricks I've learned over the past few months.

Resources:
Additional Resources: