Angular Universal: serving critical CSS and delaying non-critical - css

This is basically the code I am using and lighthouse says my (almost empty!) css bundle is delaying my initial load.
So how do I put a link to critical.scss in the
<head><style>DONT_WANT_TO_WRITE_STUFF_INLINED</style>...</head>
Is there a better solution than https://www.npmjs.com/package/critical or writing everything inlined?
And how do I delay the load of the main styles.scss until the prerendered Universal content is loaded in the browser? The server-app's config in angular-cli.json does not contain styles or assets, so I don't understand why styles.scss is loaded initially

Regarding delaying the load of the main styles.scss, I rely on two things:
First of all, I build the application with the --extract-css=false flag. That ensures that the styles bundle will be a JavaScript file.
Second, I defer the loading of that file. To do so I rely on a small script that I called defer.styles.js and that runs at the end of the build process. That script simply looks for the script tag that loads the styles, and adds a defer to it.
const path = require('path');
const htmlPath = path.join(__dirname, 'dist', 'browser', 'index.html');
fs.readFile(htmlPath, 'utf8', (err, data) => {
if (err) {
console.log(err);
return;
}
const index = data.search(`src="styles.`);
if (index > -1) {
const output = [data.slice(0, index), 'defer ', data.slice(index)].join('');
console.log(output);
fs.writeFile(htmlPath, output, 'utf8', () => {
console.log('Styles succesfully deferred');
})
}
});
Regarding your other question - how to load the critical css -. I still didn't find a way of comfortably doing it.

Related

Running gulp postcss in parallel with webpack

I'm working on a project with a pre-configured webpack setup, that I cannot change. I do have access to the package.json though.
I'd like to use nesting in the css files. Every component in src/component has its own folder with a styles.css file. There is auto-reloading for the browser, when I change and save a .css file while the webpack based dev process is running.
I found a workaround hack to get nested css working with postcss by running a gulp watch process in parallel. The gulpfile.js looks like this:
const { src, dest, watch, series, parallel } = require('gulp')
const postcss = require('gulp-postcss')
const rename = require('gulp-rename')
const watchPcss = () => {
watch(['src/**/*.pcss'], { followSymlinks: false }, series(pcss))
}
const pcss = () => {
return src('src/**/*.pcss')
.pipe(postcss([require('autoprefixer'), require('postcss-nested')]))
.pipe(
rename((path) => {
path.extname = '.css'
}),
)
.pipe(dest('./src'))
}
exports.watch = series(watchPcss)
exports.default = series(pcss)
I have additional style.pcss files in each folder, in which I can use nesting. The gulp watch task looks for changes in .pcss files and then runs postcss to convert it to a normal css styles.css file.
This does work. The styles.css files get correctly generated. And saving changes in the .pcss files, do not trigger the webpack watch process.
My plan was that the generated/updated styles.css would trigger webpack and therefore update site in the browser. However, this only works sometimes. Most of the time, all the styles in the .css of the corresponding changed .pcss are not there at all (also not the styles that were in the file before the changes). I then have to save the .css file manually to make the styles show.
Is it possible that the gulp process does not "update" the file, but deletes it and creates a new file? And the webpack watch process notices the "deleting" of the file, but the creating of the new file happens too soon for the watch process to be triggered again?
Is there a way to fix this? Maybe just "touch" the .css file again after xxx ms?
This is probably not the best or "correct" way to make it work, but it seems to do the trick:
const { src, dest, watch, series, parallel } = require('gulp')
const postcss = require('gulp-postcss')
const rename = require('gulp-rename')
const touch = require('gulp-touch-cmd')
const watchPcss = () => {
watch(['src/**/*.pcss'], { followSymlinks: false }, series(pcss, css))
}
const css = () => {
return src('src/**/*.css').pipe(touch())
}
const pcss = () => {
return src('src/**/*.pcss')
.pipe(postcss([require('autoprefixer'), require('postcss-nested')]))
.pipe(
rename((path) => {
path.extname = '.css'
}),
)
.pipe(dest('./src'))
}
exports.watch = parallel(watchPcss)
exports.default = series(pcss)

How do I import a scss npm module into Ember

