How to use critical css with angular views - css

I want to use grunt-critical (https://www.npmjs.com/package/grunt-critical) within the Grunt taskmanager (first time using it) to increase the loadtime of my web application.
The web-app is built on AngularJS with different HTML partials. All my partials are loaded within my <div ui-view=""></div>. However when I try to run grunt-critical on the index (with the ui-view) the partials for the homepage are not loaded yet, making it hard for grunt-critical to know which classes are needed for the first render. Is there a way to solve this?
My grunt-critical task is as follows:
test: {
options: {
base: './',
css: 'src/styles/import.less',
width: 1280,
height: 960
},
src: 'src/statics/index.html',
dest: 'src/statics/index-critical.html'
}
Thanks guys!

Related

How can I make a sass file global instead of importing it in every file and bloating bundle size?

Question
How can I globally import variables.scss 1. without importing them in every file and 2. by referencing instead of duplicating them in my build?
Setup
I use Vue2 and laravel-mix and I have index.scss imported in my main.js
variables.scss
$--test: #ff0000;
index.scss
#import 'variables';
.dashboard-title {
color: $--test;
}
This colors the title red. But when I try to do the same thing inside of the component, it doesnt work:
<style scoped lang="scss">
.dashboard-title {
color: $--test;
}
</style>
This doesn't work, but I proved that index.scss is global in my first example. How is
variables.scss not global, when I import it in my global index.scss?
I can fix the error by importing the variables file in the component, but by doing this, I essentially duplicate the whole variables.scss file every time I import it in a vue component.
I found this out by analyzing my bundle with a webpack bundle analyzer, this is the output:
webpack bundle analysis image (all blue crossed parts are increased in size because the variables file is imported, this isn't a big problem now, but this will exponentially increase my bundle size with time)
It would reduce my bundle size by atleast 20% right now...
How can I reference the variables.scss file instead of duplicating its content?
What I've tried:
https://css-tricks.com/how-to-import-a-sass-file-into-every-vue-component-in-an-app/ (I wasn't able to "migrate" this to a laravel-mix config)
I've also tried using purgeCss to remove duplicate css, this just completely messed up my styles but reduced the bundle size by 50% lol
Adding this to the webpack.mix.js
mix.webpackConfig({
module: {
rules: [
{
test: /\.scss$/,
use: [
{
loader: 'sass-loader',
options: {
//this might be "data" or "prependData" depening on your version
additionalData: `#import "./resources/js/styles/variables.scss";`
}
}
]
}
]
}
})
This does make the variables global, but imports(duplicates) them for every vue component, even if they aren't being used.
Edit: This only is an issue, when the imported file is relatively big. In my project, the imported file itsself imported a theme scss (to get access to the themes variables), which ultimately copied this whole thing everywhere I needed the variables.
I fixed this by defining my custom variables in a seperate file and using those variables in the "overwriting-variables" file, something like this:
custom-variables.scss
$red: #ff0000;
overwriting-variables.scss
import 'theme.scss'; //this bloated my project
import 'custom-variables';
$--theme-red: $red
And when I needed this theme color in my vue components I just imported the custom-variables.scss instead of overwriting-variables.scss.
This does fix my bloating issue, but doesn't fully solve the problem, I still have multiple instances of the custom-variables.scss in my project, it just doesn't matter (yet) because its really small. So I'd be still happy to hear about other solutions!
If you import every .scss in your index.scss, then every variable should work. Try this in your vue.config
css: {
loaderOptions: {
// by default the `sass` option will apply to both syntaxes
// because `scss` syntax is also processed by sass-loader underlyingly
// but when configuring the `data` option
// `scss` syntax requires an semicolon at the end of a statement, while `sass` syntax requires none
// in that case, we can target the `scss` syntax separately using the `scss` option
scss: {
prependData: `#import "#/style/index.scss"`
}
}
},
So I got it working with laravel-mix like this:
mix.webpackConfig({
module: {
rules: [
{
test: /\.scss$/,
use: [
{
loader: 'sass-loader',
options: {
//this might be "data" or "prependData" depening on your version
additionalData: `#import "./resources/js/styles/variables.scss";`
}
}
]
}
]
}
})
Not sure yet if this prevents the duplication though
Edit: It does not prevent duplication, it does increase the bundle size in every vue component. So I now have a 150% bigger bundle, because the variables file is imported in every single vue component. Even if the variable isn't even used.

Can't access global SASS variables from my component

In my Nuxt app I load all my SASS thus:
css: [
'~assets/scss/main.scss'
],
It works perfectly, except when I'm trying to use some SASS variable from within a component.
<style lang="scss">
.container {
background-color: $white;
}
</style>
In this case I get this error message:
SassError: Undefinied variable: $white
Yet, all of the SCSS contained in the SASS file where the variable is defined works throughout the app.
It is as if the app as a whole knew these files, but each individual component doesn't.
What's going on?
Most of the other answers don't take into account that Nuxt.js hides all the Webpack setup and forces you to do everything through nuxt.config.js.
My guess is that Webpack isn't compiling all the SCSS declarations together and therefore can't find the variable.
It's been a few months since I had this issue so things may have changed but here goes...
Make sure you have the correct Node packages installed (Nuxt DID NOT do this by default for me) npm i -D node-sass sass-loader
Add your CSS & SCSS files to the css: [] section of nuxt.config.js Order matters here so make sure things like variables are added before things that use them if you have separate files.
If you're using layouts (I think that's the default Nuxt setup) make sure that layouts/default.vue has a <style lang="sass"></style> block in it. If I remember correctly this can be empty but had to exist. I only have one layout but it may need to exist in all of them.
If all that seems like too much of a pain, there's a Nuxt Plugin that takes most of the work/management out of that process. Nuxt Style Resources Module
The confusing part is that:
styles from scss files CAN be loaded like this
//nuxt.config.js
css: [
'~assets/scss/main.scss'
],
//global scss file
$varcolor: black
h1{background: $varcolor}
BUT
the variables inside CAN NOT be used inside a component
//inside component
.component {background: $varcolor} // DOES NOT WORK
I also suggest the use of the nuxt style resource module:
https://github.com/nuxt-community/style-resources-module
new founded solution, checked and it's work. Founded here
add #nuxtjs/style-resources
export default {
css: [
'vendor.min.css'
],
modules: [
'#nuxtjs/style-resources'
],
//You will have to add this new object if it doesn't exist already
styleResources: {
scss: ['./assets/scss/main.scss'] // here I use only main scss with globally styles (variables, base etc)
},
}
it's strange, but if u change tilda (~) to dot(.), it's help for someone
from css: [ '~assets/scss/main.scss' ] to css: [ './assets/scss/main.scss' ]
this solution finded here
Us should either load the scss in your component
<style lang="sass">
#import 'path/to/your/_variable.scss'; // Using this should get you the variables
.my-color {
color: $primary-color;
}
Or adding the following to you to your vue.config.js
module.exports = {
css: {
loaderOptions: {
sass: {
data: `#import "#/pathto/variables.scss";`
}
}
}
};
Ref:
SassError: Undefinied variable: $white
Each <style lang="scss"> is compiled individually. You need to #import the file which defines $white into your component before the parser knows what $white means.
This is why most frameworks keep their variables in a _variables.scss file which is imported in all the other SCSS files/contexts.
The _variables.scss is not even loaded in the page, because in most cases it doesn't actually contain any rules. It only contains variable definitions which are imported into other .scss files, which output .css.
Ref:
Yet, all of the SCSS contained in the SASS file where the variable is defined works throughout the app.
If you import an SCSS file in your vue.config.js the output will be an ordinary <style> tag. Its contents will be generated at compile/build time and will result into some CSS (which apply to the entire document).
Unless specifically imported into the component SCSS, (using an #import command), the compiler will not know what $white means.
There is an important distinction to make between compilation context and browser context. Compilation happens at compile time (most likely in node-sass). Browser context is the actual browser, which only understands the CSS resulted from compilation.
How does Vue only apply style rules to the parent and not to the children with the same class? That's achieved by scoping.
It means applying a custom data-v-{key} attribute to all selectors in the generated <style> tag and to all elements the style should apply to.
See this example and inspect it using your web console: https://codesandbox.io/s/vue-template-ge2hb
It produces this markup:
As you can see, the scoped CSS has an extra [data-v-763db97b] added to the selector, which means it only applies to elements having that data attribute.

Vue.js CSS bundle precedence (?)

I have a weird issue with Vue.js CLI production bundling where I cannot quite pinpoint the root cause and I appreciate some help.
I have a Vue CLI 3 application with the following (relevant extract) in my main.js:
// Bootstrap
import "#/assets/bootstrap/bootstrap.scss";
import "bootstrap-vue/dist/bootstrap-vue.css";
import BootstrapVue from "bootstrap-vue";
Vue.use(BootstrapVue);
// Toastr
import Toastr from "vue-toastr";
import "vue-toastr/dist/vue-toastr.css";
Vue.use(Toastr, {
defaultPosition: "toast-bottom-right"
});
Running this in my dev environment (npm run serve) CSS works fine.
When I run this after the production complilation (npm run build) some classes... are not applied and I cannot reason why. Given that the only difference I can see is the bundling process, I'm incline to look for an issue in that direction.
I customized the bundling as follow (relevant extract) in my vue.config.js:
cacheGroups: {
icons: {
name: "icons",
test: /[\\/]node_modules[\\/](#fortawesome)[\\/]/,
chunks: "all",
priority: 3
},
vendors: {
name: "vendors",
test: /[\\/]node_modules[\\/]/,
chunks: "all",
priority: 1
}
}
And, as result, my CSS bundled are correctly created as follows:
a vendor chunk that includes the Toaster CSS.
This includes a 'toaster' class and a 'toaster-info' class (this latest has just a background-color)
a app chink that include my custom built bootstrap CSS.
As much as the bootstrap files are in the node_modules folder and, as such, they should go in the previous chink, they get in here because I'm compiling them as import in a the SASS file above that actually comes from by code.
This includes a 'toaster' class again.
Now, what I can see is that:
both chunks appear to be loaded by the browser
the markup correctly uses the 'toastr toastr-info'
only the 'toaster' class from app (bootstrap) is applied
the 'toaster' and 'toaster-info' class are totally ignored by the browser and the background color from 'toaster-info' is not applied
I tested this with several browsers to exclude any specific browser weirdness.
Browser computed styles shows that the classes are "excluded" for some reason I don't understand (with "excluded" meaning are in the style tree but strikethrough).
Can anyone help me understand why this is happening?
Thank you.
This is purely a CSS problem.
Since your app and vendor CSS defines a .toast class style, whatever is loaded last is given the highest specificity.
I'm assuming all you're trying to do is change the default .toast background colour to white (instead of black) but you want to leave the more specific classes like .toast-info, .toast-success, etc as they are.
To do so (without altering the vendor files), you can change your .toast definition to...
.toast:not(.toast-info),
.toast:not(.toast-success),
.toast:not(.toast-warning),
.toast:not(.toast-error) {
background-color: rgba(255, 255, 255, .85);
}
If you're using a pre-processor like Sass (which I think you are), this can be written more succinctly.
Ideally, the vendor files should have defined their styles like...
.toast {
background-color: #030303;
}
.toast.toast-success {
background-color: #51a351;
}
which would give the "info", "success", etc styles a higher specificity than the .toast class but it doesn't look like they do. Perhaps you could create a pull-request 🙂

How can I add a namespace to all my .less files in a project?

I am working with two web applications in the same repository, and need a way to 'namespace' the CSS class names for each application.
I am using LESS as my style preprocessor, and and using webpack to bundle all the LESS files into a single .css file which then gets loaded into the webpage via HTML.
What I am currently doing is manually prepending every CSS class with my namespace like so:
// mylessfile.less
.nmsp {
&__some-class-name {
color: blue;
}
}
this is annoying and not very maintainable.
Would something like this scale?:
// global.less
#my_namespace="nmsp";
// mylessfile.less
#import "global.less";
#my_namespace {
&__some-class-name {
color: blue;
}
}
Is there a way to do this via LESS or even Webpack?
Ideally I would like to use a namespace for app1, app2, and a "common" styles namespace for all the styles that both applications share.
SASS and LESS both support namespacing out of the box.
.app1-styles {
#import (app1) url("app1.css");
}
Webpack supports CSS Modules as a way to scope an app. Using CSS Modules gets you namespacing as well.
In your webpack config:
{
test: /\.css$/,
loader: 'style!css-loader?modules&importLoaders=1&localIdentName=[name]__[local]___[hash:base64:5]'
}
Stuff inside the [ ] should be customized to your app.
Here's a demo project repo

Extract CSS in NUXTjs generate

When using nuxt generate I am generating various HTML pages that happen to be about 300 kB in size. Majority of the file is CSS style placed inline to it. Is it a way to put it in an external file and reduce size of HTML ?
You can extract the CSS in the main chunk into a separate CSS file using
nuxt.config.js
module.exports = {
build: {
extractCSS: true
}
}
This is documented here
If you want to import css files globally use
module.exports = {
css: [
// Load a Node.js module directly (here it's a Sass file)
'bulma',
// CSS file in the project
'#/assets/css/main.css',
// SCSS file in the project
'#/assets/css/main.scss'
]
}
as documented here

Resources