Flag of Ukraine
SymfonyCasts stands united with the people of Ukraine

CSS: Styling a Component

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.

Our main focus in this tutorial will be to build a rich product listing page inside of products.vue. To get that started, I'm going to replace the h1 with some new markup - you can copy this from the code block on this page.

<template>
<div class="container-fluid">
<div class="row">
... lines 4 - 53
</div>
</div>
</template>
... lines 57 - 68

Notice that there is nothing special yet. We're not rendering any variables: this is 100% static HTML. If you refresh the page, ok! We have a sidebar on the left, and an area on the right where we will eventually list some products. Good start!

Global CSS and Vue Components

And, though it's not pretty, it does already have some styling. If you look back at the HTML I just pasted, the basic styling is thanks to some Bootstrap classes that are on the elements. This works because, in the assets/scss/app.scss file, we're importing Bootstrap and I've decided to include the built app.css file on every page. So, naturally, if we use any Bootstrap classes in Vue, those elements get styled.

Custom Component style Section

But I also want to add some extra styling that's specific to the sidebar. The question is: where should that CSS live? We could, of course, add some classes to app.scss and use those inside our Vue component.

But Vue gives us a better option: because we want to style an element that's created in this component, Vue allows us to also put the styles in the component.

First, inside the aside element, give this <div> a new class called sidebar.

<template>
<div class="container-fluid">
<div class="row">
<aside class="col-xs-12 col-3">
<div class="sidebar p-3 mb-5">
... lines 6 - 29
</div>
</aside>
... lines 32 - 53
</div>
</div>
</template>
... lines 57 - 76

Next, at the bottom - though it doesn't matter where - there is a third special section that any .vue file can have: you can have a <template> tag, a <script> tag and also a <style> tag. Inside, we're writing CSS: add .sidebar and let's give it a border, a box-shadow and a border-radius.

... lines 1 - 68
<style>
.sidebar {
border: 1px solid #efefee;
box-shadow: 0px 0px 7px 4px #efefee;
border-radius: 5px;
}
</style>

Styling done! Remember: we're still running yarn watch in the background, so Webpack is constantly re-dumping the built JavaScript and CSS files as we're working. Thanks to that, without doing anything else, we can refresh and... the sidebar is styled!

This works thanks to some Vue and Webpack teamwork. When Webpack sees the style tag, it grabs that CSS and puts it into the entry file's CSS file, so products.css.

View the page source: here's the link tag to /build/products.css. Whenever we add any styles to any component that this entry uses, Webpack will find those and put them in here. Like a lot of things with Webpack, it's not something you really need to think about: it just works.

Using Sass Styles

So this is awesome... but I do like using Sass. If you look at webpack.config.js, down here... yep! I've already added Sass support to Encore with .enableSassLoader().

Open up assets/scss/components/light-component.scss. This is a Sass mixin that holds the exact CSS I just added manually. If we could use Sass code inside the style tag, we could import this and save ourselves some duplication!

And that's totally possible: just add lang="scss".

... lines 1 - 68
<style lang="scss">
... lines 70 - 74
</style>

Now that we're writing Sass we can @import '../../scss/components/light-component' and inside .sidebar, @include light-component;.

... lines 1 - 68
<style lang="scss">
@import '../../scss/components/light-component';
.sidebar {
@include light-component;
}
</style>

Let's try it! Back over on your browser, refresh and... we have Sass!

To finish off the styling, I'll use Sass's nesting syntax to add a hover state on the links. Now after we refresh... got it!

... lines 1 - 71
.sidebar {
... lines 73 - 74
ul {
li a:hover {
background: $blue-component-link-hover;
}
}
}
... lines 81 - 82

Being able to put your styles right inside the component is one of my favorite features of Vue. And in a bit, we're going to do something even fancier with modular CSS.

Next, let's add some dynamic data back to our app and play with one of the coolest things in Vue: the developer tools.

Leave a comment!

19
Login or Register to join the conversation
davidmintz Avatar
davidmintz Avatar davidmintz | posted 1 year ago | edited

Here's a warning I see in my console (using Vue 3).
<blockquote>
Feature flags VUE_OPTIONS_API, VUE_PROD_DEVTOOLS are not explicitly defined. You are running the esm-bundler build of Vue, which expects these compile-time feature flags to be globally injected via the bundler config in order to get better tree-shaking in the production bundle.

For more details, see https://link.vuejs.org/feature-flags.
</blockquote>I was about to ask you to spoon-feed me the answer but then sighed and did my own homework like a good citizen. :-) PhpStorm's hinting and autocompleting makes it so easy even a noob can muddle through! You can get rid of this warning by adding this to webpack.config.js:


.configureDefinePlugin(() => ({
     __VUE_OPTIONS_API__: true,
     __VUE_PROD_DEVTOOLS__: false,
}))
3 Reply

Thanks for the tip!

In general, I would assume you could leave these flags undefined, but it definitely helps to have them!

Reply
It O. Avatar

Hey friends! We miss the code block in every video. It’s important to copy and paste in our code. The final code inside the dos loas its diferent than the current in each video. For example: all the css and html in this video. I hope yo can put them in every video like always.
Thanks