How do I correctly import a non-plugin npm module into Ember?
I'm trying to use the sass version of flag-icon-css with ember-cli so that the sass is being built during deploy with the rest of ember-cli-sass, but I can't figure out how to do it in an automated fashion (e.g. without manually copying files over to public).
Using ember-auto-import seems like a good place to start but it is more tailored towards javascript imports.
I have tried this configuration in ember-cli-build.js:
'sassOptions': {
includePaths: [
'node_modules/flag-icon-css/sass' // flag-icon.scss
]
},
It will add the styles, but it doesn't include the images used in the styles.
I have read this documentation, but it doesn't specify something more complicated than a single file.
Just use ember-cli-sass:
first add it to includePaths in your ember-cli-build.js
new EmberApp({
sassOptions: {
includePaths: [
'node_modules/flag-icon-css/sass'
]
}
});
use it with #import "flag-icon";
Have a look in the readme.
now while this will sucessfully add the compiled sass output to your /assets/app-name.js there is no automated way to add any kind of assets to your dist folder.
In the case of flag-icon-css it will just add background-image: url(../flags/4x3/gr.svg); to your dist/assets/app-name.css, but not add the svg itself. You can manually do this with broccolis Funnel and MergeTrees:
install broccoli-funnnel and broccoli-merge-trees
import them in ember-cli-build.js:
const Funnel = require('broccoli-funnel');
const MergeTrees = require('broccoli-merge-trees');
use them by replacing return app.toTree() in your ember-cli-build.js with
const flagIcons = Funnel('node_modules/flag-icon-css', { include: ['flags/**/*'] });
return new MergeTrees([app.toTree(), flagIcons]);
so your entire ember-cli-build.js could look like this:
'use strict';
const EmberApp = require('ember-cli/lib/broccoli/ember-app');
const Funnel = require('broccoli-funnel');
const MergeTrees = require('broccoli-merge-trees');
module.exports = function(defaults) {
let app = new EmberApp(defaults, {
// Add options here
sassOptions: {
includePaths: [
'node_modules/flag-icon-css/sass'
]
}
});
const flagIcons = Funnel('node_modules/flag-icon-css', { include: ['flags/**/*'] });
return new MergeTrees([app.toTree(), flagIcons]);
};
a short sidenote: I would usually recommend to put assets into the assets folder of your output, but in this case this wont work because the flag-icon-css expects the flags folder to be in the parent directory of the .css.
I figured this out, but I'm not sure it's the best or easiest way. It has some drawbacks.
const EmberApp = require('ember-cli/lib/broccoli/ember-app')
const Funnel = require('broccoli-funnel')
module.exports = function(defaults) {
const app = new EmberApp(defaults, {
'sassOptions': {
includePaths: [
'node_modules/flag-icon-css/sass'
]
}
})
const flags = new Funnel('node_modules/flag-icon-css/', {
srcDir: 'flags',
destDir: '/flags',
})
return app.toTree([flags])
}
The drawback is that the css image urls are not processed, and hardlinked to ../flags, so I have to funnel them into /flags, which is not the convention, as these assets should be compiled into public/assets/images.
This is a two-step implementation (or more steps if the npm module would be more complex). It would be preferred to include just the scss and have (an) Ember (plugin) automatically fetch the dependent resources.

Node.JS with Express and SASS - Compiled CSS file not applied

