Flag of Ukraine
SymfonyCasts stands united with the people of Ukraine

Bootstrap CSS & Fonts

Keep on Learning!

If you liked what you've learned so far, dive in!
Subscribe to get access to this tutorial plus
video, code and script downloads.

Start your All-Access Pass
Buy just this tutorial for $12.00

With a Subscription, click any sentence in the script to jump to that part of the video!

Login Subscribe

In our base layout - base.html.twig - I want to remove all of the CSS link tags and require them. We only have two left: Bootstrap and Font Awesome.

Start by removing Bootstrap. Instead, in layout.js, before main.css, add the require() function.

But, what do we put here?

Finding Non-Standard node_modules Paths

We installed Bootstrap via Yarn. That's why we can require the Bootstrap JS file. But what about the Bootstrap CSS file? Does that package even contain the CSS? Let's find out! Open your node_modules/ directory, find bootstrap/, and look inside.

Ok! This also has a dist/ directory with css/bootstrap.css. That means our require path should be bootstrap/dist/css/bootstrap.css:

... lines 1 - 7
require('bootstrap/dist/css/bootstrap.css');
require('../css/main.css');
... lines 10 - 14

Oh, and we don't need to worry about requiring a minified file because, eventually, Webpack will minify all our CSS for us. Thanks Webpack!

This require path makes perfect sense: since the path does not start with a dot, it looks in node_modules/ for a bootstrap/ directory, and then we can reference any file we want.

What Happens when you Require a Module Name?

But then, how does this work? When we just say require('bootstrap')... what does that do? Does it look for an index.js file in that directory? Well, there isn't one, so there goes that theory.

The secret is inside of the package.json file for that package. It should have a key called main. Not all libraries have this key - some old, legacy libraries won't - but most new libraries will have this.

The main key tells Node which file to include when someone simply requires the module by name. When we say, require('bootstrap') it actually requires dist/js/npm.js.

file-loader for Fonts

Close package.json, and go refresh the page! Woh! That did not go well. In the console... ah! A familiar error:

Module parse failed for fonts/glyphicons-halflings-regular.eot. Unexpected character

Yep, Bootstrap's CSS is referencing a font file, and just like with images, Webpack has no idea what to do with it!

Back on the Asset Management section of Webpack's site, there's also a link for Loading Fonts. And, yea, it's the same exact thing as images.

In webpack.config.js, copy the image loader, and, this time I'll use the test regex from the docs exactly:

... lines 1 - 3
module.exports = {
... lines 5 - 14
module: {
rules: [
... lines 17 - 39
{
test: /\.(woff|woff2|eot|ttf|otf)$/,
use: [
'file-loader'
]
}
]
},
... lines 48 - 53
};

Excellent! Find your terminal, clear out all these ugly error - oof - and restart Webpack:

./node_modules/.bin/webpack --watch

It's happy! I'm happy! And if you close node_modules/ and look in build/, ha! We have a bunch of new font files. If you refresh, everything is back to normal. We're not using any of the Glyphicons from Bootstrap, but if we were, they would totally work.

Leave a comment!

7
Login or Register to join the conversation
Dragoslav-I Avatar
Dragoslav-I Avatar Dragoslav-I | posted 9 months ago

How can the user download the fonts and add them to the app and have them immediately available ?

Reply

Hey Dragoslav,

I think you can just download them through their CDN, store them in your app and let Webpack to process and move the files into the public directory

Cheers!

1 Reply
Dragoslav-I Avatar

Do not work.

Reply

Could you tell me what's not working? What did you try, and what error you got?

Reply
Dragoslav-I Avatar

I put this in webpack. But my app still do not see new fonts

`.addLoader( {

    test: /.(ttf|otf|eot|svg|woff(2)?)(\?[a-z0-9]+)?$/,

    use: [{
      loader: 'file-loader',
      options: {
        name: '[name].[ext]',
        outputPath: 'assets/fonts/',
        publicPath: '../fonts/'
      }
    }]
});`
Reply
GDIBass Avatar
GDIBass Avatar GDIBass | posted 5 years ago

