What's the difference between node-sass and regular sass and which one do I use?
- Bootstrap 4 uses scss (Compass, which is built around sass)
- they use similar syntax but there's a difference in some of the functions
- node-sass is a library that provides binding for Node. It allows you to natively compile .scss files to css. Since using package managers like NPM, you'll be using Node.js. This library binds to your environment and Node version, to pre-process your scss files.
- using a css watcher in gulp, requires other packages to make it work
- you don't need gulp for a watcher, but then you most likely will have to install Ruby Compass (which is a bit outdated as a setup these days, I could be wrong)
What's the correct way to install Sass and set it up to watch for changes?
So hold on, you're already decided to use sass or are you referring to scss?
The right thing to do is to stick with what's in the Bootstrap package. If you use a package.json file, and you should, you'll be needing node-sass (I think). Probably there are other packages out there, but that's how I know it works.
Another thing to think about is the transpiler, like Babel. Babel requires a piece of setup in the package.json or in a .browserslistrc file.
So some essentials in the package.json:
{
"dependencies": {
"bootstrap": "^4.3.1", // your bs version
"font-awesome": "^4.7.0", // we'll get to fonts later
"jquery": "^3.4.1", // bs dependency
"popper.js": "^1.15.0" // bs dependency
},
"devDependencies": {
"@babel/core": "^7.5.5", // transpiler requirement
"@babel/preset-env": "^7.5.5", // transpiler requirement
"debounce": "^1.2.0", // gulp task, depends on your Gulpfile.js
"es6-promise": "^4.2.8",
"eslint": "^5.16.0",
"graceful-fs": "^4.2.2",
"gulp": "^4.0.2", // your task runner Gulp
"gulp-autoprefixer": "^6.1.0", // gulp util
"gulp-babel": "^8.0.0-beta.2", // transpiler for gulp tasks
"gulp-clean": "^0.4.0", // gulp util
"gulp-clean-css": "^3.10.0",
"gulp-cli": "^2.2.0", // gulp requirement
"gulp-concat": "^2.6.1", // gulp util
"gulp-fs-cache": "^0.1.0", // gulp util
"gulp-modernizr": "^3.3.0", // great way to detect js requirements
"gulp-sass": "^4.0.2", // node-sass version for gulp
"gulp-sourcemaps": "^2.6.5", // gulp util
"gulp-uglify": "^3.0.2", // gulp util
"gulp-util": "^3.0.8", // hey gulp util
"gulp-watch-sass": "^1.4.1", // css compiler
"invariant": "^2.2.4", // not sure, probably got some warning
"minimatch": "^3.0.4", // not sure, probably got some warning
"node-gyp": "^3.8.0", // meh
"npm": "^6.11.2", // your package manager
"requirejs": "^2.3.6", // requirements in Gulpfile.js
"stream-series": "^0.1.1", // task util
"strip-ansi": "^4.0.0" // not sure, probably got some warning
},
"browserslist": [
"> 0.5%",
"not ie <= 10",
"not dead"
]
}
So ok, not really node-sass but gulp-sass is a wrapper around node-sass, which in turn binds to your environment. It's easy to get mixed with packages these days, I know.
The Babel setup can be changed by using https://browserl.ist/. This is used for your autoprefixer in CSS. No need to write -webkit-blabla anymore. Depending on the browserslist setup, it will pick this for you.
If I want to use Google fonts, where do I import/link? In the HTML? In my custom SCSS file? Both?
What I prefer is to copy the package scss files and create a fonts.scss in the root.
From there we can import google fonts or custom fonts like font-awesome.
My fonts.scss file
@import '_variables/myprojectname.custom.variables';
/*
* font-weight: 100; // Thin (Hairline)
* font-weight: 200; // Extra Light (Ultra Light)
* font-weight: 300; // Light
* font-weight: 400; // Normal
* font-weight: 500; // Medium
* font-weight: 600; // Semi Bold (Demi Bold)
* font-weight: 700; // Bold
* font-weight: 800; // Extra Bold (Ultra Bold)
* font-weight: 900; // Black (Heavy)
*/
//https://github.com/webpack-contrib/less-loader/issues/67
//https://github.com/madskristensen/BundlerMinifier/issues/191
// DIN PRO (converted)
@font-face {
font-family: "DINPro";
src: url('#{$converted-font-path}/DINPro-Light.eot');
src: url('#{$converted-font-path}/DINPro-Light.eot?#iefix') format('embedded-opentype'), url('#{$converted-font-path}/DINPro-Light.woff2') format('woff2'), url('#{$converted-font-path}/DINPro-Light.woff') format('woff'), url('#{$converted-font-path}/DINPro-Light.ttf') format('truetype'), url('#{$converted-font-path}/DINPro-Light.svg#DINPro-Regular') format('svg');
font-weight: 300;
font-style: normal;
}
@font-face {
font-family: "DINPro";
src: url('#{$converted-font-path}/DINPro-Regular.eot');
src: url('#{$converted-font-path}/DINPro-Regular.eot?#iefix') format('embedded-opentype'), url('#{$converted-font-path}/DINPro-Regular.woff2') format('woff2'), url('#{$converted-font-path}/DINPro-Regular.woff') format('woff'), url('#{$converted-font-path}/DINPro-Regular.ttf') format('truetype'), url('#{$converted-font-path}/DINPro-Regular.svg#DINPro-Regular') format('svg');
font-weight: 400;
font-style: normal;
}
@font-face {
font-family: "DINPro";
src: url('#{$converted-font-path}/DINPro-Medium.eot');
src: url('#{$converted-font-path}/DINPro-Medium.eot?#iefix') format('embedded-opentype'), url('#{$converted-font-path}/DINPro-Medium.woff2') format('woff2'), url('#{$converted-font-path}/DINPro-Medium.woff') format('woff'), url('#{$converted-font-path}/DINPro-Medium.ttf') format('truetype'), url('#{$converted-font-path}/DINPro-Medium.svg#DINPro-Medium') format('svg');
font-weight: 500;
font-style: normal;
}
@font-face {
font-family: "DINPro";
src: url('#{$converted-font-path}/DINPro-MediumItalic.eot');
src: url('#{$converted-font-path}/DINPro-MediumItalic.eot?#iefix') format('embedded-opentype'), url('#{$converted-font-path}/DINPro-MediumItalic.woff2') format('woff2'), url('#{$converted-font-path}/DINPro-MediumItalic.woff') format('woff'), url('#{$converted-font-path}/DINPro-MediumItalic.ttf') format('truetype'), url('#{$converted-font-path}/DINPro-MediumItalic.svg#DINPro-MediumItalic') format('svg');
font-weight: 500;
font-style: italic;
}
@font-face {
font-family: "DINPro";
src: url('#{$converted-font-path}/DINPro-Bold.eot');
src: url('#{$converted-font-path}/DINPro-Bold.eot?#iefix') format('embedded-opentype'), url('#{$converted-font-path}/DINPro-Bold.woff2') format('woff2'), url('#{$converted-font-path}/DINPro-Bold.woff') format('woff'), url('#{$converted-font-path}/DINPro-Bold.ttf') format('truetype'), url('#{$converted-font-path}/DINPro-Bold.svg#DINPro-Bold') format('svg');
font-weight: 700;
font-style: normal;
}
// HELVETICA ROUNDED
@font-face {
font-family: "Helvetica";
src: url('#{$cond-font-path}/HelveticaRounded-BoldCond.eot');
src: url('#{$cond-font-path}/HelveticaRounded-BoldCond.eot?#iefix') format('embedded-opentype'), url('#{$cond-font-path}/HelveticaRounded-BoldCond.woff2') format('woff2'), url('#{$cond-font-path}/HelveticaRounded-BoldCond.woff') format('woff'), url('#{$cond-font-path}/HelveticaRounded-BoldCond.ttf') format('truetype'), url('#{$cond-font-path}/HelveticaRounded-BoldCond.svg#HelveticaRounded-BoldCond') format('svg');
font-weight: 700;
font-stretch: condensed;
font-style: normal;
}
// ICON
@font-face {
font-family: $ico-font-family;
src: url('#{$ico-font-path}/#{$ico-font-family}.eot?v=#{$ico-font-version}');
src:
//url('#{$ico-font-path}/WHN-Icons.eot?#iefix&v=#{$ico-font-version}') format('embedded-opentype'),
url('#{$ico-font-path}/#{$ico-font-family}.woff?v=#{$ico-font-version}') format('woff'),
url('#{$ico-font-path}/#{$ico-font-family}.ttf?v=#{$ico-font-version}') format('truetype'),
url('#{$ico-font-path}/#{$ico-font-family}.svg?v=#{$ico-font-version}#fontawesomeregular') format('svg');
font-weight: normal;
font-style: normal;
}
Notice the first line how I use a custom variables file. This is simply a copy of Bootstrap's _variables.scss. I re-create the setup in my own scss file and override the variables file.
The most important reason for using a separate file is because fonts render block while loading. Separating assets (fonts, artwork, images, ...) allows me to use async CSS <link /> to eliminate render blocking issues.
The custom bootstrap setup projectname.atomic.scss:
/*!
* Atomic design principle
*/
@import "../vendor/bootstrap/scss/functions";
@import "_variables/fa.custom.variables"; // font awesome override variables
@import "_variables/projectname.custom.variables"; // bootstrap override variables
@import "../vendor/bootstrap/scss/variables";
@import "../vendor/bootstrap/scss/mixins";
@import "_mixins/company.custom.mixins"; // custom mixins
@import "../vendor/bootstrap/scss/root";
@import "../vendor/bootstrap/scss/reboot";
@import "../vendor/bootstrap/scss/type";
@import "../vendor/bootstrap/scss/images";
@import "../vendor/bootstrap/scss/code";
@import "../vendor/bootstrap/scss/grid";
@import "../vendor/bootstrap/scss/tables";
@import "../vendor/bootstrap/scss/forms";
@import "../vendor/bootstrap/scss/buttons";
@import "../vendor/bootstrap/scss/transitions";
@import "../vendor/bootstrap/scss/dropdown";
@import "../vendor/bootstrap/scss/button-group";
@import "../vendor/bootstrap/scss/input-group";
@import "../vendor/bootstrap/scss/custom-forms";
@import "../vendor/bootstrap/scss/nav";
@import "../vendor/bootstrap/scss/navbar";
@import "../vendor/bootstrap/scss/card";
@import "../vendor/bootstrap/scss/breadcrumb";
@import "../vendor/bootstrap/scss/pagination";
@import "../vendor/bootstrap/scss/badge";
@import "../vendor/bootstrap/scss/jumbotron";
@import "../vendor/bootstrap/scss/alert";
@import "../vendor/bootstrap/scss/progress";
@import "../vendor/bootstrap/scss/media";
@import "../vendor/bootstrap/scss/list-group";
@import "../vendor/bootstrap/scss/close";
@import "../vendor/bootstrap/scss/modal";
@import "../vendor/bootstrap/scss/tooltip";
@import "../vendor/bootstrap/scss/popover";
@import "../vendor/bootstrap/scss/carousel";
@import "../vendor/bootstrap/scss/utilities";
@import "../vendor/bootstrap/scss/print";
// This is where my real work comes in, for big projects this has proven to be succesful in maintaining and multisite setup
@import "atoms/atoms";
@import "molecules/molecules";
@import "organisms/organisms";
@import "tenants/projectname/themes/themes";
@import "tenants/projectname/pages/pages";
By re-creating the setup, it becomes easy to override the default. Just be aware of the !default usage when you're inserting your own variables and not use !default when it's a bootstrap variable.
A piece of my _projectname.custom.variables.scss
$design-path: "/Design" !default;
$font-family-headings: Helvetica, sans-serif !default;
$font-family-headings-fallback: DINPro, sans-serif !default;
$cond-font-path: "../../../fonts/app/helvetica-rounded-converted" !default;
$pro-font-path: "" !default;
$converted-font-path: "../../../fonts/app/din-pro-converted" !default;
$ico-font-path: "../../../fonts/app/icomoon/projectname" !default;
$ico-font-version: "xyftn5" !default;
$ico-font-family: 'icomoon' !default;
$theme-green: #7EC796 !default; // de york
$theme-yellow: #FFBD51 !default; // texas rose
$theme-red: #E24B43 !default; // cinnabar
$theme-blue: #7CB1D1 !default; // half baked
$theme-pink: #FE8980 !default; // vivid tangerine
$theme-cyan: #A5B54E !default; // olive green
$theme-indigo: #12884E !default; // salem
$theme-orange: #F77D54 !default; // coral
$theme-teal: #307A83 !default; // paradiso
// FONTAWESOME VARIABLES override
$fa-var-arrow-down: "\e903";
$fa-var-arrow-left: "\e900";
$fa-var-arrow-right: "\e901";
$fa-var-arrow-up: "\e902";
$fa-var-bars: "\e914";
$fa-var-calendar: "\e908";
$fa-var-chevron-down: "\e907";
$fa-var-chevron-left: "\e904";
$fa-var-chevron-up: "\e906";
$fa-var-chevron-right: "\e905";
// BOOTSTRAP VARIABLES override
$grid-breakpoints: ( xs: 0, sm: 576px, md: 768px, lg: 992px, xl: 1200px, xxl: 1400px );
$grid-gutter-width: 2rem;
$grid-gutter-fluid-width: 2.1875rem !default; // custom variable
$container-max-widths: ( sm: 540px, md: 720px, lg: 960px, xl: 1140px, xxl: 1340px );
// and many many more become available at your fingertips
What is the correct way to import the Bootstrap functions I want to override?
This should be covered in the previous question.
Simply create your own _functions.scss and add it below the original one.
@import "../vendor/bootstrap/scss/functions";
@import "_functions/company.custom.functions"; // override or extend, your choice
I realise this is a lot to take in, but you asked quite a lot :)
Seems like you got the hang of it already or just some pieces here and there. Feel free to ask more info, like the Gulpfile.js or something.
Good luck and have fun digesting one of my longer answers ^^