Search code examples
twitter-bootstraptwitter-bootstrap-3webpackwebfonts

How can I restrict the font format used by Webpack / Bootstrap?


Using React 16, Bootstrap 3, Webpack 2 and Typescript 2.3.

By default Bootstrap includes WOFF, WOFF2, EOT, TTF and SVG font formats for the Glyphicons-Halflings font-family.

I'm happy to support only WOFF font format - or at least, I definitely don't want to deal with EOT because I don't support IE8, and SVG and TTF are annoyingly big.

Embedding the fonts into my javascript deliverable is done via this webpack configuration:

module: {
  rules: [
    {test: /\.(tsx|ts)$/, loader: "awesome-typescript-loader"},
    {test: /\.css$/, use: ['style-loader', 'css-loader']},
    {test: /\.(png|woff|woff2|eot|ttf|svg)$/, 
      loader: 'url-loader?limit=10240',
      options: { name: '[path][name].[ext]'},
    },
    {test: /\.js$/, enforce: "pre", loader: "source-map-loader"},
  ]
},

I'm actually not sure about how that url-loader config works. I was originally just trying to get the font files to be processed without mangling their names with the content hash. But instead Webpack inlined them into the javascript file and I decided I liked that much better - especially if I can get the size down by stripping the other font formats.

At first, I thought I could just omit the font formats from the url-loader config, like:

    {test: /\.(png|woff)$/, 
      loader: 'url-loader?limit=10240',
      options: { name: '[path][name].[ext]'},
    },

But that fails with these kind of errors:

ERROR in ./~/bootstrap/dist/fonts/glyphicons-halflings-regular.eot
Module parse failed: ....\node_modules\bootstrap\dist\fonts\glyphicons-halflings-regular.eot Unexpected character '�' (1:0)
You may need an appropriate loader to handle this file type.
(Source code omitted for this binary file)
 @ ./~/css-loader!./~/bootstrap/dist/css/bootstrap.css 6:4445-4497 6:4520-4572
 @ ./~/bootstrap/dist/css/bootstrap.css
 @ ./src/main/ts/AppReactRoot.tsx

So then I thought maybe I could override the bootstrap font-face in my app.css file and only specify a single font format, e.g.:

@font-face {
  font-family: "Glyphicons Halflings";
  src: url("~bootstrap/dist/css/bootstrap.css") format('woff');
  font-weight: normal;
}

But that seems to make no difference. The app.css file, if it makes a difference, looks like this:

import * as React from "react";
import * as ReactDOM from "react-dom";

import "bootstrap/dist/css/bootstrap.css"; 
import './app.css';

import {UserApp} from "app/UserApp";

ReactDOM.render(
  <UserApp/>,
  document.getElementById("root")
);

So, the question is: How can I override the Bootstrap defaults so that I'm only using the WOFF font format?


Solution

  • Here's what I came up with.

    Add a custom font with only the formats you want to support, and tell bootstrap to use that instead:

    @font-face {
      font-family: "Glyphicons Custom";
      src: url('~bootstrap/fonts/glyphicons-halflings-regular.woff') format('woff');
      font-weight: normal;
    }
    
    .glyphicon {
      font-family: 'Glyphicons Custom';
    }
    

    Then exclude the font formats you don't want from the webpack config by adding an emitFiles=false loader rule for the formats you don't want. My webpack module rules now looks like:

    module: {
      rules: [
        {test: /\.(tsx|ts)$/, loader: "awesome-typescript-loader"},
        {test: /\.css$/, use: ['style-loader', 'css-loader']},
        // small PNGs (< limit bytes) will be inlined into the js
        // larger will be dumped into assets/appVersion
        { test: /\.(png)$/,
          loader: 'url-loader',
          options: {
            limit: 10000,
            name: '[name].[ext]',
            publicPath: publicAssetPath,
          },
        },
        /* This embeds WOFF format fonts (including haflings) regardless of
        size.  Embedding to get rid of "Flash of Invisible Text" issue with
        glyphicons.
        */
        { test: /\.woff$/, loader: 'url-loader'},
        // filter out glyphicons font formats we don't care about
        { test: /bootstrap.dist.fonts.glyphicons-halflings-regular\.(woff|eot|svg|ttf|woff2)$/,
          loader: 'file-loader?emitFile=false'
        },
        {test: /\.js$/, enforce: "pre", loader: "source-map-loader"},
      ]
    },
    

    Note that there will now be a second small font-woff data url in your javascript that contains the code module.exports = __webpack_public_path__ + "fa2772327f55d8198301fdb8bcfc8158.woff"; Not sure why it's emitting that.

    The problem with the url-loader code in the original question was that you can't use both url parameters in the loader property and the options property. My limit setting was being ignored and url-loader seems to take that as meaning that it should embed everything.