had to drop the version of bootstrap to get this to work (now that bootstrap 4 is out).

yarn remove bootstrap
yarn add bootstrap@3.3.7 --dev

Reply

Hey GDIBass

Yep, sorry about that, this tutorial was recorded before Bootstrap v4 was released, we added a note in the script to inform our users about it, but you may not noticed it :)

Have a nice day.

Reply
Cat in space

"Houston: no signs of life"
Start the conversation!

This tutorial explains the concepts of an old version of Webpack using an old version of Symfony. The most important concepts are still the same, but you should expect significant differences in new versions.

What PHP libraries does this tutorial use?

// composer.json
{
    "require": {
        "php": "^7.2.0",
        "symfony/symfony": "3.3.*", // v3.3.16
        "twig/twig": "2.10.*", // v2.10.0
        "doctrine/orm": "^2.5", // v2.7.0
        "doctrine/doctrine-bundle": "^1.6", // 1.10.3
        "doctrine/doctrine-cache-bundle": "^1.2", // 1.3.5
        "symfony/swiftmailer-bundle": "^2.3", // v2.6.3
        "symfony/monolog-bundle": "^2.8", // v2.12.1
        "symfony/polyfill-apcu": "^1.0", // v1.4.0
        "sensio/distribution-bundle": "^5.0", // v5.0.22
        "sensio/framework-extra-bundle": "^3.0.2", // v3.0.26
        "incenteev/composer-parameter-handler": "^2.0", // v2.1.2
        "friendsofsymfony/user-bundle": "^2.0", // v2.1.2
        "doctrine/doctrine-fixtures-bundle": "~2.3", // v2.4.1
        "doctrine/doctrine-migrations-bundle": "^1.2", // v1.3.2
        "composer/package-versions-deprecated": "^1.11", // 1.11.99
        "friendsofsymfony/jsrouting-bundle": "^1.6" // 1.6.0
    },
    "require-dev": {
        "sensio/generator-bundle": "^3.0", // v3.1.6
        "symfony/phpunit-bridge": "^3.0" // v3.3.5
    }
}

What JavaScript libraries does this tutorial use?

// package.json
{
    "dependencies": [],
    "devDependencies": {
        "babel-core": "^6.25.0", // 6.25.0
        "babel-loader": "^7.1.1", // 7.1.1
        "babel-plugin-syntax-dynamic-import": "^6.18.0", // 6.18.0
        "babel-preset-env": "^1.6.0", // 1.6.0
        "bootstrap-sass": "^3.3.7", // 3.3.7
        "clean-webpack-plugin": "^0.1.16", // 0.1.16
        "copy-webpack-plugin": "^4.0.1", // 4.0.1
        "core-js": "^2.4.1", // 2.4.1
        "css-loader": "^0.28.4", // 0.28.4
        "extract-text-webpack-plugin": "^3.0.0", // 3.0.0
        "file-loader": "^0.11.2", // 0.11.2
        "font-awesome": "^4.7.0", // 4.7.0
        "jquery": "^3.2.1", // 3.2.1
        "lodash": "^4.17.4", // 4.17.4
        "node-sass": "^4.5.3", // 4.5.3
        "resolve-url-loader": "^2.1.0", // 2.1.0
        "sass-loader": "^6.0.6", // 6.0.6
        "style-loader": "^0.18.2", // 0.18.2
        "sweetalert2": "^6.6.6", // 6.6.6
        "webpack": "^3.4.1", // 3.4.1
        "webpack-chunk-hash": "^0.4.0", // 0.4.0
        "webpack-dev-server": "^2.6.1", // 2.6.1
        "webpack-manifest-plugin": "^1.2.1" // 1.2.1
    }
}
userVoice