I'm new to NodeJS and I'm using Express to serve my pug files/view. Furthermore I'm using "express-sass-middleware" to compile and serve the scss/css files. Everything works very well but unfortunately, the CSS are not applied.
My app.js files looks like:
var express = require('express');
var sassMiddleware = require('express-sass-middleware');
var app = express();
app.set('view engine', 'pug');
app.get('/css/bootstrap.css', sassMiddleware({
file: 'css/bootstrap.scss', // the location of the entry point,
// this can also be a directory
precompile: true, // should it be compiled on server start
// or deferred to the first request
// - defaults to false
}));
app.get('/', function (req, res) {
res.render('index', {
varTitle: 'Hello World'
});
});
app.listen(3000, function() {
console.log('Example app listening on port 3000!');
});
And my simple css file looks like:
// $icon-font-path: /3rdparty/fonts;
// #import 'bootstrap/bootstrap';
// #import './node_modules/bootstrap-sass/assets/stylesheets/bootstrap/variables';
body
{
background-color: green;
font-size: 100px;
}
My index.pug file is:
doctype html
html(lang='en')
head
title= varTitle
link(ref='stylesheet' type='text/css' href='/css/bootstrap.css')
body
h1= varTitle
Now, when I start my webserver using "node app.js", accessing http://localhost:3000, I see "Hello World" but unfortunately the body background isn't green and the text is also not 100px. That means that the css file is not applied. But when I access http://localhost:3000/css/bootstrap.css, I see the valid, css file.
Anyone know what I'm missing here? I'm a bit confused that I see the CSS source when accessing it directly but the browser doesn't apply the css styling. I already tried different browsers without any success. None of them applying the css file.
You have typing error in index.pug file for loading css file. You had mentioned ref whereas it should be rel.
link(rel='stylesheet' type='text/css' href='/css/bootstrap.css')
Happy to help you.
you don't seem to be serving the static files from your nodejs server code. You have to add your css dir in order to allow access from your html code:
app.use('/static', express.static('public'))
Now, you can load the files that are in the public directory from the /static path prefix.
http://localhost:3000/static/images/kitten.jpg
http://localhost:3000/static/css/style.css
http://localhost:3000/static/js/app.js
http://localhost:3000/static/images/bg.png
http://localhost:3000/static/hello.html

gulp-base64 - stop on error

I use gulp as a build system in my web project
I have a number of CSS files (well, actually, LESS, but it does not matter)
I do link image files from my CSS rules in this way:
.my-rule {
background-image: url('/images/kitten.svg');
}
Next, I'm embedding those images as Base-64 into my production CSS files.
That is a gulp task:
var gulp = require("gulp"),
...
base64 = require('gulp-base64');
gulp.task("sass", function () {
return gulp.src(paths.styles)
.pipe(base64())
.pipe(concat("concatedstyles.css"))
.pipe(gulp.dest(paths.webroot + "css"));
});
The problem is:
When I link to non-existing file:
.my-rule {
background-image: url('/images/non-existing-kitten.svg');
}
the build system does not report me about failure.
It just leave this line as is.
So, I'm not aware about error at build time, only at runtime.
I tried to play with .pipe(sass.sync().on("error", sass.logError)), but no luck.
Any advice?

How to switch css file when using Webpack to load css?

I use gulp to compile my sass file to css files, and reference the css file in my html. The project support theme switch. For example, I have 3 css theme files:
red.css
yellow.css
blue.css
I can currently switch the theme css like this:
var styleDom = $('#theme-style');
var newHref = 'styles/themes/' + themeName + '.css';
if (styleDom.attr('href') !== newHref) {
styleDom.attr('href', newHref);
}
Now I want to use webpack to load the css file.
require('styles/themes/red.css');
It seems work well, but I cannot find a way to switch the theme css file now, does anyone have a solution?
Your approach doesn’t need to change. Just use Extract Text plugin to save out the CSS files. You’ll need to make multiple entry points to create multiple CSS files.
OR
More ideally, (the approach I would take) make your CSS switch based on a different html or body class and just change the class. It won’t add much overhead, and it will be a more ideal UX when changing themes.
You'll need to use a combination of webpacks style-loader and file-loader (second example ) and use require.ensure (second example "dynamic imports") to accomplish this:
function switchTheme(name) {
// Remove the current theme stylesheet
// Note: it is important that your theme css always is the last
// <link/> tag within the <head/>
$('head link[rel="stylesheet"]').last().remove();
// Since webpack needs all filePaths at build-time
// you can't dynamically build the filePath
switch(name) {
case 'red':
// With require.ensure, it is possible to tell webpack
// to only load the module (css) when require is actually called
return require.ensure([], function () {
require('style-loader/url!file-loader!styles/themes/red.css');
});
case 'yellow':
return require.ensure([], function () {
require('style-loader/url!file-loader!styles/themes/yellow.css');
});
case 'blue':
return require.ensure([], function () {
require('style-loader/url!file-loader!styles/themes/blue.css');
});
default:
throw new Error('Unknown theme "' + name + '"');
}
}
Then a call like switchTheme('blue') should do the trick.
And you might have to check your current webpack.config.js, in case you already have configured a loader for .css files.

Resources