Flag of Ukraine
SymfonyCasts stands united with the people of Ukraine

Support any Browser with PostCSS & Babel

Video not working?

It looks like your browser may not support the H264 codec. If you're using Linux, try a different browser or try installing the gstreamer0.10-ffmpeg gstreamer0.10-plugins-good packages.

Thanks! This saves us from needing to use Flash or encode videos in multiple formats. And that let's us get back to making more videos :). But as always, please feel free to message us.

Go back to /admin/article and click to edit one of the articles. View the source and search for .js. Okay, we have several JavaScript files, because Webpack is splitting them. Click to look at build/admin_article_form.js, which will probably contain all the non-vendor code from that entry point.

The top of the file contains some Webpack boootstrap stuff, then our code is below, still mixed in with some things that makes Webpack work.

Now, check this out: in the original admin_article_form.js file, we created a class called ReferenceList:

... lines 1 - 8
$(document).ready(function() {
const $autoComplete = $('.js-user-autocomplete');
... lines 11 - 44
});
// todo - use Webpack Encore so ES6 syntax is transpiled to ES5
class ReferenceList
{
... lines 50 - 134
}
... lines 136 - 161

And we also use the const keyword for const $autoComplete. Back in the compiled file, search for $autoComplete. Woh! It's not const $autoComplete, it's var $autoComplete! And if you search for ReferenceList... and get down to the class... there's no class syntax! It's wrapped in some sort of a "pure" function thingy.

Surprise! Something is rewriting our code! But, who? And, why?

Hello Babel

The who is Babel: an amazing library that has the superpower of reading your JavaScript and rewriting it to older JavaScript that's compatible with older browsers. And this is seriously important! Because if JavaScript comes out with a new feature, we do not want to wait 10 years for all of the browsers to support it! Babel solves this: you can use brand new language features and it compiles it to boring, traditional code.

But... wait. How is Babel deciding which browsers our site needs to support? Different sites need to support different browsers... and so, in theory, Babel should be able to rewrite the code differently for different sites. For example, if you need to support super old browsers, you probably need to rewrite const to var. But if all of your users are awesome... like our SymfonyCasts users... and all use new browsers, then you don't need to rewrite this. In general, converting new code to old code makes your JavaScript larger, so avoiding unnecessary changes is a good thing.

Rewriting CSS for Older Browsers?

Let's answer the question of "how" we can control Babel by talking about something completely different: CSS. Babel does not rewrite CSS. But, if you think about it, it would sorta make sense.

For example, if you're using a border-radius and need to support older browsers, you need to add some vendor prefixes, like -webkit-border-radius. You can see one we added manually down here: we have box-shadow, but we also have -webkit-box-shadow to make it work in some older browsers... which we might not even need, depending on what browsers we decide we need to support:

... lines 1 - 13
div.user-menu-container {
... lines 15 - 20
-webkit-box-shadow: 0 1px 6px rgba(0, 0, 0, 0.175);
box-shadow: 0 1px 6px rgba(0, 0, 0, 0.175);
}
... lines 24 - 90

Anyways, forgetting about Webpack and Babel for a minute, in the CSS world, you do not need to add these vendor prefixes by hand. Nope! There's a wonderful library that can do it for you called autoprefixer. You write code correctly - like using box-shadow - tell it which browsers you need to support, and it adds the vendor prefixes for you.

Enabling PostCSS

Because that sounds amazing... let's add it! In webpack.config.js, anywhere, but how about below .enableSassLoader(), add .enablePostCssLoader():

... lines 1 - 2
Encore
... lines 4 - 56
.enablePostCssLoader()
... lines 58 - 76
;
... lines 78 - 79

PostCSS is a library that allows you to run things at the "end" of your CSS being processed. And it's the easiest way to integrate autoprefixer.

Next, because we just changed our webpack.config.js file, go restart Encore:

yarn watch

Hey! This is familiar! Just like when we enabled Sass, this requires us to install a few things. Copy the command, go to your open terminal and run that!

yarn add postcss-loader@^3.0.0 --dev

