gstreamer0.10-ffmpeg
gstreamer0.10-plugins-good
packages.
What if I want to use Sass instead of normal CSS, or maybe Less or Stylus? Normally, that takes some setup: you need to create a system that can compile all of your Sass files into CSS. But with Encore, we get this for free!
Rename app.css
to app.scss
. Of course, when we do that, the build fails because we need to update the import
in app.js
:
... lines 1 - 7 | |
// any CSS you require will output into a single css file (app.css in this case) | |
import '../css/app.scss'; | |
... lines 10 - 26 |
But the build still fails. Go check out the error. Woh! That's awesome! It basically says:
Hey! How are you? Great weather lately, right? Listen, it looks like you're trying to load a Sass file. That's super! To do that, enable the feature in Encore and install these libraries.
This is the philosophy of Encore: give you a really solid, but small-ish core, and then offer a ton of optional features.
Go back to webpack.config.js
. The enableSassLoader()
line is already here. Uncomment it:
... lines 1 - 2 | |
Encore | |
... lines 4 - 50 | |
// enables Sass/SCSS support | |
.enableSassLoader() | |
... lines 53 - 66 | |
; | |
... lines 68 - 69 |
Back at the terminal, copy the yarn add
command, go to the open tab, and run it!
Tip
Instead of node-sass
, install sass
. It's a pure-JavaScript implementation that
is easier to install and is now recommended.
yarn add sass-loader@^7.0.1 sass --dev
This could take a minute or two: node-sass
is a C library and it may need to compile itself depending on your system. Ding!
Thanks to the watch script, we normally don't need to worry about stopping or restarting Encore. There is one notable exception: when you make a change to webpack.config.js
, you must stop and restart Encore. That's just a limitation of Webpack itself: it can't re-read the fresh configuration until you restart.
Hit Control
+C
and then run yarn watch
again.
yarn watch
And this time... yes! We just added Sass support in like... two minutes - how awesome is that?
This next part is optional, but I want to get organized... instead of having one big file, create a new directory called layout/
. And for this top stuff, create a file called _header.scss
. Little-by-little, we're going to move all of this code into different files. Grab the first section and put it into header:
body { | |
position: relative; | |
background: #efefee; | |
min-height: 45rem; | |
padding-bottom: 80px; | |
} | |
html {height:100%} | |
/* NAVIGATION */ | |
.navbar-bg { | |
background: url('../images/space-nav.jpg'); | |
background-size: 80%; | |
} | |
.dropdown-menu, .dropdown-menu.show { | |
right: 0; | |
} | |
.space-brand { | |
color: #fff; | |
font-weight: bold; | |
} | |
.nav-profile-img { | |
width: 50px; | |
border: 1px solid #fff; | |
} | |
.nav-tabs .nav-link:focus, .nav-tabs .nav-link:hover { | |
color: #efefee; | |
} |
We'll import the new files when we finish.
Next is the "advertisement" CSS. Create another folder called components/
. And inside, a new _ad.scss
file. I'll delete the header... then move the code there:
.ad-space { | |
background: #fff; | |
border-radius: 5px; | |
border-top: 5px solid green; | |
} | |
.advertisement-img { | |
width: 150px; | |
height: auto; | |
border: 2px solid #efefee; | |
border-radius: 5px; | |
} | |
.advertisement-text { | |
font-weight: bold; | |
} | |
.quote-space { | |
background: #fff; | |
margin-top: 30px; | |
border-radius: 5px; | |
border-top: 5px solid hotpink; | |
} |
Let's keep going! For the article stuff, create _articles.scss
, and move the code:
.main-article { | |
border: 2px solid #efefee; | |
Background: #fff; | |
border-top-left-radius: 6px; | |
border-top-right-radius: 6px; | |
} | |
.main-article img { | |
width: 100%; | |
height: 250px; | |
border-top-right-radius: 5px; | |
border-top-left-radius: 5px; | |
border-top: 5px solid lightblue; | |
} | |
.article-container { | |
border: 1px solid #efefee; | |
border-top-left-radius: 5px; | |
border-bottom-left-radius: 5px; | |
background: #fff; | |
} | |
.main-article-link, .article-container a { | |
text-decoration: none; | |
color: #000; | |
} | |
.main-article-link:hover { | |
text-decoration: none; | |
color: #000; | |
} | |
.article-title { | |
min-width: 300px; | |
} | |
@media (max-width: 440px) { | |
.article-title { | |
min-width: 100px; | |
max-width: 245px; | |
} | |
} | |
.article-img { | |
height: 100px; | |
width: 100px; | |
border-top-left-radius: 5px; | |
border-bottom-left-radius: 5px; | |
} | |
.article-author-img { | |
height: 25px; | |
border: 1px solid darkgray; | |
} | |
.article-details { | |
font-size: .8em; | |
} | |
... lines 60 - 140 |
Then, _profile.scss
, copy that code... and paste:
.profile-img { | |
width: 150px; | |
height: auto; | |
border: 2px solid #fff; | |
} | |
.profile-name { | |
font-size: 1.5em; | |
} | |
.my-article-container { | |
background: #FFBC49; | |
border: solid 1px #efefee; | |
border-radius: 5px; | |
} |
For the "Create Article" and "Article Show" sections, let's copy all of that and put it into _article.scss
:
.main-article { | |
border: 2px solid #efefee; | |
Background: #fff; | |
border-top-left-radius: 6px; | |
border-top-right-radius: 6px; | |
} | |
.main-article img { | |
width: 100%; | |
height: 250px; | |
border-top-right-radius: 5px; | |
border-top-left-radius: 5px; | |
border-top: 5px solid lightblue; | |
} | |
.article-container { | |
border: 1px solid #efefee; | |
border-top-left-radius: 5px; | |
border-bottom-left-radius: 5px; | |
background: #fff; | |
} | |
.main-article-link, .article-container a { | |
text-decoration: none; | |
color: #000; | |
} | |
.main-article-link:hover { | |
text-decoration: none; | |
color: #000; | |
} | |
.article-title { | |
min-width: 300px; | |
} | |
@media (max-width: 440px) { | |
.article-title { | |
min-width: 100px; | |
max-width: 245px; | |
} | |
} | |
.article-img { | |
height: 100px; | |
width: 100px; | |
border-top-left-radius: 5px; | |
border-bottom-left-radius: 5px; | |
} | |
.article-author-img { | |
height: 25px; | |
border: 1px solid darkgray; | |
} | |
.article-details { | |
font-size: .8em; | |
} | |
/* CREATE ARTICLE */ | |
.create-article-container { | |
min-width: 400px; | |
background-color: lightblue; | |
border-radius: 5px; | |
} | |
/* ARTICLE SHOW PAGE */ | |
.show-article-container { | |
width: 100%; | |
background-color: #fff; | |
} | |
.show-article-container.show-article-container-border-green { | |
border-top: 3px solid green; | |
border-radius: 3px; | |
} | |
.show-article-img { | |
width: 250px; | |
height: auto; | |
border-radius: 5px; | |
} | |
.show-article-title { | |
font-size: 2em; | |
} | |
.like-article, .like-article:hover { | |
color: red; | |
text-decoration: none; | |
} | |
@media (max-width: 991px) { | |
.show-article-title { | |
font-size: 1.5em; | |
} | |
.show-article-title-container { | |
max-width: 220px; | |
} | |
} | |
.article-text { | |
margin-top: 20px; | |
} | |
.share-icons i { | |
font-size: 1.5em; | |
} | |
.comment-container { | |
max-width: 600px; | |
} | |
.comment-img { | |
width: 50px; | |
height: auto; | |
border: 1px solid darkgray; | |
} | |
.commenter-name { | |
font-weight: bold; | |
} | |
.comment-form { | |
min-width: 500px; | |
} | |
@media (max-width: 767px) { | |
.comment-form { | |
min-width: 260px; | |
} | |
.comment-container { | |
max-width: 280px; | |
} | |
} |
And for the footer, inside layout/
, create one more file there called _footer.scss
and... move the footer code:
.footer { | |
position: absolute; | |
bottom: 0; | |
width: 100%; | |
height: 60px; /* Set the fixed height of the footer here */ | |
line-height: 60px; /* Vertically center the text there */ | |
background-color: #fff; | |
margin-top: 10px; | |
} |
And finally, copy the sortable code, create another components partial called _sortable.scss
and paste:
.sortable-ghost { | |
background-color: lightblue; | |
} | |
.drag-handle { | |
cursor: grab; | |
} |
Now we can import all of this with @import './layout/header'
and @import './layout/footer'
:
@import '~bootstrap'; | |
@import '~font-awesome'; | |
@import './layout/header'; | |
@import './layout/footer'; | |
... lines 6 - 11 |
Notice: you don't need the _
or the .scss
parts: that's a Sass thing. Let's add a few more imports for the components: ad
, articles
, profile
and sortable
:
... lines 1 - 3 | |
@import './layout/header'; | |
@import './layout/footer'; | |
@import './components/ad'; | |
@import './components/articles'; | |
@import './components/profile'; | |
@import './components/sortable'; |
Phew! That took some work, but I like the result! But, of course, Encore is here to ruin our party with a build failure:
Cannot resolve
./images/space-nav.jpeg
We know that error! In _header.scss
... ah, there it is:
... lines 1 - 11 | |
.navbar-bg { | |
background: url('../images/space-nav.jpg'); | |
... line 14 | |
} | |
... lines 16 - 34 |
The path needs to go up one more level now:
... lines 1 - 11 | |
.navbar-bg { | |
background: url('../../images/space-nav.jpg'); | |
... line 14 | |
} | |
... lines 16 - 34 |
And... it works.
Move over and make sure nothing looks weird. Brilliant!
To celebrate that we're processing through Sass, let's at least use one of its features. Create a new directory called helper/
and a new file called _variables.scss
.
At the top of _header.scss
, we have a gray background
color:
body { | |
... line 2 | |
background: #efefee; | |
... lines 4 - 5 | |
} | |
... lines 7 - 34 |
Just to prove we can do it, in _variables
, create a new variable called $lightgray
set to #efefee
:
$lightgray: #efefee; |
And back in headers, reference that: $lightgray
:
body { | |
... line 2 | |
background: $lightgray; | |
... lines 4 - 5 | |
} | |
... lines 7 - 34 |
We even get auto-completion on that! As soon as we save, the build fails!
Undefined variable: "$lightgray"
Perfect! Because... inside of app.scss
, all the way on top, we still need to @import
the helper/variables
file:
@import './helper/variables'; | |
... lines 2 - 13 |
About a second later... ding! It builds and... the background is still there.
But wait, there's more! When we import bootstrap
, Encore has some logic to find the right CSS file in that package. But now that we're inside a Sass file, it's smart enough to instead import the bootstrap.scss
file! Woh!
Check it out. Hold Command or Ctrl
and click ~bootstrap
to jump to that directory. Then open up package.json
. This has a style
key, but it also has a sass
key! Because we're importing from inside a Sass file, Encore first looks for the sass
key and loads that file. If there isn't a sass
key, it falls back to using style
.
Now look at the font-awesome/
directory and find its package.json
file. It actually does not have a sass
key! And so, it's still loading the font-awesome.css
file, which is fine. If you did want to load the Sass file, you would just need to point at the file path directly.
Anyways, to prove that the Bootstrap Sass file is being loaded, we can override some of its variables. See this search button? It's blue because it has the btn-info
class. It's color hash is... here: #1782b8
.
Suppose you want to change the info color globally to be a bit darker. Bootstrap lets you do that in Sass by overriding a variable called $info
.
Try it: inside the variables file, set $info:
to darken()
, the hash, and 10%
:
$info: darken(#17a2b8, 10%); | |
... lines 2 - 5 |
Once the build finishes... watch closely. It got darker! How cool is that?
Next, let's fix our broken img
tags thanks to one of my favorite new Encore features called copyFiles()
.
Hey Evgeny,
Good question! :) The answer is in "!default" flag, see the docs for more context: https://getbootstrap.com/do...
Cheers!
I had great difficulty getting node-sass to install without errors (Nov 2020; Symfony 4.2.5, webpack-encore-bundle 1.8.0, yarn 1.22.4, npm 6.14.8). The sass-loader installed ok, but the node-sass command kept giving me: gyp ERR! stack Error:
make failed with exit code: 2
(and sometimes error code 1).
After much travail, I finally got it to work by manually adding the following two devDependencies to package.json:
"sass-loader": "^9.0.1",
"node-sass": "^4.5.0"
and then running yarn install
Hope this helps someone else :)
John
Hey John christensen!
Ah, sorry about the trouble! Ok, so the short story is that node-sass is kind of a pain in the butt. Whenever new operating systems comes out, you need to upgrade to a new version of node-sass, otherwise it won't build. So, our code download gets quickly out of date.
The solution (and I've just updated the "finished" code download to reflect this and we'll soon add a note to the video and script) is to install "sass" instead of node-sass. It's a drop-in replacement written entirely in JavaScript. So, the command to install would be something like:
yarn add sass-loader sass --dev
Anyways, sorry you hit the issue, but I appreciate the comment - it bumped me to get the out of our tutorial to make life easier.
Cheers!
Hello World!
I'm trying to override Bootstrap's variable $custom-select-indicator to change the url().
I can't seem to find how to refer to that image in my scss file.
My directory structure is the following.
`
assets
├── css
│ ├── component
│ │ ├── theme-colors
│ │ └── widgets
│ ├── core
│ │ ├── animation
│ │ ├── breadcrumb
│ │ ├── buttons
│ │ ├── extra
│ │ ├── layout
│ │ ├── loader
│ │ ├── scafholdings
│ │ ├── sidebar
│ │ ├── topbar
│ │ └── wave-effects
│ ├── helper
│ │ └── mixins
│ └── lib
├── images
│ └── users
└── js
└── pages
└── dashboards
`
My variable file is in assets/css/helper and my image is in assets/images.
So in my _variables.scss I refer to it like this.
$custom-select-indicator: url(../../images/custom-select.png) !default;
But when I compile I get.
Syntax Error: ModuleNotFoundError: Module not found: Error: Can't resolve '../../node_modules/bootstrap/images/custom-select.png' in '/home/julien/dev/project/symfony/assets/css'
Can any one help me with that?
Thanks,
Julien
Hey Julien,
Hm, first of all, I suppose you're missing quotes in the path, did you try:$custom-select-indicator: url("../../images/custom-select.png") !default;
Does it help? Or do you see the exact same error?
Cheers!
No it does not help... I really don't understand how it resolves the path.
See tests and results which really don't make sense to me.
Test 1$custom-select-indicator: url('../../images/custom-select.png') !default;
ResultSyntax Error: ModuleNotFoundError: Module not found: Error: Can't resolve '../../node_modules/bootstrap/images/custom-select.png' in '/home/julien/dev/project/symfony/assets/css'<br />
Test 2$custom-select-indicator: url('../../../images/custom-select.png') !default;
ResultSyntax Error: ModuleNotFoundError: Module not found: Error: Can't resolve '../../images/custom-select.png' in '/home/julien/dev/project/symfony/assets/css'<br />
node_modules and bootstrap are gone from the path
Test 3$custom-select-indicator: url('../../../../images/custom-select.png') !default;
ResultSyntax Error: ModuleNotFoundError: Module not found: Error: Can't resolve '../../../images/custom-select.png' in '/home/julien/dev/project/symfony/assets/css'<br />
node_modules and bootstrap are gone from the path and extra ../ is added
In theory if the path where it looks for the image is relative to '/home/julien/dev/project/symfony/assets/css' then Test 2 should be very close to what I'm looking for but I would need to add assets/ to my path. So let's try it
Test 4$custom-select-indicator: url('../../../assets/images/custom-select.png') !default;
ResultSyntax Error: ModuleNotFoundError: Module not found: Error: Can't resolve '../../node_modules/assets/images/custom-select.png' in '/home/julien/dev/project/symfony/assets/css'<br />
node_modules is back but bootstrap is still gone from the path.
What the...?
I'm kinda lost here...
Hey Julien,
Hm, this sounds like the path was resolved first, and then the already resolved path was passed to different spots, but looks like for some spots it should be different? I'd recommend you to comment out all places where you use that $custom-select-indicator var except one place, and try to get it working for that spot. When it's done, uncomment one more line and check if it still works. Probably path will be different for different files where you use that var... Really not sure how rewriting paths works with variables. Also, you can try to start the path with "./", e.g:
$custom-select-indicator: url('./../../assets/images/custom-select.png') !default;
It should work the same was as starting with "../", but just in case will be good to check I think.
I hope this helps!
Cheers!
Well, that's my problem... This variable is only used in Bootstrap (which is in node_modules)... I'm overriding Bootstrap default with that one. So the path I'm using is the one relative to my file, but once Bootstrap uses it Webpack explodes. I will keep digging but so far I don't have a suitable solution.
Holly smoke! I can't believe it. I found a working solution.
It's kind of an ugly (or least weird) hack. But it works.
Since that variable is only used in Bootstrap but the image is in my assets folder, well I'm "hacking" my way into and... It works!
$custom-select-indicator: url(~bootstrap/scss/../../../assets/images/custom-select.png);
<!-- insert joy emoji -->
Cheers!
Oh, and one more suggestion... I think you can simplify your path to:
$custom-select-indicator: url(~bootstrap/../../assets/images/custom-select.png);<br />
I.e. in theory, you don't need to go inside scss/ dir and then go back... but that's just in theory, I won't be surprised too much if you say that it does not work this way :)
Cheers!
Yeah, I thought I tried that already it didn't work thus ending up with that extra /scss but apparently I was wrong. It does work without it.
Hey Julien,
Great! Fairly speaking, if it didn't work - I would be surprised :) Anyway, I'm glad this workaround works for you!
Cheers!
Hey Julien,
Woh, thanks for sharing this solution with others! We do talk about "~" here: https://symfonycasts.com/sc... - this helps to point to the files that are in node_modules/ directory. But I've never heard that it might help in this case too, so good catch! So yeah, it looks weird to me too fairly speaking, but if it works as you need - great, that's really nice workaround I think :)
Cheers!
Hello,
Looks like overriding the $info variable is not working for me. (the $lightgray worked correctly)
here is my variables file <br />$info: darken(#17a2b8, 10%);<br />$lightgray:red;<br />
here is my app.scss <br />@import './helper/_variables.scss';<br />@import '~bootstrap/dist/css/bootstrap.css';<br />@import '~font-awesome';<br />@import '/layout/_footer.scss';<br />@import '/layout/_header.scss';<br />@import '/components/_ad.scss';<br />@import '/components/_article.scss';<br />@import '/components/_profile.scss';<br />@import '/components/_sortable.scss';<br />
Hi Virgile,
If you are trying to override bootstrap variables, you'll have to import bootstrap SCSS file. You are importing a compiled css file, thus it is not going to work.
Try with
@import '~bootstrap/scss/bootstrap';
Hey julien_bonnier
That's correct! Thank you for replying on this thread, sometimes happens that we can miss comments (it's not too often) and it's awesome that users help each other!
Cheers! Have a nice day!
How does `$info: darken(...)` in helper/_variables.scss override the bootstrap's one if `@import './helper/variables'` is imported before `@import '~bootstrap'`? Thank you!
Hey @Anton!
SUCH a good question - because the order doesn’t make any sense, right? This works because of a special Sasa feature. In bootstrap, when those variables are set, they are set with a special flag that says “only use this value if nobody else ever sets this variable”. THAT is why it works. Otherwise, you’re right, bootstrap would override *our* values :).
Cheers!
Are you using PHPStorm? I'm using VS Code, and there Command/Ctrl + click trick on `~bootstrap` to jump to the node_modules/bootstrap dir doesn't seem to work in VS Code. Maybe you know if there is a way to enable it? Thanks!
Hey @Anton!
Yep - I’m using phpstorm! The whole ~ thing to refer to css files in the node_modules directory is, I believe, a special thing that Webpack does. Phpstorm is aware of this Webpack trick, but apparently VSCode is not. If you can manually configure Webpack aliases in VSCode, you might be able to map ~ to node_modules... but if that doesn’t work, I’m afraid that there won’t be much you can do (but I could be wrong!).
Let me know if you find anything out!
Cheers!
Hey Ryan,
Nice tuto. Thank.
Why when you call variables.scss, you write @import "helper/variables"; while when you call others scss you write @import "./layout/header";
I realise when I clean ./ it's working.
Hey Stephane,
Nice catch! Yeah, that was a misprint, it should be `@import "./helper/variables"`, you can check it with related code blocks below the video: https://symfonycasts.com/sc... . Unfortunately, that part in the video was not re-recorded, but we fixed it in the code.
But actually, looks like it works both ways, so it's kinda matter of preference, but we do recommend to prefix with "./" in such cases.
Cheers!
// 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
}
}
// 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
}
}
Hello!
Could you please explain me one thing that I can't understand.
In assets/css/app.scss we firstly import _variables.scss, where we overload the bootsrap's $info variable, and after that we import ~bootstrap itself, where the $info is declared.
So we managed to overload the variable which hasn't been declared yet. How did we do that? :)