-
Notifications
You must be signed in to change notification settings - Fork 234
Styles
In this section of the tutorial we'll discuss what's involved in structuring styles...
Let's install Sass (for working with Vendors) and Stylus (for cleaner syntax in our own styles) and their respective loaders for Webpack:
$ npm install sass-loader node-sass stylus stylus-loader --save-dev
If you look in your .vscode/extensions.json
file we already added the extension as a recommended extension for this project. Make sure you've installed that extension for VSCode to get stylus syntax highlighting.
It's not well documented but you can load your Stylus variables/functions/mixins to all Vue components easily. (Because we aren't loading any actual styles here, we won't have any files unnecessarily exported in our final CSS multiple times). Update the following stylus
and styl
properties in the return
statement here:
build/utils.js
exports.cssLoaders = function (options) {
// ...
return {
// ...
stylus: generateLoaders('stylus', { import: ['~src/styles/stylus/utils/utils.styl'] }),
styl: generateLoaders('stylus', { import: ['~src/styles/stylus/utils/utils.styl'] })
}
}
Whenever you need to override some vendor styles (even if the vendor has them scoped in a component), you can just increase the specificity of your CSS by adding a document-level #app
to your styles. This is easily done with Stylus. You just need to add #app
to the top of the file.
Since we're using Stylus, we'll just have a few Sass files to handle vendor variables and imports. Sass (particularly SCSS) is very popular, so it's good to have a folder specifically for loading vendor packages who use this language. You can load them and override any variables here.
Here's a breakdown of the Sass directory structure found in src/styles/scss
:
File | Description |
---|---|
_variables.scss | Add overrides for vendor variables here. For new variables, use the Stylus folder instead. I've added some Buefy/Bulma variables already here. The underscore denotes that this file is a sub-module and would not be loaded directly at the project's entry point. |
_vendor.scss | Load up your vendor SCSS files here, in the order you want them. Here's where we load up Buefy/Bulma styles. |
Stylus is the syntax of the future. Even though this project depends on packages still using SCSS, we can go forward in our own work by using Stylus. Typically I will take the variables in _variables.scss
that are important to me and create equivalents in Stylus. This is a bit redundant but it does create a nice bounded context for you. If vendor Sass variables change, your Stylus files don't need immediate updating since they don't directly depend on the Sass variables. Whenever you cross a bridge to newer/improved technology there's usually extra crossover code involved.
Here's a breakdown of the Stylus directory structure found in src/styles/stylus
:
File | Description |
---|---|
utils/utils.styl | Entry point for the folder. Contains all files we need to load before any other styles are loaded (preprocessor utilities and variables). We'll also import this into every Vue component. |
utils/variables.styl | Variables (ie. theming). |
utils/functions.styl | Functions. |
utils/mixins.styl | Mixins. |
base/base.styl | Entry point for the folder. Contains all the files for normalizing across browsers, generic HTML tag styling, resets, etc. |
base/generic.styl | Any rules you want to set for generic HTML tags. Since we're using Bulma in this project, this is mostly taken care of here. |
base/helpers.styl | Put helper classes here. Add anything you want to add that Bulma doesn't offer. Remember to prefix our own helpers so we can know which is which throughout our app. |
base/reset.styl | Browser normalization here. Add anything you want to build on top of Bulmas reset. |
base/transitions.styl | Just a place you can put all your transitions. |
components/components.styl | Entry point for the folder. Contains all files we need for CSS-only components (Vue component styles are placed directly into the .vue files). A card or panel might be an example of a component. |
elements/elements.styl | Entry point for the folder. Contains all the files we need for CSS-only elements. A link or a button might be an example of an element. |
vendor/vendor.styl | Entry point file that contains all the style imports from vendor packages. |
Collect all your Stylus files and arranges them in the correct loading order you want.
(For example, variables would be loaded first):
src/styles/stylus/main.styl
@import 'utils'
@import 'vendor'
@import 'base'
@import 'elements'
@import 'components'
Finally, bring these files into your main project entry point:
require('./styles/scss/main.scss')
require('./styles/stylus/main.styl')
Read up on BEM. Or just read this and this.
This is BEM
CSS
/* Block component */
.btn {}
/* Element that depends upon the block */
.btn__price {}
/* Modifier that changes the style of the block */
.btn--orange {}
.btn--big {}
HTML
<a class="btn btn--big btn--orange" href="http://css-tricks.com">
<span class="btn__price">$9.99</span>
<span class="btn__text">Subscribe</span>
</a>
Stylus
// The component (with project namespace "my-").
.my-list
padding: 40px 0
background-color: white
color: black
// A list variation. A dark list.
&--dark
background-color: black
color: white
// A list element.
&__item
text-align: center
height: 50px
// A list item variation.
&--nav
cursor: pointer
// A list item variation, but one that is from a general set
// typically used by Javascript (ie. active, disabled, etc.).
// Demonstrates that BEM is a guide, not law.
.my-active
cursor: auto
background-color: grey
// Another list element (even though it's underneath .my-list__item)
&--avatar
border-radius: 50%
Pug
// Pug template
#app
.my-list.my-list--dark
.my-list__item.my-list--nav.my-active Home
.my-list__item.my-list--nav About
.my-list__item.my-list--nav Contact Us
img.my-list__avatar(src='/images/phone.png')
CSS
.nav .nav__list-item .btn--orange {
background-color: green;
}
Take Bulma as an example, where nothing depends on the HTML element tag. This doesn't mean you can't or shouldn't use HTML elements, but frees you from that restriction. It's perfectly fine to use elements in your template or even for selectors underneath scoping BEM classes (and sometimes you have to do to access vendor components):
Stylus
.navbar
&__hamburger
float: right
i
font-size: 32px
To avoid collisions/confusion from vendor styles, your own global styles, or parent/children styles (or even other BEM components, see /examples/login/login-page
and /features/login/login-page
) in your Vue components, use BEM naming along with Vue-loader's scoped attribute: < style scoped >
. If you use BEM in both Vue components and also CSS-only components/elements, then you have full project consistency!
Without a naming methodology like BEM, you're more likely to name stuff in your Vue file that could collide with vendor CSS rules (or even your own global rules). Imagine a
.btn
in your Vue file's template. Who owns it? Maybe you prefix it, so you have.my-btn
so you know it's yours. But is that one of your global generic button styles, or one specific to this component? So you rename it.my-foo-btn
. Hmmm. Look familiar (cough...BEM...cough)?