Ok, let's try Encore again:

yarn watch

Hmm, another error! This is kinda cool: to use PostCSS, you need to create a postcss.config.js file. Encore walks you through that process and sets it up to use autoprefixer to start. Copy that, go to the root of your project, create the postcss.config.js file and paste:

module.exports = {
plugins: {
'autoprefixer': {},
}
}

Ok, hit Control + C and try that again:

yarn watch

Sheesh! One last error. PostCSS is probably the most involved thing to get running. This error isn't as obvious:

loading PostCSS plugin failed: Cannot find module autoprefixer

We know what that word "module" means! It's trying to find that library. We told PostCSS to use autoprefixer, but that doesn't exist in our project yet. Run:

yarn add autoprefixer --dev

And now try Encore.

Tip

If you get an error like true is not a PostCSS plugin, either downgrade autoprefixer to version 9 or upgrade PostCSS to version 8. Basically, autoprefixer 10 doesn't play nicely with PostCSS 7 and lower.

yarn watch

No errors! So... it's probably working? Let's see it in action next and learn how we can tell PostCSS and Babel exactly which browsers we need to support.

Leave a comment!

12
Login or Register to join the conversation
Krzysztof K. Avatar
Krzysztof K. Avatar Krzysztof K. | posted 3 years ago

Hey Guys, I have similar setup as per this tutorial and I have weird issue with vue2-animate library. Everything works under IE11 when my assets are in dev mode, but under production mode (yarn build) it do not animate under IE11 (one particular animation doesn't work). I have tried lots of different configurations (browserslist) including postcss-preset-env (with different stages) but it do not help.

If I will include vue2-animate directly form public without any processing it works fine under IE11.

What can I do to resolve it? I do not want to copy that css to public and include separately, I would prefer to keep it under node modules. Can I somehow add it to post css ignore? Or is there anything else that I could do?

1 Reply

Hey Krzysztof K.!

Ah, I think this probably relates to your other question, right? :) So, there are two possible things that are happening, and they're the opposite:

1) It's possible that the vue2-animate in node_modules is written in a way where it's not compat with IE11 (it's actually often the case that they have a "source" version of a file that uses new features, then a "built" version that is rewritten to work with whatever browsers they support). Then, when you include that in Webpack, it is actually not being rewritten to be compat with IE11. That's actually the default behavior in Encore - anything in node_modules/ is not run through Babel. Btw, there are two different things happening to "rewrite code for older browsers": babel rewrites JavaScript code and PostCSS adds auto-prefixes to your CSS to support older browsers. It seems like you're focused on PostCSS being a problem (and you may be right), but I would have expected JavaScript to be the issue.

2) Or, the opposite could be happening (but I think? this is less likely): the file in node_modules could be written in a way that supports IE11, but then when it's included in Webpack, something is rewriting it in a way where it is not compat with IE11 anymore. Babel wouldn't do that to your JavaScript, but it is true that PostCSS would remove any unnecessary CSS vendor prefixes. So, I guess it comes back to the question: is the IE11 problem caused by JavaScript or CSS?

Let me know what you find out!

Cheers!

1 Reply
Krzysztof K. Avatar

It is definitely #2 because when I include this css directly with link tag (without any modification) then it works, it only doesn't work if I am bundling it with other css through encore.

I have tried many different things to resolve it.. I have disabled autoprefixer (removed it from config) so post css was doing nothing and this didn't help. Somehow my current encore configuration is modifying this css library when it is bundled with others.

So what I want to archive is to include it in encore, bundle it with others, but without any modifications, and I do not know how to do it.

Reply

Hey Krzysztof K.!

Hmm, this is tricky.

> Somehow my current encore configuration is modifying this css library when it is bundled with others

With auto-prefixer disabled, I'm not aware of any modifications that Encore would be making to your CSS, other than the obvious combining and minifying. The only loader applied for CSS files is MiniCssExtractPlugin - https://webpack.js.org/plug.... So, I'm really not sure what's going on :/.

> It is definitely #2 because when I include this css directly with link tag (without any modification) then it works