1 Reply
fred Avatar

hi Alberto
Here the code :
https://github.com/SymfonyC...

1 Reply
It O. Avatar

Wow, thanks!

Reply

Hey It O.

We are sorry about that, we have a little internal process where we add "code blocks" to the page - so they are not available immediately when chapter is released. We understand that it's a bit odd because Ryan said "copy the HTML from this page!"... but there is no HTML...yet
Anyways code blocks will be able soon!

Thanks for your attention to the details and watching new chapter as soon as it was released we really appreciate it!

Cheers!

Reply
Luca Avatar
Luca Avatar Luca | posted 2 years ago | edited

Hey Ryan + Team,

I know this issues does not really belong to this lecture, however, I could not find any solution to my issue in other lectures or on the web, so I decided to ask this here.
The point is, that I want to use a static image inside my vue component. The image lives in assets/static/images/image.jpg.
Referencing the image with <img src="/build/images/image.jpg"> does work, however, I am using .enableVersioning(Encore.isProduction()) in my webpack.config.js. So with hashing I don't know how to reference my image anymore since the hash changes for every build.
In twig this is usually handled like this, as far as I know:
src="{{ asset('build/images/image.jpg') }}" (from manifest.json)

Is there a way to handle this problem in vue as well? :-)

Cheers!

Reply

Hey @Luca!

Great timing on this - I just answered this question a day or two ago :). The tl;dr is that it's easy! But... due to bad behavior between a few libraries... it's only kinda easy :p. Here is the answer: https://symfonycasts.com/sc...

On a philosophical Webpack level, the answer is "just import the image in the same way you would import another JavaScript file", which is kinda cool ;).

Let me know if that helps!

Cheers!

Reply
Jakob B. Avatar
Jakob B. Avatar Jakob B. | posted 3 years ago

Hey! It does not work for me. If I write simple css at the end of my vue-file, it will not include anything. It is simply ignored and does not work at all. Any ideas? I've done everything one-to-one...

Reply

Hey @Jakob!

Hmm. Can you post your Vue file for us? I know you're doing everything one-to-one, but maybe there is something tiny that's wrong - that's the hardest stuff to find :). Also, what version of vue, vue-loader, vue-style-loader and vue-template-compiler do you have? You can find this by running yarn list --depth=0.

Oh, and two more things - if you view source on the page, do you see a link tag for products.css? I want to make sure that is being included on the page (it is if you started with our code), And finally, when you run yarn watch what files does it say it is rendering? Do you see a products.css there?

Cheers!

Reply
Jakob B. Avatar

Hey, thank you for your answer!

├─ vue-hot-reload-api@2.3.4
├─ vue-loader@15.9.3
├─ vue-style-loader@4.1.2
├─ vue-template-compiler@2.6.11
├─ vue-template-es2015-compiler@1.9.1
├─ vue@2.6.11

I've just a style tag in the end of the template🙂

Reply
Jakob B. Avatar

I am one step further: The .css is packed into the js directory, but it is not included on the page

Reply
Jakob B. Avatar

I fixed it - Thank you very much for your answer :)

Reply

Hey @Jakob!

Nice work! What was the final solution?

Cheers!

Reply
Jakob B. Avatar

Hey!

I think one package was missing, I deleted node_modules and ran yarn install again :) It was a little weird, but now it works perfectly😀

1 Reply
Marco P. Avatar

Hey! unfortunately i have the same problem and this solution didn't work. The css file is not created ☹️

Reply

Hi Caprarolainfo!

Can you tell us a little bit more about your issue? Have you looked into your packages.json to see if your packages are all there and are the latest versions? That does seem like a webpack issue to me!

Let me know what you can find!

Reply
Marco P. Avatar

Hi Matias Jose!
Sorry if I wasted your time, I found the problem. 😳I wrote the link encore_entry_link_tags wrong. Now everything works.
Thanks so much

Reply

No problem! I'm glad you've found it! :)

Reply
Cat in space

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

This course is also built to work with Vue 3!

What JavaScript libraries does this tutorial use?

// package.json
{
    "devDependencies": {
        "@symfony/webpack-encore": "^0.30.0", // 0.30.2
        "axios": "^0.19.2", // 0.19.2
        "bootstrap": "^4.4.1", // 4.5.0
        "core-js": "^3.0.0", // 3.6.5
        "eslint": "^6.7.2", // 6.8.0
        "eslint-config-airbnb-base": "^14.0.0", // 14.1.0
        "eslint-plugin-import": "^2.19.1", // 2.20.2
        "eslint-plugin-vue": "^6.0.1", // 6.2.2
        "regenerator-runtime": "^0.13.2", // 0.13.5
        "sass": "^1.29.0", // 1.29.0
        "sass-loader": "^8.0.0", // 8.0.2
        "vue": "^2.6.11", // 2.6.11
        "vue-loader": "^15.9.1", // 15.9.2
        "vue-template-compiler": "^2.6.11", // 2.6.11
        "webpack-notifier": "^1.6.0" // 1.8.0
    }
}
userVoice