Bootstrap: data: URIs containing spaces break some CSS minifiers

Created on 5 Jan 2018  路  5Comments  路  Source: twbs/bootstrap

As best I understand, this is not strictly a bug in Bootstrap, but I think you can work around it and make deployment easier for people without hurting anything.

The navbar-toggler-icon background-image is a url() with a data: URI that contains space characters. At least rCSSmin, which is the default minifier for django-compressor, assumes that url() can't contain spaces, according to ndparker/rcssmin#8.

If you wrap variables such as $navbar-dark-toggler-icon-bg in an additional str-replace(..., " ", "%20") then these minifiers work fine.

Since these variables are only used once each this isn't a big change to the generated CSS but makes Bootstrap easier to deploy, so I hope you'll consider making this change.

css v4

Most helpful comment

Some additional ideas from webpack with roots/sage:

A 'Clean' Workaround

TL;DR

1.
In Your assets/styles/main.scss insert at the top of the file (yes, before @import "common/variables";, we need the functions for str-replace()):

@import "~bootstrap/scss/functions";
@import "~bootstrap/scss/variables";

2.
In Your styles/common/_variables.scss insert:

$navbar-dark-toggler-icon-bg:       url("data:image/svg+xml;charset=utf8,%3Csvg viewBox='0 0 30 30' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath stroke='#{str-replace(str-replace(#{$navbar-dark-color}, "(", "%28"), ")", "%29")}' stroke-width='2' stroke-linecap='round' stroke-miterlimit='10' d='M4 7h22M4 15h22M4 23h22'/%3E%3C/svg%3E");
$navbar-light-toggler-icon-bg:      url("data:image/svg+xml;charset=utf8,%3Csvg viewBox='0 0 30 30' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath stroke='#{str-replace(str-replace(#{$navbar-light-color}, "(", "%28"), ")", "%29")}' stroke-width='2' stroke-linecap='round' stroke-miterlimit='10' d='M4 7h22M4 15h22M4 23h22'/%3E%3C/svg%3E");

Detail

The Problem aren't the spaces. The brackets from the stroke='#{$navbar-dark-color}' causes the problem. So if we str-replace() the ( with %28 and the ) with %29 you can run yarn run build:production without removing your scss.

Reference:
Bootstrap 4 CSS error when minifying with webpack, causes loss of SCSS running build:production

All 5 comments

I don't get why the minifier you mention doesn't follow the specs.

If it's valid, the minifier should be fixed.

can anyone help me to set up an other (working) css loader/minifier in webpack, to get a working minified build?

my sass config is

            {
                test: /\.s[ac]ss$/,
                loader: ExtractTextPlugin.extract({
                    use: [
                        'css-loader',
                        {
                            loader: 'postcss-loader',
                            options: {
                                plugins: (loader) => [
                                    require('autoprefixer')()
                                ]
                            }
                        },
                        'resolve-url-loader',
                        'sass-loader?sourceMap'
                    ],
                    fallback: 'style-loader'
                })
            }

and I added a plugin to specify the loader options:

       new webpack.LoaderOptionsPlugin({
            minimize: isProduction,
            options: {
                context: __dirname,
                output: { path: './' }
            }
        })

or should I oberride the $navbar-dark-toggler-icon-bg variables?

I haven't used webpack, but I found that clean-css is the minifier that the official Bootstrap distribution is built with, so if you can figure out how to make webpack use both postcss and clean-css you should be set. I can't help you do that though since my workflow is completely different.

Together with roots/sage#2017 it seems there's evidence that this is a pain point for people trying to use Bootstrap 4, so I still think it's worth including in upstream Bootstrap the small workaround of replacing spaces with %20 in these data: URIs, even if the minifiers are technically wrong here.

Some additional ideas from webpack with roots/sage:

A 'Clean' Workaround

TL;DR

1.
In Your assets/styles/main.scss insert at the top of the file (yes, before @import "common/variables";, we need the functions for str-replace()):

@import "~bootstrap/scss/functions";
@import "~bootstrap/scss/variables";

2.
In Your styles/common/_variables.scss insert:

$navbar-dark-toggler-icon-bg:       url("data:image/svg+xml;charset=utf8,%3Csvg viewBox='0 0 30 30' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath stroke='#{str-replace(str-replace(#{$navbar-dark-color}, "(", "%28"), ")", "%29")}' stroke-width='2' stroke-linecap='round' stroke-miterlimit='10' d='M4 7h22M4 15h22M4 23h22'/%3E%3C/svg%3E");
$navbar-light-toggler-icon-bg:      url("data:image/svg+xml;charset=utf8,%3Csvg viewBox='0 0 30 30' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath stroke='#{str-replace(str-replace(#{$navbar-light-color}, "(", "%28"), ")", "%29")}' stroke-width='2' stroke-linecap='round' stroke-miterlimit='10' d='M4 7h22M4 15h22M4 23h22'/%3E%3C/svg%3E");

Detail

The Problem aren't the spaces. The brackets from the stroke='#{$navbar-dark-color}' causes the problem. So if we str-replace() the ( with %28 and the ) with %29 you can run yarn run build:production without removing your scss.

Reference:
Bootstrap 4 CSS error when minifying with webpack, causes loss of SCSS running build:production

I know I know... who uses stylelint... but my OCD loves it. If you want this little patch to work and still validate with your stylelint, this is what you need to be putting in your /common/_variables.scss file:

$new-navbar-dark-color: str-replace(str-replace(#{$navbar-dark-color}, "(", "%28"), ")", "%29");
$new-navbar-light-color: str-replace(str-replace(#{$navbar-light-color}, "(", "%28"), ")", "%29");

$navbar-dark-toggler-icon-bg: url("data:image/svg+xml;charset=utf8,%3Csvg viewBox='0 0 30 30' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath stroke='#{$new-navbar-dark-color}' stroke-width='2' stroke-linecap='round' stroke-miterlimit='10' d='M4 7h22M4 15h22M4 23h22'/%3E%3C/svg%3E");
$navbar-light-toggler-icon-bg: url("data:image/svg+xml;charset=utf8,%3Csvg viewBox='0 0 30 30' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath stroke='#{$new-navbar-light-color}' stroke-width='2' stroke-linecap='round' stroke-miterlimit='10' d='M4 7h22M4 15h22M4 23h22'/%3E%3C/svg%3E");

The problem was that the str-replace inline in the data:image blob was causing stylelint to fail. Creating a new variable outside of that string and passing it into the blob worked like a charm.

Along with the rest of what @sandrowuermli recommends above.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

matsava picture matsava  路  3Comments

devfrey picture devfrey  路  3Comments

athimannil picture athimannil  路  3Comments

tiendq picture tiendq  路  3Comments

kamov picture kamov  路  3Comments