How exactly are you linking to the CSS file (since it lives in node_modules)? And are you absolutely positive that the same CSS file that you're requiring is the same that you're linking to directly? Just asking because sometimes there are multiple CSS or JS files in a library, and some are pre-compiled to work with older browsers.

> So what I want to archive is to include it in encore, bundle it with others, but without any modifications, and I do not know how to do it.

Ultimately, this would be working around Webpack... because something unexplainable is currently happening. Since we don't know what's happening, you would basically need to *not* include this CSS file in Webpack/Encore and instead link to it directly. It's a total workaround... because we don't actually know what the problem is :).

Cheers!

Reply

Hey Ryan and Team! I know I am late to the game on this tutorial series l but I am currently updating an old 3.4 project to prepare for 4 and beyond! My question is, when you have a larger project that has a lot of template inherence, when do you import bootstrap in your js files?

To start I have bootstrap imported in my app.js since it is used pretty much everywhere.

If I try to import bootstrap in any other file it causes my dropdowns and other bootstrap stuff not to work. For example in the orders/layout I have specific orders.js that requires bootstrap collapse and tooltip. If I import bootstrap in my orders.js I don't get the not a function error, but then other silent errors like my drop downs don't work and it almost seems like there may be two instances of bootstrap??

orders.js rendered on orders/layout.html.twig which extends --> company/layout.html.twig extends --> base.html.twig

Each template calls the parent function in the javascripts blocks so how I picture it is in each layer things are being added via {{ encore_entry_scripts_tags('') }}.

Maybe I am missing something but I am curious why I can't import bootstrap when I need it in my orders.js file and not mess up other js down the "extended line" :)

Thanks and as always great stuff!
Jonathan

Reply

Hey Jonathan!

Glad to hear you again! :)

Well, on SymfonyCasts we import Bootstrap in the main JS file called app.js, there we also include the app.scss SASS file that contains all the base styles we want to have on all pages. And in that app.scss we import Bootstrap SASS files we need. So, both those app.js and app.css files are included in our base Twig template that's extended by other templates and this way we have that Bootstrap on all the pages.

Then, on some specific pages, we include other Bootstrap features if needed. Though, we're doing it with Stimulus controllers lately. I.e. we have a Stimulus controller called tooltip_controller.js that import jQuery and bootstrap/js/dist/tooltip JS file, and with some custom JS code we activate the tooltip on a specific element.

I hope this helps!

Cheers!

Reply

in case anyone arrives here stuck with a postCss error Error: true is not a PostCSS plugin
You might need to downgrade your autoprefixer installation to autoprefixer@9.8.6 - as mentioned <a href="https://github.com/vercel/next.js/issues/17236&quot;&gt;here&lt;/a&gt;

Reply

Hey melvinware

Thanks for the tip! We definitely should put a note about this issue!

Cheers!

Reply
Krzysztof K. Avatar
Krzysztof K. Avatar Krzysztof K. | posted 3 years ago

How can I configure encore to bundle but not modify already minified css files?

Reply

Hey Krzysztof K.!

Hmm, what do you mean by "not modify"? Do you mean PostCSS modifications? Something else? Mostly, if you include CSS, it will go through the same process as everything else and go through any possible transformations. But, that can sometimes be configured. Let me know!

Cheers!

Reply
Krzysztof K. Avatar

Ryan, in current configuration for this tutorial, what can modify included css files? I suspect that it will be Post CSS and TerserPlugin, what else?

I would like to include already minified library and bundle it with other "stuff" but without any modifications. I do not understand why files with extension min.css are modified, these should be ignored.

1 Reply

> what can modify included css files? I suspect that it will be Post CSS and TerserPlugin, what else?

Yep, you're right - it would just be PostCSS (if enabled) - you can see it in this file: https://github.com/symfony/...

Also, just to help, TerserPlugin minifies your JavaScript, not your CSS - the MiniCssExtractPlugin handles that :).

