mirror of
https://github.com/kogakure/website-astro-stefanimhoff.de.git
synced 2026-02-03 12:05:28 +00:00
feat: move essays of 2014
This commit is contained in:
1
public/assets/images/cover/gulp.svg
Normal file
1
public/assets/images/cover/gulp.svg
Normal file
File diff suppressed because one or more lines are too long
|
After Width: | Height: | Size: 8.5 KiB |
BIN
public/assets/images/posts/moleskine-pencil-drawings.jpg
Normal file
BIN
public/assets/images/posts/moleskine-pencil-drawings.jpg
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 56 KiB |
BIN
public/assets/images/posts/rakkan.jpg
Normal file
BIN
public/assets/images/posts/rakkan.jpg
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 40 KiB |
@@ -17,7 +17,7 @@ export const Figure: FunctionalComponent<Props> = ({
|
|||||||
...props
|
...props
|
||||||
}) => {
|
}) => {
|
||||||
const classes = cx(
|
const classes = cx(
|
||||||
'rounded-4 bg-white/50 p-10 mbe-13 mbs-0 mie-0 mis-0 dark:bg-black/80',
|
'rounded-4 bg-white/50 p-8 mbe-13 mbs-0 mie-0 mis-0 dark:bg-black/80',
|
||||||
{ 'figure-wide': size === 'wide', 'figure-fullsize': size === 'fullsize' },
|
{ 'figure-wide': size === 'wide', 'figure-fullsize': size === 'fullsize' },
|
||||||
className
|
className
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -21,4 +21,6 @@ Each page of the book shows three colors that are always juxtaposed with one or
|
|||||||
|
|
||||||
I took the time to create the RGB palette with the correct color names as an Adobe Exchange file (ASE). This color palette can be used in Photoshop or Illustrator.
|
I took the time to create the RGB palette with the correct color names as an Adobe Exchange file (ASE). This color palette can be used in Photoshop or Illustrator.
|
||||||
|
|
||||||
<MoreLink href="/traditional-colors-of-japan/" text="See Traditional Colors of Japan" />
|
<Figure>
|
||||||
|
<MoreLink href="/traditional-colors-of-japan/" text="See Traditional Colors of Japan" />
|
||||||
|
</Figure>
|
||||||
|
|||||||
@@ -41,4 +41,6 @@ The exact installation instructions are included in the package on GitHub.
|
|||||||
<Image src="/assets/images/posts/gitweb-theme-tree.png" alt="Tree View" />
|
<Image src="/assets/images/posts/gitweb-theme-tree.png" alt="Tree View" />
|
||||||
</Figure>
|
</Figure>
|
||||||
|
|
||||||
<MoreLink href="https://github.com/kogakure/gitweb-theme/" text="GitWeb Theme on GitHub" />
|
<Figure>
|
||||||
|
<MoreLink href="https://github.com/kogakure/gitweb-theme/" text="GitWeb Theme on GitHub" />
|
||||||
|
</Figure>
|
||||||
|
|||||||
147
src/content/journal/2014/gulp-tutorial-1-intro-setup.mdx
Normal file
147
src/content/journal/2014/gulp-tutorial-1-intro-setup.mdx
Normal file
@@ -0,0 +1,147 @@
|
|||||||
|
---
|
||||||
|
title: "Introduction to Gulp.js 1: Intro and Setup"
|
||||||
|
slug: gulp-tutorial-1-intro-setup
|
||||||
|
author: Stefan Imhoff
|
||||||
|
date: 2014-10-18T08:35:47+02:00
|
||||||
|
description: "The ultimate tutorial and guide for Gulp.js: The first part of my series on Gulp.js. What is Gulp.js? Why use it? And how to install Gulp and Jekyll."
|
||||||
|
cover: /assets/images/cover/gulp.svg
|
||||||
|
tags: ["code"]
|
||||||
|
series: gulp
|
||||||
|
---
|
||||||
|
|
||||||
|
My website is running [Jekyll](https://jekyllrb.com/) now since the beginning of 2014. But I wasn’t happy with my build and development process. I started with Rake tasks and chose later [Grunt.js](https://gruntjs.com/) as my build system, but parts of the process were left in Ruby. I used [Compass](http://compass-style.org/) a lot, and [Jekyll Assets](http://ixti.net/jekyll-assets/) was handling my versioning. But Grunt.js and the Jekyll Asset Pipeline didn’t play well together. Then a new solution came along: [Gulp.js](https://gulpjs.com/).
|
||||||
|
|
||||||
|
## What to expect from this Tutorial
|
||||||
|
|
||||||
|
This is the first part in a series of posts where I describe my whole development and build process step-by-step from start to finish. I am sick of all these _Hello World_ tutorials spreading around the Internet, describing the basics and don’t show a whole process, going deeper, or sharing things learned during the process.
|
||||||
|
|
||||||
|
I’ll describe everything **step-by-step** and provide **specific version numbers** for modules, to make sure they will run on your computer. If you have problems, feel free to compare your code to the finished [GitHub repository](https://github.com/kogakure/gulp-tutorial). Each tutorial builds on the things done before. If you need a specific thing, better look at the final codebase. You’ll learn how I created my complete development and build process, which I use on this website and my [Ninja & Ninjutsu website](https://www.kogakure.de). The source of both websites can be found on my [GitHub account](https://github.com/kogakure).
|
||||||
|
|
||||||
|
When I started with Gulp.js, I fortunately stumbled upon a GitHub project called <del>gulp-starter</del> <ins>[blendid](https://github.com/vigetlabs/blendid)</ins>, that helped me a lot to structure my code and understand Gulp.js. My process is partly derived from this fantastic project.
|
||||||
|
|
||||||
|
## What is Gulp.js?
|
||||||
|
|
||||||
|
Gulp.js is _the streaming build system_ and its main focus is speed, efficiency, and simplicity. Where Grunt.js uses a lot of configuration with the process hidden in plugins, Gulp.js uses a simple and minimal API. You code your build process by yourself and use JavaScript as the language. You don’t have to program everything by yourself, there are nearly 800 plugins ready for Gulp.js. But even more, Node.js modules can be used to build the perfect build and development process for **your** needs.
|
||||||
|
|
||||||
|
## Why do I want this at all?
|
||||||
|
|
||||||
|
As a front-end developer or web designer you will likely need plenty of things to build a modern website: a development server, a preprocessor for your [Sass](http://sass-lang.com/), [Less](http://lesscss.org/) or [Stylus](http://learnboost.github.io/stylus/) files, automation to bundle your JavaScript, tools to optimize your code, to compress, compile or move things around. And if you change something, you want your files to update automatically, or refresh the browser. You don’t want to do this manually, do you?
|
||||||
|
|
||||||
|
It’s 2014, and we don’t copy our files per drag-and-drop to a server via an FTP program, reload our browser by hitting continuously <kbd>F5</kbd> or crunch our images for a smaller file size manually.
|
||||||
|
|
||||||
|
## Node.js
|
||||||
|
|
||||||
|
Gulp and all plugins are written in JavaScript and use the Node.js® platform. You don’t have to know Node.js (but it will help a lot), but basic JavaScript skills are required to follow along. For this tutorial, you need to install Node.js on your computer. This can be done by installing the package on the [Node.js website](https://nodejs.org/). Advanced users may install Node.js with their favorite package installer (Homebrew, nvm, or similar).
|
||||||
|
|
||||||
|
## Gulpfile
|
||||||
|
|
||||||
|
As with Grunt.js, all you need to start is the main file. In Gulp.js, this file is called `gulpfile.js`. The first thing I learned from `gulp-starter` was to write my project in small parts and don’t use a large monolithic file with everything in it. This way I can share my Gulp.js files with other projects or pass individual tasks around.
|
||||||
|
|
||||||
|
My base `gulpfile.js` is short:
|
||||||
|
|
||||||
|
#### gulpfile.js
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
var requireDir = require("require-dir");
|
||||||
|
|
||||||
|
// Require all tasks in gulp/tasks, including subfolders
|
||||||
|
requireDir("./gulp/tasks", { recurse: true });
|
||||||
|
```
|
||||||
|
|
||||||
|
All this task is doing is loading all tasks that live in `./gulp/tasks` or in any subfolder.
|
||||||
|
|
||||||
|
## First Things First
|
||||||
|
|
||||||
|
The first thing to do is to install the required Node module `require-dir`. To reinstall all my node modules later, I will need to have a file where this information is saved.
|
||||||
|
|
||||||
|
You may use the installation helper by typing the command `npm init`. But I find it faster to create a new file `package.json` and fill it with these values:
|
||||||
|
|
||||||
|
#### package.json
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"name": "gulp-build",
|
||||||
|
"version": "0.0.1",
|
||||||
|
"description": "The build process of my website with Gulp.js",
|
||||||
|
"private": true
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Now I can install Node modules and save them to this file for later reinstallation. Go ahead and install `require-dir`:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
$ npm install --save-dev require-dir@0.3.0
|
||||||
|
```
|
||||||
|
|
||||||
|
This will install our first Node module and place it into a folder with the name `node_modules`. Don’t delete this folder, or you will have to reinstall all modules again. This can be done later with `npm install`. The command will install all modules in the `package.json` file.
|
||||||
|
|
||||||
|
My Jekyll website lives in a folder called `app`. All my tasks will be placed in a folder with the name `gulp`. Go ahead and create a folder, and within a subfolder with the name `tasks`. After installing the first module, the structure of our project will look like this:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
.
|
||||||
|
├── app
|
||||||
|
│ ├── _assets
|
||||||
|
│ ├── _data
|
||||||
|
│ ├── _drafts
|
||||||
|
│ ├── _includes
|
||||||
|
│ ├── _layouts
|
||||||
|
│ ├── _plugins
|
||||||
|
│ ├── _posts
|
||||||
|
│ └── index.html
|
||||||
|
├── gulp
|
||||||
|
│ └── tasks
|
||||||
|
├── gulpfile.js
|
||||||
|
├── node_modules
|
||||||
|
│ └── require-dir
|
||||||
|
└── package.json
|
||||||
|
```
|
||||||
|
|
||||||
|
## Jekyll
|
||||||
|
|
||||||
|
This tutorial uses [Jekyll](https://jekyllrb.com/) to build the HTML of the website. Creating a new Jekyll website is fast and easy:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
$ gem install jekyll
|
||||||
|
$ jekyll new app
|
||||||
|
$ cd app
|
||||||
|
$ jekyll serve
|
||||||
|
```
|
||||||
|
|
||||||
|
I don’t install Jekyll globally, but with [Bundler](https://bundler.io/). This way, all my Gems will be installed with my project and I don’t have to be concerned about the correct version.
|
||||||
|
|
||||||
|
I install Bundler globally:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
$ gem install bundler
|
||||||
|
```
|
||||||
|
|
||||||
|
Now I create an empty file in my projects folder with the name `Gemfile` and add these lines:
|
||||||
|
|
||||||
|
```ruby
|
||||||
|
source "https://rubygems.org"
|
||||||
|
|
||||||
|
gem 'jekyll', '~> 2.5.2'
|
||||||
|
gem 'sass', '>= 3.3'
|
||||||
|
```
|
||||||
|
|
||||||
|
Now I install the Gems by typing:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
$ bundle install
|
||||||
|
```
|
||||||
|
|
||||||
|
If you installed Jekyll with Bundler, run inside your project directory:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
$ bundle exec jekyll new app
|
||||||
|
```
|
||||||
|
|
||||||
|
Did you install it globally? Then drop the `bundle exec` from the command.
|
||||||
|
|
||||||
|
## Conclusion
|
||||||
|
|
||||||
|
This concludes the 1st part of my series _Introduction to Gulp.js_. We learned what Gulp.js is used for and created the basic folder structure for the project.
|
||||||
|
|
||||||
|
<Figure>
|
||||||
|
<MoreLink href="https://github.com/kogakure/gulp-tutorial" text="View Source on GitHub" />
|
||||||
|
</Figure>
|
||||||
@@ -0,0 +1,86 @@
|
|||||||
|
---
|
||||||
|
title: "Introduction to Gulp.js 10: Generating CSS Image Sprites"
|
||||||
|
slug: gulp-tutorial-10-generating-sprites
|
||||||
|
author: Stefan Imhoff
|
||||||
|
date: 2014-10-27T07:40:00+02:00
|
||||||
|
description: "The ultimate tutorial and guide for Gulp.js: How to generate image sprite maps with Spritesmith."
|
||||||
|
cover: /assets/images/cover/gulp.svg
|
||||||
|
tags: ["code"]
|
||||||
|
series: gulp
|
||||||
|
---
|
||||||
|
|
||||||
|
This is the 10th part of my series, _Introduction to Gulp.js_. Today, I will use Gulp.js to create CSS image sprites.
|
||||||
|
|
||||||
|
Just to be sure everybody knows what I’m talking about: A CSS image sprite is a collection of images put into a single image. This way, fewer requests are needed, and the website will load faster. The CSS file will move the image for each sprite to the correct position.
|
||||||
|
|
||||||
|
CSS image sprites are not used often anymore, because of SVG or vector fonts. But I use them as a fallback for browsers incapable of displaying vector fonts.
|
||||||
|
|
||||||
|
I will need a Spritesmith plugin for Gulp.js:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
$ npm install --save-dev gulp.spritesmith@4.1.1
|
||||||
|
```
|
||||||
|
|
||||||
|
#### gulp/config.js
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
sprites: {
|
||||||
|
src: srcAssets + '/images/sprites/icon/*.png',
|
||||||
|
dest: {
|
||||||
|
css: srcAssets + '/scss/base/',
|
||||||
|
image: srcAssets + '/images/sprites/'
|
||||||
|
},
|
||||||
|
options: {
|
||||||
|
cssName: '_sprites.scss',
|
||||||
|
cssFormat: 'css',
|
||||||
|
cssOpts: {
|
||||||
|
cssClass: function (item) {
|
||||||
|
// If this is a hover sprite, name it as a hover one (e.g. 'home-hover' -> 'home:hover')
|
||||||
|
if (item.name.indexOf('-hover') !== -1) {
|
||||||
|
return '.icon-' + item.name.replace('-hover', ':hover');
|
||||||
|
// Otherwise, use the name as the selector (e.g. 'home' -> 'home')
|
||||||
|
} else {
|
||||||
|
return '.icon-' + item.name;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
imgName: 'icon-sprite.png',
|
||||||
|
imgPath: '/assets/images/sprites/icon-sprite.png'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
I split my config into three subsections: The source files (individual icons for the sprite), the destination for the sprite, and the CSS partial and the options for the image sprite. I use a custom `cssClass` which will generate `:hover` states by naming the hover sprites with `-hover`.
|
||||||
|
|
||||||
|
#### gulp/tasks/development/sprites.js
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
var gulp = require("gulp");
|
||||||
|
var spritesmith = require("gulp.spritesmith");
|
||||||
|
var config = require("../../config").sprites;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generate sprite and CSS files from PNGs
|
||||||
|
*/
|
||||||
|
gulp.task("sprites", function () {
|
||||||
|
var spriteData = gulp.src(config.src).pipe(spritesmith(config.options));
|
||||||
|
|
||||||
|
spriteData.img.pipe(gulp.dest(config.dest.image));
|
||||||
|
|
||||||
|
spriteData.css.pipe(gulp.dest(config.dest.css));
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
In the end, I get two files: a partial `_sprites.scss` containing the class attributes and a sprite `icon-sprite.png` containing all images.
|
||||||
|
|
||||||
|
All development tasks are done now. We have got a running development server, tasks to create the Jekyll site, and all assets and tasks for linting, sprite, and vector font creation.
|
||||||
|
|
||||||
|
Next, I will write the tasks needed to get a production-ready code.
|
||||||
|
|
||||||
|
## Conclusion
|
||||||
|
|
||||||
|
This concludes the 10th part of my series, _Introduction to Gulp.js_. Today, we learned how to create CSS image sprites with Gulp.js and Spritesmith.
|
||||||
|
|
||||||
|
<Figure>
|
||||||
|
<MoreLink href="https://github.com/kogakure/gulp-tutorial" text="View Source on GitHub" />
|
||||||
|
</Figure>
|
||||||
@@ -0,0 +1,164 @@
|
|||||||
|
---
|
||||||
|
title: "Introduction to Gulp.js 11: Production Build, Server and Jekyll"
|
||||||
|
slug: gulp-tutorial-11-production-build-server-and-jekyll
|
||||||
|
author: Stefan Imhoff
|
||||||
|
date: 2014-10-28T07:30:00+02:00
|
||||||
|
description: "The ultimate tutorial and guide for Gulp.js: How to write the production task for Jekyll and BrowserSync."
|
||||||
|
cover: /assets/images/cover/gulp.svg
|
||||||
|
tags: ["code"]
|
||||||
|
series: gulp
|
||||||
|
---
|
||||||
|
|
||||||
|
This is the 11th part of my series, _Introduction to Gulp.js_. Today I will start writing the production build task, set up a server to view the production code and build the production site with Jekyll.
|
||||||
|
|
||||||
|
In development, I used the `default` Gulp.js tasks to run the development server, build the assets, and watch for changes. For production, I will need another entry point.
|
||||||
|
|
||||||
|
I decided to name my task `publish`. Later I can get a production build with the command `gulp publish`.
|
||||||
|
|
||||||
|
#### gulp/tasks/publish.js
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
var gulp = require("gulp");
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Run task browsersync:production
|
||||||
|
*/
|
||||||
|
gulp.task("publish", ["browsersync:production"]);
|
||||||
|
```
|
||||||
|
|
||||||
|
I put this file on the same level as the `default.js` file. This task is short: It does one thing. Start a BrowserSync task for production. This way I can have a look at the production site before deploying it to my server.
|
||||||
|
|
||||||
|
## BrowserSync for Production
|
||||||
|
|
||||||
|
All production tasks will live in a folder `production/` inside `gulp/tasks/`. I name the tasks of development and production the same but put them in different folders.
|
||||||
|
|
||||||
|
#### gulp/config.js
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
browsersync: {
|
||||||
|
development: {
|
||||||
|
...
|
||||||
|
},
|
||||||
|
production: {
|
||||||
|
server: {
|
||||||
|
baseDir: [production]
|
||||||
|
},
|
||||||
|
port: 9998
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
The only differences to the `browsersync` of `development` are these: I serve the production folder and use a different port for the server. This way, I can run `development` and `production` in parallel.
|
||||||
|
|
||||||
|
#### gulp/tasks/production/browser-sync.js
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
var gulp = require("gulp");
|
||||||
|
var browsersync = require("browser-sync");
|
||||||
|
var config = require("../../config").browsersync.production;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Start a server and watch changes with BrowserSync
|
||||||
|
*/
|
||||||
|
gulp.task("browsersync:production", ["build:production"], function () {
|
||||||
|
browsersync(config);
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
This task is boring. It starts the production build.
|
||||||
|
|
||||||
|
## Build Task for Production
|
||||||
|
|
||||||
|
#### gulp/tasks/production/build.js
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
var gulp = require("gulp");
|
||||||
|
var runSequence = require("run-sequence");
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Run all tasks needed for a build in the defined order
|
||||||
|
*/
|
||||||
|
gulp.task("build:production", function (callback) {
|
||||||
|
runSequence(
|
||||||
|
"delete",
|
||||||
|
"jekyll:production",
|
||||||
|
["sass", "scripts", "images", "copy:fonts"],
|
||||||
|
"base64",
|
||||||
|
["optimize:css", "optimize:js", "optimize:images", "optimize:html", "copy:fonts:production"],
|
||||||
|
"revision",
|
||||||
|
"rev:collect",
|
||||||
|
callback
|
||||||
|
);
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
A lot is going on in this task: I run tasks in a specific order with `run-sequence`. First, I delete the assets folder for fresh creation. Then I run the Jekyll build for production, and create the development assets as I did in development. And after this is finished, I start with optimizing my assets and revisioning the files.
|
||||||
|
|
||||||
|
## Jekyll for Production
|
||||||
|
|
||||||
|
The Jekyll task is similar except for two things: I create my site to the production folder and I add another config file `_config.build.yml` as an option (be careful, add no space between two files).
|
||||||
|
|
||||||
|
My Jekyll production config overwrites values as the `url`, hide future posts (`future: false`), or hides drafts (`show_drafts: false`).
|
||||||
|
|
||||||
|
<Banner summary="Speed up development with Jekyll">
|
||||||
|
|
||||||
|
To speed up the generation of your site in development, you may set `limit_post: 5`, which will generate the last five posts.
|
||||||
|
|
||||||
|
Additionally, I set `future: true` and `show_drafts: true` to see Drafts and Posts with a future date.
|
||||||
|
|
||||||
|
</Banner>
|
||||||
|
|
||||||
|
#### gulp/config.js
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
jekyll: {
|
||||||
|
development: {
|
||||||
|
...
|
||||||
|
},
|
||||||
|
production: {
|
||||||
|
src: src,
|
||||||
|
dest: production,
|
||||||
|
config: '_config.yml,_config.build.yml'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### gulp/tasks/production/jekyll.js
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
var gulp = require("gulp");
|
||||||
|
var cp = require("child_process");
|
||||||
|
var browsersync = require("browser-sync");
|
||||||
|
var config = require("../../config").jekyll.production;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Build the Jekyll Site
|
||||||
|
*/
|
||||||
|
gulp.task("jekyll:production", function (done) {
|
||||||
|
browsersync.notify("Compiling Jekyll (Production)");
|
||||||
|
|
||||||
|
return cp
|
||||||
|
.spawn(
|
||||||
|
"bundle",
|
||||||
|
[
|
||||||
|
"exec",
|
||||||
|
"jekyll",
|
||||||
|
"build",
|
||||||
|
"-q",
|
||||||
|
"--source=" + config.src,
|
||||||
|
"--destination=" + config.dest,
|
||||||
|
"--config=" + config.config,
|
||||||
|
],
|
||||||
|
{ stdio: "inherit" }
|
||||||
|
)
|
||||||
|
.on("close", done);
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
## Conclusion
|
||||||
|
|
||||||
|
This concludes the 11th part of my series _Introduction to Gulp.js_. Today, I started to work on the production part of my website, including a server to view the production site and generate a production build of my Jekyll site.
|
||||||
|
|
||||||
|
<Figure>
|
||||||
|
<MoreLink href="https://github.com/kogakure/gulp-tutorial" text="View Source on GitHub" />
|
||||||
|
</Figure>
|
||||||
@@ -0,0 +1,215 @@
|
|||||||
|
---
|
||||||
|
title: "Introduction to Gulp.js 12: Optimize CSS, JavaScript, Images and HTML"
|
||||||
|
slug: gulp-tutorial-12-optimize-css-javascript-images-and-html
|
||||||
|
author: Stefan Imhoff
|
||||||
|
date: 2014-10-29T08:00:00+02:00
|
||||||
|
description: "The ultimate tutorial and guide for Gulp.js: How to optimize CSS, JavaScript, images, and HTML to speed up your website."
|
||||||
|
cover: /assets/images/cover/gulp.svg
|
||||||
|
tags: ["code"]
|
||||||
|
series: gulp
|
||||||
|
---
|
||||||
|
|
||||||
|
This is the 12th part of my series, _Introduction to Gulp.js_. Today I will write tasks to optimize the assets of my website: CSS, JavaScript, Images, and HTML.
|
||||||
|
|
||||||
|
Every Kilobyte, which has to be loaded, will slow down the loading of my website. That’s why I will minimize all my CSS and JavaScript and run my images through an optimizer, to remove as many bytes as possible. I will also add a task for minimizing HTML, but I don’t use this task because the reduction is minimal.
|
||||||
|
|
||||||
|
## CSS
|
||||||
|
|
||||||
|
First, I will write a task, which will optimize the CSS. Compass can minimize the CSS for production, but this Gulp.js task squeezed another 6 KB out of my files.
|
||||||
|
|
||||||
|
I install the needed Gulp.js plugins:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
$ npm install --save-dev gulp-csso@2.0.0 gulp-size@2.0.0
|
||||||
|
```
|
||||||
|
|
||||||
|
#### gulp/config.js
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
optimize: {
|
||||||
|
css: {
|
||||||
|
src: developmentAssets + '/css/*.css',
|
||||||
|
dest: productionAssets + '/css/',
|
||||||
|
options: {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### gulp/tasks/production/optimize-css.js
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
var gulp = require("gulp");
|
||||||
|
var csso = require("gulp-csso");
|
||||||
|
var size = require("gulp-size");
|
||||||
|
var config = require("../../config").optimize.css;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Copy and minimize CSS files
|
||||||
|
*/
|
||||||
|
gulp.task("optimize:css", function () {
|
||||||
|
return gulp.src(config.src).pipe(csso(config.options)).pipe(gulp.dest(config.dest)).pipe(size());
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
This task will copy the CSS files from the assets folder, minimize them, remove comments, output the size of the file, and copy them to the production assets folder.
|
||||||
|
|
||||||
|
## JavaScript
|
||||||
|
|
||||||
|
Now the CSS is minimized, and the same has to be done to the JavaScript files. I use UglifyJS for this task. If you don’t like it, go ahead and use a Google Closure or YUI compressor Gulp.js task.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
$ npm install --save-dev gulp-uglify@1.0.1
|
||||||
|
```
|
||||||
|
|
||||||
|
#### gulp/config.js
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
optimize: {
|
||||||
|
css: {
|
||||||
|
...
|
||||||
|
},
|
||||||
|
js: {
|
||||||
|
src: developmentAssets + '/js/*.js',
|
||||||
|
dest: productionAssets + '/js/',
|
||||||
|
options: {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### gulp/tasks/production/optimize-js.js
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
var gulp = require("gulp");
|
||||||
|
var uglify = require("gulp-uglify");
|
||||||
|
var size = require("gulp-size");
|
||||||
|
var config = require("../../config").optimize.js;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Copy and minimize JS files
|
||||||
|
*/
|
||||||
|
gulp.task("optimize:js", function () {
|
||||||
|
return gulp
|
||||||
|
.src(config.src)
|
||||||
|
.pipe(uglify(config.options))
|
||||||
|
.pipe(gulp.dest(config.dest))
|
||||||
|
.pipe(size());
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
This task will take the JavaScript files, minimize and optimize them, put them in my production assets folder, and output the size.
|
||||||
|
|
||||||
|
## Images
|
||||||
|
|
||||||
|
Next, I will work on the images. They need to be copied to the production assets folder and crunched (reduce the size). This may take a while, depending on the size and amount of your images, that’s why I optimize the images for production.
|
||||||
|
|
||||||
|
<Banner summary="Show more details">
|
||||||
|
|
||||||
|
To get a more detailed output in Gulp.js, you may add a flag to your command:
|
||||||
|
|
||||||
|
`gulp publish --verbose`. It will list each individual image for the optimized task and how much it was compressed.
|
||||||
|
|
||||||
|
</Banner>
|
||||||
|
|
||||||
|
I’ll need `gulp-imagemin` for my task, which can minify PNG, JPG, GIF, and SVG images:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
$ npm install --save-dev gulp-imagemin@2.3.0
|
||||||
|
```
|
||||||
|
|
||||||
|
#### gulp/config.js
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
optimize: {
|
||||||
|
css: {
|
||||||
|
...
|
||||||
|
},
|
||||||
|
js: {
|
||||||
|
...
|
||||||
|
},
|
||||||
|
images: {
|
||||||
|
src: developmentAssets + '/images/**/*.{jpg,jpeg,png,gif}',
|
||||||
|
dest: productionAssets + '/images/',
|
||||||
|
options: {
|
||||||
|
optimizationLevel: 3,
|
||||||
|
progressive: true,
|
||||||
|
interlaced: true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### gulp/tasks/production/optimize-images.js
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
var gulp = require("gulp");
|
||||||
|
var imagemin = require("gulp-imagemin");
|
||||||
|
var size = require("gulp-size");
|
||||||
|
var config = require("../../config").optimize.images;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Copy and minimize image files
|
||||||
|
*/
|
||||||
|
gulp.task("optimize:images", function () {
|
||||||
|
return gulp
|
||||||
|
.src(config.src)
|
||||||
|
.pipe(imagemin(config.options))
|
||||||
|
.pipe(gulp.dest(config.dest))
|
||||||
|
.pipe(size());
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
This task will take my images, optimize them, copy them to the assets folder, and output the size.
|
||||||
|
|
||||||
|
## HTML
|
||||||
|
|
||||||
|
As said before, I wrote this task, and you can see how to do it, but I don’t use it because the reduction is minimal and not worth the messy markup. I like to keep it readable, so other people can learn from it.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
$ npm install --save-dev gulp-htmlmin@1.2.0
|
||||||
|
```
|
||||||
|
|
||||||
|
#### gulp/config.js
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
optimize: {
|
||||||
|
css: {
|
||||||
|
...
|
||||||
|
},
|
||||||
|
js: {
|
||||||
|
...
|
||||||
|
},
|
||||||
|
images: {
|
||||||
|
...
|
||||||
|
},
|
||||||
|
html: {
|
||||||
|
src: production + '/**/*.html',
|
||||||
|
dest: production,
|
||||||
|
options: {
|
||||||
|
collapseWhitespace: true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### gulp/tasks/production/optimize-html.js
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
var gulp = require("gulp");
|
||||||
|
var htmlmin = require("gulp-htmlmin");
|
||||||
|
var config = require("../../config").optimize.html;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Minimize HTML
|
||||||
|
*/
|
||||||
|
gulp.task("optimize:html", function () {
|
||||||
|
return gulp.src(config.src).pipe(htmlmin(config.options)).pipe(gulp.dest(config.dest));
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
## Conclusion
|
||||||
|
|
||||||
|
This concludes the 12th part of my series _Introduction to Gulp.js_. Today we learned how to minimize CSS and JavaScript files and reduce the size of images.
|
||||||
|
|
||||||
|
<Figure>
|
||||||
|
<MoreLink href="https://github.com/kogakure/gulp-tutorial" text="View Source on GitHub" />
|
||||||
|
</Figure>
|
||||||
159
src/content/journal/2014/gulp-tutorial-13-revisioning.mdx
Normal file
159
src/content/journal/2014/gulp-tutorial-13-revisioning.mdx
Normal file
@@ -0,0 +1,159 @@
|
|||||||
|
---
|
||||||
|
title: "Introduction to Gulp.js 13: Revisioning"
|
||||||
|
slug: gulp-tutorial-13-revisioning
|
||||||
|
author: Stefan Imhoff
|
||||||
|
date: 2014-10-30T07:45:00+02:00
|
||||||
|
description: "The ultimate tutorial and guide for Gulp.js: How to use revisioning to allow long caching of your assets and replace them with hashed file names, that can be cache-busted."
|
||||||
|
cover: /assets/images/cover/gulp.svg
|
||||||
|
tags: ["code"]
|
||||||
|
series: gulp
|
||||||
|
---
|
||||||
|
|
||||||
|
This is the 13th part of my series, _Introduction to Gulp.js_. Today I will write the task to revise my static assets.
|
||||||
|
|
||||||
|
## Copy Vector Fonts for Production
|
||||||
|
|
||||||
|
Before I can work on the fun part of revisioning my asset files, I first have to write another boring and short task, which is doing one simple thing: Copying the fonts to the production assets folder.
|
||||||
|
|
||||||
|
#### gulp/config.js
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
copyfonts: {
|
||||||
|
development: {
|
||||||
|
...
|
||||||
|
},
|
||||||
|
production: {
|
||||||
|
src: developmentAssets + '/fonts/*',
|
||||||
|
dest: productionAssets + '/fonts'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### gulp/tasks/production/copy-fonts.js
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
var gulp = require("gulp");
|
||||||
|
var config = require("../../config").copyfonts.production;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Copy fonts to folder
|
||||||
|
*/
|
||||||
|
gulp.task("copy:fonts:production", function () {
|
||||||
|
return gulp.src(config.src).pipe(gulp.dest(config.dest));
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
## Revisioning
|
||||||
|
|
||||||
|
Optimizing my assets is done. But one important thing is missing: Revisioning.
|
||||||
|
|
||||||
|
For better performance, one should always cache the assets for a long time. Hard drives are huge these days, but the speed for requesting assets isn’t that wonderful (on mobile devices). But one problem occurs if you cache the assets on a hard drive of a visitor. If you update a file, the browser will serve the old file. And if you cache it for 10 years, the user will never get the new asset, unless s/he deletes the browser cache manually. Yet what user does this?
|
||||||
|
|
||||||
|
That’s why we need to rename every file that has been changed to invalidate the cache. I remember the days when we had to add a number manually to our assets, like `image_r1.png`, or `image_r2.png`. This sucks. Today, reading the content of a file and generating a checksum can achieve this easier. This checksum will always be the same unless something gets changed.
|
||||||
|
|
||||||
|
My next task will rename all assets. This way, the `application.css` will become `application-f084d03b.css`. If I change one tiny thing in this file, it will get a new filename.
|
||||||
|
|
||||||
|
I install `gulp-rev`, which will handle this renaming of assets:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
$ npm install --save-dev gulp-rev@2.0.1
|
||||||
|
```
|
||||||
|
|
||||||
|
#### gulp/config.js
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
revision: {
|
||||||
|
src: {
|
||||||
|
assets: [
|
||||||
|
productionAssets + '/css/*.css',
|
||||||
|
productionAssets + '/js/*.js',
|
||||||
|
productionAssets + '/images/**/*'
|
||||||
|
],
|
||||||
|
base: production
|
||||||
|
},
|
||||||
|
dest: {
|
||||||
|
assets: production,
|
||||||
|
manifest: {
|
||||||
|
name: 'manifest.json',
|
||||||
|
path: productionAssets
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
This task will rename all assets and create a JSON file containing all files, which were renamed and their old and new file names.
|
||||||
|
|
||||||
|
#### gulp/tasks/production/revision.js
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
var gulp = require("gulp");
|
||||||
|
var rev = require("gulp-rev");
|
||||||
|
var config = require("../../config").revision;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Revision of all asset files and
|
||||||
|
* write a manifest file
|
||||||
|
*/
|
||||||
|
gulp.task("revision", function () {
|
||||||
|
return gulp
|
||||||
|
.src(config.src.assets, { base: config.src.base })
|
||||||
|
.pipe(gulp.dest(config.dest.assets))
|
||||||
|
.pipe(rev())
|
||||||
|
.pipe(gulp.dest(config.dest.assets))
|
||||||
|
.pipe(rev.manifest({ path: config.dest.manifest.name }))
|
||||||
|
.pipe(gulp.dest(config.dest.manifest.path));
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
## Replacing Paths to Assets
|
||||||
|
|
||||||
|
The last step of my production build is to replace all occurrences of assets with a revised file name in all files.
|
||||||
|
|
||||||
|
This task will need `gulp-rev-collector` to replace the paths names to assets:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
$ npm install --save-dev gulp-rev-collector@0.1.1
|
||||||
|
```
|
||||||
|
|
||||||
|
#### gulp/config.js
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
collect: {
|
||||||
|
src: [
|
||||||
|
productionAssets + '/manifest.json',
|
||||||
|
production + '/**/*.{html,xml,txt,json,css,js}',
|
||||||
|
'!' + production + '/feed.xml'
|
||||||
|
],
|
||||||
|
dest: production
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
I replace these paths in files I know could contain paths to assets. Don’t include any images or binary files. The revision collector will try to open them and destroy binary files.
|
||||||
|
|
||||||
|
#### gulp/tasks/production/rev-collector.js
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
var gulp = require("gulp");
|
||||||
|
var collect = require("gulp-rev-collector");
|
||||||
|
var config = require("../../config").collect;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Replace all links to assets in files
|
||||||
|
* from a manifest file
|
||||||
|
*/
|
||||||
|
gulp.task("rev:collect", function () {
|
||||||
|
return gulp.src(config.src).pipe(collect()).pipe(gulp.dest(config.dest));
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
This task will look into the `manifest.json` file and replace every path to one of the assets in every HTML, CSS, JavaScript, and Text.
|
||||||
|
|
||||||
|
The production build is finished! Only one thing is missing to complete this series of tutorials about Gulp.js: Deploying the Website to my server.
|
||||||
|
|
||||||
|
## Conclusion
|
||||||
|
|
||||||
|
This concludes the 13th part of my series, _Introduction to Gulp.js_. Today we learned how to revise the asset files and replace links to these files.
|
||||||
|
|
||||||
|
<Figure>
|
||||||
|
<MoreLink href="https://github.com/kogakure/gulp-tutorial" text="View Source on GitHub" />
|
||||||
|
</Figure>
|
||||||
@@ -0,0 +1,85 @@
|
|||||||
|
---
|
||||||
|
title: "Introduction to Gulp.js 14: Deploying the Website with Rsync"
|
||||||
|
slug: gulp-tutorial-14-deploying-the-website
|
||||||
|
author: Stefan Imhoff
|
||||||
|
date: 2014-10-31T08:00:00+02:00
|
||||||
|
description: "The ultimate tutorial and guide for Gulp.js: How to deploy your website with rsync to your server."
|
||||||
|
cover: /assets/images/cover/gulp.svg
|
||||||
|
tags: ["code"]
|
||||||
|
series: gulp
|
||||||
|
---
|
||||||
|
|
||||||
|
This is the 14th of my series _Introduction to Gulp.js_. Today I will write a task to sync the files of my Jekyll site to my web server.
|
||||||
|
|
||||||
|
## Deploying the Website
|
||||||
|
|
||||||
|
There are plenty of possibilities to get a website on the server. You may use FTP, SFTP, SCP, SCP2, Rsync, or Git. I use Rsync because it’s fast and easy to use.
|
||||||
|
|
||||||
|
I write another task as an entry point: `deploy`
|
||||||
|
|
||||||
|
#### gulp/tasks/deploy.js
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
var gulp = require("gulp");
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Start rsync task
|
||||||
|
*/
|
||||||
|
gulp.task("deploy", ["rsync"]);
|
||||||
|
```
|
||||||
|
|
||||||
|
This will start the `rsync` task. But I could add more tasks, for example, a task, which creates a zip archive of the build and copies it to a backup on my hard drive.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
$ npm install --save-dev gulp-rsync@0.0.5
|
||||||
|
```
|
||||||
|
|
||||||
|
#### gulp/config.js
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
rsync: {
|
||||||
|
src: production + '/**',
|
||||||
|
options: {
|
||||||
|
destination: '~/path/to/my/website/root/',
|
||||||
|
root: production,
|
||||||
|
hostname: 'mydomain.com',
|
||||||
|
username: 'user',
|
||||||
|
incremental: true,
|
||||||
|
progress: true,
|
||||||
|
relative: true,
|
||||||
|
emptyDirectories: true,
|
||||||
|
recursive: true,
|
||||||
|
clean: true,
|
||||||
|
exclude: ['.DS_Store'],
|
||||||
|
include: []
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
This task will grab all files in my production folder, connect to my server, and copy all files recursively to my website root. It will delete old files and add changes to the server.
|
||||||
|
|
||||||
|
#### gulp/tasks/production/rsync.js
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
var gulp = require("gulp");
|
||||||
|
var rsync = require("gulp-rsync");
|
||||||
|
var config = require("../../config").rsync;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Copy files and folder to server
|
||||||
|
* via rsync
|
||||||
|
*/
|
||||||
|
gulp.task("rsync", function () {
|
||||||
|
return gulp.src(config.src).pipe(rsync(config.options));
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
## Conclusion
|
||||||
|
|
||||||
|
This concludes the series _Introduction to Gulp.js_. Developing and deploying with Gulp.js is fun.
|
||||||
|
|
||||||
|
I like the UNIX philosophy of Gulp.js: Having small files, which do one task and connect these to larger workflows. And because I kept my Gulp.js tasks small, pluggable, and shareable, I was able to add Gulp.js to my second website in less than five minutes.
|
||||||
|
|
||||||
|
<Figure>
|
||||||
|
<MoreLink href="https://github.com/kogakure/gulp-tutorial" text="View Source on GitHub" />
|
||||||
|
</Figure>
|
||||||
@@ -0,0 +1,186 @@
|
|||||||
|
---
|
||||||
|
title: "Introduction to Gulp.js 15: Performance Improvements with WebP and Gzip"
|
||||||
|
slug: gulp-tutorial-15-performance-improvements-webp-gzip
|
||||||
|
author: Stefan Imhoff
|
||||||
|
date: 2014-12-21T11:15:00+01:00
|
||||||
|
description: "The ultimate tutorial and guide for Gulp.js: How to improve the speed and performance of your website with WebP and Gzip."
|
||||||
|
cover: /assets/images/cover/gulp.svg
|
||||||
|
tags: ["code"]
|
||||||
|
series: gulp
|
||||||
|
---
|
||||||
|
|
||||||
|
This is the 15th part of my series, _Introduction to Gulp.js_. Today I’ll add tasks for performance improvement of the website with WebP for images and Gzip for text files.
|
||||||
|
|
||||||
|
## Using WebP for images
|
||||||
|
|
||||||
|
[WebP](https://developers.google.com/speed/webp/) is a new image format developed by Google. With WebP, it’s possible to achieve much better compression with better quality, as with JPEG or PNG. Multiple browsers like **Google Chrome**, **Opera**, or **Konquerer** support this image format.
|
||||||
|
|
||||||
|
On my website, I use a header image which is in JPEG format **69 KB** in size, the same image is in WebP **44 KB**. WebP can reduce the size of images by **25-34%**, which is a lot.
|
||||||
|
|
||||||
|
That’s why I will create a task, which creates WebP images of my PNG and JPEG images and let the server deliver the smaller format to browsers, which supports it.
|
||||||
|
|
||||||
|
First, I install the Gulp.js module for WebP:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
$ npm install --save-dev gulp-webp@2.1.1
|
||||||
|
```
|
||||||
|
|
||||||
|
I add an entry to the configuration file:
|
||||||
|
|
||||||
|
#### gulp/config.js
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
webp: {
|
||||||
|
src: productionAssets + '/images/**/*.{jpg,jpeg,png}',
|
||||||
|
dest: productionAssets + '/images/',
|
||||||
|
options: {}
|
||||||
|
},
|
||||||
|
```
|
||||||
|
|
||||||
|
The task is short and straightforward:
|
||||||
|
|
||||||
|
#### gulp/tasks/production/webp.js
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
var gulp = require("gulp");
|
||||||
|
var webp = require("gulp-webp");
|
||||||
|
var config = require("../../config").webp;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Convert images to WebP
|
||||||
|
*/
|
||||||
|
gulp.task("webp", function () {
|
||||||
|
return gulp.src(config.src).pipe(webp(config.options)).pipe(gulp.dest(config.dest));
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
This task needs to be run for production and has to be executed after the revisioning of the images is finished because the server will deliver a WebP image of the same name to the browser.
|
||||||
|
|
||||||
|
#### gulp/tasks/production/build.js
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
var gulp = require("gulp");
|
||||||
|
var runSequence = require("run-sequence");
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Run all tasks needed for a build in the defined order
|
||||||
|
*/
|
||||||
|
gulp.task("build:production", function (callback) {
|
||||||
|
runSequence(
|
||||||
|
"delete",
|
||||||
|
"jekyll:production",
|
||||||
|
// ...,
|
||||||
|
"revision",
|
||||||
|
"rev:collect",
|
||||||
|
"webp",
|
||||||
|
callback
|
||||||
|
);
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
It’s necessary to tell the server to rewrite the URLs of our images. There are multiple techniques for this, but I’ll use a `.htaccess` file:
|
||||||
|
|
||||||
|
#### app/htaccess
|
||||||
|
|
||||||
|
```apache
|
||||||
|
---
|
||||||
|
layout: null
|
||||||
|
slug: .htaccess
|
||||||
|
---
|
||||||
|
|
||||||
|
<IfModule mod_rewrite.c>
|
||||||
|
RewriteEngine On
|
||||||
|
RewriteCond %{HTTP_ACCEPT} image/webp
|
||||||
|
RewriteCond %{DOCUMENT_ROOT}/$1.webp -f
|
||||||
|
RewriteRule (.+)\.(jpe?g|png)$ $1.webp [T=image/webp,E=accept:1]
|
||||||
|
</IfModule>
|
||||||
|
|
||||||
|
<IfModule mod_headers.c>
|
||||||
|
Header append Vary Accept env=REDIRECT_accept
|
||||||
|
</IfModule>
|
||||||
|
|
||||||
|
AddType image/webp .webp
|
||||||
|
```
|
||||||
|
|
||||||
|
It is possible to use a `.htaccess` file and include it in the [configuration file](https://jekyllrb.com/docs/configuration/). Otherwise, Jekyll will ignore hidden files and don’t copy them to the target directory.
|
||||||
|
|
||||||
|
But I like it more to add [YAML Front Matter](https://jekyllrb.com/docs/frontmatter/) and create the file this way. Another advantage is that the file isn’t invisible.
|
||||||
|
|
||||||
|
If you sync your production website to a server, it will deliver to browsers, which support WebP the WebP format when requesting a JPEG or PNG.
|
||||||
|
|
||||||
|
<Banner summary="It isn’t working …">
|
||||||
|
|
||||||
|
Don’t wonder: The `.htaccess` file won’t work with the development server. It will need a server with support for `mod_rewrite` and `mod_headers` and support `.htaccess` files.
|
||||||
|
|
||||||
|
</Banner>
|
||||||
|
|
||||||
|
## Gzip text files
|
||||||
|
|
||||||
|
Many servers compress files by default with Gzip before sending them to the browser. But it is always good to pre-gzip the files because it will be faster, as the server doesn’t need to compress the file on every request. And it will need less CPU and the compression rate will be much higher with pre-gzipped files because many servers don’t use the maximum compression rate.
|
||||||
|
|
||||||
|
First, I install the Gulp.js module:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
$ npm install --save-dev gulp-gzip@1.2.0
|
||||||
|
```
|
||||||
|
|
||||||
|
I add an entry to the configuration file:
|
||||||
|
|
||||||
|
#### gulp/config.js
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
gzip: {
|
||||||
|
src: production + '/**/*.{html,xml,json,css,js}',
|
||||||
|
dest: production,
|
||||||
|
options: {}
|
||||||
|
},
|
||||||
|
```
|
||||||
|
|
||||||
|
Next, I create the task, which is short:
|
||||||
|
|
||||||
|
#### gulp/tasks/production/gzip.js
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
var gulp = require("gulp");
|
||||||
|
var gzip = require("gulp-gzip");
|
||||||
|
var config = require("../../config").gzip;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gzip text files
|
||||||
|
*/
|
||||||
|
gulp.task("gzip", function () {
|
||||||
|
return gulp.src(config.src).pipe(gzip(config.options)).pipe(gulp.dest(config.dest));
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
I add the task in my production build file to a JavaScript Array together with the `webp` task because this task and the Gzip task may run in parallel; WebP works with images and Gzip with text files.
|
||||||
|
|
||||||
|
#### gulp/tasks/production/build.js
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
var gulp = require("gulp");
|
||||||
|
var runSequence = require("run-sequence");
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Run all tasks needed for a build in the defined order
|
||||||
|
*/
|
||||||
|
gulp.task("build:production", function (callback) {
|
||||||
|
runSequence(
|
||||||
|
"delete",
|
||||||
|
"jekyll:production",
|
||||||
|
// ...,
|
||||||
|
"revision",
|
||||||
|
"rev:collect",
|
||||||
|
["webp", "gzip"],
|
||||||
|
callback
|
||||||
|
);
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
## Conclusion
|
||||||
|
|
||||||
|
This concludes the 15th part of my series _Introduction to Gulp.js_. We learned how to convert images to the WebP format and how to compress text files with Gzip. Every byte we can reduce will increase the speed of the website.
|
||||||
|
|
||||||
|
<Figure>
|
||||||
|
<MoreLink href="https://github.com/kogakure/gulp-tutorial" text="View Source on GitHub" />
|
||||||
|
</Figure>
|
||||||
310
src/content/journal/2014/gulp-tutorial-16-postcss.mdx
Normal file
310
src/content/journal/2014/gulp-tutorial-16-postcss.mdx
Normal file
@@ -0,0 +1,310 @@
|
|||||||
|
---
|
||||||
|
title: "Introduction to Gulp.js 16: PostCSS"
|
||||||
|
slug: gulp-tutorial-16-postcss
|
||||||
|
author: Stefan Imhoff
|
||||||
|
date: 2014-12-30T07:50:29+00:00
|
||||||
|
description: "The ultimate tutorial and guide for Gulp.js: How to use PostCSS with Gulp to process CSS and how to lint your CSS files with Stylelint."
|
||||||
|
cover: /assets/images/cover/gulp.svg
|
||||||
|
tags: ["code"]
|
||||||
|
series: gulp
|
||||||
|
---
|
||||||
|
|
||||||
|
This is the 16th part of my series _Introduction to Gulp.js_. Today, I will show how to use PostCSS to process CSS files. I will replace Ruby Sass with PostCSS and additionally show how to lint stylesheets automatically in the background while developing with Stylelint.
|
||||||
|
|
||||||
|
## Compass, Sass, LESS, Stylus … why do we need more tools?
|
||||||
|
|
||||||
|
I use Sass and SCSS now for a long time, started with _Compass_, moved to _Ruby Sass_ and wanted to move to _libSass_, but dependencies on my beloved Gems held me back.
|
||||||
|
|
||||||
|
There are numerous Preprocessors, Libraries, and Frameworks, which extend CSS. And a lot of them do good work and I didn’t want to miss variables, nesting, or mixins. But Ruby Sass and Compass in particular are **slooooooooow**because Ruby is slow. Compiling my website’s styles took 7-8 seconds, but I know of projects where one change will trigger a recompile, which takes **more than a minute**!
|
||||||
|
|
||||||
|
## What is PostCSS, and why should I use it?
|
||||||
|
|
||||||
|
There is a new kid on the block: [PostCSS](https://github.com/postcss/postcss). I don’t care if it’s a Preprocessor, a Postprocessor, or a Processor. You write something, it will process your files, and it will put out CSS.
|
||||||
|
|
||||||
|
Why should you use a new tool, if Sass and its competitors do their job? Because it’s **fast** ([3-30 times faster](https://github.com/postcss/benchmark)), **modular** and **extendible**. I bet you need a small fraction of your Preprocessors functionality.
|
||||||
|
|
||||||
|
With PostCSS, you can choose what you require from currently over [200 plugins](https://www.postcss.parts/). If you don’t find the plugin you require, you can write a [plugin](https://github.com/postcss/postcss/blob/master/docs/guidelines/plugin.md) or [syntax](https://github.com/postcss/postcss/blob/master/docs/syntax.md).
|
||||||
|
|
||||||
|
**You get it all**: variables, mixins, extends, color helpers, fallbacks, optimizations, grids … you pick. You can even start using future CSS syntax today, let PostCSS transpile it for you.
|
||||||
|
|
||||||
|
I swapped out _Ruby Sass_ with PostCSS and my CSS is now transformed in 2-3 seconds. Go and take a look at my [beautiful new code](https://github.com/kogakure/jekyll-stefanimhoff.de). I use Responsive Typography, Autoprefixer, and the fantastic [LostGrid](https://github.com/peterramsing/lost).
|
||||||
|
|
||||||
|
## PostCSS
|
||||||
|
|
||||||
|
In this part of my series, I will rip out Ruby Sass and add PostCSS instead. So follow me along …
|
||||||
|
|
||||||
|
First, I will install all needed Node modules:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
$ npm install gulp-postcss@6.0.1 precss@1.2.3 gulp-cssnano@2.0.0 gulp-util@3.0.6 autoprefixer@6.0.3 css-mqpacker@4.0.0 --save-dev
|
||||||
|
```
|
||||||
|
|
||||||
|
I add basic plugins, but you can add more if you like later. I add [gulp-postcss](https://github.com/postcss/gulp-postcss), [precss](https://github.com/jonathantneal/precss) (which will allow Sass-like syntax), [gulp-cssnano](https://cssnano.co/) (which will compress and optimize the CSS), [autoprexifer](https://github.com/postcss/autoprefixer) for automatic vendor prefixes and [css-mqpacker](https://github.com/hail2u/node-css-mqpacker) for combining media queries.
|
||||||
|
|
||||||
|
Next, I will add the configuration for the new task:
|
||||||
|
|
||||||
|
#### gulp/config.js
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
styles: {
|
||||||
|
src: srcAssets + '/styles/*.css',
|
||||||
|
dest: developmentAssets + '/css',
|
||||||
|
options: {
|
||||||
|
precss: {},
|
||||||
|
autoprefixer: {
|
||||||
|
browsers: [
|
||||||
|
'last 2 versions',
|
||||||
|
'safari 5',
|
||||||
|
'ie 8',
|
||||||
|
'ie 9',
|
||||||
|
'opera 12.1',
|
||||||
|
'ios 6',
|
||||||
|
'android 4'
|
||||||
|
],
|
||||||
|
cascade: true
|
||||||
|
},
|
||||||
|
mqpacker: {}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
```
|
||||||
|
|
||||||
|
I add the new task to my `development` folder:
|
||||||
|
|
||||||
|
#### gulp/tasks/development/styles.js
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
var gulp = require("gulp");
|
||||||
|
var postcss = require("gulp-postcss");
|
||||||
|
var precss = require("precss");
|
||||||
|
var nano = require("gulp-cssnano");
|
||||||
|
var plumber = require("gulp-plumber");
|
||||||
|
var sourcemaps = require("gulp-sourcemaps");
|
||||||
|
var gutil = require("gulp-util");
|
||||||
|
var browsersync = require("browser-sync");
|
||||||
|
var autoprefixer = require("autoprefixer");
|
||||||
|
var mqpacker = require("css-mqpacker");
|
||||||
|
var config = require("../../config");
|
||||||
|
|
||||||
|
function onError(err) {
|
||||||
|
gutil.beep();
|
||||||
|
console.log(err);
|
||||||
|
this.emit("end");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Run CSS through PostCSS and its plugins
|
||||||
|
* Build sourcemaps and minimize
|
||||||
|
*/
|
||||||
|
var processors = [
|
||||||
|
precss(config.styles.options.precss),
|
||||||
|
autoprefixer(config.styles.options.autoprefixer),
|
||||||
|
mqpacker(config.styles.options.mqpacker),
|
||||||
|
];
|
||||||
|
|
||||||
|
gulp.task("styles", function () {
|
||||||
|
browsersync.notify("Transforming CSS with PostCSS");
|
||||||
|
|
||||||
|
return gulp
|
||||||
|
.src(config.styles.src)
|
||||||
|
.pipe(
|
||||||
|
plumber({
|
||||||
|
errorHandler: onError,
|
||||||
|
})
|
||||||
|
)
|
||||||
|
.pipe(sourcemaps.init())
|
||||||
|
.pipe(postcss(processors))
|
||||||
|
.pipe(nano())
|
||||||
|
.pipe(sourcemaps.write("."))
|
||||||
|
.pipe(gulp.dest(config.styles.dest));
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
### Rename SCSS files and folders
|
||||||
|
|
||||||
|
I rename the `scss` folder to `styles` and create inside this folder a new folder `partials`. I rename all `.scss` files to `.css` and move them into the partial’s folder (except the `main.css`). Furthermore, I recommend commenting out all lines in this file for now and bringing them back later. Otherwise, it will not run properly because the syntax is different.
|
||||||
|
|
||||||
|
### Update the Code
|
||||||
|
|
||||||
|
Next, I will need to replace the lines of the old `sass` task with my new `styles` task. If you have never seen a **DIFF** file: a `-` (and red line) in front of a line has to be removed and a `+` (and green line) in front of a line has to be added:
|
||||||
|
|
||||||
|
#### gulp/config.js:132
|
||||||
|
|
||||||
|
```diff
|
||||||
|
- sass: srcAssets + '/scss/**/*.{sass,scss}',
|
||||||
|
+ styles: srcAssets + '/styles/**/*.css',
|
||||||
|
```
|
||||||
|
|
||||||
|
#### gulp/config.js:154
|
||||||
|
|
||||||
|
```diff
|
||||||
|
- css: srcAssets + '/scss/base/',
|
||||||
|
+ css: srcAssets + '/styles/partials/base/',
|
||||||
|
```
|
||||||
|
|
||||||
|
#### gulp/tasks/development/watch.js:9
|
||||||
|
|
||||||
|
```diff
|
||||||
|
- gulp.watch(config.sass, ['sass', 'scsslint']);
|
||||||
|
+ gulp.watch(config.styles, ['styles', 'scsslint']);
|
||||||
|
```
|
||||||
|
|
||||||
|
#### gulp/tasks/development/build.js:11
|
||||||
|
|
||||||
|
```diff
|
||||||
|
- 'sass',
|
||||||
|
+ 'styles',
|
||||||
|
```
|
||||||
|
|
||||||
|
#### gulp/tasks/production/build.js:10
|
||||||
|
|
||||||
|
```diff
|
||||||
|
- 'sass',
|
||||||
|
+ 'styles',
|
||||||
|
```
|
||||||
|
|
||||||
|
#### gulp/tasks/development/base64.js:8
|
||||||
|
|
||||||
|
```diff
|
||||||
|
- gulp.task('base64', ['sass'], function() {
|
||||||
|
+ gulp.task('base64', ['styles'], function() {
|
||||||
|
```
|
||||||
|
|
||||||
|
I remove these lines from my `optimize-css.js` task because `cssnano` does the job:
|
||||||
|
|
||||||
|
#### gulp/tasks/production/optimize-css.js:2
|
||||||
|
|
||||||
|
```diff
|
||||||
|
- var csso = require('gulp-csso');
|
||||||
|
```
|
||||||
|
|
||||||
|
#### gulp/tasks/production/optimize-css.js:7
|
||||||
|
|
||||||
|
```diff
|
||||||
|
- * Copy and minimize CSS files
|
||||||
|
+ * Copy CSS files
|
||||||
|
```
|
||||||
|
|
||||||
|
#### gulp/tasks/production/optimize-css.js:11
|
||||||
|
|
||||||
|
```diff
|
||||||
|
- .pipe(minifycss(config.options))
|
||||||
|
```
|
||||||
|
|
||||||
|
FontCustom has to be changed to copy the Vector fonts CSS to the new location and provide CSS instead of SCSS:
|
||||||
|
|
||||||
|
#### fontcustom.yml:34
|
||||||
|
|
||||||
|
```diff
|
||||||
|
- css: app/_assets/scss
|
||||||
|
+ css: app/_assets/styles
|
||||||
|
```
|
||||||
|
|
||||||
|
#### fontcustom.yml:48
|
||||||
|
|
||||||
|
```diff
|
||||||
|
- templates: [ scss, preview ]
|
||||||
|
+ templates: [ css, preview ]
|
||||||
|
```
|
||||||
|
|
||||||
|
After I updated the FontCustom config file, I have to run the task for creating the Vector Fonts again:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
$ gulp fontcustom
|
||||||
|
```
|
||||||
|
|
||||||
|
The syntax of the different PostCSS plugins is different. I use [PreCSS](https://github.com/jonathantneal/precss), which is a lot like Sass, but things look different. Writing all the changes I made to the CSS would extend the scope of this tutorial too much, as it’s a long file. But you can have a look, at how I refactored all CSS files with updated syntax in my [GitHub repository](https://github.com/kogakure/gulp-tutorial/commit/fc2398d933e2094832a00ac123b30c772269e08c). If you are interested how I replaced [Singularity](https://github.com/at-import/Singularity) (which is the best Sass grid available) with [LostGrid](https://github.com/peterramsing/lost) and all the other things, look into [my websites source code](https://github.com/kogakure/jekyll-stefanimhoff.de).
|
||||||
|
|
||||||
|
You can run `gulp` again now, and PostCSS will process the styles.
|
||||||
|
|
||||||
|
## Linting CSS with PostCSS plugins
|
||||||
|
|
||||||
|
We want to write clean and beautiful stylesheets. That’s why we should always run a Linter, which checks our CSS.
|
||||||
|
|
||||||
|
Until now, a Ruby gem did this job and was checking the SCSS syntax for errors. But now we use CSS and need a better (and faster) solution.
|
||||||
|
|
||||||
|
First, I install the Node modules, which are needed:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
$ npm install stylelint@1.2.1 postcss-reporter@1.3.0 --save-dev
|
||||||
|
```
|
||||||
|
|
||||||
|
Now I’ll add the configuration for Linting to my Gulp config:
|
||||||
|
|
||||||
|
#### gulp/config.js
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
lintStyles: {
|
||||||
|
src: [
|
||||||
|
srcAssets + '/styles/**/*.css',
|
||||||
|
'!' + srcAssets + '/styles/partials/_syntax-highlighting.css',
|
||||||
|
'!' + srcAssets + '/styles/partials/_sprites.css',
|
||||||
|
'!' + srcAssets + '/styles/partials/fontcustom.css'
|
||||||
|
],
|
||||||
|
options: {
|
||||||
|
stylelint: {
|
||||||
|
'rules': {
|
||||||
|
'string-quotes': [2, 'double'],
|
||||||
|
'color-hex-case': [2, 'lower'],
|
||||||
|
'value-no-vendor-prefix': 2,
|
||||||
|
'declaration-no-important': 0,
|
||||||
|
'rule-non-nested-empty-line-before': [2, 'always', {
|
||||||
|
ignore: ['after-comment']
|
||||||
|
}]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
reporter: {
|
||||||
|
clearMessages: true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
```
|
||||||
|
|
||||||
|
I watch all CSS files, except the generated (FontCustom and Sprites) and the syntax highlighting file (which I don’t touch).
|
||||||
|
|
||||||
|
You can define linting rules for [stylelint](https://github.com/stylelint). These 5 rules are just an example, there are a [lot more](https://github.com/stylelint/stylelint/blob/master/docs/user-guide/rules.md). As CSS style is taste, you should pick the rules you like. `0` means ignore (default), `1` is a warning and `2` is an error. Sometimes rules require additional options.
|
||||||
|
|
||||||
|
Next, I will add the Gulp task:
|
||||||
|
|
||||||
|
#### gulp/tasks/development/lint-styles.js
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
var gulp = require("gulp");
|
||||||
|
var postcss = require("gulp-postcss");
|
||||||
|
var stylelint = require("stylelint");
|
||||||
|
var reporter = require("postcss-reporter");
|
||||||
|
var config = require("../../config");
|
||||||
|
|
||||||
|
gulp.task("lint-styles", function () {
|
||||||
|
return gulp
|
||||||
|
.src(config.lintStyles.src)
|
||||||
|
.pipe(
|
||||||
|
postcss([
|
||||||
|
stylelint(config.lintStyles.options.stylelint),
|
||||||
|
reporter(config.lintStyles.options.reporter),
|
||||||
|
])
|
||||||
|
);
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
All I need to do now is replace the linting task in my watch task:
|
||||||
|
|
||||||
|
#### gulp/tasks/development/watch.js:9
|
||||||
|
|
||||||
|
```diff
|
||||||
|
- gulp.watch(config.styles, ['styles', 'scsslint']);
|
||||||
|
+ gulp.watch(config.styles, ['styles', 'lint-styles']);
|
||||||
|
```
|
||||||
|
|
||||||
|
If you run `gulp`, the task will lint your CSS files, save them, and show errors with filename, line number, and broken rule in the Terminal. PostCSS provides even a [plugin](https://github.com/postcss/postcss-browser-reporter) to bring the errors to your browser.
|
||||||
|
|
||||||
|
PostCSS will likely have a bright future. Since it got popular, countless people got excited. Companies like Google, Twitter, Alibaba, and Shopify use PostCSS. And Bootstrap v5 will be likely in PostCSS.
|
||||||
|
|
||||||
|
I’m sure we will see more exciting Plugins in the future.
|
||||||
|
|
||||||
|
## Conclusion
|
||||||
|
|
||||||
|
This concludes the 16th part of my series _Introduction to Gulp.js_. We learned how to use PostCSS to process our CSS files and how to use Stylelint to lint the CSS files for errors.
|
||||||
|
|
||||||
|
<Figure>
|
||||||
|
<MoreLink href="https://github.com/kogakure/gulp-tutorial" text="View Source on GitHub" />
|
||||||
|
</Figure>
|
||||||
@@ -0,0 +1,149 @@
|
|||||||
|
---
|
||||||
|
title: "Introduction to Gulp.js 2: Server with BrowserSync"
|
||||||
|
slug: gulp-tutorial-2-development-server-browsersync-configuration
|
||||||
|
author: Stefan Imhoff
|
||||||
|
date: 2014-10-19T10:17:00+02:00
|
||||||
|
description: "The ultimate tutorial and guide for Gulp.js: How to set up a development server with BrowserSync."
|
||||||
|
cover: /assets/images/cover/gulp.svg
|
||||||
|
tags: ["code"]
|
||||||
|
series: gulp
|
||||||
|
---
|
||||||
|
|
||||||
|
This is the 2nd part of my series, _Introduction to Gulp.js_. Today I will write the first few Gulp.js tasks and set up a development server with BrowserSync. And I will start to write a configuration file.
|
||||||
|
|
||||||
|
## Installing Gulp.js
|
||||||
|
|
||||||
|
To run my `gulpfile.js` I need to install gulp:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
$ npm install --save-dev gulp@3.9.0
|
||||||
|
```
|
||||||
|
|
||||||
|
If I run the command `gulp` on my command line, I get an error message <samp>Task 'default' is not in your gulpfile</samp>. This is because I haven’t written a gulp task until now.
|
||||||
|
|
||||||
|
I create inside the `gulp/tasks` folder a file `default.js` and write this code:
|
||||||
|
|
||||||
|
#### gulp/tasks/default.js
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
var gulp = require("gulp");
|
||||||
|
|
||||||
|
gulp.task("default", function () {
|
||||||
|
console.log("Hello Gulp.js!");
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
I know … I said I’m sick of _Hello World_ tutorials, but this won’t last very long. I’ll soon replace it with valuable code. Stay with me.
|
||||||
|
|
||||||
|
If you execute the command `gulp`, this Gulp.js task will output <samp>Hello Gulp.js!</samp> to the console.
|
||||||
|
|
||||||
|
I will speed up the pace from now on.
|
||||||
|
|
||||||
|
## Watch
|
||||||
|
|
||||||
|
Instead of calling a function and outputting text to the console, I can execute tasks. I decided to execute the watch task when running `gulp`. This task will later watch for changes in files and update my files.
|
||||||
|
|
||||||
|
#### gulp/tasks/default.js
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
var gulp = require("gulp");
|
||||||
|
|
||||||
|
gulp.task("default", ["watch"]);
|
||||||
|
```
|
||||||
|
|
||||||
|
It’s possible to run multiple tasks at once, which is why I write my `watch` task in an Array. Be careful: These tasks will run in parallel, not in sequential order. Later I will show how to run tasks in a predefined order.
|
||||||
|
|
||||||
|
I will create another folder within my `tasks` folder with the name `development` and put all tasks needed for development in this folder. This is not necessary, but I did so:
|
||||||
|
|
||||||
|
#### gulp/tasks/development/watch.js
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
var gulp = require("gulp");
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Start browser-sync task and then watch files for changes
|
||||||
|
*/
|
||||||
|
gulp.task("watch", ["browsersync"], function () {});
|
||||||
|
```
|
||||||
|
|
||||||
|
I will come back later to write the `watch` task. For now, the function will be empty, and run another task before running the watch task: `browser-sync`. All tasks within the Array will be executed _before_ the task is executed.
|
||||||
|
|
||||||
|
## BrowserSync
|
||||||
|
|
||||||
|
You might have heard of [LiveReload](http://livereload.com/), a tool that is watching for changes in your files and automatically reloads the server. With Stylesheets, even reloading is not needed. The page refreshes with the changes instantly.
|
||||||
|
|
||||||
|
But [BrowserSync](https://browsersync.io/) is even better: It does all LiveReload does, but you don’t need a browser plugin, and it syncs your actions like a scroll, click, refresh, or filling out forms to all browsers connected. This works even with mobile devices. And BrowserSync even has support for a development server. That’s why I will need nothing more than BrowserSync to get a development server with live reloading.
|
||||||
|
|
||||||
|
But first, I install BrowserSync:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
$ npm install --save-dev browser-sync@2.9.11
|
||||||
|
```
|
||||||
|
|
||||||
|
I create a new file `browser-sync.js` in `gulp/tasks/development/`. This file will start BrowserSync and the development server.
|
||||||
|
|
||||||
|
#### gulp/tasks/development/browser-sync.js
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
var gulp = require("gulp");
|
||||||
|
var browsersync = require("browser-sync");
|
||||||
|
var config = require("../../config").browsersync.development;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Run the build task and start a server with BrowserSync
|
||||||
|
*/
|
||||||
|
gulp.task("browsersync", ["build"], function () {
|
||||||
|
browsersync(config);
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
This code does need explanation: First, I load Gulp.js and BrowserSync, which are needed in this task. Then I load the configuration for BrowserSync. I will create this configuration file shortly. Keeping all configurations out of the tasks will make them more usable, and they can be shared between different projects.
|
||||||
|
|
||||||
|
The second thing worth mentioning is `['build']`. This does mean before starting BrowserSync it first will run the `build` Gulp.js task (which I will write later). Every Gulp.js task needs a name. As a second parameter, you can either add a JavaScript callback or tasks, or both.
|
||||||
|
|
||||||
|
## Configuration
|
||||||
|
|
||||||
|
I create a new file `config.js` in the main Gulp.js folder:
|
||||||
|
|
||||||
|
#### gulp/config.js
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
var src = "app";
|
||||||
|
var build = "build";
|
||||||
|
var development = "build/development";
|
||||||
|
var production = "build/production";
|
||||||
|
var srcAssets = "app/_assets";
|
||||||
|
var developmentAssets = "build/assets";
|
||||||
|
var productionAssets = "build/production/assets";
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
browsersync: {
|
||||||
|
development: {
|
||||||
|
server: {
|
||||||
|
baseDir: [development, build, src],
|
||||||
|
},
|
||||||
|
port: 9999,
|
||||||
|
files: [
|
||||||
|
developmentAssets + "/css/*.css",
|
||||||
|
developmentAssets + "/js/*.js",
|
||||||
|
developmentAssets + "/images/**",
|
||||||
|
developmentAssets + "/fonts/*",
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
```
|
||||||
|
|
||||||
|
First I extract paths needed over and over again later to variables, and then I create a CommonJS module and add an entry for BrowserSync. BrowserSync runs with default options, but I want to override the port and I tell BrowserSync which folders should be served.
|
||||||
|
|
||||||
|
Jekyll wipes out all files on recreation and to speed up development I have to be creative because I don’t want to recreate all assets on every Jekyll build. That’s why I serve more than one folder. I serve the folder `build/development`, which will hold the files created by Jekyll. The assets I will generate into a different folder, `build/assets` that Jekyll doesn’t wipe out. And additionally, the folder `app/_assets` to link source maps later.
|
||||||
|
|
||||||
|
BrowserSync watches my asset files, in order that my browser won’t reload like hell, every time Jekyll creates one file. I will later write one task, which reloads the Browser one time after the Jekyll build is complete.
|
||||||
|
|
||||||
|
## Conclusion
|
||||||
|
|
||||||
|
This concludes the 2nd part of my series _Introduction to Gulp.js_. We learned how to install Gulp.js, write a Gulp.js task, run other tasks, and set up a development server with BrowserSync.
|
||||||
|
|
||||||
|
<Figure>
|
||||||
|
<MoreLink href="https://github.com/kogakure/gulp-tutorial" text="View Source on GitHub" />
|
||||||
|
</Figure>
|
||||||
156
src/content/journal/2014/gulp-tutorial-3-build-clean-jekyll.mdx
Normal file
156
src/content/journal/2014/gulp-tutorial-3-build-clean-jekyll.mdx
Normal file
@@ -0,0 +1,156 @@
|
|||||||
|
---
|
||||||
|
title: "Introduction to Gulp.js 3: Build, Clean and Jekyll"
|
||||||
|
slug: gulp-tutorial-3-build-clean-jekyll
|
||||||
|
author: Stefan Imhoff
|
||||||
|
date: 2014-10-20T10:00:00+02:00
|
||||||
|
description: "The ultimate tutorial and guide for Gulp.js: How to write tasks for cleaning files and folders, generating the build and the website with Jekyll."
|
||||||
|
cover: /assets/images/cover/gulp.svg
|
||||||
|
tags: ["code"]
|
||||||
|
series: gulp
|
||||||
|
---
|
||||||
|
|
||||||
|
This is the 3rd part of my series, _Introduction to Gulp.js_. Today I will write the build task, which will execute all other tasks needed for a build, the task to delete assets for a fresh start, and the task to create my Jekyll site.
|
||||||
|
|
||||||
|
## Build
|
||||||
|
|
||||||
|
Now I create a `build` task. This task will run all other tasks, which are needed to create the site. By default, Gulp.js runs all tasks in parallel. That’s why I will get a problem if a specific order is needed. I will need a node module that runs tasks in a sequence:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
$ npm install --save-dev run-sequence@1.1.4
|
||||||
|
```
|
||||||
|
|
||||||
|
Next, I create the task:
|
||||||
|
|
||||||
|
#### gulp/tasks/development/build.js
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
var gulp = require("gulp");
|
||||||
|
var runSequence = require("run-sequence");
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Run all tasks needed for a build in a defined order
|
||||||
|
*/
|
||||||
|
gulp.task("build", function (callback) {
|
||||||
|
runSequence("delete", ["jekyll", "sass", "scripts", "images", "copy:fonts"], "base64", callback);
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
This task will first delete the assets’ folder (Jekyll is deleted by default), then create in parallel the Jekyll site, CSS files from SASS files, bundle the JavaScript files, copy images to the assets folder and copy vector fonts. After the `sass` task is finished, I replace links to small PNG files with Base64 encoding to inline them in my CSS files.
|
||||||
|
|
||||||
|
You should comment out tasks, we haven’t written until now, or Gulp cannot run. I just included them, so we don’t need to come back for each task we write and add a line.
|
||||||
|
|
||||||
|
## Delete Assets
|
||||||
|
|
||||||
|
To wipe out all files in the asset folder, I use the node module `del`.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
$ npm install --save-dev del@0.1.3
|
||||||
|
```
|
||||||
|
|
||||||
|
I need to add a config for deleting:
|
||||||
|
|
||||||
|
#### gulp/config.js
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
browsersync: {
|
||||||
|
...
|
||||||
|
},
|
||||||
|
delete: {
|
||||||
|
src: [developmentAssets]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
I will shorten all configuration options from now on. Every task will have its option section. These are JavaScript objects, please remember the trailing comma if you add a new configuration option.
|
||||||
|
|
||||||
|
The task will look like this:
|
||||||
|
|
||||||
|
#### gulp/tasks/development/delete.js
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
var gulp = require("gulp");
|
||||||
|
var del = require("del");
|
||||||
|
var config = require("../../config").delete;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Delete folders and files
|
||||||
|
*/
|
||||||
|
gulp.task("delete", function (callback) {
|
||||||
|
del(config.src, callback);
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
If you use a newer version of `del` or run into trouble because `del` doesn’t finish, try deleting the `callback` from the function.
|
||||||
|
|
||||||
|
## Jekyll
|
||||||
|
|
||||||
|
Next, I will write the configuration and the task to create the Jekyll site:
|
||||||
|
|
||||||
|
#### gulp/config.js
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
jekyll: {
|
||||||
|
development: {
|
||||||
|
src: src,
|
||||||
|
dest: development,
|
||||||
|
config: '_config.yml'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### gulp/config/development/jekyll.js
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
var gulp = require("gulp");
|
||||||
|
var cp = require("child_process");
|
||||||
|
var browsersync = require("browser-sync");
|
||||||
|
var config = require("../../config").jekyll.development;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Build the Jekyll Site
|
||||||
|
*/
|
||||||
|
gulp.task("jekyll", function (done) {
|
||||||
|
browsersync.notify("Compiling Jekyll");
|
||||||
|
|
||||||
|
return cp
|
||||||
|
.spawn(
|
||||||
|
"bundle",
|
||||||
|
[
|
||||||
|
"exec",
|
||||||
|
"jekyll",
|
||||||
|
"build",
|
||||||
|
"-q",
|
||||||
|
"--source=" + config.src,
|
||||||
|
"--destination=" + config.dest,
|
||||||
|
"--config=" + config.config,
|
||||||
|
],
|
||||||
|
{ stdio: "inherit" }
|
||||||
|
)
|
||||||
|
.on("close", done);
|
||||||
|
});
|
||||||
|
|
||||||
|
gulp.task("jekyll-rebuild", ["jekyll"], function () {
|
||||||
|
browsersync.reload();
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
There is a gulp plugin for Jekyll, but it’s alpha and was blacklisted because it’s not needed, as you can run shell tasks with a node. But I have to send the `done` status when the task is finished.
|
||||||
|
|
||||||
|
All this task is doing is running `jekyll build` with some options. I use `app` as the source folder, `build/development` as the target, and point to my `_config.yml`.
|
||||||
|
|
||||||
|
I put my `_config.yml` and other configuration files always at the root of my project. If you don’t like that, you need to update the configuration to point to the location of your `_config.yml`.
|
||||||
|
|
||||||
|
<Banner summary="To bundle or not to bundle">
|
||||||
|
|
||||||
|
**Be careful:** If you didn’t install Jekyll with a Gemfile you’ll have to change the Jekyll tasks and remove the `bundle exec` part. Instead of `return cp.spawn('bundle', ['exec', 'jekyll' …` you write `return cp.spawn('jekyll', ['build', '-q' …`. All other options stay the same.
|
||||||
|
|
||||||
|
</Banner>
|
||||||
|
|
||||||
|
I have a second Jekyll build task `jekyll-rebuild`, which is only a wrapper for a rebuild. All it does is reload the Browser when the build is completed.
|
||||||
|
|
||||||
|
## Conclusion
|
||||||
|
|
||||||
|
This concludes the 3rd part of my series, _Introduction to Gulp.js_. We learned how to run files in specified order with `run-sequence`, how to delete files and folders, and how to execute a shell task like Jekyll.
|
||||||
|
|
||||||
|
<Figure>
|
||||||
|
<MoreLink href="https://github.com/kogakure/gulp-tutorial" text="View Source on GitHub" />
|
||||||
|
</Figure>
|
||||||
107
src/content/journal/2014/gulp-tutorial-4-css-generation-sass.mdx
Normal file
107
src/content/journal/2014/gulp-tutorial-4-css-generation-sass.mdx
Normal file
@@ -0,0 +1,107 @@
|
|||||||
|
---
|
||||||
|
title: "Introduction to Gulp.js 4: Creating CSS with Sass and Compass"
|
||||||
|
slug: gulp-tutorial-4-css-generation-sass
|
||||||
|
author: Stefan Imhoff
|
||||||
|
date: 2014-10-21T10:30:00+02:00
|
||||||
|
description: "The ultimate tutorial and guide for Gulp.js: How to create CSS and Source Maps with Sass and Compass."
|
||||||
|
cover: /assets/images/cover/gulp.svg
|
||||||
|
tags: ["code"]
|
||||||
|
series: gulp
|
||||||
|
---
|
||||||
|
|
||||||
|
This is the 4th part of my series, _Introduction to Gulp.js_. Today I will show how to use Sass (and Compass if you want) to create CSS files. Furthermore, I will add vendor prefixes with Autoprefixer and create Source Maps for easier debugging of the Sass files.
|
||||||
|
|
||||||
|
## Sass and Autoprefixer
|
||||||
|
|
||||||
|
I use [Sass](http://sass-lang.com/) as a preprocessor for my CSS files. If you like to use [Compass](http://compass-style.org/), you have to set an option for this task.
|
||||||
|
|
||||||
|
Go ahead and install the npm modules needed:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
$ npm install --save-dev gulp-plumber@1.0.1 gulp-ruby-sass@2.0.4 gulp-filter@3.0.1 gulp-changed@1.0.0 gulp-autoprefixer@3.0.2 gulp-sourcemaps@1.6.0
|
||||||
|
```
|
||||||
|
|
||||||
|
That’s a lot, but this task will do a lot.
|
||||||
|
|
||||||
|
#### gulp/config.js
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
sass: {
|
||||||
|
src: srcAssets + '/scss/**/*.{sass,scss}',
|
||||||
|
dest: developmentAssets + '/css',
|
||||||
|
options: {
|
||||||
|
noCache: true,
|
||||||
|
compass: false,
|
||||||
|
bundleExec: true,
|
||||||
|
sourcemap: true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
autoprefixer: {
|
||||||
|
browsers: [
|
||||||
|
'last 2 versions',
|
||||||
|
'safari 5',
|
||||||
|
'ie 8',
|
||||||
|
'ie 9',
|
||||||
|
'opera 12.1',
|
||||||
|
'ios 6',
|
||||||
|
'android 4'
|
||||||
|
],
|
||||||
|
cascade: true
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### gulp/task/development/sass.js
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
var gulp = require("gulp");
|
||||||
|
var plumber = require("gulp-plumber");
|
||||||
|
var browsersync = require("browser-sync");
|
||||||
|
var sass = require("gulp-ruby-sass");
|
||||||
|
var gulpFilter = require("gulp-filter");
|
||||||
|
var autoprefixer = require("gulp-autoprefixer");
|
||||||
|
var sourcemaps = require("gulp-sourcemaps");
|
||||||
|
var config = require("../../config");
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generate CSS from SCSS
|
||||||
|
* Build sourcemaps
|
||||||
|
*/
|
||||||
|
gulp.task("sass", function () {
|
||||||
|
var sassConfig = config.sass.options;
|
||||||
|
|
||||||
|
sassConfig.onError = browsersync.notify;
|
||||||
|
|
||||||
|
// Don’t write sourcemaps of sourcemaps
|
||||||
|
var filter = gulpFilter(["*.css", "!*.map"], { restore: true });
|
||||||
|
|
||||||
|
browsersync.notify("Compiling Sass");
|
||||||
|
|
||||||
|
return sass(config.sass.src, sassConfig)
|
||||||
|
.pipe(plumber())
|
||||||
|
.pipe(sourcemaps.init())
|
||||||
|
.pipe(autoprefixer(config.autoprefixer))
|
||||||
|
.pipe(filter) // Don’t write sourcemaps of sourcemaps
|
||||||
|
.pipe(
|
||||||
|
sourcemaps.write(".", {
|
||||||
|
includeContent: false,
|
||||||
|
sourceRoot: "app/_assets/scss",
|
||||||
|
})
|
||||||
|
)
|
||||||
|
.pipe(filter.restore) // Restore original files
|
||||||
|
.pipe(gulp.dest(config.sass.dest));
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
I load all my files with the suffix of `*.sass` or `*.scss`. First, I pipe the files through _Plumber_. It will keep Gulp.js running if I create a syntax error in one of my files. It would normally crash with an error. The next step creates the CSS files, running the `sass` command. I create source maps and finally put the CSS files to their destination.
|
||||||
|
|
||||||
|
And I run the CSS files through Autoprefixer, which will add vendor prefixes. I used the Mixins of Compass a long time, but stopped now and write pure CSS. All vendor prefixes are added later for the browsers I want to support.
|
||||||
|
|
||||||
|
You might have guessed: If you wish to use Compass, set the option `compass` to `true`.
|
||||||
|
|
||||||
|
## Conclusion
|
||||||
|
|
||||||
|
This concludes the 4th part of my series, _Introduction to Gulp.js_. We learned how to keep Gulp.js running, even when we produce errors, how to preprocess SCSS files with Sass, create Source Maps, and add vendor prefixes to the CSS files.
|
||||||
|
|
||||||
|
<Figure>
|
||||||
|
<MoreLink href="https://github.com/kogakure/gulp-tutorial" text="View Source on GitHub" />
|
||||||
|
</Figure>
|
||||||
@@ -0,0 +1,296 @@
|
|||||||
|
---
|
||||||
|
title: "Introduction to Gulp.js 5: Bundling JavaScript with Browserify"
|
||||||
|
slug: gulp-tutorial-5-javascripts-browserify
|
||||||
|
author: Stefan Imhoff
|
||||||
|
date: 2014-10-22T08:00:00+02:00
|
||||||
|
description: "The ultimate tutorial and guide for Gulp.js: How to bundle JavaScript files with Browserify and use CommonJS modules to structure and organize your code."
|
||||||
|
cover: /assets/images/cover/gulp.svg
|
||||||
|
tags: ["code"]
|
||||||
|
series: gulp
|
||||||
|
---
|
||||||
|
|
||||||
|
This is the 5th part of my series, _Introduction to Gulp.js_. Today I will show how to use Browserify to bundle your JavaScript and use CommonJS modules to run node modules in the Browser.
|
||||||
|
|
||||||
|
## Browserify
|
||||||
|
|
||||||
|
This task is more complex because I use [Browserify](http://browserify.org/) to bundle my JavaScript. If this is too complex for your needs, you may use [gulp-concat](https://www.npmjs.com/package/gulp-concat) to concatenate all your JavaScript files into one file.
|
||||||
|
|
||||||
|
Browserify is a wonderful tool, which allows you to use node modules in your browser. Over 70% of the node modules will run! And it will bundle up all of your dependencies. If you want to find out more about writing CommonJS modules for Browserify, have a look at the documentation.
|
||||||
|
|
||||||
|
This task I saw in the <del>gulp-starter</del> <ins>[blendid](https://github.com/vigetlabs/blendid)</ins>. It’s long but clever. It allows the creation of multiple files with Browserify. I create two files. One file is loaded in the head of my website containing _Modernizr_ and one file with the rest of my JavaScript at the bottom.
|
||||||
|
|
||||||
|
## Creating JavaScript files with Browserify
|
||||||
|
|
||||||
|
Install the node modules needed for this task:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
$ npm install --save-dev browserify@11.2.0 vinyl-source-stream@1.0.0 watchify@3.4.0 gulp-util@3.0.1 pretty-hrtime@1.0.1 gulp-notify@2.0.0
|
||||||
|
```
|
||||||
|
|
||||||
|
Create the entry in the `config.js` file:
|
||||||
|
|
||||||
|
#### gulp/config.js
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
browserify: {
|
||||||
|
// Enable source maps
|
||||||
|
debug: true,
|
||||||
|
// Additional file extensions to make optional
|
||||||
|
extensions: ['.coffee', '.hbs'],
|
||||||
|
// A separate bundle will be generated for each
|
||||||
|
// bundle config in the list below
|
||||||
|
bundleConfigs: [{
|
||||||
|
entries: './' + srcAssets + '/javascripts/application.js',
|
||||||
|
dest: developmentAssets + '/js',
|
||||||
|
outputName: 'application.js'
|
||||||
|
}, {
|
||||||
|
entries: './' + srcAssets + '/javascripts/head.js',
|
||||||
|
dest: developmentAssets + '/js',
|
||||||
|
outputName: 'head.js'
|
||||||
|
}]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### gulp/tasks/development/scripts.js
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
var gulp = require("gulp");
|
||||||
|
var browsersync = require("browser-sync");
|
||||||
|
var browserify = require("browserify");
|
||||||
|
var source = require("vinyl-source-stream");
|
||||||
|
var watchify = require("watchify");
|
||||||
|
var bundleLogger = require("../../util/bundleLogger");
|
||||||
|
var handleErrors = require("../../util/handleErrors");
|
||||||
|
var config = require("../../config").browserify;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Run JavaScript through Browserify
|
||||||
|
*/
|
||||||
|
gulp.task("scripts", function (callback) {
|
||||||
|
browsersync.notify("Compiling JavaScript");
|
||||||
|
|
||||||
|
var bundleQueue = config.bundleConfigs.length;
|
||||||
|
|
||||||
|
var browserifyThis = function (bundleConfig) {
|
||||||
|
var bundler = browserify({
|
||||||
|
// Required watchify args
|
||||||
|
cache: {},
|
||||||
|
packageCache: {},
|
||||||
|
fullPaths: false,
|
||||||
|
// Specify the entry point of your app
|
||||||
|
entries: bundleConfig.entries,
|
||||||
|
// Add file extensions to make optional in your requires
|
||||||
|
extensions: config.extensions,
|
||||||
|
// Enable source maps!
|
||||||
|
debug: config.debug,
|
||||||
|
});
|
||||||
|
|
||||||
|
var bundle = function () {
|
||||||
|
// Log when bundling starts
|
||||||
|
bundleLogger.start(bundleConfig.outputName);
|
||||||
|
|
||||||
|
return (
|
||||||
|
bundler
|
||||||
|
.bundle()
|
||||||
|
// Report compile errors
|
||||||
|
.on("error", handleErrors)
|
||||||
|
// Use vinyl-source-stream to make the
|
||||||
|
// stream gulp compatible. Specify the
|
||||||
|
// desired output filename here.
|
||||||
|
.pipe(source(bundleConfig.outputName))
|
||||||
|
// Specify the output destination
|
||||||
|
.pipe(gulp.dest(bundleConfig.dest))
|
||||||
|
.on("end", reportFinished)
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
if (global.isWatching) {
|
||||||
|
// Wrap with watchify and rebundle on changes
|
||||||
|
bundler = watchify(bundler);
|
||||||
|
// Rebundle on update
|
||||||
|
bundler.on("update", bundle);
|
||||||
|
}
|
||||||
|
|
||||||
|
var reportFinished = function () {
|
||||||
|
// Log when bundling completes
|
||||||
|
bundleLogger.end(bundleConfig.outputName);
|
||||||
|
|
||||||
|
if (bundleQueue) {
|
||||||
|
bundleQueue--;
|
||||||
|
if (bundleQueue === 0) {
|
||||||
|
// If queue is empty, tell gulp the task is complete.
|
||||||
|
// https://github.com/gulpjs/gulp/blob/master/docs/API.md#accept-a-callback
|
||||||
|
callback();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
return bundle();
|
||||||
|
};
|
||||||
|
|
||||||
|
// Start bundling with Browserify for each bundleConfig specified
|
||||||
|
config.bundleConfigs.forEach(browserifyThis);
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
This task has additional utilities for handling errors and logging the bundling process. Put these into a `util` folder in your `gulp` folder:
|
||||||
|
|
||||||
|
#### gulp/util/bundleLogger.js
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
/* bundleLogger
|
||||||
|
------------
|
||||||
|
Provides gulp style logs to the bundle method in browserify.js
|
||||||
|
*/
|
||||||
|
|
||||||
|
var gutil = require("gulp-util");
|
||||||
|
var prettyHrtime = require("pretty-hrtime");
|
||||||
|
var startTime;
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
start: function (filepath) {
|
||||||
|
startTime = process.hrtime();
|
||||||
|
gutil.log("Bundling", gutil.colors.green(filepath));
|
||||||
|
},
|
||||||
|
|
||||||
|
end: function (filepath) {
|
||||||
|
var taskTime = process.hrtime(startTime);
|
||||||
|
var prettyTime = prettyHrtime(taskTime);
|
||||||
|
gutil.log("Bundled", gutil.colors.green(filepath), "in", gutil.colors.magenta(prettyTime));
|
||||||
|
},
|
||||||
|
};
|
||||||
|
```
|
||||||
|
|
||||||
|
#### gulp/util/handleErrors.js
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
var notify = require("gulp-notify");
|
||||||
|
|
||||||
|
module.exports = function () {
|
||||||
|
var args = Array.prototype.slice.call(arguments);
|
||||||
|
|
||||||
|
// Send error to notification center with gulp-notify
|
||||||
|
notify
|
||||||
|
.onError({
|
||||||
|
title: "Compile Error",
|
||||||
|
message: "<%= error.message %>",
|
||||||
|
})
|
||||||
|
.apply(this, args);
|
||||||
|
|
||||||
|
// Keep gulp from hanging on to this task
|
||||||
|
this.emit("end");
|
||||||
|
};
|
||||||
|
```
|
||||||
|
|
||||||
|
## Using CommonJS Modules
|
||||||
|
|
||||||
|
Writing CommonJS modules is nice. You export your function, object, string, or integer, you like to export as a module or individually:
|
||||||
|
|
||||||
|
#### math.js
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
exports.add = function() {
|
||||||
|
var sum = 0, i = 0, args = arguments, 1 = args.length;
|
||||||
|
while (i < 1) {
|
||||||
|
sum += args[i++];
|
||||||
|
}
|
||||||
|
return sum;
|
||||||
|
};
|
||||||
|
```
|
||||||
|
|
||||||
|
#### navigation.js
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
module.exports = {
|
||||||
|
toggleNavigation: function() {
|
||||||
|
...
|
||||||
|
}
|
||||||
|
};
|
||||||
|
```
|
||||||
|
|
||||||
|
Later, you import your modules and use them:
|
||||||
|
|
||||||
|
#### increment.js
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
var add = require("./math").add;
|
||||||
|
|
||||||
|
exports.increment = function (val) {
|
||||||
|
return add(val, 1);
|
||||||
|
};
|
||||||
|
```
|
||||||
|
|
||||||
|
#### application.js
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
var navigation = require("./navigation");
|
||||||
|
var triggerNavigation = document.querySelector(".toggle-navigation");
|
||||||
|
|
||||||
|
document.addEventListener("DOMContentLoaded", function () {
|
||||||
|
triggerNavigation.addEventListener("click", navigation.toggleNavigation);
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
## Loading non-CommonJS files
|
||||||
|
|
||||||
|
But one problem remains: How do I use JavaScript files, which aren’t written in CommonJS syntax? Like Modernizr or jQuery?
|
||||||
|
|
||||||
|
I need to install `browserify-shim`:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
$ npm install --save-dev browserify-shim@3.8.0
|
||||||
|
```
|
||||||
|
|
||||||
|
I open my `package.json` file and need to add a few lines:
|
||||||
|
|
||||||
|
#### package.json
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"...": "...",
|
||||||
|
"browser": {
|
||||||
|
"modernizr": "./app/_bower_components/modernizr/modernizr.js",
|
||||||
|
"jquery": "./app/_bower_components/jquery/dist/jquery.js"
|
||||||
|
},
|
||||||
|
"browserify-shim": {
|
||||||
|
"modernizr": "Modernizr",
|
||||||
|
"jquery": "$"
|
||||||
|
},
|
||||||
|
"browserify": {
|
||||||
|
"transform": ["browserify-shim"]
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"...": "..."
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
In the section `"browser"` you point `browserify-shim` to the asset you want to shim. I use [Bower](https://bower.io/) and have installed my packages into `app/_bower_components/`. The name you choose is the name you have to require later in your JavaScript.
|
||||||
|
|
||||||
|
Within `"browerify-shim"` you decide where to map this `require` to. To include jQuery or Modernizr later you would write:
|
||||||
|
|
||||||
|
#### app/assets/javascripts/head.js
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
require("modernizr");
|
||||||
|
```
|
||||||
|
|
||||||
|
#### app/\_assets/javascripts/application.js
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
require("jquery");
|
||||||
|
|
||||||
|
$(function () {
|
||||||
|
console.log("jQuery and Modernizr loaded");
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
You have to run `npm install` once you added a new entry to your `package.json` file.
|
||||||
|
|
||||||
|
## Conclusion
|
||||||
|
|
||||||
|
This concludes the 5th part of my series, _Introduction to Gulp.js_. We learned how to use Browserify to bundle JavaScript files, how to use CommonJS modules to run Node in your Browser, and how to use non-CommonJS JavaScript files.
|
||||||
|
|
||||||
|
<Figure>
|
||||||
|
<MoreLink href="https://github.com/kogakure/gulp-tutorial" text="View Source on GitHub" />
|
||||||
|
</Figure>
|
||||||
183
src/content/journal/2014/gulp-tutorial-6-images-vector-fonts.mdx
Normal file
183
src/content/journal/2014/gulp-tutorial-6-images-vector-fonts.mdx
Normal file
@@ -0,0 +1,183 @@
|
|||||||
|
---
|
||||||
|
title: "Introduction to Gulp.js 6: Images and Vector Fonts"
|
||||||
|
slug: gulp-tutorial-6-images-vector-fonts
|
||||||
|
author: Stefan Imhoff
|
||||||
|
date: 2014-10-23T08:00:00+02:00
|
||||||
|
description: "The ultimate tutorial and guide for Gulp.js: How to move images and generate vector fonts from SVG."
|
||||||
|
cover: /assets/images/cover/gulp.svg
|
||||||
|
tags: ["code"]
|
||||||
|
series: gulp
|
||||||
|
---
|
||||||
|
|
||||||
|
This is the 6th part of my series, _Introduction to Gulp.js_. The last post was long and complicated. This time it’s an easier one: I will show how I move my images and generate vector fonts.
|
||||||
|
|
||||||
|
## Images
|
||||||
|
|
||||||
|
The image task is a simple one again. All it does for now is copy the images to the asset directory. I will optimize my images later during the production build.
|
||||||
|
|
||||||
|
#### gulp/config.js
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
images: {
|
||||||
|
src: srcAssets + '/images/**/*',
|
||||||
|
dest: developmentAssets + '/images'
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### gulp/tasks/development/images.js
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
var gulp = require("gulp");
|
||||||
|
var changed = require("gulp-changed");
|
||||||
|
var config = require("../../config").images;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Copy images to build folder
|
||||||
|
* if not changed
|
||||||
|
*/
|
||||||
|
gulp.task("images", function () {
|
||||||
|
return gulp
|
||||||
|
.src(config.src)
|
||||||
|
.pipe(changed(config.dest)) // Ignore unchanged files
|
||||||
|
.pipe(gulp.dest(config.dest));
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
## Vector Fonts
|
||||||
|
|
||||||
|
I use vector fonts for my website. Vector fonts are one option to include high-quality icons on a website. Another option is using SVG directly or to use high-resolution images.
|
||||||
|
|
||||||
|
I am using [Font Custom](http://fontcustom.github.io/fontcustom/) to generate my vector fonts. There is a [gulp plugin](https://www.npmjs.com/package/gulp-fontcustom/) for this, but I couldn’t get it running. But I’m fine with running this task with a shell command (via Gulp.js). I will use Gulp.js later to watch the folder containing the SVG files and recreate the vector fonts if needed.
|
||||||
|
|
||||||
|
First, I need to install Font Custom (with Homebrew, you can find more installation methods on the Font Custom website):
|
||||||
|
|
||||||
|
```bash
|
||||||
|
$ brew install fontforge --with-python
|
||||||
|
$ brew install eot-utils
|
||||||
|
```
|
||||||
|
|
||||||
|
Next, I run the command `bundle exec fontcustom config` inside my main projects’ directory, which will create a file `fontcustom.yml`. I adjust my file until it looks like this:
|
||||||
|
|
||||||
|
#### fontcustom.yml
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
# --------------------------------------------------------------------------- #
|
||||||
|
# Project Info
|
||||||
|
# Default values shown. Learn more about these options by running
|
||||||
|
# `fontcustom help` or visiting <http://fontcustom.com>.
|
||||||
|
# --------------------------------------------------------------------------- #
|
||||||
|
|
||||||
|
font_name: fontcustom
|
||||||
|
css_selector: .icon-{{glyph}}
|
||||||
|
css_prefix: icon-
|
||||||
|
preprocessor_path: "/assets/fonts"
|
||||||
|
autowidth: false
|
||||||
|
no_hash: false
|
||||||
|
force: false
|
||||||
|
debug: false
|
||||||
|
quiet: false
|
||||||
|
|
||||||
|
# --------------------------------------------------------------------------- #
|
||||||
|
# Project Paths
|
||||||
|
# Relative paths are expanded from PROJECT_ROOT (defaults to the directory
|
||||||
|
# where the fontcustom command is run). INPUT and OUTPUT can be strings or
|
||||||
|
# hashes or file types/names.
|
||||||
|
# --------------------------------------------------------------------------- #
|
||||||
|
|
||||||
|
#project_root: some/other/place
|
||||||
|
#manifest: tmp/fontcustom
|
||||||
|
|
||||||
|
input:
|
||||||
|
vectors: vectors # required
|
||||||
|
# templates: app/assets/fonts/fontcustom/templates
|
||||||
|
|
||||||
|
output:
|
||||||
|
fonts: app/_assets/fonts # required
|
||||||
|
css: app/_assets/scss
|
||||||
|
preview: docs
|
||||||
|
# my-custom-template.yml: config
|
||||||
|
|
||||||
|
# --------------------------------------------------------------------------- #
|
||||||
|
# Templates
|
||||||
|
# Included in Font Custom:
|
||||||
|
# preview, css, scss, scss-rails, bootstrap, bootstrap-scss, bootstrap-ie7,
|
||||||
|
# bootstrap-ie7-scss
|
||||||
|
# Custom templates should be saved in the INPUT[:templates] directory and
|
||||||
|
# referenced by their base file name.
|
||||||
|
# --------------------------------------------------------------------------- #
|
||||||
|
|
||||||
|
templates: [scss, preview]
|
||||||
|
```
|
||||||
|
|
||||||
|
Next, I add configuration and the task to copy the fonts to their location:
|
||||||
|
|
||||||
|
#### gulp/config.js
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
copyfonts: {
|
||||||
|
development: {
|
||||||
|
src: srcAssets + '/fonts/*',
|
||||||
|
dest: developmentAssets + '/fonts'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### gulp/tasks/development/copy-fonts.js
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
var gulp = require("gulp");
|
||||||
|
var config = require("../../config").copyfonts.development;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Copy fonts to folder
|
||||||
|
*/
|
||||||
|
gulp.task("copy:fonts", ["fontcustom"], function () {
|
||||||
|
return gulp.src(config.src).pipe(gulp.dest(config.dest));
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
As you may have seen, before copying the fonts to the asset folder, another task gets executed: `fontcustom`.
|
||||||
|
|
||||||
|
Font Custom checks the files for changes and doesn’t generate anything if the files are the same.
|
||||||
|
|
||||||
|
To execute a shell command, I use the Gulp.js plugin `gulp-shell`:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
$ npm install --save-dev gulp-shell@0.5.0
|
||||||
|
```
|
||||||
|
|
||||||
|
#### gulp/tasks/development/fontcustom.js
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
var gulp = require("gulp");
|
||||||
|
var shell = require("gulp-shell");
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generate fonts with Fontcustom
|
||||||
|
* `brew install fontforge --with-python`
|
||||||
|
* `brew install eot-utils`
|
||||||
|
*/
|
||||||
|
gulp.task("fontcustom", shell.task(["bundle exec fontcustom compile"]));
|
||||||
|
```
|
||||||
|
|
||||||
|
Fontcustom is a Ruby Gem, and you’ll need to install the Gem either globally or in your Gemfile (if you install it globally, you have to drop the `bundle exec` from your command). I choose to install it with my Gemfile:
|
||||||
|
|
||||||
|
#### Gemfile
|
||||||
|
|
||||||
|
```ruby
|
||||||
|
source "https://rubygems.org"
|
||||||
|
|
||||||
|
gem 'jekyll', '~> 2.5.2'
|
||||||
|
gem 'sass', '>= 3.3'
|
||||||
|
gem 'fontcustom', '~> 1.3.7'
|
||||||
|
```
|
||||||
|
|
||||||
|
After you add the line for `fontcustom` you will have to run `bundle install` again.
|
||||||
|
|
||||||
|
## Conclusion
|
||||||
|
|
||||||
|
This concludes the 6th part of my series, _Introduction to Gulp.js_. We learned how to move files with Gulp.js (and don’t even need a plugin for that), and how I create my vector fonts. Nothing special, but the next part will be more interesting again.
|
||||||
|
|
||||||
|
<Figure>
|
||||||
|
<MoreLink href="https://github.com/kogakure/gulp-tutorial" text="View Source on GitHub" />
|
||||||
|
</Figure>
|
||||||
100
src/content/journal/2014/gulp-tutorial-7-base64.mdx
Normal file
100
src/content/journal/2014/gulp-tutorial-7-base64.mdx
Normal file
@@ -0,0 +1,100 @@
|
|||||||
|
---
|
||||||
|
title: "Introduction to Gulp.js 7: Base64 Encoded Images"
|
||||||
|
slug: gulp-tutorial-7-base64
|
||||||
|
author: Stefan Imhoff
|
||||||
|
date: 2014-10-24T07:30:00+02:00
|
||||||
|
description: "The ultimate tutorial and guide for Gulp.js: How to replace small images with base64 encoded images."
|
||||||
|
cover: /assets/images/cover/gulp.svg
|
||||||
|
tags: ["code"]
|
||||||
|
series: gulp
|
||||||
|
---
|
||||||
|
|
||||||
|
This is the 7th part of my series, _Introduction to Gulp.js_. Today, I will use Gulp.js to replace a lot of my URLs with small images with Base64 encoded images.
|
||||||
|
|
||||||
|
The last task executed by my `build` task is one, which replaces the URLs of small images in my CSS files with Base64 encoded images. This way, the images get embedded into the CSS file and don’t need an additional server request. If the images are not too large, this will speed up the loading of my website a lot.
|
||||||
|
|
||||||
|
I use plenty of small patterns on my website because I don’t like the _Flat Design_ approach a lot. The physical world isn’t flat. Nowhere. There is always structure, pattern, shade, and light. The patterns I use are from the fantastic website [Subtle Pattern](https://www.toptal.com/designers/subtlepatterns/). They have a few hundred nice subtle patterns.
|
||||||
|
|
||||||
|
To load the background pattern, I use SCSS like this:
|
||||||
|
|
||||||
|
```scss
|
||||||
|
%pattern-light-grey {
|
||||||
|
background-color: $background-color;
|
||||||
|
background-image: url(/assets/images/patterns/light_grey.png);
|
||||||
|
background-size: 301px 621px;
|
||||||
|
}
|
||||||
|
|
||||||
|
body {
|
||||||
|
@extend %pattern-light-grey;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
The generated CSS looks like this:
|
||||||
|
|
||||||
|
```css
|
||||||
|
body {
|
||||||
|
background-color: #fdfdfd;
|
||||||
|
background-image: url(/assets/images/patterns/light_grey.png);
|
||||||
|
background-size: 301px 621px;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
After the task ran, the CSS will look like this:
|
||||||
|
|
||||||
|
```css
|
||||||
|
body {
|
||||||
|
background-color: #fdfdfd;
|
||||||
|
background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAloAAATaBAMAAAB4FdU7AAAA…);
|
||||||
|
background-size: 301px 621px;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
For this task, I will need another Gulp.js plugin:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
$ npm install --save-dev gulp-base64@0.1.2
|
||||||
|
```
|
||||||
|
|
||||||
|
I add a new configuration entry and create the task:
|
||||||
|
|
||||||
|
#### gulp/config.js
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
base64: {
|
||||||
|
src: developmentAssets + '/css/*.css',
|
||||||
|
dest: developmentAssets + '/css',
|
||||||
|
options: {
|
||||||
|
baseDir: build,
|
||||||
|
extensions: ['png'],
|
||||||
|
maxImageSize: 20 * 1024, // bytes
|
||||||
|
debug: false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
I replace images with the ending PNG and if they have a maximum of 20 KB of size. This way, my high-resolution images don’t get embedded into the CSS file.
|
||||||
|
|
||||||
|
#### gulp/tasks/development/base64.js
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
var gulp = require("gulp");
|
||||||
|
var base64 = require("gulp-base64");
|
||||||
|
var config = require("../../config").base64;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Replace urls in CSS files with base64 encoded data
|
||||||
|
*/
|
||||||
|
gulp.task("base64", ["sass"], function () {
|
||||||
|
return gulp.src(config.src).pipe(base64(config.options)).pipe(gulp.dest(config.dest));
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
We are now finished with the development `build` task.
|
||||||
|
|
||||||
|
## Conclusion
|
||||||
|
|
||||||
|
This concludes the 7th part of my series, _Introduction to Gulp.js_. We learned how to replace URLs to PNGs with Base64 encoded images. And we are now finished with our `build` task.
|
||||||
|
|
||||||
|
<Figure>
|
||||||
|
<MoreLink href="https://github.com/kogakure/gulp-tutorial" text="View Source on GitHub" />
|
||||||
|
</Figure>
|
||||||
80
src/content/journal/2014/gulp-tutorial-8-watch.mdx
Normal file
80
src/content/journal/2014/gulp-tutorial-8-watch.mdx
Normal file
@@ -0,0 +1,80 @@
|
|||||||
|
---
|
||||||
|
title: "Introduction to Gulp.js 8: Watch for Changes"
|
||||||
|
slug: gulp-tutorial-8-watch
|
||||||
|
author: Stefan Imhoff
|
||||||
|
date: 2014-10-25T10:00:00+02:00
|
||||||
|
description: "The ultimate tutorial and guide for Gulp.js: How to set up a watch task, which triggers other tasks on file changes."
|
||||||
|
cover: /assets/images/cover/gulp.svg
|
||||||
|
tags: ["code"]
|
||||||
|
series: gulp
|
||||||
|
---
|
||||||
|
|
||||||
|
This is the 8th part of my series, _Introduction to Gulp.js_. Today, I will set up watch tasks for many files with Gulp.js.
|
||||||
|
|
||||||
|
Do you remember the `watch` task from the beginning? It started BrowserSync and the development server until now, but didn’t watch for anything. I will write these watch tasks now.
|
||||||
|
|
||||||
|
`Watch` is part of the API of gulp. It will watch files for changes, addition or deletion, and trigger tasks.
|
||||||
|
|
||||||
|
#### gulp/config.js
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
watch: {
|
||||||
|
jekyll: [
|
||||||
|
'_config.yml',
|
||||||
|
'_config.build.yml',
|
||||||
|
src + '/_data/**/*.{json,yml,csv}',
|
||||||
|
src + '/_includes/**/*.{html,xml}',
|
||||||
|
src + '/_layouts/*.html',
|
||||||
|
src + '/_plugins/*.rb',
|
||||||
|
src + '/_posts/*.{markdown,md}',
|
||||||
|
src + '/**/*.{html,markdown,md,yml,json,txt,xml}',
|
||||||
|
src + '/*'
|
||||||
|
],
|
||||||
|
sass: srcAssets + '/scss/**/*.{sass,scss}',
|
||||||
|
scripts: srcAssets + '/javascripts/**/*.js',
|
||||||
|
images: srcAssets + '/images/**/*',
|
||||||
|
sprites: srcAssets + '/images/**/*.png',
|
||||||
|
svg: 'vectors/*.svg'
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
I watch countless different file types for Jekyll. Changes in configuration files, data files, layouts, including plugins, and posts.
|
||||||
|
|
||||||
|
The Sass task will watch for changes in files with the suffix `sass` or `scss`. JavaScript gets triggered if I change JavaScript files. You get the point.
|
||||||
|
|
||||||
|
#### gulp/tasks/development/watch.js
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
var gulp = require("gulp");
|
||||||
|
var config = require("../../config").watch;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Start browsersync task and then watch files for changes
|
||||||
|
*/
|
||||||
|
gulp.task("watch", ["browsersync"], function () {
|
||||||
|
gulp.watch(config.jekyll, ["jekyll-rebuild"]);
|
||||||
|
gulp.watch(config.sass, ["sass", "scsslint"]);
|
||||||
|
gulp.watch(config.scripts, ["scripts", "jshint"]);
|
||||||
|
gulp.watch(config.images, ["images"]);
|
||||||
|
gulp.watch(config.svg, ["copy:fonts"]);
|
||||||
|
gulp.watch(config.sprites, ["sprites"]);
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
I set up six watch tasks. When a file of the Jekyll watch gets changed, deleted, or added, the `jekyll-rebuild` task gets executed. This task will run the Jekyll build, and after it’s finished reload the page.
|
||||||
|
|
||||||
|
For `SCSS` files, I run the `sass` tasks, and additionally, I run a `scsslint` task, which will check my files for syntax errors.
|
||||||
|
|
||||||
|
Changes in JavaScript files trigger the `scripts` tasks and a `jshint` task, which will check my files for syntax errors.
|
||||||
|
|
||||||
|
If I add, modify or delete an SVG file, my vector fonts get recreated. And as a fallback for browsers without vector font support, I create a PNG sprite map when I change an image of the sprite. It would be possible to auto-create the PNG files of the SVG files with [gulp-svg2png](https://www.npmjs.com/package/gulp-svg2png/), but I have an additional design on the sprite images, that’s why I don’t use it.
|
||||||
|
|
||||||
|
I miss now three tasks: `scsslint`, `jshint`, and `sprites`.
|
||||||
|
|
||||||
|
## Conclusion
|
||||||
|
|
||||||
|
This concludes the 8th part of my series, _Introduction to Gulp.js_. We learned how to use Gulp.js to watch for changes, deletion, or creation of files and how to trigger tasks. And the best part is: This is part of the Gulp.js API. We don’t need any plugins.
|
||||||
|
|
||||||
|
<Figure>
|
||||||
|
<MoreLink href="https://github.com/kogakure/gulp-tutorial" text="View Source on GitHub" />
|
||||||
|
</Figure>
|
||||||
@@ -0,0 +1,101 @@
|
|||||||
|
---
|
||||||
|
title: "Introduction to Gulp.js 9: Syntax-Check of SCSS and JavaScript"
|
||||||
|
slug: gulp-tutorial-9-linting-scss-and-javascript
|
||||||
|
author: Stefan Imhoff
|
||||||
|
date: 2014-10-26T08:10:00+02:00
|
||||||
|
description: "The ultimate tutorial and guide for Gulp.js: How to check the syntax of SCSS and JavaScript files."
|
||||||
|
cover: /assets/images/cover/gulp.svg
|
||||||
|
tags: ["code"]
|
||||||
|
series: gulp
|
||||||
|
---
|
||||||
|
|
||||||
|
This is the 9th part of my series, _Introduction to Gulp.js_. Today, I will use Gulp.js to automatically check my SCSS and JavaScript files for syntax errors and warnings.
|
||||||
|
|
||||||
|
I decided to lint my SCSS files and not the CSS files because it’s pointless to lint generated CSS. But you can do this with [gulp-csslint](https://www.npmjs.com/package/gulp-csslint/).
|
||||||
|
|
||||||
|
```bash
|
||||||
|
$ npm install --save-dev gulp-scss-lint@0.3.6 gulp-jshint@1.8.5 jshint-stylish@2.0.1
|
||||||
|
```
|
||||||
|
|
||||||
|
Additionally, you’ll need to install the `scss-lint` Gem and run `bundle install`:
|
||||||
|
|
||||||
|
#### Gemfile
|
||||||
|
|
||||||
|
```ruby
|
||||||
|
source "https://rubygems.org"
|
||||||
|
|
||||||
|
gem 'jekyll', '~> 2.5.2'
|
||||||
|
gem 'sass', '>= 3.3'
|
||||||
|
gem 'scss-lint', '~> 0.31.0'
|
||||||
|
gem 'fontcustom', '~> 1.3.7'
|
||||||
|
```
|
||||||
|
|
||||||
|
Add the options for `jshint` and `scss-lint`:
|
||||||
|
|
||||||
|
#### gulp/config.js
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
scsslint: {
|
||||||
|
src: [
|
||||||
|
srcAssets + '/scss/**/*.{sass,scss}',
|
||||||
|
'!' + srcAssets + '/scss/base/_sprites.scss',
|
||||||
|
'!' + srcAssets + '/scss/helpers/_meyer-reset.scss'
|
||||||
|
],
|
||||||
|
options: {
|
||||||
|
bundleExec: true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
jshint: {
|
||||||
|
src: srcAssets + '/javascripts/*.js'
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
I ignore files from checking (by adding a `!` in front of the path) because I didn’t write them or don’t have control over the syntax.
|
||||||
|
|
||||||
|
#### gulp/tasks/development/scss-lint.js
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
var gulp = require("gulp");
|
||||||
|
var scsslint = require("gulp-scss-lint");
|
||||||
|
var config = require("../../config").scsslint;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Lint SCSS files
|
||||||
|
* `gem install scss-lint` needed
|
||||||
|
*/
|
||||||
|
gulp.task("scsslint", function () {
|
||||||
|
return gulp.src(config.src).pipe(scsslint(config.options));
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
#### gulp/tasks/development/jshint.js
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
var gulp = require("gulp");
|
||||||
|
var jshint = require("gulp-jshint");
|
||||||
|
var stylish = require("jshint-stylish");
|
||||||
|
var config = require("../../config").jshint;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check JavaScript syntax with JSHint
|
||||||
|
*/
|
||||||
|
gulp.task("jshint", function () {
|
||||||
|
return gulp.src(config.src).pipe(jshint()).pipe(jshint.reporter(stylish));
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
<Banner summary="Configuration of Syntax Check Tools">
|
||||||
|
|
||||||
|
You may change the rules for linting SCSS or JavaScript by adding a hidden file `.scss-lint.yml` for SCSS lint and `.jshintrc` for JSHint to your project root.
|
||||||
|
|
||||||
|
To find out which options are available, look into the documentation of [SCSS-Lint](https://github.com/brigade/scss-lint) and [JSHint](https://jshint.com/docs/).
|
||||||
|
|
||||||
|
</Banner>
|
||||||
|
|
||||||
|
## Conclusion
|
||||||
|
|
||||||
|
This concludes the 9th part of my series, _Introduction to Gulp.js_. Today, we learned how to use Gulp.js to check the syntax of SCSS and JavaScript files. This task will run continuously while I write my files and print out errors to my console the moment I created them.
|
||||||
|
|
||||||
|
<Figure>
|
||||||
|
<MoreLink href="https://github.com/kogakure/gulp-tutorial" text="View Source on GitHub" />
|
||||||
|
</Figure>
|
||||||
37
src/content/journal/2014/john-seymour-books.mdx
Normal file
37
src/content/journal/2014/john-seymour-books.mdx
Normal file
@@ -0,0 +1,37 @@
|
|||||||
|
---
|
||||||
|
title: John Seymour’s Books on Self-Sufficiency
|
||||||
|
slug: john-seymour-books
|
||||||
|
author: Stefan Imhoff
|
||||||
|
date: 2014-08-03T16:00:00+02:00
|
||||||
|
description: On John Seymour’s fantastic, beautifully illustrated books on self-sufficiency, agriculture, and crafts. The optimal equipment for an upcoming zombie apocalypse.
|
||||||
|
tags: ["book"]
|
||||||
|
---
|
||||||
|
|
||||||
|
My earliest memories of books are that I lie on my parents’ soft carpet on my stomach in the living room and read the <cite>Atlas of World History</cite> or the books of _John Seymour_. Who was John Seymour, and why did his books remain in my memory?
|
||||||
|
|
||||||
|
## John Seymour
|
||||||
|
|
||||||
|
Born in England in 1914, he went to boarding school in Switzerland and later began studying agricultural sciences. He went to Africa at the age of 20 to work as a farmer. After serving in World War II in North Africa and Asia, he returned to England and moved to an old remote farm with his family in 1957 and started living solely on home-grown produce. After moving to a farm in Wales in the 1970s, he wrote his world-famous books <cite>The Complete Book of Self-Sufficiency</cite> (<cite lang="de"><AffiliateLink asin="3831015775" text="Das große Buch vom Leben auf dem Lande" /></cite>) and <cite>The Self-Sufficient Gardener</cite> (<cite lang="de"><AffiliateLink asin="3783161452" text="Selbstversorgung aus dem Garten" /></cite>).
|
||||||
|
|
||||||
|
<Bookshelf>
|
||||||
|
<AmazonBook asin="3831015775" />
|
||||||
|
<AmazonBook asin="3783161452" />
|
||||||
|
</Bookshelf>
|
||||||
|
|
||||||
|
## The Books
|
||||||
|
|
||||||
|
In his books, he writes comprehensible for laymen, enriched by beautiful, detailed drawings and illustrations. Whether you run a small garden or a large farm, his books describe well what needs to be done to create a closed and healthy cycle.
|
||||||
|
|
||||||
|
In _The New Complete Book of Self-Sufficiency_ he explains the food chain, soil types, and the seasons. He describes various types of gardens, addresses all kinds of fruits and vegetables, tells how to keep animals, cultivate and utilize land, grow long-lasting food, and a huge number of things. His second book, _The New Self-Sufficient Gardener_, goes into more detail and shows how to work with a garden.
|
||||||
|
|
||||||
|
## Part of my Survival Kit
|
||||||
|
|
||||||
|
I sometimes joke that if the zombie apocalypse arrives or civilization collapsed for other reasons, I first get my [Kukri](https://en.wikipedia.org/wiki/Khukuri), my bow with arrows, and the books of John Seymour. Because all the knowledge that we use every day, be it programming, design, management, spreadsheets, or any other activity of the modern age is then worth nothing. However, the knowledge in his books is almost timeless and will be up-to-date even in the distant future.
|
||||||
|
|
||||||
|
John Seymour wrote many more books during his life, for example, the commendable book <cite><AffiliateLink asin="0863181740" text="The National Trust Book of Forgotten Household Crafts" /></cite>. It displays various arts well-illustrated, such as woodcraft, construction, workshop or chores, the tools used, and the products they create.
|
||||||
|
|
||||||
|
## Conclusion
|
||||||
|
|
||||||
|
Even if you live in the city presently or do not have a garden or a plot of land, it is always a pleasure to page through the books. Therefore, his books should not be missing in any good library.
|
||||||
|
|
||||||
|
John Seymour would have turned 100 last month. His books contain valuable knowledge about self-sufficiency and have been expanded over the years. The current version of his bestseller <cite><AffiliateLink asin="1405345101" text="The New Complete Book of Self-Sufficiency" /></cite> has information on wind energy and solar energy. And you will learn how beer is brewed or wine is pressed.
|
||||||
83
src/content/journal/2014/logo-design.mdx
Normal file
83
src/content/journal/2014/logo-design.mdx
Normal file
@@ -0,0 +1,83 @@
|
|||||||
|
---
|
||||||
|
title: Design of My New Logo
|
||||||
|
slug: logo-design
|
||||||
|
author: Stefan Imhoff
|
||||||
|
date: 2014-04-18T19:00:00+02:00
|
||||||
|
description: "Insight into the design of my new logo and Rakkan: idea, research, and a short digression on the Chinese script."
|
||||||
|
tags: ["design"]
|
||||||
|
---
|
||||||
|
|
||||||
|
Since a logo is one of the essential elements that recognize a website, I wanted from the beginning to design a logo that fits the theme of the rest of the design of the site.
|
||||||
|
|
||||||
|
Although I think that a private person does not need a logo. But without a logo is a nice visual element missing that can be used later in all sorts of places, for example as the favicon.
|
||||||
|
|
||||||
|
It was clear to me early that I wanted to use a _Rakkan_ instead of a modern logo.
|
||||||
|
|
||||||
|
## Rakkan
|
||||||
|
|
||||||
|
A Rakkan (or Hanko) is called in Japan a seal, which is carved in stone, representing the signature of an artist and is stamped under the artwork, and thus works as a signature.
|
||||||
|
|
||||||
|
<Figure caption="Imprint of my Rakkan below a symbol for 忍 (Shinobi) written by me.">
|
||||||
|
<Image src="/assets/images/posts/rakkan.jpg" alt="Rakkan written ink below symbol" />
|
||||||
|
</Figure>
|
||||||
|
|
||||||
|
Even with my [martial arts portal](https://www.kogakure.de/), I use a seal as a logo.
|
||||||
|
|
||||||
|
## Research
|
||||||
|
|
||||||
|
Before I could start designing my new seal, I first had to conduct research. For this, I have looked at historical stamps in numerous works and studied their style and characteristics.
|
||||||
|
|
||||||
|
Stamps use multiple characters, four or even more. They exist in all forms, with the angular shape seeming to be the most common.
|
||||||
|
|
||||||
|
The next step was to choose the right font. As a typeface, many artists pick the _small seal script_, which was introduced by the Chinese Emperor Qin Shi Huang about 2200 years ago.
|
||||||
|
|
||||||
|
## Tour in Chinese Writing Styles
|
||||||
|
|
||||||
|
The first Chinese characters were scratched on potsherds 6000 years ago. The [oracle bone script](https://en.wikipedia.org/wiki/Oracle_bone_script) was developed 3400 years ago during the Shang Dynasty and was used for prophecy with animal bone oracles.
|
||||||
|
|
||||||
|
As the bronze casting technique around the 11th century BC the [bronze inscriptions](https://en.wikipedia.org/wiki/Chinese_bronze_inscriptions) were developed, which were used to write texts for politics, trade, military, administration, and oracles.
|
||||||
|
|
||||||
|
At the time of the Seven Kingdoms (475-221 BC), brush and ink were invented and written on bamboo, wood, and silk.
|
||||||
|
|
||||||
|
However, there were many spellings of the characters, which was not conducive to exchange and trading. King Zhou Xuanwang tried to solve this problem through the [large seal script](https://en.wikipedia.org/wiki/Large_seal_script), but he did not succeed. As Emperor Qin Shin Huang unified the empire in 221 BC, he ordered a uniform font. Chancellor Li Si eliminated characters and reduced strokes, thus founding the [small seal script](https://en.wikipedia.org/wiki/Small_seal_script).
|
||||||
|
|
||||||
|
The small seal is indeed beautiful by its curved lines, but was cumbersome to write in everyday use. At the beginning of the Han Dynasty, the [clerical script](https://en.wikipedia.org/wiki/Clerical_script), had much straighter lines and was easier and faster to write. This font further abstracted many characters.
|
||||||
|
|
||||||
|
At the same time, the _cursive script_ was invented, which is called [grass font](<https://en.wikipedia.org/wiki/Cursive_script_(East_Asia)>). It was used for private correspondence, but written by the common people. In this font, the strokes are connected. It looks like the brush has not been lifted off the paper.
|
||||||
|
|
||||||
|
In the 2nd century AD, scribes who disliked the concept of chancery script developed the [regular script](https://en.wikipedia.org/wiki/Regular_script).
|
||||||
|
|
||||||
|
At the end of the Han Dynasty, there was another style for private use besides the grass script: the [semi-cursive script](https://en.wikipedia.org/wiki/Semi-cursive_script).
|
||||||
|
|
||||||
|
The next significant change took place in 1956 when the government simplified the text. More than 1000 characters were deleted and with 2200 characters, the number of lines was reduced. This should lead to a better literacy of the population.
|
||||||
|
|
||||||
|
## The Meaning of the Characters on My Rakkan
|
||||||
|
|
||||||
|
For my new Rakkan, I have taken over the characters of my old Rakkan and then added two more characters. The old character contains the characters 木隠 (kogakure), which is not only the name of my martial arts portal, but my pseudonym on the Internet for many years. Translated, it means <q>hidden behind trees and leaves.</q>
|
||||||
|
|
||||||
|
The two new characters to the left of it mean 草 (grass) and 刀 (blade) in the small seal script. The signs allow numerous possibilities of interpretation, which I will not go into here to get anyone bored.
|
||||||
|
|
||||||
|
A seal can be created in two different variants: **Yin** or **Yang** (☯). In the Yin variant (as I have it on [kogakure.de](https://www.kogakure.de/)), the letters are carved out of the stone, and the surface remains. This time, I wanted to create a rakkan in the yang variant where everything is cut away except for the letters (and an edge).
|
||||||
|
|
||||||
|
## The Creation of the Rakkan
|
||||||
|
|
||||||
|
First, I made numerous pencil drawings, first of individual characters that I liked, and later of combinations. I wondered how an artist would carve a traditional stamp, how the letters touch and support to give the seal the necessary stability.
|
||||||
|
|
||||||
|
<Figure caption="Moleskine with pencil drawings of characters">
|
||||||
|
<Image
|
||||||
|
src="/assets/images/posts/moleskine-pencil-drawings.jpg"
|
||||||
|
alt="Moleskine with pencil drawings of characters"
|
||||||
|
/>
|
||||||
|
</Figure>
|
||||||
|
|
||||||
|
After I had developed my final variant, I drew the pencil lines with a thick highlighter (not without almost ruining my bamboo table, since I omitted out of laziness to use a pad). The texture that emerges when ink spreads through the paper looks much like a stone-carved edge.
|
||||||
|
|
||||||
|
In the next step, I then digitized the Rakkan and corrected it in Adobe Photoshop to remove unsightly parts or improve the lines.
|
||||||
|
|
||||||
|
To create a vector shape from this pixel image, I imported the image into Adobe Illustrator and converted it to a vector shape, then reduced the number of vector points and exported it as an SVG file.
|
||||||
|
|
||||||
|
To use my logo as a vector font on the page, I used [Font Custom](http://fontcustom.com/) to convert SVGs into web fonts. Thus, several vector graphics are combined as a single character in a font file. Once the font is loaded, the icons can then be displayed in any size and styled with CSS.
|
||||||
|
|
||||||
|
## The Font
|
||||||
|
|
||||||
|
Now the Rakkan can be used as a character and can be generated by a CSS class attribute on any element. Then, the character can be colored at will and get the desired font size or text effects (such as a shadow edge).
|
||||||
179
src/content/journal/2014/website-typography.mdx
Normal file
179
src/content/journal/2014/website-typography.mdx
Normal file
@@ -0,0 +1,179 @@
|
|||||||
|
---
|
||||||
|
title: Typography of My Website
|
||||||
|
slug: website-typography
|
||||||
|
author: Stefan Imhoff
|
||||||
|
date: 2014-06-19T10:30:00+02:00
|
||||||
|
description: "About the typography of my new website: scale, font, vertical rhythm, font size, font size, and sentence width."
|
||||||
|
tags: ["design", "code", "typography"]
|
||||||
|
---
|
||||||
|
|
||||||
|
I’m a _typophile_. But this is nothing for a self-help group because it means to love typography. It was clear to me from the start that I would start with typography for my new website.
|
||||||
|
|
||||||
|
Normally, designers pay attention to typography, to the rest of the people typography is unimportant. But typography influences each of us, consciously and subconsciously.
|
||||||
|
|
||||||
|
Readability or contrast decides whether the message of a text reaches the reader at all. And since words have incredible power, typography is underestimated.
|
||||||
|
|
||||||
|
The right choice of font, font size, weight, layout, sentence width, lead, letter spacing, or many other things unconsciously affects the reader and thus transports the statement. Proper typography can support and reinforce a message that makes the wrong message even meaningless or ridiculous.
|
||||||
|
|
||||||
|
I’ve always found typography fascinating, but after reading the book <cite><AffiliateLink asin="0881792128" text="The Elements of Typographic Style" /></cite> by _Robert Bringhurst_ I studied the matter more deeply. There is a Web-adapted version of his book on the website [The Elements _of_ Typographic Style Applied _to the_ Web](http://webtypography.net/).
|
||||||
|
|
||||||
|
<Bookshelf>
|
||||||
|
<AmazonBook asin="0881792128" />
|
||||||
|
</Bookshelf>
|
||||||
|
|
||||||
|
## Typography
|
||||||
|
|
||||||
|
Selecting good typography does not only mean selecting a scripture but is an art form that I am far from mastering.
|
||||||
|
|
||||||
|
It is important to choose the right page layout, the right sentence width, the correct lead, the right combination of fonts, the right scale, the correct font size, and a variety of other guidelines.
|
||||||
|
|
||||||
|
## Scale
|
||||||
|
|
||||||
|
Several years ago, I stumbled on two interesting lectures by Tim Brown on typography. Tim Brown works as _Type Manager_ for [Adobe Typekit](https://typekit.com/), a platform that offers Web font hosting. In his lectures, he offers a fascinating insight into the history of typography and how to achieve good typography.
|
||||||
|
|
||||||
|
I started with the selection of a scale. A typographic scale is the selection of harmonic proportions, which are then applied to font size, sentence width, and other parts.
|
||||||
|
|
||||||
|
The most famous proportion is the _Golden Ratio_, which has a ratio of `1:1.618`. But many other proportions have their origins in geometry, music, nature, or history. Tim Brown has even created the website [Modular Scale](http://www.modularscale.com/), on which a scale can be calculated.
|
||||||
|
|
||||||
|
However, this was not necessary for my project because Team-Sass offers a [SASS extension](https://github.com/modularscale/modularscale-sass).
|
||||||
|
|
||||||
|
So, I add the gem to my Gemfile first:
|
||||||
|
|
||||||
|
#### Gemfile
|
||||||
|
|
||||||
|
```ruby
|
||||||
|
source "https://rubygems.org"
|
||||||
|
|
||||||
|
group :development do
|
||||||
|
gem 'modular-scale'
|
||||||
|
end
|
||||||
|
```
|
||||||
|
|
||||||
|
Then I load the Compass plugin in `config.rb`:
|
||||||
|
|
||||||
|
#### config.rb
|
||||||
|
|
||||||
|
```ruby
|
||||||
|
# Require any additional compass plugins here.
|
||||||
|
require 'modular-scale'
|
||||||
|
```
|
||||||
|
|
||||||
|
Afterward, the module of Compass has to be loaded in a partial:
|
||||||
|
|
||||||
|
#### helpers/\_imports.scss
|
||||||
|
|
||||||
|
```scss
|
||||||
|
@import "compass";
|
||||||
|
@import "compass/reset";
|
||||||
|
@import "modular-scale";
|
||||||
|
```
|
||||||
|
|
||||||
|
I've decided to use the _golden ratio_ and then select the ideal text size and an important number:
|
||||||
|
|
||||||
|
```scss
|
||||||
|
$ms-base: 16px 18px;
|
||||||
|
$ms-ratio: $golden;
|
||||||
|
```
|
||||||
|
|
||||||
|
Now the helpers of Gems can be used everywhere in the SCSS. Instead of writing somewhere manually `42px` (`41.887px`) or `2.618em`, I write `modular-scale(4)` to choose the fourth value of the scale:
|
||||||
|
|
||||||
|
```scss
|
||||||
|
$font-scale-h1: modular-scale(4);
|
||||||
|
```
|
||||||
|
|
||||||
|
## Sentence Width
|
||||||
|
|
||||||
|
I could have started to choose the sentence width (line length), and then choose the grid appropriately. However, now that a scale has been selected, you can use it for the correct sentence width.
|
||||||
|
|
||||||
|
A responsive website with fluid line length is not an optimal prerequisite for a fixed sentence width. All sorts of values are circulating on the Internet, which is an optimal line length. All sorts of truisms such as no less than 45 characters, no more than 85 characters, or similar values can be found there. But nothing is scientifically proven, and one should measure in words rather than in characters because we see word images and not letters.
|
||||||
|
|
||||||
|
If the line is too long, the eyes are tired and the readers may lose the connection to the next line, if the line is too short, the constant interruption of the reading flow will be equally severe. There is one thing to say: The correct line length does not exist, it depends on the font, typesetting, and line spacing.
|
||||||
|
|
||||||
|
I tried to determine the optimal line length of my website with about 66 characters or about 10 words. Depending on the browser size, the line length then shrinks up to a threshold that I have selected. If this falls below, I select a different number of columns of the grid.
|
||||||
|
|
||||||
|
But I make sure that the line length is not wider than I determined to be optimal because as soon as the head has to be moved, the line is too long.
|
||||||
|
|
||||||
|
## Vertical Rhythm
|
||||||
|
|
||||||
|
Next, I set the line spacing and decided in addition to the technique of _vertical rhythm_. In the case of fonts of different sizes, the line spacing is mathematically changed in such a way that two different-sized fonts would be visually adjacent to each other.
|
||||||
|
|
||||||
|
Convenient is that the annoying mathematics behind the formula (which spits out crooked values with many decimal places) does not have to be calculated by yourself. Compass comes with a [module](http://compass-style.org/reference/compass/typography/vertical_rhythm/) for it.
|
||||||
|
|
||||||
|
There are some variables to be set:
|
||||||
|
|
||||||
|
```scss
|
||||||
|
$base-font-size: modular-scale(1); // 18px
|
||||||
|
$base-line-height-count: 1.5;
|
||||||
|
$base-line-height: $base-font-size * $base-line-height-count; // 27px
|
||||||
|
$relative-font-sizing: true; // use em and not px
|
||||||
|
$round-to-nearest-half-line: true; // prevent too big gaps between lines
|
||||||
|
```
|
||||||
|
|
||||||
|
A simple call to this mixin activates the vertical rhythm:
|
||||||
|
|
||||||
|
```scss
|
||||||
|
@include establish-baseline();
|
||||||
|
```
|
||||||
|
|
||||||
|
To adjust the font size, you do not use absolute values anymore, but a mixin. This can be used with the Mixin of _Modular Scale_:
|
||||||
|
|
||||||
|
```scss
|
||||||
|
h1 {
|
||||||
|
@include adjust-font-size-to(modular-scale(6));
|
||||||
|
@include trailer(1, modular-scale(6), margin);
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
You no longer directly define the distances above or below an element, but use mixins, as in the example `trailer`.
|
||||||
|
|
||||||
|
## Fonts
|
||||||
|
|
||||||
|
I had the most problems choosing the font, as this is a difficult task. There are tons of fonts, but finding the right one for the occasion is a big hassle. To make matters worse, there are far more bad fonts on the Internet than good fonts.
|
||||||
|
|
||||||
|
Creating a font is an immense effort and therefore a license for a font step also costs a lot of money. But to use web typography, the designer of a font must release it for embedding in a website. Beautiful, free fonts are available, for example on [The League of Movable Type](https://www.theleagueofmoveabletype.com/).
|
||||||
|
|
||||||
|
In addition to owning the font, there is the possibility to integrate the font of a web service. There are for example pay-as-you-go solutions such as [Adobe Typekit](https://typekit.com/), which offer many professional fonts. Anyone unwilling to regularly pay to use a font can still choose from many fonts that have been freely made available by nice designers.
|
||||||
|
|
||||||
|
I first looked at Typekit, but then chose two free fonts that I integrate into my website via [Google Fonts](https://fonts.google.com):
|
||||||
|
|
||||||
|
- _Gentium Basic_ for long-running texts
|
||||||
|
- _Yanone Kaffeesatz_ for headlines and shorter texts, as on the homepage
|
||||||
|
|
||||||
|
Gentian Basic is an appealing typeface with serifs and an italic typeface, while Yanone Kaffeesatz is a sans serif typeface. Both fonts in combination offer a harmonious picture.
|
||||||
|
|
||||||
|
## Font Size
|
||||||
|
|
||||||
|
On November 17, 2006, Oliver Reichenstein wrote on the Blog of Information Architects about the [100% Easy-2-Read Standard](https://ia.net/topics/100e2r), this concept was new to me. At that time, the font on websites was consistently set too small. The browser standard has always been `16px`. But many pages use values between 10 and 12 pixels.
|
||||||
|
|
||||||
|
After this blog post, designers started to increase the base font size. I even use `18px` for my floating text font.
|
||||||
|
|
||||||
|
## Font Weight
|
||||||
|
|
||||||
|
I load _Gentium Basic_ in 400 and 700 and _Yanone Kaffeesatz_ in 200 and 400. I can put bold parts of the body text in 700 and the rest in 400. On the homepage, I use _Yanone Kaffeesatz_. For high-resolution displays (Retina) I use a font width of 200, for all other displays 400.
|
||||||
|
|
||||||
|
## Tracking, Widows and Orphans
|
||||||
|
|
||||||
|
_Tracking_, the inserting of spaces between individual letters, I use on my website in a few places and _Kerning_, reducing the spacing between individual letters, not at all. Manual kerning on the Internet is a time-consuming task. There are JavaScript solutions, but the whole thing is not worth the effort or load size on the web.
|
||||||
|
|
||||||
|
To prevent a single word from being alone on a new line, I have been using JavaScript for a while. Since this jQuery plugin did not work reliably in cases and had a large file size, I removed it after a few weeks.
|
||||||
|
|
||||||
|
## Other Typographic Formatting
|
||||||
|
|
||||||
|
I use more typographic formatting, such as centered verse blocks, and correct quotation marks for inline citations depending on the language. I’ve written myself a Jekyll plugin that ensures that `e.g.`, `1000 €`, or `5 + 3` are provided with invisible spaces so that everything slips as a block in a new line and not character by character.
|
||||||
|
|
||||||
|
## Interesting Links About Typography
|
||||||
|
|
||||||
|
For those who have become curious about typography, I have put together a small list of recommended links to this topic:
|
||||||
|
|
||||||
|
- [The Elements _of_ Typographic Style Applied _to the_ Web](http://webtypography.net/)
|
||||||
|
- [Thinking with Type](http://thinkingwithtype.com/)
|
||||||
|
- [Butterick’s Practical Typography](https://practicaltypography.com/)
|
||||||
|
- [Type Basics](http://www.typeworkshop.com/index.php?id1=type-basics)
|
||||||
|
- [Web Design is 95% Typography](https://ia.net/topics/the-web-is-all-about-typography-period)
|
||||||
|
- [Five simple steps to better typography](https://markboulton.co.uk/journal/2005-04-13.five-simple-steps-to-better-typography/)
|
||||||
|
- [Nice Web Type](http://nicewebtype.com/)
|
||||||
|
- [More Meaningful Typography](https://alistapart.com/article/more-meaningful-typography)
|
||||||
|
- [A List Apart: Typography](https://alistapart.com/topic/design/typography/)
|
||||||
|
- [Kerntype](https://type.method.ac/)
|
||||||
|
- [Modular Scale](http://www.modularscale.com/)
|
||||||
|
- [Helvetica – A Documentary Film by Gary Hustwit](https://www.hustwit.com/helvetica/)
|
||||||
Reference in New Issue
Block a user