Flag of Ukraine
SymfonyCasts stands united with the people of Ukraine

Using Shared CSS

Keep on Learning!

If you liked what you've learned so far, dive in!
Subscribe to get access to this tutorial plus
video, code and script downloads.

Start your All-Access Pass
Buy just this tutorial for $12.00

With a Subscription, click any sentence in the script to jump to that part of the video!

Login Subscribe

So let's finally use the new sidebarCollapsed data inside products. Right now, when we collapse, we add a collapsed class... which makes the element really small - just 70px.

Remove the .collapsed class entirely: we're going to simplify.

... lines 1 - 68
<style lang="scss" module>
@import '~styles/components/light-component';
.component {
... lines 73 - 79
}
</style>

Inside the componentClass computed property, copy the three classes that the sidebar always has. Then delete that completely. We're going to move all of the size logic to the parent component. Paste the three classes onto the outer div.

<template>
<div :class="[$style.component, 'p-3', 'mb-5']">
... lines 3 - 39
</div>
</template>
... lines 42 - 82

Over in products.vue, the classes on the <aside> and this <div> now need to be dynamic based on the sidebarCollapsed data. Hey! That's a perfect use-case for computed properties!

Down in our code, add a computed option with two computed properties. Call the first asideClass: it will determine the classes for the <aside> element. Inside, return this.sidebarCollapsed. If we are collapsed, use a class called aside-collapsed - that doesn't exist yet, we'll create it in a minute. If we are not collapsed, use the normal col-xs-12 and col-3.

... lines 1 - 17
<script>
... lines 19 - 21
export default {
... lines 23 - 32
computed: {
asideClass() {
return this.sidebarCollapsed ? 'aside-collapsed' : 'col-xs-12 col-3';
},
... lines 37 - 39
},
... lines 41 - 45
};
</script>

Oh, and Webpack is mad because, of course, this needs to be a function. That's better.

Copy asideClass(), paste and call it contentClass(). For this one, when we're collapsed, use col-xs-12 col-11 so that it takes up almost all of the space. And then when it's not collapsed, use the normal col-xs-12 col-9 so that it shares the space.

... lines 1 - 32
computed: {
... lines 34 - 36
contentClass() {
return this.sidebarCollapsed ? 'col-xs-12 col-11' : 'col-xs-12 col-9';
},
},
... lines 41 - 48

Perfect! Well... no, not perfect: ESLint isn't happy about this computed option:

The computed property should be above the methods property.

This makes no difference technically, but there are some general best-practices about the order of your options. Let's move this above methods to follow those.

... lines 1 - 21
export default {
... lines 23 - 32
computed: {
... lines 34 - 39
},
methods: {
... lines 42 - 44
},
};
... lines 47 - 48

Ok: now that we have 2 keys under computed, we have two new variables inside of our template. Scroll up to the top. For the <aside> element, we can say class="asideClass". Ah, but I'm sure you're starting to spot my mistake - we need :class to make that dynamic.

Do the same for the div below: class="contentClass" and then make it :class.

<template>
... lines 2 - 3
<aside :class="asideClass">
... lines 5 - 8
</aside>
... line 10
<div :class="contentClass">
... line 12
</div>
... lines 14 - 15
</template>
... lines 17 - 48

Sweet! Let's give it a try! I'll refresh just to be safe. And... ah! It works! It might feel smoother with some CSS transitions, but on a Vue level, this is working brilliantly!

Adding a Global Class

Though, I could use a little more padding on the sidebar when it's collapsed... I don't want it all the way against the edge.

Go back to the computed property. When the sidebar is collapsed, it has an aside-collapsed class... which I totally made up: that does not exist yet. To fix our padding issue, we could have said this.$style['aside-collapsed'] and then added a new .aside-collapsed class to the style tag of this component.

But... to make this more interesting, let's pretend that we're going to have multiple components across our app that will need to use this class. And so, I don't want to add it as a modular style to this component: I'd rather put this CSS in a central spot and share it.

That is why I used .aside-collapsed instead of using the $style variable. Open up scss/app.scss and, at the bottom, add the style: aside-collapsed with padding: 0 15px.

... lines 1 - 56
.aside-collapsed {
padding: 0 15px;
}

Cool! When we move over to our browser... yes! It looks better already. So this is just a reminder that while modular styles are cool, if you want to re-use something, you can continue to use normal CSS files.

Importing Shared, Non-Modular CSS Files

By the way, you could also use @import to import CSS files from inside the style tag of your component... you can even do it in a way that prevents the styles from getting the module prefix. To do that, add a second style tag, leave off the module and make the language css. You can still import SASS files, but if you make the language scss, for some reason, your CSS rules will get duplicated inside Webpack.

Next, it's finally time to make some Ajax calls and bring in some truly dynamic data!

Leave a comment!

2
Login or Register to join the conversation
Nathan D. Avatar
Nathan D. Avatar Nathan D. | posted 3 years ago

Hey ! Am I right if I'm saying : "SidebarCollapsed is just a state for the product page layout" ?

I feel like all the logic is about the product layout and not only the sidebar. So the SidebarCollapsed can be something like ProductLayoutCollapsed, and we can have another state with the SidebarExpanded for example, and a third state if we wish. With the naming SideBarCollapsed I feel like we are storing SideBar logic inside the product component and that break all the good vibes about splitting code into components. =D I mean, this is SideBar behavior inside the Product layout, but the button also change property on the content area. And maybe this button is going to be outside the sidebar ...

Just to be clear : I'm not saying the naming is bad. I'm just wondering if we can do even cleaner / sweeter code for the next developer going through this code. Don't want to be rude. Just a feeling that I want to share and discuss it =)

Reply

Hey Nathan!

That's an interesting point of view. Extracting the UI Layout logic into its own code is actually a valid architectural decision. There isn't a single way of organizing an application, it all depends on your needs and the complexity of it!

The architectural decisions made in this tutorial have to do with the natural way of evolving in complexity and we follow simple rules such as: "dumb versus smart components" and "what is the deepest component that needs some data".

In fat, in general, in real world applications, the UI logic belong to components (not Vuex, for example) and they are kept as simple as possible, which is what we are doing here. :)

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