So... not much modifies it! That's why it's such a mystery! The fact that the problem only occurs in prod *seems* to suggest a MiniCSSExtractPlugin problem (that's not the only difference between a dev and prod build, but the most significant for CSS). You could try temporarily hack this file from Encore - https://github.com/symfony/... - to NEVER add this plugin. Then try a production build and see if you can the same result.

Cheers!

Reply
Cat in space

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

This tutorial works great with Symfony5!

What PHP libraries does this tutorial use?

// composer.json
{
    "require": {
        "php": "^7.1.3",
        "ext-iconv": "*",
        "aws/aws-sdk-php": "^3.87", // 3.91.4
        "composer/package-versions-deprecated": "^1.11", // 1.11.99
        "knplabs/knp-markdown-bundle": "^1.7", // 1.7.1
        "knplabs/knp-paginator-bundle": "^2.7", // v2.8.0
        "knplabs/knp-time-bundle": "^1.8", // 1.9.0
        "league/flysystem-aws-s3-v3": "^1.0", // 1.0.22
        "league/flysystem-cached-adapter": "^1.0", // 1.0.9
        "liip/imagine-bundle": "^2.1", // 2.1.0
        "nexylan/slack-bundle": "^2.0,<2.2.0", // v2.1.0
        "oneup/flysystem-bundle": "^3.0", // 3.0.3
        "php-http/guzzle6-adapter": "^1.1", // v1.1.1
        "sensio/framework-extra-bundle": "^5.1", // v5.3.1
        "stof/doctrine-extensions-bundle": "^1.3", // v1.3.0
        "symfony/asset": "^4.0", // v4.2.5
        "symfony/console": "^4.0", // v4.2.5
        "symfony/flex": "^1.9", // v1.17.6
        "symfony/form": "^4.0", // v4.2.5
        "symfony/framework-bundle": "^4.0", // v4.2.5
        "symfony/orm-pack": "^1.0", // v1.0.6
        "symfony/security-bundle": "^4.0", // v4.2.5
        "symfony/serializer-pack": "^1.0", // v1.0.2
        "symfony/twig-bundle": "^4.0", // v4.2.5
        "symfony/validator": "^4.0", // v4.2.5
        "symfony/web-server-bundle": "^4.0", // v4.2.5
        "symfony/webpack-encore-bundle": "^1.4", // v1.5.0
        "symfony/yaml": "^4.0", // v4.2.5
        "twig/extensions": "^1.5" // v1.5.4
    },
    "require-dev": {
        "doctrine/doctrine-fixtures-bundle": "^3.0", // 3.1.0
        "easycorp/easy-log-handler": "^1.0.2", // v1.0.7
        "fzaninotto/faker": "^1.7", // v1.8.0
        "symfony/debug-bundle": "^3.3|^4.0", // v4.2.5
        "symfony/dotenv": "^4.0", // v4.2.5
        "symfony/maker-bundle": "^1.0", // v1.11.5
        "symfony/monolog-bundle": "^3.0", // v3.3.1
        "symfony/phpunit-bridge": "^3.3|^4.0", // v4.2.5
        "symfony/profiler-pack": "^1.0", // v1.0.4
        "symfony/var-dumper": "^3.3|^4.0" // v4.2.5
    }
}

What JavaScript libraries does this tutorial use?

// package.json
{
    "devDependencies": {
        "@symfony/webpack-encore": "^0.27.0", // 0.27.0
        "autocomplete.js": "^0.36.0",
        "autoprefixer": "^9.5.1", // 9.5.1
        "bootstrap": "^4.3.1", // 4.3.1
        "core-js": "^3.0.0", // 3.0.1
        "dropzone": "^5.5.1", // 5.5.1
        "font-awesome": "^4.7.0", // 4.7.0
        "jquery": "^3.4.0", // 3.4.0
        "popper.js": "^1.15.0",
        "postcss-loader": "^3.0.0", // 3.0.0
        "sass": "^1.29.0", // 1.29.0
        "sass-loader": "^7.0.1", // 7.3.1
        "sortablejs": "^1.8.4", // 1.8.4
        "webpack-notifier": "^1.6.0" // 1.7.0
    }
}
userVoice